2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2006, 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
27 #include "ButterflyInlines.h"
28 #include "CopiedSpaceInlines.h"
29 #include "CopyVisitor.h"
30 #include "CopyVisitorInlines.h"
31 #include "CustomGetterSetter.h"
32 #include "DatePrototype.h"
33 #include "ErrorConstructor.h"
34 #include "Exception.h"
35 #include "Executable.h"
36 #include "GetterSetter.h"
37 #include "IndexingHeaderInlines.h"
38 #include "JSCatchScope.h"
39 #include "JSFunction.h"
40 #include "JSFunctionNameScope.h"
41 #include "JSGlobalObject.h"
43 #include "NativeErrorConstructor.h"
45 #include "ObjectPrototype.h"
46 #include "JSCInlines.h"
47 #include "PropertyDescriptor.h"
48 #include "PropertyNameArray.h"
50 #include "SlotVisitorInlines.h"
52 #include <wtf/Assertions.h>
56 // We keep track of the size of the last array after it was grown. We use this
57 // as a simple heuristic for as the value to grow the next array from size 0.
58 // This value is capped by the constant FIRST_VECTOR_GROW defined in
59 // ArrayConventions.h.
60 static unsigned lastArraySize
= 0;
62 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject
);
63 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject
);
65 const char* StrictModeReadonlyPropertyWriteError
= "Attempted to assign to readonly property.";
67 const ClassInfo
JSObject::s_info
= { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject
) };
69 const ClassInfo
JSFinalObject::s_info
= { "Object", &Base::s_info
, 0, CREATE_METHOD_TABLE(JSFinalObject
) };
71 static inline void getClassPropertyNames(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
, EnumerationMode mode
, bool didReify
)
75 // Add properties from the static hashtables of properties
76 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
77 const HashTable
* table
= classInfo
->staticPropHashTable
;
81 for (auto iter
= table
->begin(); iter
!= table
->end(); ++iter
) {
82 if ((!(iter
->attributes() & DontEnum
) || mode
.includeDontEnumProperties()) && !((iter
->attributes() & BuiltinOrFunctionOrAccessor
) && didReify
))
83 propertyNames
.add(Identifier::fromString(&vm
, iter
.key()));
88 ALWAYS_INLINE
void JSObject::copyButterfly(CopyVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
92 Structure
* structure
= this->structure();
94 size_t propertyCapacity
= structure
->outOfLineCapacity();
96 size_t indexingPayloadSizeInBytes
;
97 bool hasIndexingHeader
= this->hasIndexingHeader();
98 if (UNLIKELY(hasIndexingHeader
)) {
99 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
100 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
103 indexingPayloadSizeInBytes
= 0;
105 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
106 if (visitor
.checkIfShouldCopy(butterfly
->base(preCapacity
, propertyCapacity
))) {
107 Butterfly
* newButterfly
= Butterfly::createUninitializedDuringCollection(visitor
, preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
109 // Copy the properties.
110 PropertyStorage currentTarget
= newButterfly
->propertyStorage();
111 PropertyStorage currentSource
= butterfly
->propertyStorage();
112 for (size_t count
= storageSize
; count
--;)
113 (--currentTarget
)->setWithoutWriteBarrier((--currentSource
)->get());
115 if (UNLIKELY(hasIndexingHeader
)) {
116 *newButterfly
->indexingHeader() = *butterfly
->indexingHeader();
118 // Copy the array if appropriate.
120 WriteBarrier
<Unknown
>* currentTarget
;
121 WriteBarrier
<Unknown
>* currentSource
;
124 switch (this->indexingType()) {
125 case ALL_UNDECIDED_INDEXING_TYPES
:
126 case ALL_CONTIGUOUS_INDEXING_TYPES
:
127 case ALL_INT32_INDEXING_TYPES
:
128 case ALL_DOUBLE_INDEXING_TYPES
: {
129 currentTarget
= newButterfly
->contiguous().data();
130 currentSource
= butterfly
->contiguous().data();
131 RELEASE_ASSERT(newButterfly
->publicLength() <= newButterfly
->vectorLength());
132 count
= newButterfly
->vectorLength();
136 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
137 newButterfly
->arrayStorage()->copyHeaderFromDuringGC(*butterfly
->arrayStorage());
138 currentTarget
= newButterfly
->arrayStorage()->m_vector
;
139 currentSource
= butterfly
->arrayStorage()->m_vector
;
140 count
= newButterfly
->arrayStorage()->vectorLength();
151 memcpy(currentTarget
, currentSource
, count
* sizeof(EncodedJSValue
));
154 m_butterfly
.setWithoutWriteBarrier(newButterfly
);
155 visitor
.didCopy(butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
159 ALWAYS_INLINE
void JSObject::visitButterfly(SlotVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
163 Structure
* structure
= this->structure(visitor
.vm());
165 size_t propertyCapacity
= structure
->outOfLineCapacity();
167 size_t indexingPayloadSizeInBytes
;
168 bool hasIndexingHeader
= this->hasIndexingHeader();
169 if (UNLIKELY(hasIndexingHeader
)) {
170 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
171 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
174 indexingPayloadSizeInBytes
= 0;
176 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
178 // Mark the properties.
179 visitor
.appendValues(butterfly
->propertyStorage() - storageSize
, storageSize
);
181 this, ButterflyCopyToken
,
182 butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
184 // Mark the array if appropriate.
185 switch (this->indexingType()) {
186 case ALL_CONTIGUOUS_INDEXING_TYPES
:
187 visitor
.appendValues(butterfly
->contiguous().data(), butterfly
->publicLength());
189 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
190 visitor
.appendValues(butterfly
->arrayStorage()->m_vector
, butterfly
->arrayStorage()->vectorLength());
191 if (butterfly
->arrayStorage()->m_sparseMap
)
192 visitor
.append(&butterfly
->arrayStorage()->m_sparseMap
);
199 void JSObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
201 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
202 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
204 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
205 visitor
.m_isCheckingForDefaultMarkViolation
= false;
208 JSCell::visitChildren(thisObject
, visitor
);
210 Butterfly
* butterfly
= thisObject
->butterfly();
212 thisObject
->visitButterfly(visitor
, butterfly
, thisObject
->structure(visitor
.vm())->outOfLineSize());
215 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
219 void JSObject::copyBackingStore(JSCell
* cell
, CopyVisitor
& visitor
, CopyToken token
)
221 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
222 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
224 if (token
!= ButterflyCopyToken
)
227 Butterfly
* butterfly
= thisObject
->butterfly();
229 thisObject
->copyButterfly(visitor
, butterfly
, thisObject
->structure()->outOfLineSize());
232 void JSFinalObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
234 JSFinalObject
* thisObject
= jsCast
<JSFinalObject
*>(cell
);
235 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
237 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
238 visitor
.m_isCheckingForDefaultMarkViolation
= false;
241 JSCell::visitChildren(thisObject
, visitor
);
243 Structure
* structure
= thisObject
->structure();
244 Butterfly
* butterfly
= thisObject
->butterfly();
246 thisObject
->visitButterfly(visitor
, butterfly
, structure
->outOfLineSize());
248 size_t storageSize
= structure
->inlineSize();
249 visitor
.appendValues(thisObject
->inlineStorage(), storageSize
);
252 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
256 String
JSObject::className(const JSObject
* object
)
258 const ClassInfo
* info
= object
->classInfo();
260 return info
->className
;
263 String
JSObject::calculatedClassName(JSObject
* object
)
265 String prototypeFunctionName
;
266 ExecState
* exec
= object
->globalObject()->globalExec();
267 PropertySlot
slot(object
->structure()->storedPrototype());
268 PropertyName
constructor(exec
->propertyNames().constructor
);
269 if (object
->getPropertySlot(exec
, constructor
, slot
)) {
270 if (slot
.isValue()) {
271 JSValue constructorValue
= slot
.getValue(exec
, constructor
);
272 if (constructorValue
.isCell()) {
273 if (JSCell
* constructorCell
= constructorValue
.asCell()) {
274 if (JSObject
* ctorObject
= constructorCell
->getObject()) {
275 if (JSFunction
* constructorFunction
= jsDynamicCast
<JSFunction
*>(ctorObject
))
276 prototypeFunctionName
= constructorFunction
->calculatedDisplayName(exec
);
277 else if (InternalFunction
* constructorFunction
= jsDynamicCast
<InternalFunction
*>(ctorObject
))
278 prototypeFunctionName
= constructorFunction
->calculatedDisplayName(exec
);
285 if (prototypeFunctionName
.isNull() || prototypeFunctionName
== "Object") {
286 String tableClassName
= object
->methodTable()->className(object
);
287 if (!tableClassName
.isNull() && tableClassName
!= "Object")
288 return tableClassName
;
290 String classInfoName
= object
->classInfo()->className
;
291 if (!classInfoName
.isNull())
292 return classInfoName
;
294 if (prototypeFunctionName
.isNull())
295 return ASCIILiteral("Object");
298 return prototypeFunctionName
;
301 bool JSObject::getOwnPropertySlotByIndex(JSObject
* thisObject
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
303 // NB. The fact that we're directly consulting our indexed storage implies that it is not
304 // legal for anyone to override getOwnPropertySlot() without also overriding
305 // getOwnPropertySlotByIndex().
307 if (i
> MAX_ARRAY_INDEX
)
308 return thisObject
->methodTable(exec
->vm())->getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
310 switch (thisObject
->indexingType()) {
311 case ALL_BLANK_INDEXING_TYPES
:
312 case ALL_UNDECIDED_INDEXING_TYPES
:
315 case ALL_INT32_INDEXING_TYPES
:
316 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
317 Butterfly
* butterfly
= thisObject
->butterfly();
318 if (i
>= butterfly
->vectorLength())
321 JSValue value
= butterfly
->contiguous()[i
].get();
323 slot
.setValue(thisObject
, None
, value
);
330 case ALL_DOUBLE_INDEXING_TYPES
: {
331 Butterfly
* butterfly
= thisObject
->butterfly();
332 if (i
>= butterfly
->vectorLength())
335 double value
= butterfly
->contiguousDouble()[i
];
336 if (value
== value
) {
337 slot
.setValue(thisObject
, None
, JSValue(JSValue::EncodeAsDouble
, value
));
344 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
345 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
346 if (i
>= storage
->length())
349 if (i
< storage
->vectorLength()) {
350 JSValue value
= storage
->m_vector
[i
].get();
352 slot
.setValue(thisObject
, None
, value
);
355 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
356 SparseArrayValueMap::iterator it
= map
->find(i
);
357 if (it
!= map
->notFound()) {
358 it
->value
.get(thisObject
, slot
);
366 RELEASE_ASSERT_NOT_REACHED();
374 void JSObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
376 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
378 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(thisObject
));
381 // Try indexed put first. This is required for correctness, since loads on property names that appear like
382 // valid indices will never look in the named property storage.
383 if (Optional
<uint32_t> index
= parseIndex(propertyName
)) {
384 putByIndex(thisObject
, exec
, index
.value(), value
, slot
.isStrictMode());
388 // Check if there are any setters or getters in the prototype chain
390 if (propertyName
!= exec
->propertyNames().underscoreProto
) {
391 for (JSObject
* obj
= thisObject
; !obj
->structure(vm
)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj
= asObject(prototype
)) {
392 prototype
= obj
->prototype();
393 if (prototype
.isNull()) {
394 ASSERT(!thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
));
395 if (!thisObject
->putDirectInternal
<PutModePut
>(vm
, propertyName
, value
, 0, slot
)
396 && slot
.isStrictMode())
397 throwTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
));
404 for (obj
= thisObject
; ; obj
= asObject(prototype
)) {
406 PropertyOffset offset
= obj
->structure(vm
)->get(vm
, propertyName
, attributes
);
407 if (isValidOffset(offset
)) {
408 if (attributes
& ReadOnly
) {
409 ASSERT(thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
410 if (slot
.isStrictMode())
411 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
)));
415 JSValue gs
= obj
->getDirect(offset
);
416 if (gs
.isGetterSetter()) {
417 callSetter(exec
, cell
, gs
, value
, slot
.isStrictMode() ? StrictMode
: NotStrictMode
);
418 if (!thisObject
->structure()->isDictionary())
419 slot
.setCacheableSetter(obj
, offset
);
422 if (gs
.isCustomGetterSetter()) {
423 callCustomSetter(exec
, gs
, obj
, slot
.thisValue(), value
);
424 slot
.setCustomProperty(obj
, jsCast
<CustomGetterSetter
*>(gs
.asCell())->setter());
427 ASSERT(!(attributes
& Accessor
));
429 // If there's an existing property on the object or one of its
430 // prototypes it should be replaced, so break here.
433 const ClassInfo
* info
= obj
->classInfo();
434 if (info
->hasStaticSetterOrReadonlyProperties()) {
435 if (const HashTableValue
* entry
= obj
->findPropertyHashEntry(propertyName
)) {
436 if (!obj
->staticFunctionsReified() || !(entry
->attributes() & BuiltinOrFunctionOrAccessor
)) {
437 putEntry(exec
, entry
, obj
, propertyName
, value
, slot
);
442 prototype
= obj
->prototype();
443 if (prototype
.isNull())
447 ASSERT(!thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
448 if (!thisObject
->putDirectInternal
<PutModePut
>(vm
, propertyName
, value
, 0, slot
) && slot
.isStrictMode())
449 throwTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
));
453 void JSObject::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
)
455 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
457 if (propertyName
> MAX_ARRAY_INDEX
) {
458 PutPropertySlot
slot(cell
, shouldThrow
);
459 thisObject
->methodTable()->put(thisObject
, exec
, Identifier::from(exec
, propertyName
), value
, slot
);
463 switch (thisObject
->indexingType()) {
464 case ALL_BLANK_INDEXING_TYPES
:
467 case ALL_UNDECIDED_INDEXING_TYPES
: {
468 thisObject
->convertUndecidedForValue(exec
->vm(), value
);
470 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
474 case ALL_INT32_INDEXING_TYPES
: {
475 if (!value
.isInt32()) {
476 thisObject
->convertInt32ForValue(exec
->vm(), value
);
477 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
483 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
484 Butterfly
* butterfly
= thisObject
->butterfly();
485 if (propertyName
>= butterfly
->vectorLength())
487 butterfly
->contiguous()[propertyName
].set(exec
->vm(), thisObject
, value
);
488 if (propertyName
>= butterfly
->publicLength())
489 butterfly
->setPublicLength(propertyName
+ 1);
493 case ALL_DOUBLE_INDEXING_TYPES
: {
494 if (!value
.isNumber()) {
495 thisObject
->convertDoubleToContiguous(exec
->vm());
497 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
500 double valueAsDouble
= value
.asNumber();
501 if (valueAsDouble
!= valueAsDouble
) {
502 thisObject
->convertDoubleToContiguous(exec
->vm());
504 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
507 Butterfly
* butterfly
= thisObject
->butterfly();
508 if (propertyName
>= butterfly
->vectorLength())
510 butterfly
->contiguousDouble()[propertyName
] = valueAsDouble
;
511 if (propertyName
>= butterfly
->publicLength())
512 butterfly
->setPublicLength(propertyName
+ 1);
516 case NonArrayWithArrayStorage
:
517 case ArrayWithArrayStorage
: {
518 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
520 if (propertyName
>= storage
->vectorLength())
523 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[propertyName
];
524 unsigned length
= storage
->length();
526 // Update length & m_numValuesInVector as necessary.
527 if (propertyName
>= length
) {
528 length
= propertyName
+ 1;
529 storage
->setLength(length
);
530 ++storage
->m_numValuesInVector
;
531 } else if (!valueSlot
)
532 ++storage
->m_numValuesInVector
;
534 valueSlot
.set(exec
->vm(), thisObject
, value
);
538 case NonArrayWithSlowPutArrayStorage
:
539 case ArrayWithSlowPutArrayStorage
: {
540 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
542 if (propertyName
>= storage
->vectorLength())
545 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[propertyName
];
546 unsigned length
= storage
->length();
548 // Update length & m_numValuesInVector as necessary.
549 if (propertyName
>= length
) {
550 if (thisObject
->attemptToInterceptPutByIndexOnHole(exec
, propertyName
, value
, shouldThrow
))
552 length
= propertyName
+ 1;
553 storage
->setLength(length
);
554 ++storage
->m_numValuesInVector
;
555 } else if (!valueSlot
) {
556 if (thisObject
->attemptToInterceptPutByIndexOnHole(exec
, propertyName
, value
, shouldThrow
))
558 ++storage
->m_numValuesInVector
;
561 valueSlot
.set(exec
->vm(), thisObject
, value
);
566 RELEASE_ASSERT_NOT_REACHED();
569 thisObject
->putByIndexBeyondVectorLength(exec
, propertyName
, value
, shouldThrow
);
572 ArrayStorage
* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM
& vm
, ArrayStorage
* storage
)
574 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
577 map
= allocateSparseIndexMap(vm
);
579 if (map
->sparseMode())
582 map
->setSparseMode();
584 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
585 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
586 JSValue value
= storage
->m_vector
[i
].get();
587 // This will always be a new entry in the map, so no need to check we can write,
588 // and attributes are default so no need to set them.
590 map
->add(this, i
).iterator
->value
.set(vm
, map
, value
);
593 DeferGC
deferGC(vm
.heap
);
594 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(vm
, this, structure(vm
), 0, ArrayStorage::sizeFor(0));
595 RELEASE_ASSERT(newButterfly
);
596 newButterfly
->arrayStorage()->m_indexBias
= 0;
597 newButterfly
->arrayStorage()->setVectorLength(0);
598 newButterfly
->arrayStorage()->m_sparseMap
.set(vm
, this, map
);
599 setButterflyWithoutChangingStructure(vm
, newButterfly
);
601 return newButterfly
->arrayStorage();
604 void JSObject::enterDictionaryIndexingMode(VM
& vm
)
606 switch (indexingType()) {
607 case ALL_BLANK_INDEXING_TYPES
:
608 case ALL_UNDECIDED_INDEXING_TYPES
:
609 case ALL_INT32_INDEXING_TYPES
:
610 case ALL_DOUBLE_INDEXING_TYPES
:
611 case ALL_CONTIGUOUS_INDEXING_TYPES
:
612 // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
613 // this case if we ever cared.
614 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, ensureArrayStorageSlow(vm
));
616 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
617 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, m_butterfly
->arrayStorage());
625 void JSObject::notifyPresenceOfIndexedAccessors(VM
& vm
)
627 if (mayInterceptIndexedAccesses())
630 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AddIndexedAccessors
));
632 if (!vm
.prototypeMap
.isPrototype(this))
635 globalObject()->haveABadTime(vm
);
638 Butterfly
* JSObject::createInitialIndexedStorage(VM
& vm
, unsigned length
, size_t elementSize
)
640 ASSERT(length
< MAX_ARRAY_INDEX
);
641 IndexingType oldType
= indexingType();
642 ASSERT_UNUSED(oldType
, !hasIndexedProperties(oldType
));
643 ASSERT(!structure()->needsSlowPutIndexing());
644 ASSERT(!indexingShouldBeSparse());
645 unsigned vectorLength
= std::max(length
, BASE_VECTOR_LEN
);
646 Butterfly
* newButterfly
= Butterfly::createOrGrowArrayRight(
647 m_butterfly
.get(), vm
, this, structure(), structure()->outOfLineCapacity(), false, 0,
648 elementSize
* vectorLength
);
649 newButterfly
->setPublicLength(length
);
650 newButterfly
->setVectorLength(vectorLength
);
654 Butterfly
* JSObject::createInitialUndecided(VM
& vm
, unsigned length
)
656 DeferGC
deferGC(vm
.heap
);
657 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
658 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateUndecided
);
659 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
663 ContiguousJSValues
JSObject::createInitialInt32(VM
& vm
, unsigned length
)
665 DeferGC
deferGC(vm
.heap
);
666 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
667 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateInt32
);
668 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
669 return newButterfly
->contiguousInt32();
672 ContiguousDoubles
JSObject::createInitialDouble(VM
& vm
, unsigned length
)
674 DeferGC
deferGC(vm
.heap
);
675 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(double));
676 for (unsigned i
= newButterfly
->vectorLength(); i
--;)
677 newButterfly
->contiguousDouble()[i
] = PNaN
;
678 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
);
679 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
680 return newButterfly
->contiguousDouble();
683 ContiguousJSValues
JSObject::createInitialContiguous(VM
& vm
, unsigned length
)
685 DeferGC
deferGC(vm
.heap
);
686 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
687 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
);
688 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
689 return newButterfly
->contiguous();
692 ArrayStorage
* JSObject::createArrayStorage(VM
& vm
, unsigned length
, unsigned vectorLength
)
694 DeferGC
deferGC(vm
.heap
);
695 Structure
* structure
= this->structure(vm
);
696 IndexingType oldType
= indexingType();
697 ASSERT_UNUSED(oldType
, !hasIndexedProperties(oldType
));
698 Butterfly
* newButterfly
= Butterfly::createOrGrowArrayRight(
699 m_butterfly
.get(), vm
, this, structure
, structure
->outOfLineCapacity(), false, 0,
700 ArrayStorage::sizeFor(vectorLength
));
701 RELEASE_ASSERT(newButterfly
);
703 ArrayStorage
* result
= newButterfly
->arrayStorage();
704 result
->setLength(length
);
705 result
->setVectorLength(vectorLength
);
706 result
->m_sparseMap
.clear();
707 result
->m_numValuesInVector
= 0;
708 result
->m_indexBias
= 0;
709 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure
, structure
->suggestedArrayStorageTransition());
710 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
714 ArrayStorage
* JSObject::createInitialArrayStorage(VM
& vm
)
716 return createArrayStorage(vm
, 0, BASE_VECTOR_LEN
);
719 ContiguousJSValues
JSObject::convertUndecidedToInt32(VM
& vm
)
721 ASSERT(hasUndecided(indexingType()));
722 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateInt32
));
723 return m_butterfly
->contiguousInt32();
726 ContiguousDoubles
JSObject::convertUndecidedToDouble(VM
& vm
)
728 ASSERT(hasUndecided(indexingType()));
730 for (unsigned i
= m_butterfly
->vectorLength(); i
--;)
731 m_butterfly
->contiguousDouble()[i
] = PNaN
;
733 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
));
734 return m_butterfly
->contiguousDouble();
737 ContiguousJSValues
JSObject::convertUndecidedToContiguous(VM
& vm
)
739 ASSERT(hasUndecided(indexingType()));
740 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
741 return m_butterfly
->contiguous();
744 ArrayStorage
* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM
& vm
, unsigned neededLength
)
746 Structure
* structure
= this->structure(vm
);
747 unsigned publicLength
= m_butterfly
->publicLength();
748 unsigned propertyCapacity
= structure
->outOfLineCapacity();
749 unsigned propertySize
= structure
->outOfLineSize();
751 Butterfly
* newButterfly
= Butterfly::createUninitialized(
752 vm
, this, 0, propertyCapacity
, true, ArrayStorage::sizeFor(neededLength
));
755 newButterfly
->propertyStorage() - propertySize
,
756 m_butterfly
->propertyStorage() - propertySize
,
757 propertySize
* sizeof(EncodedJSValue
));
759 ArrayStorage
* newStorage
= newButterfly
->arrayStorage();
760 newStorage
->setVectorLength(neededLength
);
761 newStorage
->setLength(publicLength
);
762 newStorage
->m_sparseMap
.clear();
763 newStorage
->m_indexBias
= 0;
764 newStorage
->m_numValuesInVector
= 0;
769 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
771 DeferGC
deferGC(vm
.heap
);
772 ASSERT(hasUndecided(indexingType()));
774 unsigned vectorLength
= m_butterfly
->vectorLength();
775 ArrayStorage
* storage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, vectorLength
);
776 // No need to copy elements.
778 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
779 setStructureAndButterfly(vm
, newStructure
, storage
->butterfly());
783 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
)
785 return convertUndecidedToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
788 ContiguousDoubles
JSObject::convertInt32ToDouble(VM
& vm
)
790 ASSERT(hasInt32(indexingType()));
792 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
793 WriteBarrier
<Unknown
>* current
= &m_butterfly
->contiguousInt32()[i
];
794 double* currentAsDouble
= bitwise_cast
<double*>(current
);
795 JSValue v
= current
->get();
797 *currentAsDouble
= PNaN
;
801 *currentAsDouble
= v
.asInt32();
804 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
));
805 return m_butterfly
->contiguousDouble();
808 ContiguousJSValues
JSObject::convertInt32ToContiguous(VM
& vm
)
810 ASSERT(hasInt32(indexingType()));
812 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
813 return m_butterfly
->contiguous();
816 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
818 DeferGC
deferGC(vm
.heap
);
819 ASSERT(hasInt32(indexingType()));
821 unsigned vectorLength
= m_butterfly
->vectorLength();
822 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, vectorLength
);
823 for (unsigned i
= 0; i
< m_butterfly
->publicLength(); i
++) {
824 JSValue v
= m_butterfly
->contiguous()[i
].get();
826 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
827 newStorage
->m_numValuesInVector
++;
829 ASSERT(newStorage
->m_vector
[i
].get().isEmpty());
832 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
833 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
837 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
)
839 return convertInt32ToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
842 ContiguousJSValues
JSObject::convertDoubleToContiguous(VM
& vm
)
844 ASSERT(hasDouble(indexingType()));
846 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
847 double* current
= &m_butterfly
->contiguousDouble()[i
];
848 WriteBarrier
<Unknown
>* currentAsValue
= bitwise_cast
<WriteBarrier
<Unknown
>*>(current
);
849 double value
= *current
;
850 if (value
!= value
) {
851 currentAsValue
->clear();
854 JSValue v
= JSValue(JSValue::EncodeAsDouble
, value
);
855 currentAsValue
->setWithoutWriteBarrier(v
);
858 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
859 return m_butterfly
->contiguous();
862 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
864 DeferGC
deferGC(vm
.heap
);
865 ASSERT(hasDouble(indexingType()));
867 unsigned vectorLength
= m_butterfly
->vectorLength();
868 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, vectorLength
);
869 for (unsigned i
= 0; i
< m_butterfly
->publicLength(); i
++) {
870 double value
= m_butterfly
->contiguousDouble()[i
];
871 if (value
== value
) {
872 newStorage
->m_vector
[i
].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble
, value
));
873 newStorage
->m_numValuesInVector
++;
875 ASSERT(newStorage
->m_vector
[i
].get().isEmpty());
878 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
879 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
883 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
)
885 return convertDoubleToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
888 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
890 DeferGC
deferGC(vm
.heap
);
891 ASSERT(hasContiguous(indexingType()));
893 unsigned vectorLength
= m_butterfly
->vectorLength();
894 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, vectorLength
);
895 for (unsigned i
= 0; i
< m_butterfly
->publicLength(); i
++) {
896 JSValue v
= m_butterfly
->contiguous()[i
].get();
898 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
899 newStorage
->m_numValuesInVector
++;
901 ASSERT(newStorage
->m_vector
[i
].get().isEmpty());
904 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
905 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
909 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
)
911 return convertContiguousToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
914 void JSObject::convertUndecidedForValue(VM
& vm
, JSValue value
)
916 if (value
.isInt32()) {
917 convertUndecidedToInt32(vm
);
921 if (value
.isDouble() && value
.asNumber() == value
.asNumber()) {
922 convertUndecidedToDouble(vm
);
926 convertUndecidedToContiguous(vm
);
929 void JSObject::createInitialForValueAndSet(VM
& vm
, unsigned index
, JSValue value
)
931 if (value
.isInt32()) {
932 createInitialInt32(vm
, index
+ 1)[index
].set(vm
, this, value
);
936 if (value
.isDouble()) {
937 double doubleValue
= value
.asNumber();
938 if (doubleValue
== doubleValue
) {
939 createInitialDouble(vm
, index
+ 1)[index
] = doubleValue
;
944 createInitialContiguous(vm
, index
+ 1)[index
].set(vm
, this, value
);
947 void JSObject::convertInt32ForValue(VM
& vm
, JSValue value
)
949 ASSERT(!value
.isInt32());
951 if (value
.isDouble() && !std::isnan(value
.asDouble())) {
952 convertInt32ToDouble(vm
);
956 convertInt32ToContiguous(vm
);
959 void JSObject::setIndexQuicklyToUndecided(VM
& vm
, unsigned index
, JSValue value
)
961 ASSERT(index
< m_butterfly
->publicLength());
962 ASSERT(index
< m_butterfly
->vectorLength());
963 convertUndecidedForValue(vm
, value
);
964 setIndexQuickly(vm
, index
, value
);
967 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
969 ASSERT(!value
.isInt32());
970 convertInt32ForValue(vm
, value
);
971 setIndexQuickly(vm
, index
, value
);
974 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
976 ASSERT(!value
.isNumber() || value
.asNumber() != value
.asNumber());
977 convertDoubleToContiguous(vm
);
978 setIndexQuickly(vm
, index
, value
);
981 ContiguousJSValues
JSObject::ensureInt32Slow(VM
& vm
)
983 ASSERT(inherits(info()));
985 switch (indexingType()) {
986 case ALL_BLANK_INDEXING_TYPES
:
987 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
988 return ContiguousJSValues();
989 return createInitialInt32(vm
, 0);
991 case ALL_UNDECIDED_INDEXING_TYPES
:
992 return convertUndecidedToInt32(vm
);
994 case ALL_DOUBLE_INDEXING_TYPES
:
995 case ALL_CONTIGUOUS_INDEXING_TYPES
:
996 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
997 return ContiguousJSValues();
1001 return ContiguousJSValues();
1005 ContiguousDoubles
JSObject::ensureDoubleSlow(VM
& vm
)
1007 ASSERT(inherits(info()));
1009 switch (indexingType()) {
1010 case ALL_BLANK_INDEXING_TYPES
:
1011 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
1012 return ContiguousDoubles();
1013 return createInitialDouble(vm
, 0);
1015 case ALL_UNDECIDED_INDEXING_TYPES
:
1016 return convertUndecidedToDouble(vm
);
1018 case ALL_INT32_INDEXING_TYPES
:
1019 return convertInt32ToDouble(vm
);
1021 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1022 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1023 return ContiguousDoubles();
1027 return ContiguousDoubles();
1031 ContiguousJSValues
JSObject::ensureContiguousSlow(VM
& vm
)
1033 ASSERT(inherits(info()));
1035 switch (indexingType()) {
1036 case ALL_BLANK_INDEXING_TYPES
:
1037 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
1038 return ContiguousJSValues();
1039 return createInitialContiguous(vm
, 0);
1041 case ALL_UNDECIDED_INDEXING_TYPES
:
1042 return convertUndecidedToContiguous(vm
);
1044 case ALL_INT32_INDEXING_TYPES
:
1045 return convertInt32ToContiguous(vm
);
1047 case ALL_DOUBLE_INDEXING_TYPES
:
1048 return convertDoubleToContiguous(vm
);
1050 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1051 return ContiguousJSValues();
1055 return ContiguousJSValues();
1059 ArrayStorage
* JSObject::ensureArrayStorageSlow(VM
& vm
)
1061 ASSERT(inherits(info()));
1063 switch (indexingType()) {
1064 case ALL_BLANK_INDEXING_TYPES
:
1065 if (UNLIKELY(indexingShouldBeSparse()))
1066 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
);
1067 return createInitialArrayStorage(vm
);
1069 case ALL_UNDECIDED_INDEXING_TYPES
:
1070 ASSERT(!indexingShouldBeSparse());
1071 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1072 return convertUndecidedToArrayStorage(vm
);
1074 case ALL_INT32_INDEXING_TYPES
:
1075 ASSERT(!indexingShouldBeSparse());
1076 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1077 return convertInt32ToArrayStorage(vm
);
1079 case ALL_DOUBLE_INDEXING_TYPES
:
1080 ASSERT(!indexingShouldBeSparse());
1081 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1082 return convertDoubleToArrayStorage(vm
);
1084 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1085 ASSERT(!indexingShouldBeSparse());
1086 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1087 return convertContiguousToArrayStorage(vm
);
1090 RELEASE_ASSERT_NOT_REACHED();
1095 ArrayStorage
* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM
& vm
)
1097 switch (indexingType()) {
1098 case ALL_BLANK_INDEXING_TYPES
: {
1099 createArrayStorage(vm
, 0, 0);
1100 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1101 map
->setSparseMode();
1102 return arrayStorage();
1105 case ALL_UNDECIDED_INDEXING_TYPES
:
1106 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertUndecidedToArrayStorage(vm
));
1108 case ALL_INT32_INDEXING_TYPES
:
1109 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertInt32ToArrayStorage(vm
));
1111 case ALL_DOUBLE_INDEXING_TYPES
:
1112 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertDoubleToArrayStorage(vm
));
1114 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1115 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertContiguousToArrayStorage(vm
));
1117 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1118 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, m_butterfly
->arrayStorage());
1126 void JSObject::switchToSlowPutArrayStorage(VM
& vm
)
1128 switch (indexingType()) {
1129 case ALL_UNDECIDED_INDEXING_TYPES
:
1130 convertUndecidedToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1133 case ALL_INT32_INDEXING_TYPES
:
1134 convertInt32ToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1137 case ALL_DOUBLE_INDEXING_TYPES
:
1138 convertDoubleToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1141 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1142 convertContiguousToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1145 case NonArrayWithArrayStorage
:
1146 case ArrayWithArrayStorage
: {
1147 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), SwitchToSlowPutArrayStorage
);
1148 setStructure(vm
, newStructure
);
1158 void JSObject::setPrototype(VM
& vm
, JSValue prototype
)
1161 if (prototype
.isObject())
1162 vm
.prototypeMap
.addPrototype(asObject(prototype
));
1164 Structure
* newStructure
= Structure::changePrototypeTransition(vm
, structure(vm
), prototype
);
1165 setStructure(vm
, newStructure
);
1167 if (!newStructure
->anyObjectInChainMayInterceptIndexedAccesses())
1170 if (vm
.prototypeMap
.isPrototype(this)) {
1171 newStructure
->globalObject()->haveABadTime(vm
);
1175 if (!hasIndexedProperties(indexingType()))
1178 if (shouldUseSlowPut(indexingType()))
1181 switchToSlowPutArrayStorage(vm
);
1184 bool JSObject::setPrototypeWithCycleCheck(ExecState
* exec
, JSValue prototype
)
1186 ASSERT(methodTable(exec
->vm())->toThis(this, exec
, NotStrictMode
) == this);
1187 JSValue nextPrototype
= prototype
;
1188 while (nextPrototype
&& nextPrototype
.isObject()) {
1189 if (nextPrototype
== this)
1191 nextPrototype
= asObject(nextPrototype
)->prototype();
1193 setPrototype(exec
->vm(), prototype
);
1197 bool JSObject::allowsAccessFrom(ExecState
* exec
)
1199 JSGlobalObject
* globalObject
= this->globalObject();
1200 return globalObject
->globalObjectMethodTable()->allowsAccessFrom(globalObject
, exec
);
1203 void JSObject::putGetter(ExecState
* exec
, PropertyName propertyName
, JSValue getter
)
1205 PropertyDescriptor descriptor
;
1206 descriptor
.setGetter(getter
);
1207 descriptor
.setEnumerable(true);
1208 descriptor
.setConfigurable(true);
1209 defineOwnProperty(this, exec
, propertyName
, descriptor
, false);
1212 void JSObject::putSetter(ExecState
* exec
, PropertyName propertyName
, JSValue setter
)
1214 PropertyDescriptor descriptor
;
1215 descriptor
.setSetter(setter
);
1216 descriptor
.setEnumerable(true);
1217 descriptor
.setConfigurable(true);
1218 defineOwnProperty(this, exec
, propertyName
, descriptor
, false);
1221 void JSObject::putDirectAccessor(ExecState
* exec
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1223 ASSERT(value
.isGetterSetter() && (attributes
& Accessor
));
1225 if (Optional
<uint32_t> index
= parseIndex(propertyName
)) {
1226 putDirectIndex(exec
, index
.value(), value
, attributes
, PutDirectIndexLikePutDirect
);
1230 putDirectNonIndexAccessor(exec
->vm(), propertyName
, value
, attributes
);
1233 void JSObject::putDirectCustomAccessor(VM
& vm
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1235 ASSERT(!parseIndex(propertyName
));
1237 PutPropertySlot
slot(this);
1238 putDirectInternal
<PutModeDefineOwnProperty
>(vm
, propertyName
, value
, attributes
, slot
);
1240 ASSERT(slot
.type() == PutPropertySlot::NewProperty
);
1242 Structure
* structure
= this->structure(vm
);
1243 if (attributes
& ReadOnly
)
1244 structure
->setContainsReadOnlyProperties();
1245 structure
->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName
== vm
.propertyNames
->underscoreProto
);
1248 void JSObject::putDirectNonIndexAccessor(VM
& vm
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1250 PutPropertySlot
slot(this);
1251 putDirectInternal
<PutModeDefineOwnProperty
>(vm
, propertyName
, value
, attributes
, slot
);
1253 Structure
* structure
= this->structure(vm
);
1254 if (attributes
& ReadOnly
)
1255 structure
->setContainsReadOnlyProperties();
1257 structure
->setHasGetterSetterPropertiesWithProtoCheck(propertyName
== vm
.propertyNames
->underscoreProto
);
1260 bool JSObject::hasProperty(ExecState
* exec
, PropertyName propertyName
) const
1262 PropertySlot
slot(this);
1263 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1266 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
1268 PropertySlot
slot(this);
1269 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1273 bool JSObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
1275 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1277 if (Optional
<uint32_t> index
= parseIndex(propertyName
))
1278 return thisObject
->methodTable(exec
->vm())->deletePropertyByIndex(thisObject
, exec
, index
.value());
1280 if (!thisObject
->staticFunctionsReified())
1281 thisObject
->reifyStaticFunctionsForDelete(exec
);
1283 unsigned attributes
;
1284 VM
& vm
= exec
->vm();
1285 if (isValidOffset(thisObject
->structure(vm
)->get(vm
, propertyName
, attributes
))) {
1286 if (attributes
& DontDelete
&& !vm
.isInDefineOwnProperty())
1288 thisObject
->removeDirect(vm
, propertyName
);
1292 // Look in the static hashtable of properties
1293 const HashTableValue
* entry
= thisObject
->findPropertyHashEntry(propertyName
);
1295 if (entry
->attributes() & DontDelete
&& !vm
.isInDefineOwnProperty())
1296 return false; // this builtin property can't be deleted
1298 PutPropertySlot
slot(thisObject
);
1299 if (!(entry
->attributes() & BuiltinOrFunctionOrAccessor
)) {
1300 ASSERT(thisObject
->staticFunctionsReified());
1301 putEntry(exec
, entry
, thisObject
, propertyName
, jsUndefined(), slot
);
1308 bool JSObject::hasOwnProperty(ExecState
* exec
, PropertyName propertyName
) const
1310 PropertySlot
slot(this);
1311 return const_cast<JSObject
*>(this)->methodTable(exec
->vm())->getOwnPropertySlot(const_cast<JSObject
*>(this), exec
, propertyName
, slot
);
1314 bool JSObject::hasOwnProperty(ExecState
* exec
, unsigned propertyName
) const
1316 PropertySlot
slot(this);
1317 return const_cast<JSObject
*>(this)->methodTable(exec
->vm())->getOwnPropertySlotByIndex(const_cast<JSObject
*>(this), exec
, propertyName
, slot
);
1320 bool JSObject::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
1322 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1324 if (i
> MAX_ARRAY_INDEX
)
1325 return thisObject
->methodTable(exec
->vm())->deleteProperty(thisObject
, exec
, Identifier::from(exec
, i
));
1327 switch (thisObject
->indexingType()) {
1328 case ALL_BLANK_INDEXING_TYPES
:
1329 case ALL_UNDECIDED_INDEXING_TYPES
:
1332 case ALL_INT32_INDEXING_TYPES
:
1333 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1334 Butterfly
* butterfly
= thisObject
->butterfly();
1335 if (i
>= butterfly
->vectorLength())
1337 butterfly
->contiguous()[i
].clear();
1341 case ALL_DOUBLE_INDEXING_TYPES
: {
1342 Butterfly
* butterfly
= thisObject
->butterfly();
1343 if (i
>= butterfly
->vectorLength())
1345 butterfly
->contiguousDouble()[i
] = PNaN
;
1349 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1350 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
1352 if (i
< storage
->vectorLength()) {
1353 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
1356 --storage
->m_numValuesInVector
;
1358 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1359 SparseArrayValueMap::iterator it
= map
->find(i
);
1360 if (it
!= map
->notFound()) {
1361 if (it
->value
.attributes
& DontDelete
)
1371 RELEASE_ASSERT_NOT_REACHED();
1376 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, PropertyName propertyName
)
1378 JSValue function
= object
->get(exec
, propertyName
);
1380 CallType callType
= getCallData(function
, callData
);
1381 if (callType
== CallTypeNone
)
1382 return exec
->exception();
1384 // Prevent "toString" and "valueOf" from observing execution if an exception
1386 if (exec
->hadException())
1387 return exec
->exception();
1389 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
1390 ASSERT(!result
.isGetterSetter());
1391 if (exec
->hadException())
1392 return exec
->exception();
1393 if (result
.isObject())
1398 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
1400 result
= methodTable(exec
->vm())->defaultValue(this, exec
, PreferNumber
);
1401 number
= result
.toNumber(exec
);
1402 return !result
.isString();
1406 JSValue
JSObject::defaultValue(const JSObject
* object
, ExecState
* exec
, PreferredPrimitiveType hint
)
1408 // Make sure that whatever default value methods there are on object's prototype chain are
1410 object
->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec
->vm());
1412 // Must call toString first for Date objects.
1413 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& object
->prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
1414 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1417 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1421 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1424 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1429 ASSERT(!exec
->hadException());
1431 return exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("No default value")));
1434 const HashTableValue
* JSObject::findPropertyHashEntry(PropertyName propertyName
) const
1436 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1437 if (const HashTable
* propHashTable
= info
->staticPropHashTable
) {
1438 if (const HashTableValue
* entry
= propHashTable
->entry(propertyName
))
1445 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
)
1447 VM
& vm
= exec
->vm();
1448 TypeInfo info
= structure(vm
)->typeInfo();
1449 if (info
.implementsDefaultHasInstance())
1450 return defaultHasInstance(exec
, value
, get(exec
, exec
->propertyNames().prototype
));
1451 if (info
.implementsHasInstance())
1452 return methodTable(vm
)->customHasInstance(this, exec
, value
);
1453 vm
.throwException(exec
, createInvalidInstanceofParameterError(exec
, this));
1457 bool JSObject::defaultHasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
1459 if (!value
.isObject())
1462 if (!proto
.isObject()) {
1463 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
1467 JSObject
* object
= asObject(value
);
1468 while ((object
= object
->prototype().getObject())) {
1469 if (proto
== object
)
1475 void JSObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1477 object
->methodTable(exec
->vm())->getOwnPropertyNames(object
, exec
, propertyNames
, mode
);
1479 if (object
->prototype().isNull())
1482 VM
& vm
= exec
->vm();
1483 JSObject
* prototype
= asObject(object
->prototype());
1485 if (prototype
->structure(vm
)->typeInfo().overridesGetPropertyNames()) {
1486 prototype
->methodTable(vm
)->getPropertyNames(prototype
, exec
, propertyNames
, mode
);
1489 prototype
->methodTable(vm
)->getOwnPropertyNames(prototype
, exec
, propertyNames
, mode
);
1490 JSValue nextProto
= prototype
->prototype();
1491 if (nextProto
.isNull())
1493 prototype
= asObject(nextProto
);
1497 void JSObject::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1499 if (!mode
.includeJSObjectProperties()) {
1500 // We still have to get non-indexed properties from any subclasses of JSObject that have them.
1501 object
->methodTable(exec
->vm())->getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
1505 // Add numeric properties first. That appears to be the accepted convention.
1506 // FIXME: Filling PropertyNameArray with an identifier for every integer
1507 // is incredibly inefficient for large arrays. We need a different approach,
1508 // which almost certainly means a different structure for PropertyNameArray.
1509 switch (object
->indexingType()) {
1510 case ALL_BLANK_INDEXING_TYPES
:
1511 case ALL_UNDECIDED_INDEXING_TYPES
:
1514 case ALL_INT32_INDEXING_TYPES
:
1515 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1516 Butterfly
* butterfly
= object
->butterfly();
1517 unsigned usedLength
= butterfly
->publicLength();
1518 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1519 if (!butterfly
->contiguous()[i
])
1521 propertyNames
.add(i
);
1526 case ALL_DOUBLE_INDEXING_TYPES
: {
1527 Butterfly
* butterfly
= object
->butterfly();
1528 unsigned usedLength
= butterfly
->publicLength();
1529 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1530 double value
= butterfly
->contiguousDouble()[i
];
1533 propertyNames
.add(i
);
1538 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1539 ArrayStorage
* storage
= object
->m_butterfly
->arrayStorage();
1541 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
1542 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
1543 if (storage
->m_vector
[i
])
1544 propertyNames
.add(i
);
1547 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1548 Vector
<unsigned, 0, UnsafeVectorOverflow
> keys
;
1549 keys
.reserveInitialCapacity(map
->size());
1551 SparseArrayValueMap::const_iterator end
= map
->end();
1552 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
1553 if (mode
.includeDontEnumProperties() || !(it
->value
.attributes
& DontEnum
))
1554 keys
.uncheckedAppend(static_cast<unsigned>(it
->key
));
1557 std::sort(keys
.begin(), keys
.end());
1558 for (unsigned i
= 0; i
< keys
.size(); ++i
)
1559 propertyNames
.add(keys
[i
]);
1565 RELEASE_ASSERT_NOT_REACHED();
1568 object
->methodTable(exec
->vm())->getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
1571 void JSObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1573 getClassPropertyNames(exec
, object
->classInfo(), propertyNames
, mode
, object
->staticFunctionsReified());
1575 if (!mode
.includeJSObjectProperties())
1578 VM
& vm
= exec
->vm();
1579 object
->structure(vm
)->getPropertyNamesFromStructure(vm
, propertyNames
, mode
);
1582 double JSObject::toNumber(ExecState
* exec
) const
1584 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
1585 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
1587 return primitive
.toNumber(exec
);
1590 JSString
* JSObject::toString(ExecState
* exec
) const
1592 JSValue primitive
= toPrimitive(exec
, PreferString
);
1593 if (exec
->hadException())
1594 return jsEmptyString(exec
);
1595 return primitive
.toString(exec
);
1598 JSValue
JSObject::toThis(JSCell
* cell
, ExecState
*, ECMAMode
)
1600 return jsCast
<JSObject
*>(cell
);
1603 bool JSObject::isCatchScopeObject() const
1605 return inherits(JSCatchScope::info());
1608 bool JSObject::isFunctionNameScopeObject() const
1610 return inherits(JSFunctionNameScope::info());
1613 void JSObject::seal(VM
& vm
)
1617 preventExtensions(vm
);
1618 setStructure(vm
, Structure::sealTransition(vm
, structure(vm
)));
1621 void JSObject::freeze(VM
& vm
)
1625 preventExtensions(vm
);
1626 setStructure(vm
, Structure::freezeTransition(vm
, structure(vm
)));
1629 void JSObject::preventExtensions(VM
& vm
)
1631 enterDictionaryIndexingMode(vm
);
1633 setStructure(vm
, Structure::preventExtensionsTransition(vm
, structure(vm
)));
1636 // This presently will flatten to an uncachable dictionary; this is suitable
1637 // for use in delete, we may want to do something different elsewhere.
1638 void JSObject::reifyStaticFunctionsForDelete(ExecState
* exec
)
1640 ASSERT(!staticFunctionsReified());
1641 VM
& vm
= exec
->vm();
1643 // If this object's ClassInfo has no static properties, then nothing to reify!
1644 // We can safely set the flag to avoid the expensive check again in the future.
1645 if (!classInfo()->hasStaticProperties()) {
1646 structure(vm
)->setStaticFunctionsReified(true);
1650 if (!structure(vm
)->isUncacheableDictionary())
1651 setStructure(vm
, Structure::toUncacheableDictionaryTransition(vm
, structure(vm
)));
1653 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1654 const HashTable
* hashTable
= info
->staticPropHashTable
;
1657 PropertySlot
slot(this);
1658 for (auto iter
= hashTable
->begin(); iter
!= hashTable
->end(); ++iter
) {
1659 if (iter
->attributes() & BuiltinOrFunctionOrAccessor
)
1660 setUpStaticFunctionSlot(globalObject()->globalExec(), iter
.value(), this, Identifier::fromString(&vm
, iter
.key()), slot
);
1664 structure(vm
)->setStaticFunctionsReified(true);
1667 bool JSObject::removeDirect(VM
& vm
, PropertyName propertyName
)
1669 Structure
* structure
= this->structure(vm
);
1670 if (!isValidOffset(structure
->get(vm
, propertyName
)))
1673 PropertyOffset offset
;
1674 if (structure
->isUncacheableDictionary()) {
1675 offset
= structure
->removePropertyWithoutTransition(vm
, propertyName
);
1676 if (offset
== invalidOffset
)
1678 putDirectUndefined(offset
);
1682 setStructure(vm
, Structure::removePropertyTransition(vm
, structure
, propertyName
, offset
));
1683 if (offset
== invalidOffset
)
1685 putDirectUndefined(offset
);
1689 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValue getterSetter
, unsigned attributes
, PropertyOffset offset
)
1691 if (structure()->isDictionary()) {
1692 slot
.setGetterSlot(this, attributes
, jsCast
<GetterSetter
*>(getterSetter
));
1695 slot
.setCacheableGetterSlot(this, attributes
, jsCast
<GetterSetter
*>(getterSetter
), offset
);
1698 void JSObject::putIndexedDescriptor(ExecState
* exec
, SparseArrayEntry
* entryInMap
, const PropertyDescriptor
& descriptor
, PropertyDescriptor
& oldDescriptor
)
1700 VM
& vm
= exec
->vm();
1701 auto map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1703 if (descriptor
.isDataDescriptor()) {
1704 if (descriptor
.value())
1705 entryInMap
->set(vm
, map
, descriptor
.value());
1706 else if (oldDescriptor
.isAccessorDescriptor())
1707 entryInMap
->set(vm
, map
, jsUndefined());
1708 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~Accessor
;
1712 if (descriptor
.isAccessorDescriptor()) {
1713 JSObject
* getter
= 0;
1714 if (descriptor
.getterPresent())
1715 getter
= descriptor
.getterObject();
1716 else if (oldDescriptor
.isAccessorDescriptor())
1717 getter
= oldDescriptor
.getterObject();
1718 JSObject
* setter
= 0;
1719 if (descriptor
.setterPresent())
1720 setter
= descriptor
.setterObject();
1721 else if (oldDescriptor
.isAccessorDescriptor())
1722 setter
= oldDescriptor
.setterObject();
1724 GetterSetter
* accessor
= GetterSetter::create(vm
, exec
->lexicalGlobalObject());
1726 accessor
->setGetter(vm
, exec
->lexicalGlobalObject(), getter
);
1728 accessor
->setSetter(vm
, exec
->lexicalGlobalObject(), setter
);
1730 entryInMap
->set(vm
, map
, accessor
);
1731 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~ReadOnly
;
1735 ASSERT(descriptor
.isGenericDescriptor());
1736 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
);
1739 // Defined in ES5.1 8.12.9
1740 bool JSObject::defineOwnIndexedProperty(ExecState
* exec
, unsigned index
, const PropertyDescriptor
& descriptor
, bool throwException
)
1742 ASSERT(index
<= MAX_ARRAY_INDEX
);
1744 if (!inSparseIndexingMode()) {
1745 // Fast case: we're putting a regular property to a regular array
1746 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1747 // however if the property currently exists missing attributes will override from their current 'true'
1748 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1749 if (!descriptor
.attributes()) {
1750 ASSERT(!descriptor
.isAccessorDescriptor());
1751 return putDirectIndex(exec
, index
, descriptor
.value(), 0, throwException
? PutDirectIndexShouldThrow
: PutDirectIndexShouldNotThrow
);
1754 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec
->vm());
1757 if (descriptor
.attributes() & (ReadOnly
| Accessor
))
1758 notifyPresenceOfIndexedAccessors(exec
->vm());
1760 SparseArrayValueMap
* map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1761 RELEASE_ASSERT(map
);
1763 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1764 SparseArrayValueMap::AddResult result
= map
->add(this, index
);
1765 SparseArrayEntry
* entryInMap
= &result
.iterator
->value
;
1767 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1768 // 3. If current is undefined and extensible is false, then Reject.
1769 // 4. If current is undefined and extensible is true, then
1770 if (result
.isNewEntry
) {
1771 if (!isExtensible()) {
1772 map
->remove(result
.iterator
);
1773 return reject(exec
, throwException
, "Attempting to define property on object that is not extensible.");
1776 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1777 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1778 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1779 // created property is set to its default value.
1780 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1781 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1782 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1783 // is set to its default value.
1784 // 4.c. Return true.
1786 PropertyDescriptor defaults
;
1787 entryInMap
->setWithoutWriteBarrier(jsUndefined());
1788 entryInMap
->attributes
= DontDelete
| DontEnum
| ReadOnly
;
1789 entryInMap
->get(defaults
);
1791 putIndexedDescriptor(exec
, entryInMap
, descriptor
, defaults
);
1792 if (index
>= m_butterfly
->arrayStorage()->length())
1793 m_butterfly
->arrayStorage()->setLength(index
+ 1);
1797 // 5. Return true, if every field in Desc is absent.
1798 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
1799 PropertyDescriptor current
;
1800 entryInMap
->get(current
);
1801 if (descriptor
.isEmpty() || descriptor
.equalTo(exec
, current
))
1804 // 7. If the [[Configurable]] field of current is false then
1805 if (!current
.configurable()) {
1806 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1807 if (descriptor
.configurablePresent() && descriptor
.configurable())
1808 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
1809 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
1810 if (descriptor
.enumerablePresent() && current
.enumerable() != descriptor
.enumerable())
1811 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
1814 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1815 if (!descriptor
.isGenericDescriptor()) {
1816 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1817 if (current
.isDataDescriptor() != descriptor
.isDataDescriptor()) {
1818 // 9.a. Reject, if the [[Configurable]] field of current is false.
1819 if (!current
.configurable())
1820 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
1821 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1822 // data property to an accessor property. Preserve the existing values of the converted property's
1823 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1824 // their default values.
1825 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1826 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1827 // attributes and set the rest of the property's attributes to their default values.
1828 } else if (current
.isDataDescriptor() && descriptor
.isDataDescriptor()) {
1829 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1830 // 10.a. If the [[Configurable]] field of current is false, then
1831 if (!current
.configurable() && !current
.writable()) {
1832 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1833 if (descriptor
.writable())
1834 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
1835 // 10.a.ii. If the [[Writable]] field of current is false, then
1836 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1837 if (descriptor
.value() && !sameValue(exec
, descriptor
.value(), current
.value()))
1838 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
1840 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1842 ASSERT(current
.isAccessorDescriptor() && current
.getterPresent() && current
.setterPresent());
1843 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1844 if (!current
.configurable()) {
1845 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1846 if (descriptor
.setterPresent() && descriptor
.setter() != current
.setter())
1847 return reject(exec
, throwException
, "Attempting to change the setter of an unconfigurable property.");
1848 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1849 if (descriptor
.getterPresent() && descriptor
.getter() != current
.getter())
1850 return reject(exec
, throwException
, "Attempting to change the getter of an unconfigurable property.");
1855 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
1856 putIndexedDescriptor(exec
, entryInMap
, descriptor
, current
);
1861 SparseArrayValueMap
* JSObject::allocateSparseIndexMap(VM
& vm
)
1863 SparseArrayValueMap
* result
= SparseArrayValueMap::create(vm
);
1864 arrayStorage()->m_sparseMap
.set(vm
, this, result
);
1868 void JSObject::deallocateSparseIndexMap()
1870 if (ArrayStorage
* arrayStorage
= arrayStorageOrNull())
1871 arrayStorage
->m_sparseMap
.clear();
1874 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState
* exec
, JSValue thisValue
, unsigned i
, JSValue value
, bool shouldThrow
)
1876 for (JSObject
* current
= this; ;) {
1877 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1878 // allows a prototype to intercept a put if (a) the prototype declares the property
1879 // we're after rather than intercepting it via an override of JSObject::put(), and
1880 // (b) that property is declared as ReadOnly or Accessor.
1882 ArrayStorage
* storage
= current
->arrayStorageOrNull();
1883 if (storage
&& storage
->m_sparseMap
) {
1884 SparseArrayValueMap::iterator iter
= storage
->m_sparseMap
->find(i
);
1885 if (iter
!= storage
->m_sparseMap
->notFound() && (iter
->value
.attributes
& (Accessor
| ReadOnly
))) {
1886 iter
->value
.put(exec
, thisValue
, storage
->m_sparseMap
.get(), value
, shouldThrow
);
1891 JSValue prototypeValue
= current
->prototype();
1892 if (prototypeValue
.isNull())
1895 current
= asObject(prototypeValue
);
1899 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
1901 JSValue prototypeValue
= prototype();
1902 if (prototypeValue
.isNull())
1905 return asObject(prototypeValue
)->attemptToInterceptPutByIndexOnHoleForPrototype(exec
, this, i
, value
, shouldThrow
);
1908 template<IndexingType indexingShape
>
1909 void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState
* exec
, unsigned i
, JSValue value
)
1911 ASSERT((indexingType() & IndexingShapeMask
) == indexingShape
);
1912 ASSERT(!indexingShouldBeSparse());
1914 // For us to get here, the index is either greater than the public length, or greater than
1915 // or equal to the vector length.
1916 ASSERT(i
>= m_butterfly
->vectorLength());
1918 VM
& vm
= exec
->vm();
1920 if (i
>= MAX_ARRAY_INDEX
- 1
1921 || (i
>= MIN_SPARSE_ARRAY_INDEX
1922 && !isDenseEnoughForVector(i
, countElements
<indexingShape
>(butterfly())))
1923 || indexIsSufficientlyBeyondLengthForSparseMap(i
, m_butterfly
->vectorLength())) {
1924 ASSERT(i
<= MAX_ARRAY_INDEX
);
1925 ensureArrayStorageSlow(vm
);
1926 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1927 map
->putEntry(exec
, this, i
, value
, false);
1928 ASSERT(i
>= arrayStorage()->length());
1929 arrayStorage()->setLength(i
+ 1);
1933 ensureLength(vm
, i
+ 1);
1935 RELEASE_ASSERT(i
< m_butterfly
->vectorLength());
1936 switch (indexingShape
) {
1938 ASSERT(value
.isInt32());
1939 m_butterfly
->contiguousInt32()[i
].setWithoutWriteBarrier(value
);
1943 ASSERT(value
.isNumber());
1944 double valueAsDouble
= value
.asNumber();
1945 ASSERT(valueAsDouble
== valueAsDouble
);
1946 m_butterfly
->contiguousDouble()[i
] = valueAsDouble
;
1950 case ContiguousShape
:
1951 m_butterfly
->contiguous()[i
].set(vm
, this, value
);
1959 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
, ArrayStorage
* storage
)
1961 VM
& vm
= exec
->vm();
1963 // i should be a valid array index that is outside of the current vector.
1964 ASSERT(i
<= MAX_ARRAY_INDEX
);
1965 ASSERT(i
>= storage
->vectorLength());
1967 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
1969 // First, handle cases where we don't currently have a sparse map.
1971 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
1972 ASSERT(isExtensible());
1974 // Update m_length if necessary.
1975 if (i
>= storage
->length())
1976 storage
->setLength(i
+ 1);
1978 // Check that it is sensible to still be using a vector, and then try to grow the vector.
1979 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength())
1980 && isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)
1981 && increaseVectorLength(vm
, i
+ 1))) {
1982 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1983 storage
= arrayStorage();
1984 storage
->m_vector
[i
].set(vm
, this, value
);
1985 ++storage
->m_numValuesInVector
;
1988 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1989 map
= allocateSparseIndexMap(exec
->vm());
1990 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
1994 // Update m_length if necessary.
1995 unsigned length
= storage
->length();
1997 // Prohibit growing the array if length is not writable.
1998 if (map
->lengthIsReadOnly() || !isExtensible()) {
2000 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
2004 storage
->setLength(length
);
2007 // We are currently using a map - check whether we still want to be doing so.
2008 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2009 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2010 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
)) {
2011 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
2015 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2016 storage
= arrayStorage();
2017 storage
->m_numValuesInVector
= numValuesInArray
;
2019 // Copy all values from the map into the vector, and delete the map.
2020 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2021 SparseArrayValueMap::const_iterator end
= map
->end();
2022 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2023 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2024 deallocateSparseIndexMap();
2026 // Store the new property into the vector.
2027 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2029 ++storage
->m_numValuesInVector
;
2030 valueSlot
.set(vm
, this, value
);
2033 void JSObject::putByIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
2035 VM
& vm
= exec
->vm();
2037 // i should be a valid array index that is outside of the current vector.
2038 ASSERT(i
<= MAX_ARRAY_INDEX
);
2040 switch (indexingType()) {
2041 case ALL_BLANK_INDEXING_TYPES
: {
2042 if (indexingShouldBeSparse()) {
2043 putByIndexBeyondVectorLengthWithArrayStorage(
2044 exec
, i
, value
, shouldThrow
,
2045 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2048 if (indexIsSufficientlyBeyondLengthForSparseMap(i
, 0) || i
>= MIN_SPARSE_ARRAY_INDEX
) {
2049 putByIndexBeyondVectorLengthWithArrayStorage(
2050 exec
, i
, value
, shouldThrow
, createArrayStorage(vm
, 0, 0));
2053 if (structure(vm
)->needsSlowPutIndexing()) {
2054 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2055 storage
->m_vector
[i
].set(vm
, this, value
);
2056 storage
->m_numValuesInVector
++;
2060 createInitialForValueAndSet(vm
, i
, value
);
2064 case ALL_UNDECIDED_INDEXING_TYPES
: {
2069 case ALL_INT32_INDEXING_TYPES
: {
2070 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2074 case ALL_DOUBLE_INDEXING_TYPES
: {
2075 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2079 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2080 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2084 case NonArrayWithSlowPutArrayStorage
:
2085 case ArrayWithSlowPutArrayStorage
: {
2086 // No own property present in the vector, but there might be in the sparse map!
2087 SparseArrayValueMap
* map
= arrayStorage()->m_sparseMap
.get();
2088 if (!(map
&& map
->contains(i
)) && attemptToInterceptPutByIndexOnHole(exec
, i
, value
, shouldThrow
))
2093 case NonArrayWithArrayStorage
:
2094 case ArrayWithArrayStorage
:
2095 putByIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, shouldThrow
, arrayStorage());
2099 RELEASE_ASSERT_NOT_REACHED();
2103 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
, ArrayStorage
* storage
)
2105 VM
& vm
= exec
->vm();
2107 // i should be a valid array index that is outside of the current vector.
2108 ASSERT(hasAnyArrayStorage(indexingType()));
2109 ASSERT(arrayStorage() == storage
);
2110 ASSERT(i
>= storage
->vectorLength() || attributes
);
2111 ASSERT(i
<= MAX_ARRAY_INDEX
);
2113 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
2115 // First, handle cases where we don't currently have a sparse map.
2117 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2118 ASSERT(isExtensible());
2120 // Update m_length if necessary.
2121 if (i
>= storage
->length())
2122 storage
->setLength(i
+ 1);
2124 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2127 && (isDenseEnoughForVector(i
, storage
->m_numValuesInVector
))
2128 && !indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength()))
2129 && increaseVectorLength(vm
, i
+ 1)) {
2130 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2131 storage
= arrayStorage();
2132 storage
->m_vector
[i
].set(vm
, this, value
);
2133 ++storage
->m_numValuesInVector
;
2136 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2137 map
= allocateSparseIndexMap(exec
->vm());
2138 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2141 // Update m_length if necessary.
2142 unsigned length
= storage
->length();
2144 if (mode
!= PutDirectIndexLikePutDirect
) {
2145 // Prohibit growing the array if length is not writable.
2146 if (map
->lengthIsReadOnly())
2147 return reject(exec
, mode
== PutDirectIndexShouldThrow
, StrictModeReadonlyPropertyWriteError
);
2148 if (!isExtensible())
2149 return reject(exec
, mode
== PutDirectIndexShouldThrow
, "Attempting to define property on object that is not extensible.");
2152 storage
->setLength(length
);
2155 // We are currently using a map - check whether we still want to be doing so.
2156 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2157 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2158 if (map
->sparseMode() || attributes
|| !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
))
2159 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2161 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2162 storage
= arrayStorage();
2163 storage
->m_numValuesInVector
= numValuesInArray
;
2165 // Copy all values from the map into the vector, and delete the map.
2166 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2167 SparseArrayValueMap::const_iterator end
= map
->end();
2168 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2169 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2170 deallocateSparseIndexMap();
2172 // Store the new property into the vector.
2173 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2175 ++storage
->m_numValuesInVector
;
2176 valueSlot
.set(vm
, this, value
);
2180 bool JSObject::putDirectIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
)
2182 VM
& vm
= exec
->vm();
2184 // i should be a valid array index that is outside of the current vector.
2185 ASSERT(i
<= MAX_ARRAY_INDEX
);
2187 if (attributes
& (ReadOnly
| Accessor
))
2188 notifyPresenceOfIndexedAccessors(vm
);
2190 switch (indexingType()) {
2191 case ALL_BLANK_INDEXING_TYPES
: {
2192 if (indexingShouldBeSparse() || attributes
) {
2193 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2194 exec
, i
, value
, attributes
, mode
,
2195 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2197 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
2198 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2199 exec
, i
, value
, attributes
, mode
, createArrayStorage(vm
, 0, 0));
2201 if (structure(vm
)->needsSlowPutIndexing()) {
2202 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2203 storage
->m_vector
[i
].set(vm
, this, value
);
2204 storage
->m_numValuesInVector
++;
2208 createInitialForValueAndSet(vm
, i
, value
);
2212 case ALL_UNDECIDED_INDEXING_TYPES
: {
2213 convertUndecidedForValue(exec
->vm(), value
);
2215 return putDirectIndex(exec
, i
, value
, attributes
, mode
);
2218 case ALL_INT32_INDEXING_TYPES
: {
2219 if (attributes
& (ReadOnly
| Accessor
)) {
2220 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2221 exec
, i
, value
, attributes
, mode
, convertInt32ToArrayStorage(vm
));
2223 if (!value
.isInt32()) {
2224 convertInt32ForValue(vm
, value
);
2225 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2227 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2231 case ALL_DOUBLE_INDEXING_TYPES
: {
2232 if (attributes
& (ReadOnly
| Accessor
)) {
2233 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2234 exec
, i
, value
, attributes
, mode
, convertDoubleToArrayStorage(vm
));
2236 if (!value
.isNumber()) {
2237 convertDoubleToContiguous(vm
);
2238 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2240 double valueAsDouble
= value
.asNumber();
2241 if (valueAsDouble
!= valueAsDouble
) {
2242 convertDoubleToContiguous(vm
);
2243 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2245 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2249 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2250 if (attributes
& (ReadOnly
| Accessor
)) {
2251 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2252 exec
, i
, value
, attributes
, mode
, convertContiguousToArrayStorage(vm
));
2254 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2258 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
2259 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, attributes
, mode
, arrayStorage());
2262 RELEASE_ASSERT_NOT_REACHED();
2267 void JSObject::putDirectNativeFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2269 StringImpl
* name
= propertyName
.publicName();
2271 name
= vm
.propertyNames
->anonymous
.impl();
2274 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2275 putDirect(vm
, propertyName
, function
, attributes
);
2278 JSFunction
* JSObject::putDirectBuiltinFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2280 StringImpl
* name
= propertyName
.publicName();
2282 name
= vm
.propertyNames
->anonymous
.impl();
2284 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2285 putDirect(vm
, propertyName
, function
, attributes
);
2289 JSFunction
* JSObject::putDirectBuiltinFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2291 StringImpl
* name
= propertyName
.publicName();
2293 name
= vm
.propertyNames
->anonymous
.impl();
2295 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2296 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2300 void JSObject::putDirectNativeFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2302 StringImpl
* name
= propertyName
.publicName();
2305 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2306 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2309 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned currentVectorLength
, unsigned currentLength
, unsigned desiredLength
)
2311 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
2313 unsigned increasedLength
;
2314 unsigned maxInitLength
= std::min(currentLength
, 100000U);
2316 if (desiredLength
< maxInitLength
)
2317 increasedLength
= maxInitLength
;
2318 else if (!currentVectorLength
)
2319 increasedLength
= std::max(desiredLength
, lastArraySize
);
2321 increasedLength
= timesThreePlusOneDividedByTwo(desiredLength
);
2324 ASSERT(increasedLength
>= desiredLength
);
2326 lastArraySize
= std::min(increasedLength
, FIRST_VECTOR_GROW
);
2328 return std::min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
2331 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned desiredLength
)
2333 unsigned vectorLength
;
2336 if (hasIndexedProperties(indexingType())) {
2337 vectorLength
= m_butterfly
->vectorLength();
2338 length
= m_butterfly
->publicLength();
2344 return getNewVectorLength(vectorLength
, length
, desiredLength
);
2347 template<IndexingType indexingShape
>
2348 unsigned JSObject::countElements(Butterfly
* butterfly
)
2350 unsigned numValues
= 0;
2351 for (unsigned i
= butterfly
->publicLength(); i
--;) {
2352 switch (indexingShape
) {
2354 case ContiguousShape
:
2355 if (butterfly
->contiguous()[i
])
2360 double value
= butterfly
->contiguousDouble()[i
];
2373 unsigned JSObject::countElements()
2375 switch (indexingType()) {
2376 case ALL_BLANK_INDEXING_TYPES
:
2377 case ALL_UNDECIDED_INDEXING_TYPES
:
2380 case ALL_INT32_INDEXING_TYPES
:
2381 return countElements
<Int32Shape
>(butterfly());
2383 case ALL_DOUBLE_INDEXING_TYPES
:
2384 return countElements
<DoubleShape
>(butterfly());
2386 case ALL_CONTIGUOUS_INDEXING_TYPES
:
2387 return countElements
<ContiguousShape
>(butterfly());
2395 bool JSObject::increaseVectorLength(VM
& vm
, unsigned newLength
)
2397 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2398 // to the vector. Callers have to account for that, because they can do it more efficiently.
2399 if (newLength
> MAX_STORAGE_VECTOR_LENGTH
)
2402 ArrayStorage
* storage
= arrayStorage();
2404 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
2405 && !isDenseEnoughForVector(newLength
, storage
->m_numValuesInVector
))
2408 unsigned indexBias
= storage
->m_indexBias
;
2409 unsigned vectorLength
= storage
->vectorLength();
2410 ASSERT(newLength
> vectorLength
);
2411 unsigned newVectorLength
= getNewVectorLength(newLength
);
2413 // Fast case - there is no precapacity. In these cases a realloc makes sense.
2414 Structure
* structure
= this->structure(vm
);
2415 if (LIKELY(!indexBias
)) {
2416 DeferGC
deferGC(vm
.heap
);
2417 Butterfly
* newButterfly
= storage
->butterfly()->growArrayRight(
2418 vm
, this, structure
, structure
->outOfLineCapacity(), true,
2419 ArrayStorage::sizeFor(vectorLength
), ArrayStorage::sizeFor(newVectorLength
));
2422 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2423 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2427 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2428 DeferGC
deferGC(vm
.heap
);
2429 unsigned newIndexBias
= std::min(indexBias
>> 1, MAX_STORAGE_VECTOR_LENGTH
- newVectorLength
);
2430 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(
2432 structure
->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength
),
2433 newIndexBias
, true, ArrayStorage::sizeFor(newVectorLength
));
2436 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2437 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
2438 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2442 void JSObject::ensureLengthSlow(VM
& vm
, unsigned length
)
2444 ASSERT(length
< MAX_ARRAY_INDEX
);
2445 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2446 ASSERT(length
> m_butterfly
->vectorLength());
2448 unsigned newVectorLength
= std::min(
2450 MAX_STORAGE_VECTOR_LENGTH
);
2451 unsigned oldVectorLength
= m_butterfly
->vectorLength();
2452 DeferGC
deferGC(vm
.heap
);
2453 m_butterfly
.set(vm
, this, m_butterfly
->growArrayRight(
2454 vm
, this, structure(), structure()->outOfLineCapacity(), true,
2455 oldVectorLength
* sizeof(EncodedJSValue
),
2456 newVectorLength
* sizeof(EncodedJSValue
)));
2458 m_butterfly
->setVectorLength(newVectorLength
);
2460 if (hasDouble(indexingType())) {
2461 for (unsigned i
= oldVectorLength
; i
< newVectorLength
; ++i
)
2462 m_butterfly
->contiguousDouble().data()[i
] = PNaN
;
2466 void JSObject::reallocateAndShrinkButterfly(VM
& vm
, unsigned length
)
2468 ASSERT(length
< MAX_ARRAY_INDEX
);
2469 ASSERT(length
< MAX_STORAGE_VECTOR_LENGTH
);
2470 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2471 ASSERT(m_butterfly
->vectorLength() > length
);
2472 ASSERT(!m_butterfly
->indexingHeader()->preCapacity(structure()));
2474 DeferGC
deferGC(vm
.heap
);
2475 Butterfly
* newButterfly
= m_butterfly
->resizeArray(vm
, this, structure(), 0, ArrayStorage::sizeFor(length
));
2476 m_butterfly
.set(vm
, this, newButterfly
);
2477 m_butterfly
->setVectorLength(length
);
2478 m_butterfly
->setPublicLength(length
);
2481 Butterfly
* JSObject::growOutOfLineStorage(VM
& vm
, size_t oldSize
, size_t newSize
)
2483 ASSERT(newSize
> oldSize
);
2485 // It's important that this function not rely on structure(), for the property
2486 // capacity, since we might have already mutated the structure in-place.
2488 return Butterfly::createOrGrowPropertyStorage(m_butterfly
.get(), vm
, this, structure(vm
), oldSize
, newSize
);
2491 bool JSObject::getOwnPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
2493 JSC::PropertySlot
slot(this);
2494 if (!methodTable(exec
->vm())->getOwnPropertySlot(this, exec
, propertyName
, slot
))
2496 /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */
2497 if (slot
.slotBase() != this && slot
.slotBase() && slot
.slotBase()->methodTable(exec
->vm())->toThis(slot
.slotBase(), exec
, NotStrictMode
) != this)
2499 if (slot
.isAccessor())
2500 descriptor
.setAccessorDescriptor(slot
.getterSetter(), slot
.attributes());
2501 else if (slot
.attributes() & CustomAccessor
)
2502 descriptor
.setCustomDescriptor(slot
.attributes());
2504 descriptor
.setDescriptor(slot
.getValue(exec
, propertyName
), slot
.attributes());
2508 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
2510 VM
& vm
= exec
->vm();
2511 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
2512 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
2513 GetterSetter
* accessor
= GetterSetter::create(vm
, exec
->lexicalGlobalObject());
2514 if (oldDescriptor
.getterPresent())
2515 accessor
->setGetter(vm
, exec
->lexicalGlobalObject(), oldDescriptor
.getterObject());
2516 if (oldDescriptor
.setterPresent())
2517 accessor
->setSetter(vm
, exec
->lexicalGlobalObject(), oldDescriptor
.setterObject());
2518 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2521 JSValue newValue
= jsUndefined();
2522 if (descriptor
.value())
2523 newValue
= descriptor
.value();
2524 else if (oldDescriptor
.value())
2525 newValue
= oldDescriptor
.value();
2526 target
->putDirect(vm
, propertyName
, newValue
, attributes
& ~Accessor
);
2527 if (attributes
& ReadOnly
)
2528 target
->structure(vm
)->setContainsReadOnlyProperties();
2531 attributes
&= ~ReadOnly
;
2532 GetterSetter
* accessor
= GetterSetter::create(vm
, exec
->lexicalGlobalObject());
2534 if (descriptor
.getterPresent())
2535 accessor
->setGetter(vm
, exec
->lexicalGlobalObject(), descriptor
.getterObject());
2536 else if (oldDescriptor
.getterPresent())
2537 accessor
->setGetter(vm
, exec
->lexicalGlobalObject(), oldDescriptor
.getterObject());
2538 if (descriptor
.setterPresent())
2539 accessor
->setSetter(vm
, exec
->lexicalGlobalObject(), descriptor
.setterObject());
2540 else if (oldDescriptor
.setterPresent())
2541 accessor
->setSetter(vm
, exec
->lexicalGlobalObject(), oldDescriptor
.setterObject());
2543 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2547 void JSObject::putDirectMayBeIndex(ExecState
* exec
, PropertyName propertyName
, JSValue value
)
2549 if (Optional
<uint32_t> index
= parseIndex(propertyName
))
2550 putDirectIndex(exec
, index
.value(), value
);
2552 putDirect(exec
->vm(), propertyName
, value
);
2555 class DefineOwnPropertyScope
{
2557 DefineOwnPropertyScope(ExecState
* exec
)
2560 m_vm
.setInDefineOwnProperty(true);
2563 ~DefineOwnPropertyScope()
2565 m_vm
.setInDefineOwnProperty(false);
2572 bool JSObject::defineOwnNonIndexProperty(ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2574 // Track on the globaldata that we're in define property.
2575 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2576 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2577 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2578 DefineOwnPropertyScope
scope(exec
);
2580 // If we have a new property we can just put it on normally
2581 PropertyDescriptor current
;
2582 if (!getOwnPropertyDescriptor(exec
, propertyName
, current
)) {
2583 // unless extensions are prevented!
2584 if (!isExtensible()) {
2586 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to define property on object that is not extensible.")));
2589 PropertyDescriptor oldDescriptor
;
2590 oldDescriptor
.setValue(jsUndefined());
2591 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
2594 if (descriptor
.isEmpty())
2597 if (current
.equalTo(exec
, descriptor
))
2600 // Filter out invalid changes
2601 if (!current
.configurable()) {
2602 if (descriptor
.configurable()) {
2604 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property.")));
2607 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
2609 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
2614 // A generic descriptor is simply changing the attributes of an existing property
2615 if (descriptor
.isGenericDescriptor()) {
2616 if (!current
.attributesEqual(descriptor
)) {
2617 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2618 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2623 // Changing between a normal property or an accessor property
2624 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
2625 if (!current
.configurable()) {
2627 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2630 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2631 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2634 // Changing the value and attributes of an existing property
2635 if (descriptor
.isDataDescriptor()) {
2636 if (!current
.configurable()) {
2637 if (!current
.writable() && descriptor
.writable()) {
2639 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
2642 if (!current
.writable()) {
2643 if (descriptor
.value() && !sameValue(exec
, current
.value(), descriptor
.value())) {
2645 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
2650 if (current
.attributesEqual(descriptor
) && !descriptor
.value())
2652 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2653 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2656 // Changing the accessor functions of an existing accessor property
2657 ASSERT(descriptor
.isAccessorDescriptor());
2658 if (!current
.configurable()) {
2659 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
2661 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
2664 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
2666 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2669 if (current
.attributes() & CustomAccessor
) {
2671 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2675 JSValue accessor
= getDirect(exec
->vm(), propertyName
);
2678 GetterSetter
* getterSetter
;
2679 bool getterSetterChanged
= false;
2680 if (accessor
.isCustomGetterSetter())
2681 getterSetter
= GetterSetter::create(exec
->vm(), exec
->lexicalGlobalObject());
2683 ASSERT(accessor
.isGetterSetter());
2684 getterSetter
= asGetterSetter(accessor
);
2686 if (descriptor
.setterPresent()) {
2687 getterSetter
= getterSetter
->withSetter(exec
->vm(), exec
->lexicalGlobalObject(), descriptor
.setterObject());
2688 getterSetterChanged
= true;
2690 if (descriptor
.getterPresent()) {
2691 getterSetter
= getterSetter
->withGetter(exec
->vm(), exec
->lexicalGlobalObject(), descriptor
.getterObject());
2692 getterSetterChanged
= true;
2694 if (current
.attributesEqual(descriptor
) && !getterSetterChanged
)
2696 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2697 unsigned attrs
= descriptor
.attributesOverridingCurrent(current
);
2698 putDirectAccessor(exec
, propertyName
, getterSetter
, attrs
| Accessor
);
2702 bool JSObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2704 // If it's an array index, then use the indexed property storage.
2705 if (Optional
<uint32_t> index
= parseIndex(propertyName
)) {
2706 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
2707 // d. Reject if succeeded is false.
2708 // e. If index >= oldLen
2709 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2710 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
2712 return object
->defineOwnIndexedProperty(exec
, index
.value(), descriptor
, throwException
);
2715 return object
->defineOwnNonIndexProperty(exec
, propertyName
, descriptor
, throwException
);
2718 JSObject
* throwTypeError(ExecState
* exec
, const String
& message
)
2720 return exec
->vm().throwException(exec
, createTypeError(exec
, message
));
2723 void JSObject::shiftButterflyAfterFlattening(VM
& vm
, size_t outOfLineCapacityBefore
, size_t outOfLineCapacityAfter
)
2725 Butterfly
* butterfly
= this->butterfly();
2726 size_t preCapacity
= this->butterflyPreCapacity();
2727 void* currentBase
= butterfly
->base(preCapacity
, outOfLineCapacityAfter
);
2728 void* newBase
= butterfly
->base(preCapacity
, outOfLineCapacityBefore
);
2730 memmove(newBase
, currentBase
, this->butterflyTotalSize());
2731 setButterflyWithoutChangingStructure(vm
, Butterfly::fromBase(newBase
, preCapacity
, outOfLineCapacityAfter
));
2734 uint32_t JSObject::getEnumerableLength(ExecState
* exec
, JSObject
* object
)
2736 VM
& vm
= exec
->vm();
2737 Structure
* structure
= object
->structure(vm
);
2738 if (structure
->holesMustForwardToPrototype(vm
))
2740 switch (object
->indexingType()) {
2741 case ALL_BLANK_INDEXING_TYPES
:
2742 case ALL_UNDECIDED_INDEXING_TYPES
:
2745 case ALL_INT32_INDEXING_TYPES
:
2746 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2747 Butterfly
* butterfly
= object
->butterfly();
2748 unsigned usedLength
= butterfly
->publicLength();
2749 for (unsigned i
= 0; i
< usedLength
; ++i
) {
2750 if (!butterfly
->contiguous()[i
])
2756 case ALL_DOUBLE_INDEXING_TYPES
: {
2757 Butterfly
* butterfly
= object
->butterfly();
2758 unsigned usedLength
= butterfly
->publicLength();
2759 for (unsigned i
= 0; i
< usedLength
; ++i
) {
2760 double value
= butterfly
->contiguousDouble()[i
];
2767 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
2768 ArrayStorage
* storage
= object
->m_butterfly
->arrayStorage();
2769 if (storage
->m_sparseMap
.get())
2772 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
2773 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
2774 if (!storage
->m_vector
[i
])
2777 return usedVectorLength
;
2781 RELEASE_ASSERT_NOT_REACHED();
2786 void JSObject::getStructurePropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
2788 VM
& vm
= exec
->vm();
2789 object
->structure(vm
)->getPropertyNamesFromStructure(vm
, propertyNames
, mode
);
2792 void JSObject::getGenericPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
2794 VM
& vm
= exec
->vm();
2795 object
->methodTable(vm
)->getOwnPropertyNames(object
, exec
, propertyNames
, EnumerationMode(mode
, JSObjectPropertiesMode::Exclude
));
2797 if (object
->prototype().isNull())
2800 JSObject
* prototype
= asObject(object
->prototype());
2802 if (prototype
->structure(vm
)->typeInfo().overridesGetPropertyNames()) {
2803 prototype
->methodTable(vm
)->getPropertyNames(prototype
, exec
, propertyNames
, mode
);
2806 prototype
->methodTable(vm
)->getOwnPropertyNames(prototype
, exec
, propertyNames
, mode
);
2807 JSValue nextProto
= prototype
->prototype();
2808 if (nextProto
.isNull())
2810 prototype
= asObject(nextProto
);