2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2018 Apple Inc. All rights reserved.
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.
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.
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.
25 #include "ArrayConventions.h"
26 #include "ArrayStorage.h"
27 #include "Butterfly.h"
29 #include "CagedBarrierPtr.h"
30 #include "CallFrame.h"
31 #include "ClassInfo.h"
32 #include "CustomGetterSetter.h"
33 #include "DOMAttributeGetterSetter.h"
35 #include "IndexingHeaderInlines.h"
37 #include "ObjectInitializationScope.h"
38 #include "PropertySlot.h"
39 #include "PropertyStorage.h"
40 #include "PutDirectIndexMode.h"
41 #include "PutPropertySlot.h"
42 #include "Structure.h"
43 #include "StructureTransitionTable.h"
46 #include "SparseArrayValueMap.h"
47 #include <wtf/StdLibExtras.h>
54 inline JSCell* getJSFunction(JSValue value)
56 if (value.isCell() && (value.asCell()->type() == JSFunctionType))
57 return value.asCell();
62 class InternalFunction;
64 class LLIntOffsetsExtractor;
66 class PropertyDescriptor;
67 class PropertyNameArray;
71 struct HashTableValue;
73 JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, ThrowScope&, const String&);
74 extern JS_EXPORT_PRIVATE const ASCIILiteral NonExtensibleObjectPropertyDefineError;
75 extern JS_EXPORT_PRIVATE const ASCIILiteral ReadonlyPropertyWriteError;
76 extern JS_EXPORT_PRIVATE const ASCIILiteral ReadonlyPropertyChangeError;
77 extern JS_EXPORT_PRIVATE const ASCIILiteral UnableToDeletePropertyError;
78 extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeAccessMechanismError;
79 extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeConfigurabilityError;
80 extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeEnumerabilityError;
81 extern JS_EXPORT_PRIVATE const ASCIILiteral UnconfigurablePropertyChangeWritabilityError;
83 COMPILE_ASSERT(PropertyAttribute::None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
84 COMPILE_ASSERT(PropertyAttribute::ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
85 COMPILE_ASSERT(PropertyAttribute::DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
86 COMPILE_ASSERT(PropertyAttribute::DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
87 COMPILE_ASSERT(PropertyAttribute::Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);
91 class JSObject : public JSCell {
92 friend class BatchedTransitionOptimizer;
95 friend class JSFinalObject;
96 friend class MarkedBlock;
97 JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(VM&, const HashTableValue*, JSObject*, PropertyName, PropertySlot&);
101 PutModeDefineOwnProperty,
108 static CompleteSubspace* subspaceFor(VM& vm)
110 return &vm.cellJSValueOOBSpace;
113 // This is a super dangerous method for JITs. Sometimes the JITs will want to create either a
114 // JSFinalObject or a JSArray. This is the method that will do that.
115 static JSObject* createRawObject(ExecState* exec, Structure* structure, Butterfly* = nullptr);
117 JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*, VM&);
118 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
119 JS_EXPORT_PRIVATE static void heapSnapshot(JSCell*, HeapSnapshotBuilder&);
121 JS_EXPORT_PRIVATE static String className(const JSObject*, VM&);
122 JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*);
124 // This function is what Object.prototype.toString() will use to get the name of
125 // an object when using Symbol.toStringTag fails. For the most part there is no
126 // difference between this and className(). The main use case is for new JS language
127 // objects to set the default tag to "Object".
128 JS_EXPORT_PRIVATE static String toStringName(const JSObject*, ExecState*);
130 // This is the fully virtual [[GetPrototypeOf]] internal function defined
131 // in the ECMAScript 6 specification. Use this when doing a [[GetPrototypeOf]]
132 // operation as dictated in the specification.
133 JSValue getPrototype(VM&, ExecState*);
134 JS_EXPORT_PRIVATE static JSValue getPrototype(JSObject*, ExecState*);
135 // This gets the prototype directly off of the structure. This does not do
136 // dynamic dispatch on the getPrototype method table method. It is not valid
137 // to use this when performing a [[GetPrototypeOf]] operation in the specification.
138 // It is valid to use though when you know that you want to directly get it
139 // without consulting the method table. This is akin to getting the [[Prototype]]
140 // internal field directly as described in the specification.
141 JSValue getPrototypeDirect(VM&) const;
143 // This sets the prototype without checking for cycles and without
144 // doing dynamic dispatch on [[SetPrototypeOf]] operation in the specification.
145 // It is not valid to use this when performing a [[SetPrototypeOf]] operation in
146 // the specification. It is valid to use though when you know that you want to directly
147 // set it without consulting the method table and when you definitely won't
148 // introduce a cycle in the prototype chain. This is akin to setting the
149 // [[Prototype]] internal field directly as described in the specification.
150 JS_EXPORT_PRIVATE void setPrototypeDirect(VM&, JSValue prototype);
152 // This is OrdinarySetPrototypeOf in the specification. Section 9.1.2.1
153 // https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof
154 JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet);
156 // This is the fully virtual [[SetPrototypeOf]] internal function defined
157 // in the ECMAScript 6 specification. Use this when doing a [[SetPrototypeOf]]
158 // operation as dictated in the specification.
159 bool setPrototype(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet = false);
160 JS_EXPORT_PRIVATE static bool setPrototype(JSObject*, ExecState*, JSValue prototype, bool shouldThrowIfCantSet);
162 inline bool mayInterceptIndexedAccesses(VM&);
164 JSValue get(ExecState*, PropertyName) const;
165 JSValue get(ExecState*, unsigned propertyName) const;
167 template<bool checkNullStructure = false>
168 bool getPropertySlot(ExecState*, PropertyName, PropertySlot&);
169 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
170 template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, CallbackWhenNoException) const;
171 template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, PropertySlot&, CallbackWhenNoException) const;
173 static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
174 JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
175 bool getOwnPropertySlotInline(ExecState*, PropertyName, PropertySlot&);
177 // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot
178 // currently returns incorrect results for the DOM window (with non-own properties)
179 // being returned. Once this is fixed we should migrate code & remove this method.
180 JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
182 unsigned getArrayLength() const
184 if (!hasIndexedProperties(indexingType()))
186 return m_butterfly->publicLength();
189 unsigned getVectorLength()
191 if (!hasIndexedProperties(indexingType()))
193 return m_butterfly->vectorLength();
196 static bool putInlineForJSObject(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
198 JS_EXPORT_PRIVATE static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
199 // putByIndex assumes that the receiver is this JSCell object.
200 JS_EXPORT_PRIVATE static bool putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
202 // This performs the ECMAScript Set() operation.
203 ALWAYS_INLINE bool putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
205 if (canSetIndexQuickly(propertyName)) {
206 setIndexQuickly(exec->vm(), propertyName, value);
209 return methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow);
212 // This is similar to the putDirect* methods:
213 // - the prototype chain is not consulted
214 // - accessors are not called.
215 // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
216 // This method creates a property with attributes writable, enumerable and configurable all set to true if attributes is zero,
217 // otherwise, it creates a property with the provided attributes. Semantically, this is performing defineOwnProperty.
218 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
220 ASSERT(!value.isCustomGetterSetter());
221 auto canSetIndexQuicklyForPutDirect = [&] () -> bool {
222 switch (indexingMode()) {
223 case ALL_BLANK_INDEXING_TYPES:
224 case ALL_UNDECIDED_INDEXING_TYPES:
226 case ALL_WRITABLE_INT32_INDEXING_TYPES:
227 case ALL_WRITABLE_DOUBLE_INDEXING_TYPES:
228 case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
229 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
230 return propertyName < m_butterfly->vectorLength();
232 if (isCopyOnWrite(indexingMode()))
234 RELEASE_ASSERT_NOT_REACHED();
239 if (!attributes && canSetIndexQuicklyForPutDirect()) {
240 setIndexQuickly(exec->vm(), propertyName, value);
243 return putDirectIndexSlowOrBeyondVectorLength(exec, propertyName, value, attributes, mode);
245 // This is semantically equivalent to performing defineOwnProperty(propertyName, {configurable:true, writable:true, enumerable:true, value:value}).
246 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value)
248 return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect);
251 // A generally non-throwing version of putDirect and putDirectIndex.
252 // However, it's only guaranteed to not throw based on what the receiver is.
253 // For example, if the receiver is a ProxyObject, this is not guaranteed, since
254 // it may call into arbitrary JS code. It's the responsibility of the user of
255 // this API to ensure that the receiver object is a well known type if they
256 // want to ensure that this won't throw an exception.
257 JS_EXPORT_PRIVATE bool putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
259 bool hasIndexingHeader(VM& vm) const
261 return structure(vm)->hasIndexingHeader(this);
264 bool canGetIndexQuickly(unsigned i)
266 Butterfly* butterfly = this->butterfly();
267 switch (indexingType()) {
268 case ALL_BLANK_INDEXING_TYPES:
269 case ALL_UNDECIDED_INDEXING_TYPES:
271 case ALL_INT32_INDEXING_TYPES:
272 case ALL_CONTIGUOUS_INDEXING_TYPES:
273 return i < butterfly->vectorLength() && butterfly->contiguous().at(this, i);
274 case ALL_DOUBLE_INDEXING_TYPES: {
275 if (i >= butterfly->vectorLength())
277 double value = butterfly->contiguousDouble().at(this, i);
282 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
283 return i < butterfly->arrayStorage()->vectorLength() && butterfly->arrayStorage()->m_vector[i];
285 RELEASE_ASSERT_NOT_REACHED();
290 JSValue getIndexQuickly(unsigned i)
292 Butterfly* butterfly = this->butterfly();
293 switch (indexingType()) {
294 case ALL_INT32_INDEXING_TYPES:
295 return jsNumber(butterfly->contiguous().at(this, i).get().asInt32());
296 case ALL_CONTIGUOUS_INDEXING_TYPES:
297 return butterfly->contiguous().at(this, i).get();
298 case ALL_DOUBLE_INDEXING_TYPES:
299 return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble().at(this, i));
300 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
301 return butterfly->arrayStorage()->m_vector[i].get();
303 RELEASE_ASSERT_NOT_REACHED();
308 JSValue tryGetIndexQuickly(unsigned i) const
310 Butterfly* butterfly = const_cast<JSObject*>(this)->butterfly();
311 switch (indexingType()) {
312 case ALL_BLANK_INDEXING_TYPES:
313 case ALL_UNDECIDED_INDEXING_TYPES:
315 case ALL_INT32_INDEXING_TYPES:
316 if (i < butterfly->publicLength()) {
317 JSValue result = butterfly->contiguous().at(this, i).get();
318 ASSERT(result.isInt32() || !result);
322 case ALL_CONTIGUOUS_INDEXING_TYPES:
323 if (i < butterfly->publicLength())
324 return butterfly->contiguous().at(this, i).get();
326 case ALL_DOUBLE_INDEXING_TYPES: {
327 if (i >= butterfly->publicLength())
329 double result = butterfly->contiguousDouble().at(this, i);
330 if (result != result)
332 return JSValue(JSValue::EncodeAsDouble, result);
334 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
335 if (i < butterfly->arrayStorage()->vectorLength())
336 return butterfly->arrayStorage()->m_vector[i].get();
339 RELEASE_ASSERT_NOT_REACHED();
345 JSValue getDirectIndex(ExecState* exec, unsigned i)
347 if (JSValue result = tryGetIndexQuickly(i))
349 PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
350 if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot))
351 return slot.getValue(exec, i);
355 JSValue getIndex(ExecState* exec, unsigned i) const
357 if (JSValue result = tryGetIndexQuickly(i))
362 bool canSetIndexQuickly(unsigned i)
364 Butterfly* butterfly = this->butterfly();
365 switch (indexingMode()) {
366 case ALL_BLANK_INDEXING_TYPES:
367 case ALL_UNDECIDED_INDEXING_TYPES:
369 case ALL_WRITABLE_INT32_INDEXING_TYPES:
370 case ALL_WRITABLE_DOUBLE_INDEXING_TYPES:
371 case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
372 case NonArrayWithArrayStorage:
373 case ArrayWithArrayStorage:
374 return i < butterfly->vectorLength();
375 case NonArrayWithSlowPutArrayStorage:
376 case ArrayWithSlowPutArrayStorage:
377 return i < butterfly->arrayStorage()->vectorLength()
378 && !!butterfly->arrayStorage()->m_vector[i];
380 if (isCopyOnWrite(indexingMode()))
382 RELEASE_ASSERT_NOT_REACHED();
387 void setIndexQuickly(VM& vm, unsigned i, JSValue v)
389 Butterfly* butterfly = m_butterfly.get();
390 ASSERT(!isCopyOnWrite(indexingMode()));
391 switch (indexingType()) {
392 case ALL_INT32_INDEXING_TYPES: {
393 ASSERT(i < butterfly->vectorLength());
395 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
400 case ALL_CONTIGUOUS_INDEXING_TYPES: {
401 ASSERT(i < butterfly->vectorLength());
402 butterfly->contiguous().at(this, i).set(vm, this, v);
403 if (i >= butterfly->publicLength())
404 butterfly->setPublicLength(i + 1);
407 case ALL_DOUBLE_INDEXING_TYPES: {
408 ASSERT(i < butterfly->vectorLength());
410 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
413 double value = v.asNumber();
414 if (value != value) {
415 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
418 butterfly->contiguousDouble().at(this, i) = value;
419 if (i >= butterfly->publicLength())
420 butterfly->setPublicLength(i + 1);
423 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
424 ArrayStorage* storage = butterfly->arrayStorage();
425 WriteBarrier<Unknown>& x = storage->m_vector[i];
426 JSValue old = x.get();
429 ++storage->m_numValuesInVector;
430 if (i >= storage->length())
431 storage->setLength(i + 1);
436 RELEASE_ASSERT_NOT_REACHED();
440 void initializeIndex(ObjectInitializationScope& scope, unsigned i, JSValue v)
442 initializeIndex(scope, i, v, indexingType());
445 // NOTE: Clients of this method may call it more than once for any index, and this is supposed
447 ALWAYS_INLINE void initializeIndex(ObjectInitializationScope& scope, unsigned i, JSValue v, IndexingType indexingType)
450 Butterfly* butterfly = m_butterfly.get();
451 switch (indexingType) {
452 case ALL_UNDECIDED_INDEXING_TYPES: {
453 setIndexQuicklyToUndecided(vm, i, v);
456 case ALL_INT32_INDEXING_TYPES: {
457 ASSERT(i < butterfly->publicLength());
458 ASSERT(i < butterfly->vectorLength());
460 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
465 case ALL_CONTIGUOUS_INDEXING_TYPES: {
466 ASSERT(i < butterfly->publicLength());
467 ASSERT(i < butterfly->vectorLength());
468 butterfly->contiguous().at(this, i).set(vm, this, v);
471 case ALL_DOUBLE_INDEXING_TYPES: {
472 ASSERT(i < butterfly->publicLength());
473 ASSERT(i < butterfly->vectorLength());
475 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
478 double value = v.asNumber();
479 if (value != value) {
480 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
483 butterfly->contiguousDouble().at(this, i) = value;
486 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
487 ArrayStorage* storage = butterfly->arrayStorage();
488 ASSERT(i < storage->length());
489 ASSERT(i < storage->m_numValuesInVector);
490 storage->m_vector[i].set(vm, this, v);
494 RELEASE_ASSERT_NOT_REACHED();
498 void initializeIndexWithoutBarrier(ObjectInitializationScope& scope, unsigned i, JSValue v)
500 initializeIndexWithoutBarrier(scope, i, v, indexingType());
503 // This version of initializeIndex is for cases where you know that you will not need any
504 // barriers. This implies not having any data format conversions.
505 ALWAYS_INLINE void initializeIndexWithoutBarrier(ObjectInitializationScope&, unsigned i, JSValue v, IndexingType indexingType)
507 Butterfly* butterfly = m_butterfly.get();
508 switch (indexingType) {
509 case ALL_UNDECIDED_INDEXING_TYPES: {
510 RELEASE_ASSERT_NOT_REACHED();
513 case ALL_INT32_INDEXING_TYPES: {
514 ASSERT(i < butterfly->publicLength());
515 ASSERT(i < butterfly->vectorLength());
516 RELEASE_ASSERT(v.isInt32());
519 case ALL_CONTIGUOUS_INDEXING_TYPES: {
520 ASSERT(i < butterfly->publicLength());
521 ASSERT(i < butterfly->vectorLength());
522 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
525 case ALL_DOUBLE_INDEXING_TYPES: {
526 ASSERT(i < butterfly->publicLength());
527 ASSERT(i < butterfly->vectorLength());
528 RELEASE_ASSERT(v.isNumber());
529 double value = v.asNumber();
530 RELEASE_ASSERT(value == value);
531 butterfly->contiguousDouble().at(this, i) = value;
534 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
535 ArrayStorage* storage = butterfly->arrayStorage();
536 ASSERT(i < storage->length());
537 ASSERT(i < storage->m_numValuesInVector);
538 storage->m_vector[i].setWithoutWriteBarrier(v);
542 RELEASE_ASSERT_NOT_REACHED();
548 switch (indexingType()) {
549 case ALL_BLANK_INDEXING_TYPES:
550 case ALL_UNDECIDED_INDEXING_TYPES:
551 case ALL_INT32_INDEXING_TYPES:
552 case ALL_DOUBLE_INDEXING_TYPES:
553 case ALL_CONTIGUOUS_INDEXING_TYPES:
555 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
556 return !!m_butterfly->arrayStorage()->m_sparseMap;
558 RELEASE_ASSERT_NOT_REACHED();
563 bool inSparseIndexingMode()
565 switch (indexingType()) {
566 case ALL_BLANK_INDEXING_TYPES:
567 case ALL_UNDECIDED_INDEXING_TYPES:
568 case ALL_INT32_INDEXING_TYPES:
569 case ALL_DOUBLE_INDEXING_TYPES:
570 case ALL_CONTIGUOUS_INDEXING_TYPES:
572 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
573 return m_butterfly->arrayStorage()->inSparseMode();
575 RELEASE_ASSERT_NOT_REACHED();
580 void enterDictionaryIndexingMode(VM&);
582 // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
583 // - the prototype chain is not consulted
584 // - accessors are not called.
585 // - attributes will be respected (after the call the property will exist with the given attributes)
586 // - the property name is assumed to not be an index.
587 bool putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0);
588 bool putDirect(VM&, PropertyName, JSValue, PutPropertySlot&);
589 void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0);
590 bool putDirectNonIndexAccessor(VM&, PropertyName, GetterSetter*, unsigned attributes);
591 bool putDirectAccessor(ExecState*, PropertyName, GetterSetter*, unsigned attributes);
592 JS_EXPORT_PRIVATE bool putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes);
594 bool putGetter(ExecState*, PropertyName, JSValue, unsigned attributes);
595 bool putSetter(ExecState*, PropertyName, JSValue, unsigned attributes);
597 JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const;
598 JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
599 bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const;
600 bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const;
601 bool hasOwnProperty(ExecState*, PropertyName, PropertySlot&) const;
602 bool hasOwnProperty(ExecState*, PropertyName) const;
603 bool hasOwnProperty(ExecState*, unsigned) const;
605 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
606 JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
608 JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
609 JSValue ordinaryToPrimitive(ExecState*, PreferredPrimitiveType) const;
611 JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue);
612 JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue);
613 static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
615 JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
616 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
617 JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
619 JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*);
620 JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
621 JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
623 JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
624 bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
625 JS_EXPORT_PRIVATE double toNumber(ExecState*) const;
626 JS_EXPORT_PRIVATE JSString* toString(ExecState*) const;
628 JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode);
630 // This get function only looks at the property map.
631 JSValue getDirect(VM& vm, PropertyName propertyName) const
633 Structure* structure = this->structure(vm);
634 PropertyOffset offset = structure->get(vm, propertyName);
635 checkOffset(offset, structure->inlineCapacity());
636 return offset != invalidOffset ? getDirect(offset) : JSValue();
639 JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const
641 Structure* structure = this->structure(vm);
642 PropertyOffset offset = structure->get(vm, propertyName, attributes);
643 checkOffset(offset, structure->inlineCapacity());
644 return offset != invalidOffset ? getDirect(offset) : JSValue();
647 PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName)
649 Structure* structure = this->structure(vm);
650 PropertyOffset offset = structure->get(vm, propertyName);
651 checkOffset(offset, structure->inlineCapacity());
655 PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes)
657 Structure* structure = this->structure(vm);
658 PropertyOffset offset = structure->get(vm, propertyName, attributes);
659 checkOffset(offset, structure->inlineCapacity());
663 bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
664 ConstPropertyStorage inlineStorageUnsafe() const
666 return bitwise_cast<ConstPropertyStorage>(this + 1);
668 PropertyStorage inlineStorageUnsafe()
670 return bitwise_cast<PropertyStorage>(this + 1);
672 ConstPropertyStorage inlineStorage() const
674 ASSERT(hasInlineStorage());
675 return inlineStorageUnsafe();
677 PropertyStorage inlineStorage()
679 ASSERT(hasInlineStorage());
680 return inlineStorageUnsafe();
683 const Butterfly* butterfly() const { return m_butterfly.get(); }
684 Butterfly* butterfly() { return m_butterfly.get(); }
686 ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
687 PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
689 ALWAYS_INLINE const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
691 if (isInlineOffset(offset))
692 return &inlineStorage()[offsetInInlineStorage(offset)];
693 return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
696 ALWAYS_INLINE WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset)
698 if (isInlineOffset(offset))
699 return &inlineStorage()[offsetInInlineStorage(offset)];
700 return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
703 void transitionTo(VM&, Structure*);
705 bool hasCustomProperties(VM& vm) { return structure(vm)->didTransition(); }
706 bool hasGetterSetterProperties(VM& vm) { return structure(vm)->hasGetterSetterProperties(); }
707 bool hasCustomGetterSetterProperties(VM& vm) { return structure(vm)->hasCustomGetterSetterProperties(); }
709 // putOwnDataProperty has 'put' like semantics, however this method:
710 // - assumes the object contains no own getter/setter properties.
711 // - provides no special handling for __proto__
712 // - does not walk the prototype chain (to check for accessors or non-writable properties).
713 // This is used by JSLexicalEnvironment.
714 bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&);
715 bool putOwnDataPropertyMayBeIndex(ExecState*, PropertyName, JSValue, PutPropertySlot&);
717 // Fast access to known property offsets.
718 ALWAYS_INLINE JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
719 JSValue getDirectConcurrently(Structure* expectedStructure, PropertyOffset) const;
720 void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
721 void putDirectWithoutBarrier(PropertyOffset offset, JSValue value) { locationForOffset(offset)->setWithoutWriteBarrier(value); }
722 void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
724 JS_EXPORT_PRIVATE bool putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes);
725 JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
726 JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, const DOMJIT::Signature*, unsigned attributes);
727 JS_EXPORT_PRIVATE void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
729 JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
730 JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
732 JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
734 bool isEnvironment() const;
735 bool isGlobalObject() const;
736 bool isJSLexicalEnvironment() const;
737 bool isGlobalLexicalEnvironment() const;
738 bool isStrictEvalActivation() const;
739 bool isWithScope() const;
741 bool isErrorInstance() const;
743 JS_EXPORT_PRIVATE void seal(VM&);
744 JS_EXPORT_PRIVATE void freeze(VM&);
745 JS_EXPORT_PRIVATE static bool preventExtensions(JSObject*, ExecState*);
746 JS_EXPORT_PRIVATE static bool isExtensible(JSObject*, ExecState*);
747 bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); }
748 bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); }
750 bool anyObjectInChainMayInterceptIndexedAccesses(VM&) const;
751 JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
752 bool needsSlowPutIndexing(VM&) const;
755 NonPropertyTransition suggestedArrayStorageTransition(VM&) const;
756 ALWAYS_INLINE bool isExtensibleImpl(VM& vm) { return isStructureExtensible(vm); }
758 // You should only call isStructureExtensible() when:
759 // - Performing this check in a way that isn't described in the specification
760 // as calling the virtual [[IsExtensible]] trap.
761 // - When you're guaranteed that object->methodTable(vm)->isExtensible isn't
763 ALWAYS_INLINE bool isStructureExtensible(VM& vm) { return structure(vm)->isStructureExtensible(); }
764 // You should call this when performing [[IsExtensible]] trap in a place
765 // that is described in the specification. This performs the fully virtual
766 // [[IsExtensible]] trap.
767 bool isExtensible(ExecState*);
768 bool indexingShouldBeSparse(VM& vm)
770 return !isStructureExtensible(vm)
771 || structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
774 bool staticPropertiesReified(VM& vm) { return structure(vm)->staticPropertiesReified(); }
775 void reifyAllStaticProperties(ExecState*);
777 JS_EXPORT_PRIVATE Butterfly* allocateMoreOutOfLineStorage(VM&, size_t oldSize, size_t newSize);
779 // Call this when you do not need to change the structure.
780 void setButterfly(VM&, Butterfly*);
782 // Call this if you do need to change the structure, or if you changed something about a structure
784 void nukeStructureAndSetButterfly(VM&, StructureID oldStructureID, Butterfly*);
786 void setStructure(VM&, Structure*);
788 JS_EXPORT_PRIVATE void convertToDictionary(VM&);
790 void flattenDictionaryObject(VM& vm)
792 structure(vm)->flattenDictionaryStructure(vm, this);
794 void shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM&, Structure* structure, size_t outOfLineCapacityAfter);
796 JSGlobalObject* globalObject() const
798 ASSERT(structure()->globalObject());
799 ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
800 return structure()->globalObject();
803 JSGlobalObject* globalObject(VM& vm) const
805 ASSERT(structure(vm)->globalObject());
806 ASSERT(!isGlobalObject() || ((JSObject*)structure(vm)->globalObject()) == this);
807 return structure(vm)->globalObject();
810 void switchToSlowPutArrayStorage(VM&);
812 // The receiver is the prototype in this case. The following:
814 // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...)
818 // foo->attemptToInterceptPutByIndexOnHole(...);
819 bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow, bool& putResult);
821 // Returns 0 if int32 storage cannot be created - either because
822 // indexing should be sparse, we're having a bad time, or because
823 // we already have a more general form of storage (double,
824 // contiguous, array storage).
825 ContiguousJSValues tryMakeWritableInt32(VM& vm)
827 if (LIKELY(hasInt32(indexingType()) && !isCopyOnWrite(indexingMode())))
828 return m_butterfly->contiguousInt32();
830 return tryMakeWritableInt32Slow(vm);
833 // Returns 0 if double storage cannot be created - either because
834 // indexing should be sparse, we're having a bad time, or because
835 // we already have a more general form of storage (contiguous,
836 // or array storage).
837 ContiguousDoubles tryMakeWritableDouble(VM& vm)
839 if (LIKELY(hasDouble(indexingType()) && !isCopyOnWrite(indexingMode())))
840 return m_butterfly->contiguousDouble();
842 return tryMakeWritableDoubleSlow(vm);
845 // Returns 0 if contiguous storage cannot be created - either because
846 // indexing should be sparse or because we're having a bad time.
847 ContiguousJSValues tryMakeWritableContiguous(VM& vm)
849 if (LIKELY(hasContiguous(indexingType()) && !isCopyOnWrite(indexingMode())))
850 return m_butterfly->contiguous();
852 return tryMakeWritableContiguousSlow(vm);
855 // Ensure that the object is in a mode where it has array storage. Use
856 // this if you're about to perform actions that would have required the
857 // object to be converted to have array storage, if it didn't have it
859 ArrayStorage* ensureArrayStorage(VM& vm)
861 if (LIKELY(hasAnyArrayStorage(indexingType())))
862 return m_butterfly->arrayStorage();
864 return ensureArrayStorageSlow(vm);
867 void ensureWritable(VM& vm)
869 if (isCopyOnWrite(indexingMode()))
870 convertFromCopyOnWrite(vm);
873 static size_t offsetOfInlineStorage();
875 static ptrdiff_t butterflyOffset()
877 return OBJECT_OFFSETOF(JSObject, m_butterfly);
879 void* butterflyAddress()
884 JS_EXPORT_PRIVATE JSValue getMethod(ExecState*, CallData&, CallType&, const Identifier&, const String& errorMessage);
886 bool canPerformFastPutInline(VM&, PropertyName);
887 bool canPerformFastPutInlineExcludingProto(VM&);
892 void finishCreation(VM& vm)
894 Base::finishCreation(vm);
895 ASSERT(jsDynamicCast<JSObject*>(vm, this));
896 ASSERT(structure(vm)->hasPolyProto() || getPrototypeDirect(vm).isNull() || Heap::heap(this) == Heap::heap(getPrototypeDirect(vm)));
897 ASSERT(structure(vm)->isObject());
898 ASSERT(classInfo(vm));
901 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
903 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
906 // To instantiate objects you likely want JSFinalObject, below.
907 // To create derived types you likely want JSNonFinalObject, below.
908 JSObject(VM&, Structure*, Butterfly* = nullptr);
910 // Visits the butterfly unless there is a race. Returns the structure if there was no race.
911 Structure* visitButterfly(SlotVisitor&);
913 Structure* visitButterflyImpl(SlotVisitor&);
915 void markAuxiliaryAndVisitOutOfLineProperties(SlotVisitor&, Butterfly*, Structure*, PropertyOffset lastOffset);
917 // Call this if you know that the object is in a mode where it has array
918 // storage. This will assert otherwise.
919 ArrayStorage* arrayStorage()
921 ASSERT(hasAnyArrayStorage(indexingType()));
922 return m_butterfly->arrayStorage();
925 // Call this if you want to predicate some actions on whether or not the
926 // object is in a mode where it has array storage.
927 ArrayStorage* arrayStorageOrNull()
929 switch (indexingType()) {
930 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
931 return m_butterfly->arrayStorage();
938 size_t butterflyTotalSize();
939 size_t butterflyPreCapacity();
941 Butterfly* createInitialUndecided(VM&, unsigned length);
942 ContiguousJSValues createInitialInt32(VM&, unsigned length);
943 ContiguousDoubles createInitialDouble(VM&, unsigned length);
944 ContiguousJSValues createInitialContiguous(VM&, unsigned length);
946 void convertUndecidedForValue(VM&, JSValue);
947 void createInitialForValueAndSet(VM&, unsigned index, JSValue);
948 void convertInt32ForValue(VM&, JSValue);
949 void convertDoubleForValue(VM&, JSValue);
950 void convertFromCopyOnWrite(VM&);
952 static Butterfly* createArrayStorageButterfly(VM&, JSObject* intendedOwner, Structure*, unsigned length, unsigned vectorLength, Butterfly* oldButterfly = nullptr);
953 ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength);
954 ArrayStorage* createInitialArrayStorage(VM&);
956 ContiguousJSValues convertUndecidedToInt32(VM&);
957 ContiguousDoubles convertUndecidedToDouble(VM&);
958 ContiguousJSValues convertUndecidedToContiguous(VM&);
959 ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition);
960 ArrayStorage* convertUndecidedToArrayStorage(VM&);
962 ContiguousDoubles convertInt32ToDouble(VM&);
963 ContiguousJSValues convertInt32ToContiguous(VM&);
964 ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition);
965 ArrayStorage* convertInt32ToArrayStorage(VM&);
967 ContiguousJSValues convertDoubleToContiguous(VM&);
968 ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition);
969 ArrayStorage* convertDoubleToArrayStorage(VM&);
971 ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition);
972 ArrayStorage* convertContiguousToArrayStorage(VM&);
975 ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&);
977 bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException);
979 template<IndexingType indexingShape>
980 bool putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue);
981 bool putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
983 bool increaseVectorLength(VM&, unsigned newLength);
984 void deallocateSparseIndexMap();
985 bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException);
986 SparseArrayValueMap* allocateSparseIndexMap(VM&);
988 void notifyPresenceOfIndexedAccessors(VM&);
990 bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow, bool& putResult);
992 // Call this if you want setIndexQuickly to succeed and you're sure that
993 // the array is contiguous.
994 bool WARN_UNUSED_RETURN ensureLength(VM& vm, unsigned length)
996 RELEASE_ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
997 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
999 if (m_butterfly->vectorLength() < length || isCopyOnWrite(indexingMode())) {
1000 if (!ensureLengthSlow(vm, length))
1004 if (m_butterfly->publicLength() < length)
1005 m_butterfly->setPublicLength(length);
1009 // Call this if you want to shrink the butterfly backing store, and you're
1010 // sure that the array is contiguous.
1011 void reallocateAndShrinkButterfly(VM&, unsigned length);
1013 template<IndexingType indexingShape>
1014 unsigned countElements(Butterfly*);
1016 // This is relevant to undecided, int32, double, and contiguous.
1017 unsigned countElements();
1020 friend class LLIntOffsetsExtractor;
1021 friend class VMInspector;
1023 // Nobody should ever ask any of these questions on something already known to be a JSObject.
1024 using JSCell::isAPIValueWrapper;
1025 using JSCell::isGetterSetter;
1027 void getString(ExecState* exec);
1031 Butterfly* createInitialIndexedStorage(VM&, unsigned length);
1033 ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*);
1036 bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&);
1038 JS_EXPORT_PRIVATE NEVER_INLINE bool putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&);
1040 bool getNonIndexPropertySlot(ExecState*, PropertyName, PropertySlot&);
1041 bool getOwnNonIndexPropertySlot(VM&, Structure*, PropertyName, PropertySlot&);
1042 JS_EXPORT_PRIVATE void fillGetterPropertySlot(VM&, PropertySlot&, JSCell*, unsigned, PropertyOffset);
1043 void fillCustomGetterPropertySlot(VM&, PropertySlot&, CustomGetterSetter*, unsigned, Structure*);
1045 JS_EXPORT_PRIVATE bool getOwnStaticPropertySlot(VM&, PropertyName, PropertySlot&);
1046 struct PropertyHashEntry {
1047 const HashTable* table;
1048 const HashTableValue* value;
1050 std::optional<PropertyHashEntry> findPropertyHashEntry(VM&, PropertyName) const;
1052 bool putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
1053 bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
1054 JS_EXPORT_PRIVATE bool putDirectIndexSlowOrBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode);
1056 unsigned getNewVectorLength(VM&, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength);
1057 unsigned getNewVectorLength(VM&, unsigned desiredLength);
1059 ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength);
1061 JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue);
1062 JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue);
1063 JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue);
1065 bool ensureLengthSlow(VM&, unsigned length);
1067 ContiguousJSValues tryMakeWritableInt32Slow(VM&);
1068 ContiguousDoubles tryMakeWritableDoubleSlow(VM&);
1069 ContiguousJSValues tryMakeWritableContiguousSlow(VM&);
1070 JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&);
1072 PropertyOffset prepareToPutDirectWithoutTransition(VM&, PropertyName, unsigned attributes, StructureID, Structure*);
1074 AuxiliaryBarrier<Butterfly*> m_butterfly;
1076 unsigned m_32BitPadding;
1080 // JSNonFinalObject is a type of JSObject that has some internal storage,
1081 // but also preserves some space in the collector cell for additional
1082 // data members in derived types.
1083 class JSNonFinalObject : public JSObject {
1084 friend class JSObject;
1087 typedef JSObject Base;
1089 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
1091 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
1095 explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0)
1096 : JSObject(vm, structure, butterfly)
1100 void finishCreation(VM& vm)
1102 Base::finishCreation(vm);
1103 ASSERT(!this->structure(vm)->hasInlineStorage());
1104 ASSERT(classInfo(vm));
1108 class JSFinalObject;
1110 // JSFinalObject is a type of JSObject that contains sufficient internal
1111 // storage to fully make use of the collector cell containing it.
1112 class JSFinalObject final : public JSObject {
1113 friend class JSObject;
1116 typedef JSObject Base;
1117 static const unsigned StructureFlags = Base::StructureFlags;
1119 static size_t allocationSize(Checked<size_t> inlineCapacity)
1121 return (sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>)).unsafeGet();
1124 static inline const TypeInfo typeInfo() { return TypeInfo(FinalObjectType, StructureFlags); }
1125 static const IndexingType defaultIndexingType = NonArray;
1127 static const unsigned defaultSize = 64;
1128 static inline unsigned defaultInlineCapacity()
1130 return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
1133 static const unsigned maxSize = 512;
1134 static inline unsigned maxInlineCapacity()
1136 return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
1139 static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr);
1140 static JSFinalObject* create(VM&, Structure*);
1141 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity)
1143 return Structure::create(vm, globalObject, prototype, typeInfo(), info(), defaultIndexingType, inlineCapacity);
1146 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
1148 DECLARE_EXPORT_INFO;
1151 void visitChildrenCommon(SlotVisitor&);
1153 void finishCreation(VM& vm)
1155 Base::finishCreation(vm);
1156 ASSERT(structure(vm)->totalStorageCapacity() == structure(vm)->inlineCapacity());
1157 ASSERT(classInfo(vm));
1161 friend class LLIntOffsetsExtractor;
1163 explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr)
1164 : JSObject(vm, structure, butterfly)
1166 memset(inlineStorageUnsafe(), 0, structure->inlineCapacity() * sizeof(EncodedJSValue));
1170 JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*);
1172 inline JSObject* JSObject::createRawObject(
1173 ExecState* exec, Structure* structure, Butterfly* butterfly)
1175 VM& vm = exec->vm();
1176 JSObject* finalObject = new (
1178 allocateCell<JSFinalObject>(
1180 JSFinalObject::allocationSize(structure->inlineCapacity())
1182 ) JSObject(vm, structure, butterfly);
1183 finalObject->finishCreation(vm);
1187 inline JSFinalObject* JSFinalObject::create(
1188 ExecState* exec, Structure* structure, Butterfly* butterfly)
1190 VM& vm = exec->vm();
1191 JSFinalObject* finalObject = new (
1193 allocateCell<JSFinalObject>(
1195 allocationSize(structure->inlineCapacity())
1197 ) JSFinalObject(vm, structure, butterfly);
1198 finalObject->finishCreation(vm);
1202 inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure)
1204 JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure);
1205 finalObject->finishCreation(vm);
1209 inline size_t JSObject::offsetOfInlineStorage()
1211 return sizeof(JSObject);
1214 inline bool JSObject::isGlobalObject() const
1216 return type() == GlobalObjectType;
1219 inline bool JSObject::isJSLexicalEnvironment() const
1221 return type() == LexicalEnvironmentType || type() == ModuleEnvironmentType;
1224 inline bool JSObject::isGlobalLexicalEnvironment() const
1226 return type() == GlobalLexicalEnvironmentType;
1229 inline bool JSObject::isStrictEvalActivation() const
1231 return type() == StrictEvalActivationType;
1234 inline bool JSObject::isEnvironment() const
1236 bool result = GlobalObjectType <= type() && type() <= StrictEvalActivationType;
1237 ASSERT((isGlobalObject() || isJSLexicalEnvironment() || isGlobalLexicalEnvironment() || isStrictEvalActivation()) == result);
1241 inline bool JSObject::isErrorInstance() const
1243 return type() == ErrorInstanceType;
1246 inline bool JSObject::isWithScope() const
1248 return type() == WithScopeType;
1251 inline void JSObject::setStructure(VM& vm, Structure* structure)
1254 ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this)));
1255 JSCell::setStructure(vm, structure);
1258 inline void JSObject::setButterfly(VM& vm, Butterfly* butterfly)
1260 if (isX86() || vm.heap.mutatorShouldBeFenced()) {
1261 WTF::storeStoreFence();
1262 m_butterfly.set(vm, this, butterfly);
1263 WTF::storeStoreFence();
1267 m_butterfly.set(vm, this, butterfly);
1270 inline void JSObject::nukeStructureAndSetButterfly(VM& vm, StructureID oldStructureID, Butterfly* butterfly)
1272 if (isX86() || vm.heap.mutatorShouldBeFenced()) {
1273 setStructureIDDirectly(nuke(oldStructureID));
1274 WTF::storeStoreFence();
1275 m_butterfly.set(vm, this, butterfly);
1276 WTF::storeStoreFence();
1280 m_butterfly.set(vm, this, butterfly);
1283 inline CallType getCallData(VM& vm, JSValue value, CallData& callData)
1285 CallType result = value.isCell() ? value.asCell()->methodTable(vm)->getCallData(value.asCell(), callData) : CallType::None;
1286 ASSERT(result == CallType::None || value.isValidCallee());
1290 inline ConstructType getConstructData(VM& vm, JSValue value, ConstructData& constructData)
1292 ConstructType result = value.isCell() ? value.asCell()->methodTable(vm)->getConstructData(value.asCell(), constructData) : ConstructType::None;
1293 ASSERT(result == ConstructType::None || value.isValidCallee());
1297 inline JSObject* asObject(JSCell* cell)
1299 ASSERT(cell->isObject());
1300 return jsCast<JSObject*>(cell);
1303 inline JSObject* asObject(JSValue value)
1305 return asObject(value.asCell());
1308 inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly)
1309 : JSCell(vm, structure)
1310 , m_butterfly(vm, this, butterfly)
1314 inline JSValue JSObject::getPrototypeDirect(VM& vm) const
1316 return structure(vm)->storedPrototype(this);
1319 inline JSValue JSObject::getPrototype(VM& vm, ExecState* exec)
1321 auto getPrototypeMethod = methodTable(vm)->getPrototype;
1322 MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
1323 if (LIKELY(getPrototypeMethod == defaultGetPrototype))
1324 return getPrototypeDirect(vm);
1325 return getPrototypeMethod(this, exec);
1328 // Normally, we never shrink the butterfly so if we know an offset is valid for some
1329 // past structure then it should be valid for any new structure. However, we may sometimes
1330 // shrink the butterfly when we are holding the Structure's ConcurrentJSLock, such as when we
1331 // flatten an object.
1332 inline JSValue JSObject::getDirectConcurrently(Structure* structure, PropertyOffset offset) const
1334 ConcurrentJSLocker locker(structure->lock());
1335 if (!structure->isValidOffset(offset))
1337 return getDirect(offset);
1340 // It is safe to call this method with a PropertyName that is actually an index,
1341 // but if so will always return false (doesn't search index storage).
1342 ALWAYS_INLINE bool JSObject::getOwnNonIndexPropertySlot(VM& vm, Structure* structure, PropertyName propertyName, PropertySlot& slot)
1344 unsigned attributes;
1345 PropertyOffset offset = structure->get(vm, propertyName, attributes);
1346 if (!isValidOffset(offset)) {
1347 if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags()))
1349 return getOwnStaticPropertySlot(vm, propertyName, slot);
1352 // getPropertySlot relies on this method never returning index properties!
1353 ASSERT(!parseIndex(propertyName));
1355 JSValue value = getDirect(offset);
1356 if (value.isCell()) {
1358 JSCell* cell = value.asCell();
1359 JSType type = cell->type();
1361 case GetterSetterType:
1362 fillGetterPropertySlot(vm, slot, cell, attributes, offset);
1364 case CustomGetterSetterType:
1365 fillCustomGetterPropertySlot(vm, slot, jsCast<CustomGetterSetter*>(cell), attributes, structure);
1372 slot.setValue(this, attributes, value, offset);
1376 ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(VM& vm, PropertySlot& slot, CustomGetterSetter* customGetterSetter, unsigned attributes, Structure* structure)
1378 ASSERT(attributes & PropertyAttribute::CustomAccessorOrValue);
1379 if (customGetterSetter->inherits<DOMAttributeGetterSetter>(vm)) {
1380 auto* domAttribute = jsCast<DOMAttributeGetterSetter*>(customGetterSetter);
1381 if (structure->isUncacheableDictionary())
1382 slot.setCustom(this, attributes, domAttribute->getter(), domAttribute->domAttribute());
1384 slot.setCacheableCustom(this, attributes, domAttribute->getter(), domAttribute->domAttribute());
1388 if (structure->isUncacheableDictionary())
1389 slot.setCustom(this, attributes, customGetterSetter->getter());
1391 slot.setCacheableCustom(this, attributes, customGetterSetter->getter());
1394 // It may seem crazy to inline a function this large, especially a virtual function,
1395 // but it makes a big difference to property lookup that derived classes can inline their
1396 // base class call to this.
1397 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1399 VM& vm = exec->vm();
1400 Structure* structure = object->structure(vm);
1401 if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
1403 if (std::optional<uint32_t> index = parseIndex(propertyName))
1404 return getOwnPropertySlotByIndex(object, exec, index.value(), slot);
1408 // It may seem crazy to inline a function this large but it makes a big difference
1409 // since this is function very hot in variable lookup
1410 template<bool checkNullStructure>
1411 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1413 VM& vm = exec->vm();
1414 auto& structureIDTable = vm.heap.structureIDTable();
1415 JSObject* object = this;
1417 if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) {
1418 // If propertyName is an index then we may have missed it (as this loop is using
1419 // getOwnNonIndexPropertySlot), so we cannot safely call the overridden getOwnPropertySlot
1420 // (lest we return a property from a prototype that is shadowed). Check now for an index,
1421 // if so we need to start afresh from this object.
1422 if (std::optional<uint32_t> index = parseIndex(propertyName))
1423 return getPropertySlot(exec, index.value(), slot);
1424 // Safe to continue searching from current position; call getNonIndexPropertySlot to avoid
1425 // parsing the int again.
1426 return object->getNonIndexPropertySlot(exec, propertyName, slot);
1428 ASSERT(object->type() != ProxyObjectType);
1429 Structure* structure = structureIDTable.get(object->structureID());
1431 if (checkNullStructure && UNLIKELY(!structure))
1432 CRASH_WITH_INFO(object->type(), object->structureID(), structureIDTable.size());
1434 if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
1436 // FIXME: This doesn't look like it's following the specification:
1437 // https://bugs.webkit.org/show_bug.cgi?id=172572
1438 JSValue prototype = structure->storedPrototype(object);
1439 if (!prototype.isObject())
1441 object = asObject(prototype);
1444 if (std::optional<uint32_t> index = parseIndex(propertyName))
1445 return getPropertySlot(exec, index.value(), slot);
1449 inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const
1451 VM& vm = exec->vm();
1452 auto scope = DECLARE_THROW_SCOPE(vm);
1453 PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
1454 bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1455 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
1457 RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName));
1459 return jsUndefined();
1462 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
1464 VM& vm = exec->vm();
1465 auto scope = DECLARE_THROW_SCOPE(vm);
1466 PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
1467 bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1468 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
1470 RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName));
1472 return jsUndefined();
1475 inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1478 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1479 ASSERT(!structure(vm)->hasGetterSetterProperties());
1480 ASSERT(!structure(vm)->hasCustomGetterSetterProperties());
1482 return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);
1485 inline bool JSObject::putOwnDataPropertyMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1487 VM& vm = exec->vm();
1489 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1490 ASSERT(!structure(vm)->hasGetterSetterProperties());
1491 ASSERT(!structure(vm)->hasCustomGetterSetterProperties());
1493 if (std::optional<uint32_t> index = parseIndex(propertyName))
1494 return putDirectIndex(exec, index.value(), value, 0, PutDirectIndexLikePutDirect);
1496 return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);
1499 inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1501 ASSERT(!value.isGetterSetter() && !(attributes & PropertyAttribute::Accessor));
1502 ASSERT(!value.isCustomGetterSetter() && !(attributes & PropertyAttribute::CustomAccessorOrValue));
1503 PutPropertySlot slot(this);
1504 return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1507 inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1509 ASSERT(!value.isGetterSetter());
1510 ASSERT(!value.isCustomGetterSetter());
1511 return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot);
1514 ALWAYS_INLINE JSObject* Register::object() const
1516 return asObject(jsValue());
1519 ALWAYS_INLINE Register& Register::operator=(JSObject* object)
1521 u.value = JSValue::encode(JSValue(object));
1525 inline size_t offsetInButterfly(PropertyOffset offset)
1527 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1530 inline size_t JSObject::butterflyPreCapacity()
1532 VM& vm = *this->vm();
1533 if (UNLIKELY(hasIndexingHeader(vm)))
1534 return butterfly()->indexingHeader()->preCapacity(structure(vm));
1538 inline size_t JSObject::butterflyTotalSize()
1540 VM& vm = *this->vm();
1541 Structure* structure = this->structure(vm);
1542 Butterfly* butterfly = this->butterfly();
1544 size_t indexingPayloadSizeInBytes;
1545 bool hasIndexingHeader = this->hasIndexingHeader(vm);
1547 if (UNLIKELY(hasIndexingHeader)) {
1548 preCapacity = butterfly->indexingHeader()->preCapacity(structure);
1549 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
1552 indexingPayloadSizeInBytes = 0;
1555 return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes);
1558 inline int indexRelativeToBase(PropertyOffset offset)
1560 if (isOutOfLineOffset(offset))
1561 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1562 ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
1563 return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
1566 inline int offsetRelativeToBase(PropertyOffset offset)
1568 if (isOutOfLineOffset(offset))
1569 return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage();
1570 return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
1573 // Returns the maximum offset (away from zero) a load instruction will encode.
1574 inline size_t maxOffsetRelativeToBase(PropertyOffset offset)
1576 ptrdiff_t addressOffset = offsetRelativeToBase(offset);
1577 #if USE(JSVALUE32_64)
1578 if (addressOffset >= 0)
1579 return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag);
1581 return static_cast<size_t>(addressOffset);
1584 COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment);
1586 template<unsigned charactersCount>
1587 ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char (&characters)[charactersCount])
1589 return Identifier::fromString(&vm, characters);
1592 ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name)
1594 return Identifier::fromString(&vm, name);
1597 ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name)
1602 bool validateAndApplyPropertyDescriptor(ExecState*, JSObject*, PropertyName, bool isExtensible,
1603 const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException);
1605 JS_EXPORT_PRIVATE NEVER_INLINE bool ordinarySetSlow(ExecState*, JSObject*, PropertyName, JSValue, JSValue receiver, bool shouldThrow);
1607 // Helper for defining native functions, if you're not using a static hash table.
1608 // Use this macro from within finishCreation() methods in prototypes. This assumes
1609 // you've defined variables called exec, globalObject, and vm, and they
1610 // have the expected meanings.
1611 #define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \
1612 putDirectNativeFunction(\
1613 vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \
1614 (intrinsic), (attributes))
1616 #define JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length, intrinsic) \
1617 putDirectNativeFunctionWithoutTransition(\
1618 vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \
1619 (intrinsic), (attributes))
1621 // As above, but this assumes that the function you're defining doesn't have an
1623 #define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \
1624 JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic)
1626 #define JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length) \
1627 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, (attributes), (length), NoIntrinsic)
1629 // Identical helpers but for builtins. Note that currently, we don't support builtins that are
1630 // also intrinsics, but we probably will do that eventually.
1631 #define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \
1632 putDirectBuiltinFunction(\
1633 vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
1635 #define JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(jsName, generatorName, attributes) \
1636 putDirectBuiltinFunctionWithoutTransition(\
1637 vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
1639 // Helper for defining native getters on properties.
1640 #define JSC_NATIVE_INTRINSIC_GETTER(jsName, cppName, attributes, intrinsic) \
1641 putDirectNativeIntrinsicGetter(\
1642 vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \
1643 (intrinsic), ((attributes) | PropertyAttribute::Accessor))
1645 #define JSC_NATIVE_GETTER(jsName, cppName, attributes) \
1646 JSC_NATIVE_INTRINSIC_GETTER((jsName), (cppName), (attributes), NoIntrinsic)