We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / runtime / WriteBarrier.h
1 /*
2  * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include "GCAssertions.h"
29 #include "HandleTypes.h"
30 #include "JSCPoison.h"
31 #include <type_traits>
32 #include <wtf/DumbPtrTraits.h>
33 #include <wtf/DumbValueTraits.h>
34 #include <wtf/Poisoned.h>
35
36 namespace JSC {
37
38 namespace DFG {
39 class DesiredWriteBarrier;
40 }
41
42 class JSCell;
43 class VM;
44 class JSGlobalObject;
45
46 template<class T>
47 using WriteBarrierTraitsSelect = typename std::conditional<std::is_same<T, Unknown>::value,
48     DumbValueTraits<T>, DumbPtrTraits<T>
49 >::type;
50
51 template<class T, typename Traits = WriteBarrierTraitsSelect<T>> class WriteBarrierBase;
52 template<> class WriteBarrierBase<JSValue>;
53
54 JS_EXPORT_PRIVATE void slowValidateCell(JSCell*);
55 JS_EXPORT_PRIVATE void slowValidateCell(JSGlobalObject*);
56     
57 #if ENABLE(GC_VALIDATION)
58 template<class T> inline void validateCell(T cell)
59 {
60     ASSERT_GC_OBJECT_INHERITS(cell, std::remove_pointer<T>::type::info());
61 }
62
63 template<> inline void validateCell<JSCell*>(JSCell* cell)
64 {
65     slowValidateCell(cell);
66 }
67
68 template<> inline void validateCell<JSGlobalObject*>(JSGlobalObject* globalObject)
69 {
70     slowValidateCell(globalObject);
71 }
72 #else
73 template<class T> inline void validateCell(T)
74 {
75 }
76 #endif
77
78 // We have a separate base class with no constructors for use in Unions.
79 template <typename T, typename Traits> class WriteBarrierBase {
80     using StorageType = typename Traits::StorageType;
81
82 public:
83     void set(VM&, const JSCell* owner, T* value);
84     
85     // This is meant to be used like operator=, but is called copyFrom instead, in
86     // order to kindly inform the C++ compiler that its advice is not appreciated.
87     void copyFrom(const WriteBarrierBase& other)
88     {
89         // FIXME add version with different Traits once needed.
90         Traits::exchange(m_cell, other.m_cell);
91     }
92
93     void setMayBeNull(VM&, const JSCell* owner, T* value);
94
95     // Should only be used by JSCell during early initialisation
96     // when some basic types aren't yet completely instantiated
97     void setEarlyValue(VM&, const JSCell* owner, T* value);
98     
99     T* get() const
100     {
101         // Copy m_cell to a local to avoid multiple-read issues. (See <http://webkit.org/b/110854>)
102         StorageType cell = m_cell;
103         if (cell)
104             validateCell(reinterpret_cast<JSCell*>(static_cast<void*>(Traits::unwrap(cell))));
105         return Traits::unwrap(cell);
106     }
107
108     T* operator*() const
109     {
110         StorageType cell = m_cell;
111         ASSERT(cell);
112         auto unwrapped = Traits::unwrap(cell);
113         validateCell<T>(unwrapped);
114         return Traits::unwrap(unwrapped);
115     }
116
117     T* operator->() const
118     {
119         StorageType cell = m_cell;
120         ASSERT(cell);
121         auto unwrapped = Traits::unwrap(cell);
122         validateCell(unwrapped);
123         return unwrapped;
124     }
125
126     void clear() { Traits::exchange(m_cell, nullptr); }
127
128     // Slot cannot be used when pointers aren't stored as-is.
129     template<typename BarrierT, typename BarrierTraits, std::enable_if_t<std::is_same<BarrierTraits, DumbPtrTraits<BarrierT>>::value, void*> = nullptr>
130     struct SlotHelper {
131         static BarrierT** reinterpret(typename BarrierTraits::StorageType* cell) { return reinterpret_cast<T**>(cell); }
132     };
133
134     T** slot()
135     {
136         return SlotHelper<T, Traits>::reinterpret(&m_cell);
137     }
138     
139     explicit operator bool() const { return !!m_cell; }
140     
141     bool operator!() const { return !m_cell; }
142
143     void setWithoutWriteBarrier(T* value)
144     {
145 #if ENABLE(WRITE_BARRIER_PROFILING)
146         WriteBarrierCounters::usesWithoutBarrierFromCpp.count();
147 #endif
148         Traits::exchange(this->m_cell, value);
149     }
150
151     T* unvalidatedGet() const { return Traits::unwrap(m_cell); }
152
153 private:
154     StorageType m_cell;
155 };
156
157 template <> class WriteBarrierBase<Unknown, DumbValueTraits<Unknown>> {
158 public:
159     void set(VM&, const JSCell* owner, JSValue);
160     void setWithoutWriteBarrier(JSValue value)
161     {
162         m_value = JSValue::encode(value);
163     }
164
165     JSValue get() const
166     {
167         return JSValue::decode(m_value);
168     }
169     void clear() { m_value = JSValue::encode(JSValue()); }
170     void setUndefined() { m_value = JSValue::encode(jsUndefined()); }
171     void setStartingValue(JSValue value) { m_value = JSValue::encode(value); }
172     bool isNumber() const { return get().isNumber(); }
173     bool isInt32() const { return get().isInt32(); }
174     bool isObject() const { return get().isObject(); }
175     bool isNull() const { return get().isNull(); }
176     bool isGetterSetter() const { return get().isGetterSetter(); }
177     bool isCustomGetterSetter() const { return get().isCustomGetterSetter(); }
178     
179     JSValue* slot() const
180     { 
181         return bitwise_cast<JSValue*>(&m_value);
182     }
183     
184     int32_t* tagPointer() { return &bitwise_cast<EncodedValueDescriptor*>(&m_value)->asBits.tag; }
185     int32_t* payloadPointer() { return &bitwise_cast<EncodedValueDescriptor*>(&m_value)->asBits.payload; }
186     
187     explicit operator bool() const { return !!get(); }
188     bool operator!() const { return !get(); } 
189     
190 private:
191     EncodedJSValue m_value;
192 };
193
194 template <typename T, typename Traits = WriteBarrierTraitsSelect<T>>
195 class WriteBarrier : public WriteBarrierBase<T, Traits> {
196     WTF_MAKE_FAST_ALLOCATED;
197 public:
198     WriteBarrier()
199     {
200         this->setWithoutWriteBarrier(0);
201     }
202
203     WriteBarrier(VM& vm, const JSCell* owner, T* value)
204     {
205         this->set(vm, owner, value);
206     }
207
208     WriteBarrier(DFG::DesiredWriteBarrier&, T* value)
209     {
210         ASSERT(isCompilationThread());
211         this->setWithoutWriteBarrier(value);
212     }
213
214     enum MayBeNullTag { MayBeNull };
215     WriteBarrier(VM& vm, const JSCell* owner, T* value, MayBeNullTag)
216     {
217         this->setMayBeNull(vm, owner, value);
218     }
219 };
220
221 enum UndefinedWriteBarrierTagType { UndefinedWriteBarrierTag };
222 template <>
223 class WriteBarrier<Unknown, DumbValueTraits<Unknown>> : public WriteBarrierBase<Unknown, DumbValueTraits<Unknown>> {
224     WTF_MAKE_FAST_ALLOCATED;
225 public:
226     WriteBarrier()
227     {
228         this->setWithoutWriteBarrier(JSValue());
229     }
230     WriteBarrier(UndefinedWriteBarrierTagType)
231     {
232         this->setWithoutWriteBarrier(jsUndefined());
233     }
234
235     WriteBarrier(VM& vm, const JSCell* owner, JSValue value)
236     {
237         this->set(vm, owner, value);
238     }
239
240     WriteBarrier(DFG::DesiredWriteBarrier&, JSValue value)
241     {
242         ASSERT(isCompilationThread());
243         this->setWithoutWriteBarrier(value);
244     }
245 };
246
247 template <typename U, typename V, typename TraitsU, typename TraitsV>
248 inline bool operator==(const WriteBarrierBase<U, TraitsU>& lhs, const WriteBarrierBase<V, TraitsV>& rhs)
249 {
250     return lhs.get() == rhs.get();
251 }
252
253 template<typename Poison, class T>
254 using PoisonedWriteBarrierTraitsSelect = typename std::conditional<std::is_same<T, Unknown>::value,
255     WTF::PoisonedValueTraits<Poison, T>, WTF::PoisonedPtrTraits<Poison, T>
256 >::type;
257
258 template <typename Poison, typename T>
259 using PoisonedWriteBarrier = WriteBarrier<T, PoisonedWriteBarrierTraitsSelect<Poison, T>>;
260
261 } // namespace JSC