| 1 | /* |
| 2 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) |
| 3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) |
| 4 | * Copyright (C) 2003-2017 Apple Inc. All rights reserved. |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Library General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Library General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Library General Public License |
| 17 | * along with this library; see the file COPYING.LIB. If not, write to |
| 18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 19 | * Boston, MA 02110-1301, USA. |
| 20 | * |
| 21 | */ |
| 22 | |
| 23 | #pragma once |
| 24 | |
| 25 | #include "CallData.h" |
| 26 | #include "CellState.h" |
| 27 | #include "ConstructData.h" |
| 28 | #include "EnumerationMode.h" |
| 29 | #include "Heap.h" |
| 30 | #include "HeapCell.h" |
| 31 | #include "IndexingType.h" |
| 32 | #include "JSLock.h" |
| 33 | #include "JSTypeInfo.h" |
| 34 | #include "SlotVisitor.h" |
| 35 | #include "SubspaceAccess.h" |
| 36 | #include "TypedArrayType.h" |
| 37 | #include "WriteBarrier.h" |
| 38 | |
| 39 | namespace JSC { |
| 40 | |
| 41 | class CompleteSubspace; |
| 42 | class CopyVisitor; |
| 43 | class GCDeferralContext; |
| 44 | class ExecState; |
| 45 | class Identifier; |
| 46 | class JSArrayBufferView; |
| 47 | class JSDestructibleObject; |
| 48 | class JSGlobalObject; |
| 49 | class ; |
| 50 | class PropertyDescriptor; |
| 51 | class PropertyName; |
| 52 | class PropertyNameArray; |
| 53 | class Structure; |
| 54 | class JSCellLock; |
| 55 | |
| 56 | enum class GCDeferralContextArgPresense { |
| 57 | HasArg, |
| 58 | DoesNotHaveArg |
| 59 | }; |
| 60 | |
| 61 | template<typename T> void* allocateCell(Heap&, size_t = sizeof(T)); |
| 62 | template<typename T> void* tryAllocateCell(Heap&, size_t = sizeof(T)); |
| 63 | template<typename T> void* allocateCell(Heap&, GCDeferralContext*, size_t = sizeof(T)); |
| 64 | template<typename T> void* tryAllocateCell(Heap&, GCDeferralContext*, size_t = sizeof(T)); |
| 65 | |
| 66 | #define DECLARE_EXPORT_INFO \ |
| 67 | protected: \ |
| 68 | static JS_EXPORT_PRIVATE const ::JSC::ClassInfo s_info; \ |
| 69 | public: \ |
| 70 | static constexpr const ::JSC::ClassInfo* info() { return &s_info; } |
| 71 | |
| 72 | #define DECLARE_INFO \ |
| 73 | protected: \ |
| 74 | static const ::JSC::ClassInfo s_info; \ |
| 75 | public: \ |
| 76 | static constexpr const ::JSC::ClassInfo* info() { return &s_info; } |
| 77 | |
| 78 | class JSCell : public HeapCell { |
| 79 | friend class JSValue; |
| 80 | friend class MarkedBlock; |
| 81 | template<typename T> |
| 82 | friend void* tryAllocateCellHelper(Heap&, size_t, GCDeferralContext*, AllocationFailureMode); |
| 83 | |
| 84 | public: |
| 85 | static const unsigned StructureFlags = 0; |
| 86 | |
| 87 | static const bool needsDestruction = false; |
| 88 | |
| 89 | // Don't call this directly. Call JSC::subspaceFor<Type>(vm) instead. |
| 90 | // FIXME: Refer to Subspace by reference. |
| 91 | // https://bugs.webkit.org/show_bug.cgi?id=166988 |
| 92 | template<typename CellType, SubspaceAccess> |
| 93 | static CompleteSubspace* subspaceFor(VM&); |
| 94 | |
| 95 | static JSCell* seenMultipleCalleeObjects() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); } |
| 96 | |
| 97 | enum CreatingEarlyCellTag { CreatingEarlyCell }; |
| 98 | JSCell(CreatingEarlyCellTag); |
| 99 | |
| 100 | protected: |
| 101 | JSCell(VM&, Structure*); |
| 102 | JS_EXPORT_PRIVATE static void destroy(JSCell*); |
| 103 | |
| 104 | public: |
| 105 | // Querying the type. |
| 106 | bool isString() const; |
| 107 | bool isBigInt() const; |
| 108 | bool isSymbol() const; |
| 109 | bool isObject() const; |
| 110 | bool isGetterSetter() const; |
| 111 | bool isCustomGetterSetter() const; |
| 112 | bool isProxy() const; |
| 113 | bool isFunction(VM&); |
| 114 | bool isCallable(VM&, CallType&, CallData&); |
| 115 | bool isConstructor(VM&); |
| 116 | bool isConstructor(VM&, ConstructType&, ConstructData&); |
| 117 | bool inherits(VM&, const ClassInfo*) const; |
| 118 | template<typename Target> bool inherits(VM&) const; |
| 119 | bool isAPIValueWrapper() const; |
| 120 | |
| 121 | // Each cell has a built-in lock. Currently it's simply available for use if you need it. It's |
| 122 | // a full-blown WTF::Lock. Note that this lock is currently used in JSArray and that lock's |
| 123 | // ordering with the Structure lock is that the Structure lock must be acquired first. |
| 124 | |
| 125 | // We use this abstraction to make it easier to grep for places where we lock cells. |
| 126 | // to lock a cell you can just do: |
| 127 | // auto locker = holdLock(cell->cellLocker()); |
| 128 | JSCellLock& cellLock() { return *reinterpret_cast<JSCellLock*>(this); } |
| 129 | |
| 130 | JSType type() const; |
| 131 | IndexingType indexingTypeAndMisc() const; |
| 132 | IndexingType indexingMode() const; |
| 133 | IndexingType indexingType() const; |
| 134 | StructureID structureID() const { return m_structureID; } |
| 135 | Structure* structure() const; |
| 136 | Structure* structure(VM&) const; |
| 137 | void setStructure(VM&, Structure*); |
| 138 | void setStructureIDDirectly(StructureID id) { m_structureID = id; } |
| 139 | void clearStructure() { m_structureID = 0; } |
| 140 | |
| 141 | TypeInfo::InlineTypeFlags inlineTypeFlags() const { return m_flags; } |
| 142 | |
| 143 | const char* className(VM&) const; |
| 144 | |
| 145 | // Extracting the value. |
| 146 | JS_EXPORT_PRIVATE bool getString(ExecState*, String&) const; |
| 147 | JS_EXPORT_PRIVATE String getString(ExecState*) const; // null string if not a string |
| 148 | JS_EXPORT_PRIVATE JSObject* getObject(); // NULL if not an object |
| 149 | const JSObject* getObject() const; // NULL if not an object |
| 150 | |
| 151 | // Returns information about how to call/construct this cell as a function/constructor. May tell |
| 152 | // you that the cell is not callable or constructor (default is that it's not either). If it |
| 153 | // says that the function is callable, and the OverridesGetCallData type flag is set, and |
| 154 | // this is an object, then typeof will return "function" instead of "object". These methods |
| 155 | // cannot change their minds and must be thread-safe. They are sometimes called from compiler |
| 156 | // threads. |
| 157 | JS_EXPORT_PRIVATE static CallType getCallData(JSCell*, CallData&); |
| 158 | JS_EXPORT_PRIVATE static ConstructType getConstructData(JSCell*, ConstructData&); |
| 159 | |
| 160 | // Basic conversions. |
| 161 | JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; |
| 162 | bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; |
| 163 | bool toBoolean(ExecState*) const; |
| 164 | TriState pureToBoolean() const; |
| 165 | JS_EXPORT_PRIVATE double toNumber(ExecState*) const; |
| 166 | JSObject* toObject(ExecState*, JSGlobalObject*) const; |
| 167 | |
| 168 | void dump(PrintStream&) const; |
| 169 | JS_EXPORT_PRIVATE static void dumpToStream(const JSCell*, PrintStream&); |
| 170 | |
| 171 | size_t estimatedSizeInBytes(VM&) const; |
| 172 | JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*, VM&); |
| 173 | |
| 174 | static void visitChildren(JSCell*, SlotVisitor&); |
| 175 | static void visitOutputConstraints(JSCell*, SlotVisitor&); |
| 176 | |
| 177 | JS_EXPORT_PRIVATE static void heapSnapshot(JSCell*, HeapSnapshotBuilder&); |
| 178 | |
| 179 | // Object operations, with the toObject operation included. |
| 180 | const ClassInfo* classInfo(VM&) const; |
| 181 | const MethodTable* methodTable(VM&) const; |
| 182 | static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); |
| 183 | static bool putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); |
| 184 | bool putInline(ExecState*, PropertyName, JSValue, PutPropertySlot&); |
| 185 | |
| 186 | static bool deleteProperty(JSCell*, ExecState*, PropertyName); |
| 187 | static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); |
| 188 | |
| 189 | static JSValue toThis(JSCell*, ExecState*, ECMAMode); |
| 190 | |
| 191 | static bool canUseFastGetOwnProperty(const Structure&); |
| 192 | JSValue fastGetOwnProperty(VM&, Structure&, PropertyName); |
| 193 | |
| 194 | // The recommended idiom for using cellState() is to switch on it or perform an == comparison on it |
| 195 | // directly. We deliberately avoid helpers for this, because we want transparency about how the various |
| 196 | // CellState values influences our various algorithms. |
| 197 | CellState cellState() const { return m_cellState; } |
| 198 | |
| 199 | void setCellState(CellState data) const { const_cast<JSCell*>(this)->m_cellState = data; } |
| 200 | |
| 201 | bool atomicCompareExchangeCellStateWeakRelaxed(CellState oldState, CellState newState) |
| 202 | { |
| 203 | return WTF::atomicCompareExchangeWeakRelaxed(&m_cellState, oldState, newState); |
| 204 | } |
| 205 | |
| 206 | CellState atomicCompareExchangeCellStateStrong(CellState oldState, CellState newState) |
| 207 | { |
| 208 | return WTF::atomicCompareExchangeStrong(&m_cellState, oldState, newState); |
| 209 | } |
| 210 | |
| 211 | static ptrdiff_t structureIDOffset() |
| 212 | { |
| 213 | return OBJECT_OFFSETOF(JSCell, m_structureID); |
| 214 | } |
| 215 | |
| 216 | static ptrdiff_t typeInfoFlagsOffset() |
| 217 | { |
| 218 | return OBJECT_OFFSETOF(JSCell, m_flags); |
| 219 | } |
| 220 | |
| 221 | static ptrdiff_t typeInfoTypeOffset() |
| 222 | { |
| 223 | return OBJECT_OFFSETOF(JSCell, m_type); |
| 224 | } |
| 225 | |
| 226 | // DO NOT store to this field. Always use a CAS loop, since some bits are flipped using CAS |
| 227 | // from other threads due to the internal lock. One exception: you don't need the CAS if the |
| 228 | // object has not escaped yet. |
| 229 | static ptrdiff_t indexingTypeAndMiscOffset() |
| 230 | { |
| 231 | return OBJECT_OFFSETOF(JSCell, m_indexingTypeAndMisc); |
| 232 | } |
| 233 | |
| 234 | static ptrdiff_t cellStateOffset() |
| 235 | { |
| 236 | return OBJECT_OFFSETOF(JSCell, m_cellState); |
| 237 | } |
| 238 | |
| 239 | static const TypedArrayType TypedArrayStorageType = NotTypedArray; |
| 240 | |
| 241 | void setPerCellBit(bool); |
| 242 | bool perCellBit() const; |
| 243 | protected: |
| 244 | |
| 245 | void finishCreation(VM&); |
| 246 | void finishCreation(VM&, Structure*, CreatingEarlyCellTag); |
| 247 | |
| 248 | // Dummy implementations of override-able static functions for classes to put in their MethodTable |
| 249 | static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); |
| 250 | static NO_RETURN_DUE_TO_CRASH void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
| 251 | static NO_RETURN_DUE_TO_CRASH void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
| 252 | static NO_RETURN_DUE_TO_CRASH void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
| 253 | |
| 254 | static uint32_t getEnumerableLength(ExecState*, JSObject*); |
| 255 | static NO_RETURN_DUE_TO_CRASH void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
| 256 | static NO_RETURN_DUE_TO_CRASH void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); |
| 257 | static NO_RETURN_DUE_TO_CRASH bool preventExtensions(JSObject*, ExecState*); |
| 258 | static NO_RETURN_DUE_TO_CRASH bool isExtensible(JSObject*, ExecState*); |
| 259 | static NO_RETURN_DUE_TO_CRASH bool setPrototype(JSObject*, ExecState*, JSValue, bool); |
| 260 | static NO_RETURN_DUE_TO_CRASH JSValue getPrototype(JSObject*, ExecState*); |
| 261 | |
| 262 | static String className(const JSObject*, VM&); |
| 263 | static String toStringName(const JSObject*, ExecState*); |
| 264 | JS_EXPORT_PRIVATE static bool customHasInstance(JSObject*, ExecState*, JSValue); |
| 265 | static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); |
| 266 | static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); |
| 267 | static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); |
| 268 | |
| 269 | private: |
| 270 | friend class LLIntOffsetsExtractor; |
| 271 | friend class JSCellLock; |
| 272 | |
| 273 | JS_EXPORT_PRIVATE JSObject* toObjectSlow(ExecState*, JSGlobalObject*) const; |
| 274 | |
| 275 | StructureID m_structureID; |
| 276 | IndexingType m_indexingTypeAndMisc; // DO NOT store to this field. Always CAS. |
| 277 | JSType m_type; |
| 278 | TypeInfo::InlineTypeFlags m_flags; |
| 279 | CellState m_cellState; |
| 280 | }; |
| 281 | |
| 282 | class JSCellLock : public JSCell { |
| 283 | public: |
| 284 | void lock(); |
| 285 | bool tryLock(); |
| 286 | void unlock(); |
| 287 | bool isLocked() const; |
| 288 | private: |
| 289 | JS_EXPORT_PRIVATE void lockSlow(); |
| 290 | JS_EXPORT_PRIVATE void unlockSlow(); |
| 291 | }; |
| 292 | |
| 293 | // FIXME: Refer to Subspace by reference. |
| 294 | // https://bugs.webkit.org/show_bug.cgi?id=166988 |
| 295 | template<typename Type> |
| 296 | inline auto subspaceFor(VM& vm) |
| 297 | { |
| 298 | return Type::template subspaceFor<Type, SubspaceAccess::OnMainThread>(vm); |
| 299 | } |
| 300 | |
| 301 | template<typename Type> |
| 302 | inline auto subspaceForConcurrently(VM& vm) |
| 303 | { |
| 304 | return Type::template subspaceFor<Type, SubspaceAccess::Concurrently>(vm); |
| 305 | } |
| 306 | |
| 307 | } // namespace JSC |
| 308 | |