2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012, 2013 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 "Executable.h"
35 #include "GetterSetter.h"
36 #include "IndexingHeaderInlines.h"
37 #include "JSFunction.h"
38 #include "JSGlobalObject.h"
40 #include "NativeErrorConstructor.h"
42 #include "ObjectPrototype.h"
43 #include "JSCInlines.h"
44 #include "PropertyDescriptor.h"
45 #include "PropertyNameArray.h"
47 #include "SlotVisitorInlines.h"
49 #include <wtf/Assertions.h>
53 // We keep track of the size of the last array after it was grown. We use this
54 // as a simple heuristic for as the value to grow the next array from size 0.
55 // This value is capped by the constant FIRST_VECTOR_GROW defined in
56 // ArrayConventions.h.
57 static unsigned lastArraySize
= 0;
59 JSCell
* getCallableObjectSlow(JSCell
* cell
)
61 if (cell
->type() == JSFunctionType
)
63 if (cell
->structure()->classInfo()->isSubClassOf(InternalFunction::info()))
68 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject
);
69 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject
);
71 const char* StrictModeReadonlyPropertyWriteError
= "Attempted to assign to readonly property.";
73 const ClassInfo
JSObject::s_info
= { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject
) };
75 const ClassInfo
JSFinalObject::s_info
= { "Object", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(JSFinalObject
) };
77 static inline void getClassPropertyNames(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
, EnumerationMode mode
, bool didReify
)
81 // Add properties from the static hashtables of properties
82 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
83 const HashTable
* table
= classInfo
->propHashTable(vm
);
87 for (auto iter
= table
->begin(vm
); iter
!= table
->end(vm
); ++iter
) {
88 if ((!(iter
->attributes() & DontEnum
) || (mode
== IncludeDontEnumProperties
)) && !((iter
->attributes() & BuiltinOrFunction
) && didReify
))
89 propertyNames
.add(Identifier(&vm
, iter
.key()));
94 ALWAYS_INLINE
void JSObject::copyButterfly(CopyVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
98 Structure
* structure
= this->structure();
100 size_t propertyCapacity
= structure
->outOfLineCapacity();
102 size_t indexingPayloadSizeInBytes
;
103 bool hasIndexingHeader
= this->hasIndexingHeader();
104 if (UNLIKELY(hasIndexingHeader
)) {
105 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
106 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
109 indexingPayloadSizeInBytes
= 0;
111 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
112 if (visitor
.checkIfShouldCopy(butterfly
->base(preCapacity
, propertyCapacity
))) {
113 Butterfly
* newButterfly
= Butterfly::createUninitializedDuringCollection(visitor
, preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
115 // Copy the properties.
116 PropertyStorage currentTarget
= newButterfly
->propertyStorage();
117 PropertyStorage currentSource
= butterfly
->propertyStorage();
118 for (size_t count
= storageSize
; count
--;)
119 (--currentTarget
)->setWithoutWriteBarrier((--currentSource
)->get());
121 if (UNLIKELY(hasIndexingHeader
)) {
122 *newButterfly
->indexingHeader() = *butterfly
->indexingHeader();
124 // Copy the array if appropriate.
126 WriteBarrier
<Unknown
>* currentTarget
;
127 WriteBarrier
<Unknown
>* currentSource
;
130 switch (this->indexingType()) {
131 case ALL_UNDECIDED_INDEXING_TYPES
:
132 case ALL_CONTIGUOUS_INDEXING_TYPES
:
133 case ALL_INT32_INDEXING_TYPES
:
134 case ALL_DOUBLE_INDEXING_TYPES
: {
135 currentTarget
= newButterfly
->contiguous().data();
136 currentSource
= butterfly
->contiguous().data();
137 RELEASE_ASSERT(newButterfly
->publicLength() <= newButterfly
->vectorLength());
138 count
= newButterfly
->vectorLength();
142 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
143 newButterfly
->arrayStorage()->copyHeaderFromDuringGC(*butterfly
->arrayStorage());
144 currentTarget
= newButterfly
->arrayStorage()->m_vector
;
145 currentSource
= butterfly
->arrayStorage()->m_vector
;
146 count
= newButterfly
->arrayStorage()->vectorLength();
157 memcpy(currentTarget
, currentSource
, count
* sizeof(EncodedJSValue
));
160 m_butterfly
.setWithoutWriteBarrier(newButterfly
);
161 visitor
.didCopy(butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
165 ALWAYS_INLINE
void JSObject::visitButterfly(SlotVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
169 Structure
* structure
= this->structure(visitor
.vm());
171 size_t propertyCapacity
= structure
->outOfLineCapacity();
173 size_t indexingPayloadSizeInBytes
;
174 bool hasIndexingHeader
= this->hasIndexingHeader();
175 if (UNLIKELY(hasIndexingHeader
)) {
176 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
177 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
180 indexingPayloadSizeInBytes
= 0;
182 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
184 // Mark the properties.
185 visitor
.appendValues(butterfly
->propertyStorage() - storageSize
, storageSize
);
187 this, ButterflyCopyToken
,
188 butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
190 // Mark the array if appropriate.
191 switch (this->indexingType()) {
192 case ALL_CONTIGUOUS_INDEXING_TYPES
:
193 visitor
.appendValues(butterfly
->contiguous().data(), butterfly
->publicLength());
195 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
196 visitor
.appendValues(butterfly
->arrayStorage()->m_vector
, butterfly
->arrayStorage()->vectorLength());
197 if (butterfly
->arrayStorage()->m_sparseMap
)
198 visitor
.append(&butterfly
->arrayStorage()->m_sparseMap
);
205 void JSObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
207 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
208 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
210 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
211 visitor
.m_isCheckingForDefaultMarkViolation
= false;
214 JSCell::visitChildren(thisObject
, visitor
);
216 Butterfly
* butterfly
= thisObject
->butterfly();
218 thisObject
->visitButterfly(visitor
, butterfly
, thisObject
->structure(visitor
.vm())->outOfLineSize());
221 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
225 void JSObject::copyBackingStore(JSCell
* cell
, CopyVisitor
& visitor
, CopyToken token
)
227 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
228 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
230 if (token
!= ButterflyCopyToken
)
233 Butterfly
* butterfly
= thisObject
->butterfly();
235 thisObject
->copyButterfly(visitor
, butterfly
, thisObject
->structure()->outOfLineSize());
238 void JSFinalObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
240 JSFinalObject
* thisObject
= jsCast
<JSFinalObject
*>(cell
);
241 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
243 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
244 visitor
.m_isCheckingForDefaultMarkViolation
= false;
247 JSCell::visitChildren(thisObject
, visitor
);
249 Structure
* structure
= thisObject
->structure();
250 Butterfly
* butterfly
= thisObject
->butterfly();
252 thisObject
->visitButterfly(visitor
, butterfly
, structure
->outOfLineSize());
254 size_t storageSize
= structure
->inlineSize();
255 visitor
.appendValues(thisObject
->inlineStorage(), storageSize
);
258 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
262 String
JSObject::className(const JSObject
* object
)
264 const ClassInfo
* info
= object
->classInfo();
266 return info
->className
;
269 bool JSObject::getOwnPropertySlotByIndex(JSObject
* thisObject
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
271 // NB. The fact that we're directly consulting our indexed storage implies that it is not
272 // legal for anyone to override getOwnPropertySlot() without also overriding
273 // getOwnPropertySlotByIndex().
275 if (i
> MAX_ARRAY_INDEX
)
276 return thisObject
->methodTable(exec
->vm())->getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
278 switch (thisObject
->indexingType()) {
279 case ALL_BLANK_INDEXING_TYPES
:
280 case ALL_UNDECIDED_INDEXING_TYPES
:
283 case ALL_INT32_INDEXING_TYPES
:
284 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
285 Butterfly
* butterfly
= thisObject
->butterfly();
286 if (i
>= butterfly
->vectorLength())
289 JSValue value
= butterfly
->contiguous()[i
].get();
291 slot
.setValue(thisObject
, None
, value
);
298 case ALL_DOUBLE_INDEXING_TYPES
: {
299 Butterfly
* butterfly
= thisObject
->butterfly();
300 if (i
>= butterfly
->vectorLength())
303 double value
= butterfly
->contiguousDouble()[i
];
304 if (value
== value
) {
305 slot
.setValue(thisObject
, None
, JSValue(JSValue::EncodeAsDouble
, value
));
312 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
313 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
314 if (i
>= storage
->length())
317 if (i
< storage
->vectorLength()) {
318 JSValue value
= storage
->m_vector
[i
].get();
320 slot
.setValue(thisObject
, None
, value
);
323 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
324 SparseArrayValueMap::iterator it
= map
->find(i
);
325 if (it
!= map
->notFound()) {
326 it
->value
.get(thisObject
, slot
);
334 RELEASE_ASSERT_NOT_REACHED();
342 void JSObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
344 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
346 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(thisObject
));
349 // Try indexed put first. This is required for correctness, since loads on property names that appear like
350 // valid indices will never look in the named property storage.
351 unsigned i
= propertyName
.asIndex();
352 if (i
!= PropertyName::NotAnIndex
) {
353 putByIndex(thisObject
, exec
, i
, value
, slot
.isStrictMode());
357 // Check if there are any setters or getters in the prototype chain
359 if (propertyName
!= exec
->propertyNames().underscoreProto
) {
360 for (JSObject
* obj
= thisObject
; !obj
->structure(vm
)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj
= asObject(prototype
)) {
361 prototype
= obj
->prototype();
362 if (prototype
.isNull()) {
363 ASSERT(!thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
));
364 if (!thisObject
->putDirectInternal
<PutModePut
>(vm
, propertyName
, value
, 0, slot
, getCallableObject(value
))
365 && slot
.isStrictMode())
366 throwTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
));
373 for (obj
= thisObject
; ; obj
= asObject(prototype
)) {
375 JSCell
* specificValue
;
376 PropertyOffset offset
= obj
->structure(vm
)->get(vm
, propertyName
, attributes
, specificValue
);
377 if (isValidOffset(offset
)) {
378 if (attributes
& ReadOnly
) {
379 ASSERT(thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
380 if (slot
.isStrictMode())
381 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
)));
385 JSValue gs
= obj
->getDirect(offset
);
386 if (gs
.isGetterSetter()) {
387 callSetter(exec
, cell
, gs
, value
, slot
.isStrictMode() ? StrictMode
: NotStrictMode
);
388 if (!thisObject
->structure()->isDictionary())
389 slot
.setCacheableSetter(obj
, offset
);
392 if (gs
.isCustomGetterSetter()) {
393 callCustomSetter(exec
, gs
, obj
, slot
.thisValue(), value
);
394 slot
.setCustomProperty(obj
, jsCast
<CustomGetterSetter
*>(gs
.asCell())->setter());
397 ASSERT(!(attributes
& Accessor
));
399 // If there's an existing property on the object or one of its
400 // prototypes it should be replaced, so break here.
403 const ClassInfo
* info
= obj
->classInfo();
404 if (info
->hasStaticSetterOrReadonlyProperties(vm
)) {
405 if (const HashTableValue
* entry
= obj
->findPropertyHashEntry(vm
, propertyName
)) {
406 putEntry(exec
, entry
, obj
, propertyName
, value
, slot
);
410 prototype
= obj
->prototype();
411 if (prototype
.isNull())
415 ASSERT(!thisObject
->structure(vm
)->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
416 if (!thisObject
->putDirectInternal
<PutModePut
>(vm
, propertyName
, value
, 0, slot
, getCallableObject(value
)) && slot
.isStrictMode())
417 throwTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
));
421 void JSObject::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
)
423 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
425 if (propertyName
> MAX_ARRAY_INDEX
) {
426 PutPropertySlot
slot(cell
, shouldThrow
);
427 thisObject
->methodTable()->put(thisObject
, exec
, Identifier::from(exec
, propertyName
), value
, slot
);
431 switch (thisObject
->indexingType()) {
432 case ALL_BLANK_INDEXING_TYPES
:
435 case ALL_UNDECIDED_INDEXING_TYPES
: {
436 thisObject
->convertUndecidedForValue(exec
->vm(), value
);
438 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
442 case ALL_INT32_INDEXING_TYPES
: {
443 if (!value
.isInt32()) {
444 thisObject
->convertInt32ForValue(exec
->vm(), value
);
445 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
451 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
452 Butterfly
* butterfly
= thisObject
->butterfly();
453 if (propertyName
>= butterfly
->vectorLength())
455 butterfly
->contiguous()[propertyName
].set(exec
->vm(), thisObject
, value
);
456 if (propertyName
>= butterfly
->publicLength())
457 butterfly
->setPublicLength(propertyName
+ 1);
461 case ALL_DOUBLE_INDEXING_TYPES
: {
462 if (!value
.isNumber()) {
463 thisObject
->convertDoubleToContiguous(exec
->vm());
465 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
468 double valueAsDouble
= value
.asNumber();
469 if (valueAsDouble
!= valueAsDouble
) {
470 thisObject
->convertDoubleToContiguous(exec
->vm());
472 putByIndex(cell
, exec
, propertyName
, value
, shouldThrow
);
475 Butterfly
* butterfly
= thisObject
->butterfly();
476 if (propertyName
>= butterfly
->vectorLength())
478 butterfly
->contiguousDouble()[propertyName
] = valueAsDouble
;
479 if (propertyName
>= butterfly
->publicLength())
480 butterfly
->setPublicLength(propertyName
+ 1);
484 case NonArrayWithArrayStorage
:
485 case ArrayWithArrayStorage
: {
486 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
488 if (propertyName
>= storage
->vectorLength())
491 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[propertyName
];
492 unsigned length
= storage
->length();
494 // Update length & m_numValuesInVector as necessary.
495 if (propertyName
>= length
) {
496 length
= propertyName
+ 1;
497 storage
->setLength(length
);
498 ++storage
->m_numValuesInVector
;
499 } else if (!valueSlot
)
500 ++storage
->m_numValuesInVector
;
502 valueSlot
.set(exec
->vm(), thisObject
, value
);
506 case NonArrayWithSlowPutArrayStorage
:
507 case ArrayWithSlowPutArrayStorage
: {
508 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
510 if (propertyName
>= storage
->vectorLength())
513 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[propertyName
];
514 unsigned length
= storage
->length();
516 // Update length & m_numValuesInVector as necessary.
517 if (propertyName
>= length
) {
518 if (thisObject
->attemptToInterceptPutByIndexOnHole(exec
, propertyName
, value
, shouldThrow
))
520 length
= propertyName
+ 1;
521 storage
->setLength(length
);
522 ++storage
->m_numValuesInVector
;
523 } else if (!valueSlot
) {
524 if (thisObject
->attemptToInterceptPutByIndexOnHole(exec
, propertyName
, value
, shouldThrow
))
526 ++storage
->m_numValuesInVector
;
529 valueSlot
.set(exec
->vm(), thisObject
, value
);
534 RELEASE_ASSERT_NOT_REACHED();
537 thisObject
->putByIndexBeyondVectorLength(exec
, propertyName
, value
, shouldThrow
);
540 ArrayStorage
* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM
& vm
, ArrayStorage
* storage
)
542 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
545 map
= allocateSparseIndexMap(vm
);
547 if (map
->sparseMode())
550 map
->setSparseMode();
552 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
553 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
554 JSValue value
= storage
->m_vector
[i
].get();
555 // This will always be a new entry in the map, so no need to check we can write,
556 // and attributes are default so no need to set them.
558 map
->add(this, i
).iterator
->value
.set(vm
, map
, value
);
561 DeferGC
deferGC(vm
.heap
);
562 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(vm
, this, structure(vm
), 0, ArrayStorage::sizeFor(0));
563 RELEASE_ASSERT(newButterfly
);
564 newButterfly
->arrayStorage()->m_indexBias
= 0;
565 newButterfly
->arrayStorage()->setVectorLength(0);
566 newButterfly
->arrayStorage()->m_sparseMap
.set(vm
, this, map
);
567 setButterflyWithoutChangingStructure(vm
, newButterfly
);
569 return newButterfly
->arrayStorage();
572 void JSObject::enterDictionaryIndexingMode(VM
& vm
)
574 switch (indexingType()) {
575 case ALL_BLANK_INDEXING_TYPES
:
576 case ALL_UNDECIDED_INDEXING_TYPES
:
577 case ALL_INT32_INDEXING_TYPES
:
578 case ALL_DOUBLE_INDEXING_TYPES
:
579 case ALL_CONTIGUOUS_INDEXING_TYPES
:
580 // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
581 // this case if we ever cared.
582 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, ensureArrayStorageSlow(vm
));
584 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
585 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, m_butterfly
->arrayStorage());
593 void JSObject::notifyPresenceOfIndexedAccessors(VM
& vm
)
595 if (mayInterceptIndexedAccesses())
598 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AddIndexedAccessors
));
600 if (!vm
.prototypeMap
.isPrototype(this))
603 globalObject()->haveABadTime(vm
);
606 Butterfly
* JSObject::createInitialIndexedStorage(VM
& vm
, unsigned length
, size_t elementSize
)
608 ASSERT(length
< MAX_ARRAY_INDEX
);
609 IndexingType oldType
= indexingType();
610 ASSERT_UNUSED(oldType
, !hasIndexedProperties(oldType
));
611 ASSERT(!structure()->needsSlowPutIndexing());
612 ASSERT(!indexingShouldBeSparse());
613 unsigned vectorLength
= std::max(length
, BASE_VECTOR_LEN
);
614 Butterfly
* newButterfly
= Butterfly::createOrGrowArrayRight(
615 m_butterfly
.get(), vm
, this, structure(), structure()->outOfLineCapacity(), false, 0,
616 elementSize
* vectorLength
);
617 newButterfly
->setPublicLength(length
);
618 newButterfly
->setVectorLength(vectorLength
);
622 Butterfly
* JSObject::createInitialUndecided(VM
& vm
, unsigned length
)
624 DeferGC
deferGC(vm
.heap
);
625 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
626 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateUndecided
);
627 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
631 ContiguousJSValues
JSObject::createInitialInt32(VM
& vm
, unsigned length
)
633 DeferGC
deferGC(vm
.heap
);
634 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
635 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateInt32
);
636 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
637 return newButterfly
->contiguousInt32();
640 ContiguousDoubles
JSObject::createInitialDouble(VM
& vm
, unsigned length
)
642 DeferGC
deferGC(vm
.heap
);
643 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(double));
644 for (unsigned i
= newButterfly
->vectorLength(); i
--;)
645 newButterfly
->contiguousDouble()[i
] = PNaN
;
646 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
);
647 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
648 return newButterfly
->contiguousDouble();
651 ContiguousJSValues
JSObject::createInitialContiguous(VM
& vm
, unsigned length
)
653 DeferGC
deferGC(vm
.heap
);
654 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
655 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
);
656 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
657 return newButterfly
->contiguous();
660 ArrayStorage
* JSObject::createArrayStorage(VM
& vm
, unsigned length
, unsigned vectorLength
)
662 DeferGC
deferGC(vm
.heap
);
663 Structure
* structure
= this->structure(vm
);
664 IndexingType oldType
= indexingType();
665 ASSERT_UNUSED(oldType
, !hasIndexedProperties(oldType
));
666 Butterfly
* newButterfly
= Butterfly::createOrGrowArrayRight(
667 m_butterfly
.get(), vm
, this, structure
, structure
->outOfLineCapacity(), false, 0,
668 ArrayStorage::sizeFor(vectorLength
));
669 RELEASE_ASSERT(newButterfly
);
671 ArrayStorage
* result
= newButterfly
->arrayStorage();
672 result
->setLength(length
);
673 result
->setVectorLength(vectorLength
);
674 result
->m_sparseMap
.clear();
675 result
->m_numValuesInVector
= 0;
676 result
->m_indexBias
= 0;
677 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure
, structure
->suggestedArrayStorageTransition());
678 setStructureAndButterfly(vm
, newStructure
, newButterfly
);
682 ArrayStorage
* JSObject::createInitialArrayStorage(VM
& vm
)
684 return createArrayStorage(vm
, 0, BASE_VECTOR_LEN
);
687 ContiguousJSValues
JSObject::convertUndecidedToInt32(VM
& vm
)
689 ASSERT(hasUndecided(indexingType()));
690 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateInt32
));
691 return m_butterfly
->contiguousInt32();
694 ContiguousDoubles
JSObject::convertUndecidedToDouble(VM
& vm
)
696 ASSERT(hasUndecided(indexingType()));
698 for (unsigned i
= m_butterfly
->vectorLength(); i
--;)
699 m_butterfly
->contiguousDouble()[i
] = PNaN
;
701 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
));
702 return m_butterfly
->contiguousDouble();
705 ContiguousJSValues
JSObject::convertUndecidedToContiguous(VM
& vm
)
707 ASSERT(hasUndecided(indexingType()));
708 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
709 return m_butterfly
->contiguous();
712 ArrayStorage
* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM
& vm
, unsigned neededLength
)
714 Structure
* structure
= this->structure(vm
);
715 unsigned publicLength
= m_butterfly
->publicLength();
716 unsigned propertyCapacity
= structure
->outOfLineCapacity();
717 unsigned propertySize
= structure
->outOfLineSize();
719 Butterfly
* newButterfly
= Butterfly::createUninitialized(
720 vm
, this, 0, propertyCapacity
, true, ArrayStorage::sizeFor(neededLength
));
723 newButterfly
->propertyStorage() - propertySize
,
724 m_butterfly
->propertyStorage() - propertySize
,
725 propertySize
* sizeof(EncodedJSValue
));
727 ArrayStorage
* newStorage
= newButterfly
->arrayStorage();
728 newStorage
->setVectorLength(neededLength
);
729 newStorage
->setLength(publicLength
);
730 newStorage
->m_sparseMap
.clear();
731 newStorage
->m_indexBias
= 0;
732 newStorage
->m_numValuesInVector
= 0;
737 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
739 DeferGC
deferGC(vm
.heap
);
740 ASSERT(hasUndecided(indexingType()));
742 ArrayStorage
* storage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
743 // No need to copy elements.
745 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
746 setStructureAndButterfly(vm
, newStructure
, storage
->butterfly());
750 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
752 return convertUndecidedToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
755 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
)
757 return convertUndecidedToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
760 ContiguousDoubles
JSObject::convertInt32ToDouble(VM
& vm
)
762 ASSERT(hasInt32(indexingType()));
764 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
765 WriteBarrier
<Unknown
>* current
= &m_butterfly
->contiguousInt32()[i
];
766 double* currentAsDouble
= bitwise_cast
<double*>(current
);
767 JSValue v
= current
->get();
769 *currentAsDouble
= PNaN
;
773 *currentAsDouble
= v
.asInt32();
776 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateDouble
));
777 return m_butterfly
->contiguousDouble();
780 ContiguousJSValues
JSObject::convertInt32ToContiguous(VM
& vm
)
782 ASSERT(hasInt32(indexingType()));
784 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
785 return m_butterfly
->contiguous();
788 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
790 ASSERT(hasInt32(indexingType()));
792 DeferGC
deferGC(vm
.heap
);
793 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
794 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
795 JSValue v
= m_butterfly
->contiguous()[i
].get();
798 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
799 newStorage
->m_numValuesInVector
++;
802 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
803 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
807 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
809 return convertInt32ToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
812 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
)
814 return convertInt32ToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
817 template<JSObject::DoubleToContiguousMode mode
>
818 ContiguousJSValues
JSObject::genericConvertDoubleToContiguous(VM
& vm
)
820 ASSERT(hasDouble(indexingType()));
822 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
823 double* current
= &m_butterfly
->contiguousDouble()[i
];
824 WriteBarrier
<Unknown
>* currentAsValue
= bitwise_cast
<WriteBarrier
<Unknown
>*>(current
);
825 double value
= *current
;
826 if (value
!= value
) {
827 currentAsValue
->clear();
832 case EncodeValueAsDouble
:
833 v
= JSValue(JSValue::EncodeAsDouble
, value
);
835 case RageConvertDoubleToValue
:
840 RELEASE_ASSERT_NOT_REACHED();
843 ASSERT(v
.isNumber());
844 currentAsValue
->setWithoutWriteBarrier(v
);
847 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(vm
), AllocateContiguous
));
848 return m_butterfly
->contiguous();
851 ContiguousJSValues
JSObject::convertDoubleToContiguous(VM
& vm
)
853 return genericConvertDoubleToContiguous
<EncodeValueAsDouble
>(vm
);
856 ContiguousJSValues
JSObject::rageConvertDoubleToContiguous(VM
& vm
)
858 return genericConvertDoubleToContiguous
<RageConvertDoubleToValue
>(vm
);
861 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
863 DeferGC
deferGC(vm
.heap
);
864 ASSERT(hasDouble(indexingType()));
866 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
867 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
868 double value
= m_butterfly
->contiguousDouble()[i
];
871 newStorage
->m_vector
[i
].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble
, value
));
872 newStorage
->m_numValuesInVector
++;
875 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
876 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
880 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
882 return convertDoubleToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
885 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
)
887 return convertDoubleToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
890 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
892 DeferGC
deferGC(vm
.heap
);
893 ASSERT(hasContiguous(indexingType()));
895 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
896 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
897 JSValue v
= m_butterfly
->contiguous()[i
].get();
900 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
901 newStorage
->m_numValuesInVector
++;
904 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), transition
);
905 setStructureAndButterfly(vm
, newStructure
, newStorage
->butterfly());
909 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
911 return convertContiguousToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
914 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
)
916 return convertContiguousToArrayStorage(vm
, structure(vm
)->suggestedArrayStorageTransition());
919 void JSObject::convertUndecidedForValue(VM
& vm
, JSValue value
)
921 if (value
.isInt32()) {
922 convertUndecidedToInt32(vm
);
926 if (value
.isDouble() && value
.asNumber() == value
.asNumber()) {
927 convertUndecidedToDouble(vm
);
931 convertUndecidedToContiguous(vm
);
934 void JSObject::createInitialForValueAndSet(VM
& vm
, unsigned index
, JSValue value
)
936 if (value
.isInt32()) {
937 createInitialInt32(vm
, index
+ 1)[index
].set(vm
, this, value
);
941 if (value
.isDouble()) {
942 double doubleValue
= value
.asNumber();
943 if (doubleValue
== doubleValue
) {
944 createInitialDouble(vm
, index
+ 1)[index
] = doubleValue
;
949 createInitialContiguous(vm
, index
+ 1)[index
].set(vm
, this, value
);
952 void JSObject::convertInt32ForValue(VM
& vm
, JSValue value
)
954 ASSERT(!value
.isInt32());
956 if (value
.isDouble()) {
957 convertInt32ToDouble(vm
);
961 convertInt32ToContiguous(vm
);
964 void JSObject::setIndexQuicklyToUndecided(VM
& vm
, unsigned index
, JSValue value
)
966 ASSERT(index
< m_butterfly
->publicLength());
967 ASSERT(index
< m_butterfly
->vectorLength());
968 convertUndecidedForValue(vm
, value
);
969 setIndexQuickly(vm
, index
, value
);
972 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
974 ASSERT(!value
.isInt32());
975 convertInt32ForValue(vm
, value
);
976 setIndexQuickly(vm
, index
, value
);
979 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
981 ASSERT(!value
.isNumber() || value
.asNumber() != value
.asNumber());
982 convertDoubleToContiguous(vm
);
983 setIndexQuickly(vm
, index
, value
);
986 ContiguousJSValues
JSObject::ensureInt32Slow(VM
& vm
)
988 ASSERT(inherits(info()));
990 switch (indexingType()) {
991 case ALL_BLANK_INDEXING_TYPES
:
992 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
993 return ContiguousJSValues();
994 return createInitialInt32(vm
, 0);
996 case ALL_UNDECIDED_INDEXING_TYPES
:
997 return convertUndecidedToInt32(vm
);
999 case ALL_DOUBLE_INDEXING_TYPES
:
1000 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1001 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1002 return ContiguousJSValues();
1006 return ContiguousJSValues();
1010 ContiguousDoubles
JSObject::ensureDoubleSlow(VM
& vm
)
1012 ASSERT(inherits(info()));
1014 switch (indexingType()) {
1015 case ALL_BLANK_INDEXING_TYPES
:
1016 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
1017 return ContiguousDoubles();
1018 return createInitialDouble(vm
, 0);
1020 case ALL_UNDECIDED_INDEXING_TYPES
:
1021 return convertUndecidedToDouble(vm
);
1023 case ALL_INT32_INDEXING_TYPES
:
1024 return convertInt32ToDouble(vm
);
1026 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1027 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1028 return ContiguousDoubles();
1032 return ContiguousDoubles();
1036 ContiguousJSValues
JSObject::ensureContiguousSlow(VM
& vm
, DoubleToContiguousMode mode
)
1038 ASSERT(inherits(info()));
1040 switch (indexingType()) {
1041 case ALL_BLANK_INDEXING_TYPES
:
1042 if (UNLIKELY(indexingShouldBeSparse() || structure(vm
)->needsSlowPutIndexing()))
1043 return ContiguousJSValues();
1044 return createInitialContiguous(vm
, 0);
1046 case ALL_UNDECIDED_INDEXING_TYPES
:
1047 return convertUndecidedToContiguous(vm
);
1049 case ALL_INT32_INDEXING_TYPES
:
1050 return convertInt32ToContiguous(vm
);
1052 case ALL_DOUBLE_INDEXING_TYPES
:
1053 if (mode
== RageConvertDoubleToValue
)
1054 return rageConvertDoubleToContiguous(vm
);
1055 return convertDoubleToContiguous(vm
);
1057 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1058 return ContiguousJSValues();
1062 return ContiguousJSValues();
1066 ContiguousJSValues
JSObject::ensureContiguousSlow(VM
& vm
)
1068 return ensureContiguousSlow(vm
, EncodeValueAsDouble
);
1071 ContiguousJSValues
JSObject::rageEnsureContiguousSlow(VM
& vm
)
1073 return ensureContiguousSlow(vm
, RageConvertDoubleToValue
);
1076 ArrayStorage
* JSObject::ensureArrayStorageSlow(VM
& vm
)
1078 ASSERT(inherits(info()));
1080 switch (indexingType()) {
1081 case ALL_BLANK_INDEXING_TYPES
:
1082 if (UNLIKELY(indexingShouldBeSparse()))
1083 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
);
1084 return createInitialArrayStorage(vm
);
1086 case ALL_UNDECIDED_INDEXING_TYPES
:
1087 ASSERT(!indexingShouldBeSparse());
1088 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1089 return convertUndecidedToArrayStorage(vm
);
1091 case ALL_INT32_INDEXING_TYPES
:
1092 ASSERT(!indexingShouldBeSparse());
1093 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1094 return convertInt32ToArrayStorage(vm
);
1096 case ALL_DOUBLE_INDEXING_TYPES
:
1097 ASSERT(!indexingShouldBeSparse());
1098 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1099 return convertDoubleToArrayStorage(vm
);
1101 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1102 ASSERT(!indexingShouldBeSparse());
1103 ASSERT(!structure(vm
)->needsSlowPutIndexing());
1104 return convertContiguousToArrayStorage(vm
);
1107 RELEASE_ASSERT_NOT_REACHED();
1112 ArrayStorage
* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM
& vm
)
1114 switch (indexingType()) {
1115 case ALL_BLANK_INDEXING_TYPES
: {
1116 createArrayStorage(vm
, 0, 0);
1117 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1118 map
->setSparseMode();
1119 return arrayStorage();
1122 case ALL_UNDECIDED_INDEXING_TYPES
:
1123 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertUndecidedToArrayStorage(vm
));
1125 case ALL_INT32_INDEXING_TYPES
:
1126 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertInt32ToArrayStorage(vm
));
1128 case ALL_DOUBLE_INDEXING_TYPES
:
1129 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertDoubleToArrayStorage(vm
));
1131 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1132 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertContiguousToArrayStorage(vm
));
1134 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1135 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, m_butterfly
->arrayStorage());
1143 void JSObject::switchToSlowPutArrayStorage(VM
& vm
)
1145 switch (indexingType()) {
1146 case ALL_UNDECIDED_INDEXING_TYPES
:
1147 convertUndecidedToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1150 case ALL_INT32_INDEXING_TYPES
:
1151 convertInt32ToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1154 case ALL_DOUBLE_INDEXING_TYPES
:
1155 convertDoubleToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1158 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1159 convertContiguousToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1162 case NonArrayWithArrayStorage
:
1163 case ArrayWithArrayStorage
: {
1164 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(vm
), SwitchToSlowPutArrayStorage
);
1165 setStructure(vm
, newStructure
);
1175 void JSObject::setPrototype(VM
& vm
, JSValue prototype
)
1178 if (prototype
.isObject())
1179 vm
.prototypeMap
.addPrototype(asObject(prototype
));
1181 Structure
* newStructure
= Structure::changePrototypeTransition(vm
, structure(vm
), prototype
);
1182 setStructure(vm
, newStructure
);
1184 if (!newStructure
->anyObjectInChainMayInterceptIndexedAccesses())
1187 if (vm
.prototypeMap
.isPrototype(this)) {
1188 newStructure
->globalObject()->haveABadTime(vm
);
1192 if (!hasIndexedProperties(indexingType()))
1195 if (shouldUseSlowPut(indexingType()))
1198 switchToSlowPutArrayStorage(vm
);
1201 bool JSObject::setPrototypeWithCycleCheck(ExecState
* exec
, JSValue prototype
)
1203 ASSERT(methodTable(exec
->vm())->toThis(this, exec
, NotStrictMode
) == this);
1204 JSValue nextPrototype
= prototype
;
1205 while (nextPrototype
&& nextPrototype
.isObject()) {
1206 if (nextPrototype
== this)
1208 nextPrototype
= asObject(nextPrototype
)->prototype();
1210 setPrototype(exec
->vm(), prototype
);
1214 bool JSObject::allowsAccessFrom(ExecState
* exec
)
1216 JSGlobalObject
* globalObject
= this->globalObject();
1217 return globalObject
->globalObjectMethodTable()->allowsAccessFrom(globalObject
, exec
);
1220 void JSObject::putDirectAccessor(ExecState
* exec
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1222 ASSERT(value
.isGetterSetter() && (attributes
& Accessor
));
1224 unsigned index
= propertyName
.asIndex();
1225 if (index
!= PropertyName::NotAnIndex
) {
1226 putDirectIndex(exec
, index
, value
, attributes
, PutDirectIndexLikePutDirect
);
1230 putDirectNonIndexAccessor(exec
->vm(), propertyName
, value
, attributes
);
1233 void JSObject::putDirectCustomAccessor(VM
& vm
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1235 ASSERT(propertyName
.asIndex() == PropertyName::NotAnIndex
);
1237 PutPropertySlot
slot(this);
1238 putDirectInternal
<PutModeDefineOwnProperty
>(vm
, propertyName
, value
, attributes
, slot
, getCallableObject(value
));
1240 ASSERT(slot
.type() == PutPropertySlot::NewProperty
);
1242 Structure
* structure
= this->structure(vm
);
1243 if (attributes
& ReadOnly
)
1244 structure
->setContainsReadOnlyProperties();
1245 structure
->setHasCustomGetterSetterProperties(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
, getCallableObject(value
));
1253 // putDirect will change our Structure if we add a new property. For
1254 // getters and setters, though, we also need to change our Structure
1255 // if we override an existing non-getter or non-setter.
1256 if (slot
.type() != PutPropertySlot::NewProperty
)
1257 setStructure(vm
, Structure::attributeChangeTransition(vm
, structure(vm
), propertyName
, attributes
));
1259 Structure
* structure
= this->structure(vm
);
1260 if (attributes
& ReadOnly
)
1261 structure
->setContainsReadOnlyProperties();
1263 structure
->setHasGetterSetterProperties(propertyName
== vm
.propertyNames
->underscoreProto
);
1266 bool JSObject::hasProperty(ExecState
* exec
, PropertyName propertyName
) const
1268 PropertySlot
slot(this);
1269 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1272 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
1274 PropertySlot
slot(this);
1275 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1279 bool JSObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
1281 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1283 unsigned i
= propertyName
.asIndex();
1284 if (i
!= PropertyName::NotAnIndex
)
1285 return thisObject
->methodTable(exec
->vm())->deletePropertyByIndex(thisObject
, exec
, i
);
1287 if (!thisObject
->staticFunctionsReified())
1288 thisObject
->reifyStaticFunctionsForDelete(exec
);
1290 unsigned attributes
;
1291 JSCell
* specificValue
;
1292 VM
& vm
= exec
->vm();
1293 if (isValidOffset(thisObject
->structure(vm
)->get(vm
, propertyName
, attributes
, specificValue
))) {
1294 if (attributes
& DontDelete
&& !vm
.isInDefineOwnProperty())
1296 thisObject
->removeDirect(vm
, propertyName
);
1300 // Look in the static hashtable of properties
1301 const HashTableValue
* entry
= thisObject
->findPropertyHashEntry(vm
, propertyName
);
1303 if (entry
->attributes() & DontDelete
&& !vm
.isInDefineOwnProperty())
1304 return false; // this builtin property can't be deleted
1306 PutPropertySlot
slot(thisObject
);
1307 putEntry(exec
, entry
, thisObject
, propertyName
, jsUndefined(), slot
);
1313 bool JSObject::hasOwnProperty(ExecState
* exec
, PropertyName propertyName
) const
1315 PropertySlot
slot(this);
1316 return const_cast<JSObject
*>(this)->methodTable(exec
->vm())->getOwnPropertySlot(const_cast<JSObject
*>(this), exec
, propertyName
, slot
);
1319 bool JSObject::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
1321 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1323 if (i
> MAX_ARRAY_INDEX
)
1324 return thisObject
->methodTable(exec
->vm())->deleteProperty(thisObject
, exec
, Identifier::from(exec
, i
));
1326 switch (thisObject
->indexingType()) {
1327 case ALL_BLANK_INDEXING_TYPES
:
1328 case ALL_UNDECIDED_INDEXING_TYPES
:
1331 case ALL_INT32_INDEXING_TYPES
:
1332 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1333 Butterfly
* butterfly
= thisObject
->butterfly();
1334 if (i
>= butterfly
->vectorLength())
1336 butterfly
->contiguous()[i
].clear();
1340 case ALL_DOUBLE_INDEXING_TYPES
: {
1341 Butterfly
* butterfly
= thisObject
->butterfly();
1342 if (i
>= butterfly
->vectorLength())
1344 butterfly
->contiguousDouble()[i
] = PNaN
;
1348 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1349 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
1351 if (i
< storage
->vectorLength()) {
1352 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
1355 --storage
->m_numValuesInVector
;
1357 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1358 SparseArrayValueMap::iterator it
= map
->find(i
);
1359 if (it
!= map
->notFound()) {
1360 if (it
->value
.attributes
& DontDelete
)
1370 RELEASE_ASSERT_NOT_REACHED();
1375 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, PropertyName propertyName
)
1377 JSValue function
= object
->get(exec
, propertyName
);
1379 CallType callType
= getCallData(function
, callData
);
1380 if (callType
== CallTypeNone
)
1381 return exec
->exception();
1383 // Prevent "toString" and "valueOf" from observing execution if an exception
1385 if (exec
->hadException())
1386 return exec
->exception();
1388 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
1389 ASSERT(!result
.isGetterSetter());
1390 if (exec
->hadException())
1391 return exec
->exception();
1392 if (result
.isObject())
1397 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
1399 result
= methodTable(exec
->vm())->defaultValue(this, exec
, PreferNumber
);
1400 number
= result
.toNumber(exec
);
1401 return !result
.isString();
1405 JSValue
JSObject::defaultValue(const JSObject
* object
, ExecState
* exec
, PreferredPrimitiveType hint
)
1407 // Must call toString first for Date objects.
1408 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& object
->prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
1409 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1412 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1416 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1419 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1424 ASSERT(!exec
->hadException());
1426 return exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("No default value")));
1429 const HashTableValue
* JSObject::findPropertyHashEntry(VM
& vm
, PropertyName propertyName
) const
1431 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1432 if (const HashTable
* propHashTable
= info
->propHashTable(vm
)) {
1433 if (const HashTableValue
* entry
= propHashTable
->entry(vm
, propertyName
))
1440 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
)
1442 VM
& vm
= exec
->vm();
1443 TypeInfo info
= structure(vm
)->typeInfo();
1444 if (info
.implementsDefaultHasInstance())
1445 return defaultHasInstance(exec
, value
, get(exec
, exec
->propertyNames().prototype
));
1446 if (info
.implementsHasInstance())
1447 return methodTable(vm
)->customHasInstance(this, exec
, value
);
1448 vm
.throwException(exec
, createInvalidParameterError(exec
, "instanceof" , this));
1452 bool JSObject::defaultHasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
1454 if (!value
.isObject())
1457 if (!proto
.isObject()) {
1458 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
1462 JSObject
* object
= asObject(value
);
1463 while ((object
= object
->prototype().getObject())) {
1464 if (proto
== object
)
1470 bool JSObject::getPropertySpecificValue(ExecState
* exec
, PropertyName propertyName
, JSCell
*& specificValue
) const
1472 VM
& vm
= exec
->vm();
1473 unsigned attributes
;
1474 if (isValidOffset(structure(vm
)->get(vm
, propertyName
, attributes
, specificValue
)))
1477 // This could be a function within the static table? - should probably
1478 // also look in the hash? This currently should not be a problem, since
1479 // we've currently always call 'get' first, which should have populated
1480 // the normal storage.
1484 void JSObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1486 propertyNames
.setBaseObject(object
);
1487 object
->methodTable(exec
->vm())->getOwnPropertyNames(object
, exec
, propertyNames
, mode
);
1489 if (object
->prototype().isNull())
1492 VM
& vm
= exec
->vm();
1493 JSObject
* prototype
= asObject(object
->prototype());
1495 if (prototype
->structure(vm
)->typeInfo().overridesGetPropertyNames()) {
1496 prototype
->methodTable(vm
)->getPropertyNames(prototype
, exec
, propertyNames
, mode
);
1499 prototype
->methodTable(vm
)->getOwnPropertyNames(prototype
, exec
, propertyNames
, mode
);
1500 JSValue nextProto
= prototype
->prototype();
1501 if (nextProto
.isNull())
1503 prototype
= asObject(nextProto
);
1507 void JSObject::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1509 // Add numeric properties first. That appears to be the accepted convention.
1510 // FIXME: Filling PropertyNameArray with an identifier for every integer
1511 // is incredibly inefficient for large arrays. We need a different approach,
1512 // which almost certainly means a different structure for PropertyNameArray.
1513 switch (object
->indexingType()) {
1514 case ALL_BLANK_INDEXING_TYPES
:
1515 case ALL_UNDECIDED_INDEXING_TYPES
:
1518 case ALL_INT32_INDEXING_TYPES
:
1519 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1520 Butterfly
* butterfly
= object
->butterfly();
1521 unsigned usedLength
= butterfly
->publicLength();
1522 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1523 if (!butterfly
->contiguous()[i
])
1525 propertyNames
.add(Identifier::from(exec
, i
));
1530 case ALL_DOUBLE_INDEXING_TYPES
: {
1531 Butterfly
* butterfly
= object
->butterfly();
1532 unsigned usedLength
= butterfly
->publicLength();
1533 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1534 double value
= butterfly
->contiguousDouble()[i
];
1537 propertyNames
.add(Identifier::from(exec
, i
));
1542 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1543 ArrayStorage
* storage
= object
->m_butterfly
->arrayStorage();
1545 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
1546 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
1547 if (storage
->m_vector
[i
])
1548 propertyNames
.add(Identifier::from(exec
, i
));
1551 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1552 Vector
<unsigned, 0, UnsafeVectorOverflow
> keys
;
1553 keys
.reserveInitialCapacity(map
->size());
1555 SparseArrayValueMap::const_iterator end
= map
->end();
1556 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
1557 if (mode
== IncludeDontEnumProperties
|| !(it
->value
.attributes
& DontEnum
))
1558 keys
.uncheckedAppend(static_cast<unsigned>(it
->key
));
1561 std::sort(keys
.begin(), keys
.end());
1562 for (unsigned i
= 0; i
< keys
.size(); ++i
)
1563 propertyNames
.add(Identifier::from(exec
, keys
[i
]));
1569 RELEASE_ASSERT_NOT_REACHED();
1572 object
->methodTable(exec
->vm())->getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
1575 void JSObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1577 getClassPropertyNames(exec
, object
->classInfo(), propertyNames
, mode
, object
->staticFunctionsReified());
1579 VM
& vm
= exec
->vm();
1580 bool canCachePropertiesFromStructure
= !propertyNames
.size();
1581 object
->structure(vm
)->getPropertyNamesFromStructure(vm
, propertyNames
, mode
);
1583 if (canCachePropertiesFromStructure
)
1584 propertyNames
.setNumCacheableSlotsForObject(object
, propertyNames
.size());
1587 double JSObject::toNumber(ExecState
* exec
) const
1589 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
1590 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
1592 return primitive
.toNumber(exec
);
1595 JSString
* JSObject::toString(ExecState
* exec
) const
1597 JSValue primitive
= toPrimitive(exec
, PreferString
);
1598 if (exec
->hadException())
1599 return jsEmptyString(exec
);
1600 return primitive
.toString(exec
);
1603 JSValue
JSObject::toThis(JSCell
* cell
, ExecState
*, ECMAMode
)
1605 return jsCast
<JSObject
*>(cell
);
1608 void JSObject::seal(VM
& vm
)
1612 preventExtensions(vm
);
1613 setStructure(vm
, Structure::sealTransition(vm
, structure(vm
)));
1616 void JSObject::freeze(VM
& vm
)
1620 preventExtensions(vm
);
1621 setStructure(vm
, Structure::freezeTransition(vm
, structure(vm
)));
1624 void JSObject::preventExtensions(VM
& vm
)
1626 enterDictionaryIndexingMode(vm
);
1628 setStructure(vm
, Structure::preventExtensionsTransition(vm
, structure(vm
)));
1631 // This presently will flatten to an uncachable dictionary; this is suitable
1632 // for use in delete, we may want to do something different elsewhere.
1633 void JSObject::reifyStaticFunctionsForDelete(ExecState
* exec
)
1635 ASSERT(!staticFunctionsReified());
1636 VM
& vm
= exec
->vm();
1638 // If this object's ClassInfo has no static properties, then nothing to reify!
1639 // We can safely set the flag to avoid the expensive check again in the future.
1640 if (!classInfo()->hasStaticProperties()) {
1641 structure(vm
)->setStaticFunctionsReified();
1645 if (!structure(vm
)->isUncacheableDictionary())
1646 setStructure(vm
, Structure::toUncacheableDictionaryTransition(vm
, structure(vm
)));
1648 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1649 const HashTable
* hashTable
= info
->propHashTable(vm
);
1652 PropertySlot
slot(this);
1653 for (auto iter
= hashTable
->begin(vm
); iter
!= hashTable
->end(vm
); ++iter
) {
1654 if (iter
->attributes() & BuiltinOrFunction
)
1655 setUpStaticFunctionSlot(globalObject()->globalExec(), iter
.value(), this, Identifier(&vm
, iter
.key()), slot
);
1659 structure(vm
)->setStaticFunctionsReified();
1662 bool JSObject::removeDirect(VM
& vm
, PropertyName propertyName
)
1664 Structure
* structure
= this->structure(vm
);
1665 if (!isValidOffset(structure
->get(vm
, propertyName
)))
1668 PropertyOffset offset
;
1669 if (structure
->isUncacheableDictionary()) {
1670 offset
= structure
->removePropertyWithoutTransition(vm
, propertyName
);
1671 if (offset
== invalidOffset
)
1673 putDirectUndefined(offset
);
1677 setStructure(vm
, Structure::removePropertyTransition(vm
, structure
, propertyName
, offset
));
1678 if (offset
== invalidOffset
)
1680 putDirectUndefined(offset
);
1684 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValue getterSetter
, unsigned attributes
, PropertyOffset offset
)
1686 if (structure()->isDictionary()) {
1687 slot
.setGetterSlot(this, attributes
, jsCast
<GetterSetter
*>(getterSetter
));
1690 slot
.setCacheableGetterSlot(this, attributes
, jsCast
<GetterSetter
*>(getterSetter
), offset
);
1693 void JSObject::putIndexedDescriptor(ExecState
* exec
, SparseArrayEntry
* entryInMap
, const PropertyDescriptor
& descriptor
, PropertyDescriptor
& oldDescriptor
)
1695 VM
& vm
= exec
->vm();
1696 auto map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1698 if (descriptor
.isDataDescriptor()) {
1699 if (descriptor
.value())
1700 entryInMap
->set(vm
, map
, descriptor
.value());
1701 else if (oldDescriptor
.isAccessorDescriptor())
1702 entryInMap
->set(vm
, map
, jsUndefined());
1703 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~Accessor
;
1707 if (descriptor
.isAccessorDescriptor()) {
1708 JSObject
* getter
= 0;
1709 if (descriptor
.getterPresent())
1710 getter
= descriptor
.getterObject();
1711 else if (oldDescriptor
.isAccessorDescriptor())
1712 getter
= oldDescriptor
.getterObject();
1713 JSObject
* setter
= 0;
1714 if (descriptor
.setterPresent())
1715 setter
= descriptor
.setterObject();
1716 else if (oldDescriptor
.isAccessorDescriptor())
1717 setter
= oldDescriptor
.setterObject();
1719 GetterSetter
* accessor
= GetterSetter::create(vm
);
1721 accessor
->setGetter(vm
, getter
);
1723 accessor
->setSetter(vm
, setter
);
1725 entryInMap
->set(vm
, map
, accessor
);
1726 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~ReadOnly
;
1730 ASSERT(descriptor
.isGenericDescriptor());
1731 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
);
1734 // Defined in ES5.1 8.12.9
1735 bool JSObject::defineOwnIndexedProperty(ExecState
* exec
, unsigned index
, const PropertyDescriptor
& descriptor
, bool throwException
)
1737 ASSERT(index
<= MAX_ARRAY_INDEX
);
1739 if (!inSparseIndexingMode()) {
1740 // Fast case: we're putting a regular property to a regular array
1741 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1742 // however if the property currently exists missing attributes will override from their current 'true'
1743 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1744 if (!descriptor
.attributes()) {
1745 ASSERT(!descriptor
.isAccessorDescriptor());
1746 return putDirectIndex(exec
, index
, descriptor
.value(), 0, throwException
? PutDirectIndexShouldThrow
: PutDirectIndexShouldNotThrow
);
1749 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec
->vm());
1752 if (descriptor
.attributes() & (ReadOnly
| Accessor
))
1753 notifyPresenceOfIndexedAccessors(exec
->vm());
1755 SparseArrayValueMap
* map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1756 RELEASE_ASSERT(map
);
1758 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1759 SparseArrayValueMap::AddResult result
= map
->add(this, index
);
1760 SparseArrayEntry
* entryInMap
= &result
.iterator
->value
;
1762 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1763 // 3. If current is undefined and extensible is false, then Reject.
1764 // 4. If current is undefined and extensible is true, then
1765 if (result
.isNewEntry
) {
1766 if (!isExtensible()) {
1767 map
->remove(result
.iterator
);
1768 return reject(exec
, throwException
, "Attempting to define property on object that is not extensible.");
1771 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1772 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1773 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1774 // created property is set to its default value.
1775 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1776 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1777 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1778 // is set to its default value.
1779 // 4.c. Return true.
1781 PropertyDescriptor defaults
;
1782 entryInMap
->setWithoutWriteBarrier(jsUndefined());
1783 entryInMap
->attributes
= DontDelete
| DontEnum
| ReadOnly
;
1784 entryInMap
->get(defaults
);
1786 putIndexedDescriptor(exec
, entryInMap
, descriptor
, defaults
);
1787 if (index
>= m_butterfly
->arrayStorage()->length())
1788 m_butterfly
->arrayStorage()->setLength(index
+ 1);
1792 // 5. Return true, if every field in Desc is absent.
1793 // 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).
1794 PropertyDescriptor current
;
1795 entryInMap
->get(current
);
1796 if (descriptor
.isEmpty() || descriptor
.equalTo(exec
, current
))
1799 // 7. If the [[Configurable]] field of current is false then
1800 if (!current
.configurable()) {
1801 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1802 if (descriptor
.configurablePresent() && descriptor
.configurable())
1803 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
1804 // 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.
1805 if (descriptor
.enumerablePresent() && current
.enumerable() != descriptor
.enumerable())
1806 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
1809 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1810 if (!descriptor
.isGenericDescriptor()) {
1811 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1812 if (current
.isDataDescriptor() != descriptor
.isDataDescriptor()) {
1813 // 9.a. Reject, if the [[Configurable]] field of current is false.
1814 if (!current
.configurable())
1815 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
1816 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1817 // data property to an accessor property. Preserve the existing values of the converted property's
1818 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1819 // their default values.
1820 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1821 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1822 // attributes and set the rest of the property's attributes to their default values.
1823 } else if (current
.isDataDescriptor() && descriptor
.isDataDescriptor()) {
1824 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1825 // 10.a. If the [[Configurable]] field of current is false, then
1826 if (!current
.configurable() && !current
.writable()) {
1827 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1828 if (descriptor
.writable())
1829 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
1830 // 10.a.ii. If the [[Writable]] field of current is false, then
1831 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1832 if (descriptor
.value() && !sameValue(exec
, descriptor
.value(), current
.value()))
1833 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
1835 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1837 ASSERT(current
.isAccessorDescriptor() && current
.getterPresent() && current
.setterPresent());
1838 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1839 if (!current
.configurable()) {
1840 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1841 if (descriptor
.setterPresent() && descriptor
.setter() != current
.setter())
1842 return reject(exec
, throwException
, "Attempting to change the setter of an unconfigurable property.");
1843 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1844 if (descriptor
.getterPresent() && descriptor
.getter() != current
.getter())
1845 return reject(exec
, throwException
, "Attempting to change the getter of an unconfigurable property.");
1850 // 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.
1851 putIndexedDescriptor(exec
, entryInMap
, descriptor
, current
);
1856 SparseArrayValueMap
* JSObject::allocateSparseIndexMap(VM
& vm
)
1858 SparseArrayValueMap
* result
= SparseArrayValueMap::create(vm
);
1859 arrayStorage()->m_sparseMap
.set(vm
, this, result
);
1863 void JSObject::deallocateSparseIndexMap()
1865 if (ArrayStorage
* arrayStorage
= arrayStorageOrNull())
1866 arrayStorage
->m_sparseMap
.clear();
1869 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState
* exec
, JSValue thisValue
, unsigned i
, JSValue value
, bool shouldThrow
)
1871 for (JSObject
* current
= this; ;) {
1872 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1873 // allows a prototype to intercept a put if (a) the prototype declares the property
1874 // we're after rather than intercepting it via an override of JSObject::put(), and
1875 // (b) that property is declared as ReadOnly or Accessor.
1877 ArrayStorage
* storage
= current
->arrayStorageOrNull();
1878 if (storage
&& storage
->m_sparseMap
) {
1879 SparseArrayValueMap::iterator iter
= storage
->m_sparseMap
->find(i
);
1880 if (iter
!= storage
->m_sparseMap
->notFound() && (iter
->value
.attributes
& (Accessor
| ReadOnly
))) {
1881 iter
->value
.put(exec
, thisValue
, storage
->m_sparseMap
.get(), value
, shouldThrow
);
1886 JSValue prototypeValue
= current
->prototype();
1887 if (prototypeValue
.isNull())
1890 current
= asObject(prototypeValue
);
1894 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
1896 JSValue prototypeValue
= prototype();
1897 if (prototypeValue
.isNull())
1900 return asObject(prototypeValue
)->attemptToInterceptPutByIndexOnHoleForPrototype(exec
, this, i
, value
, shouldThrow
);
1903 template<IndexingType indexingShape
>
1904 void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState
* exec
, unsigned i
, JSValue value
)
1906 ASSERT((indexingType() & IndexingShapeMask
) == indexingShape
);
1907 ASSERT(!indexingShouldBeSparse());
1909 // For us to get here, the index is either greater than the public length, or greater than
1910 // or equal to the vector length.
1911 ASSERT(i
>= m_butterfly
->vectorLength());
1913 VM
& vm
= exec
->vm();
1915 if (i
>= MAX_ARRAY_INDEX
- 1
1916 || (i
>= MIN_SPARSE_ARRAY_INDEX
1917 && !isDenseEnoughForVector(i
, countElements
<indexingShape
>(butterfly())))
1918 || indexIsSufficientlyBeyondLengthForSparseMap(i
, m_butterfly
->vectorLength())) {
1919 ASSERT(i
<= MAX_ARRAY_INDEX
);
1920 ensureArrayStorageSlow(vm
);
1921 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1922 map
->putEntry(exec
, this, i
, value
, false);
1923 ASSERT(i
>= arrayStorage()->length());
1924 arrayStorage()->setLength(i
+ 1);
1928 ensureLength(vm
, i
+ 1);
1930 RELEASE_ASSERT(i
< m_butterfly
->vectorLength());
1931 switch (indexingShape
) {
1933 ASSERT(value
.isInt32());
1934 m_butterfly
->contiguousInt32()[i
].setWithoutWriteBarrier(value
);
1938 ASSERT(value
.isNumber());
1939 double valueAsDouble
= value
.asNumber();
1940 ASSERT(valueAsDouble
== valueAsDouble
);
1941 m_butterfly
->contiguousDouble()[i
] = valueAsDouble
;
1945 case ContiguousShape
:
1946 m_butterfly
->contiguous()[i
].set(vm
, this, value
);
1954 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
, ArrayStorage
* storage
)
1956 VM
& vm
= exec
->vm();
1958 // i should be a valid array index that is outside of the current vector.
1959 ASSERT(i
<= MAX_ARRAY_INDEX
);
1960 ASSERT(i
>= storage
->vectorLength());
1962 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
1964 // First, handle cases where we don't currently have a sparse map.
1966 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
1967 ASSERT(isExtensible());
1969 // Update m_length if necessary.
1970 if (i
>= storage
->length())
1971 storage
->setLength(i
+ 1);
1973 // Check that it is sensible to still be using a vector, and then try to grow the vector.
1974 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength())
1975 && isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)
1976 && increaseVectorLength(vm
, i
+ 1))) {
1977 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1978 storage
= arrayStorage();
1979 storage
->m_vector
[i
].set(vm
, this, value
);
1980 ++storage
->m_numValuesInVector
;
1983 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1984 map
= allocateSparseIndexMap(exec
->vm());
1985 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
1989 // Update m_length if necessary.
1990 unsigned length
= storage
->length();
1992 // Prohibit growing the array if length is not writable.
1993 if (map
->lengthIsReadOnly() || !isExtensible()) {
1995 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
1999 storage
->setLength(length
);
2002 // We are currently using a map - check whether we still want to be doing so.
2003 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2004 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2005 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
)) {
2006 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
2010 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2011 storage
= arrayStorage();
2012 storage
->m_numValuesInVector
= numValuesInArray
;
2014 // Copy all values from the map into the vector, and delete the map.
2015 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2016 SparseArrayValueMap::const_iterator end
= map
->end();
2017 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2018 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2019 deallocateSparseIndexMap();
2021 // Store the new property into the vector.
2022 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2024 ++storage
->m_numValuesInVector
;
2025 valueSlot
.set(vm
, this, value
);
2028 void JSObject::putByIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
2030 VM
& vm
= exec
->vm();
2032 // i should be a valid array index that is outside of the current vector.
2033 ASSERT(i
<= MAX_ARRAY_INDEX
);
2035 switch (indexingType()) {
2036 case ALL_BLANK_INDEXING_TYPES
: {
2037 if (indexingShouldBeSparse()) {
2038 putByIndexBeyondVectorLengthWithArrayStorage(
2039 exec
, i
, value
, shouldThrow
,
2040 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2043 if (indexIsSufficientlyBeyondLengthForSparseMap(i
, 0) || i
>= MIN_SPARSE_ARRAY_INDEX
) {
2044 putByIndexBeyondVectorLengthWithArrayStorage(
2045 exec
, i
, value
, shouldThrow
, createArrayStorage(vm
, 0, 0));
2048 if (structure(vm
)->needsSlowPutIndexing()) {
2049 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2050 storage
->m_vector
[i
].set(vm
, this, value
);
2051 storage
->m_numValuesInVector
++;
2055 createInitialForValueAndSet(vm
, i
, value
);
2059 case ALL_UNDECIDED_INDEXING_TYPES
: {
2064 case ALL_INT32_INDEXING_TYPES
: {
2065 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2069 case ALL_DOUBLE_INDEXING_TYPES
: {
2070 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2074 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2075 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2079 case NonArrayWithSlowPutArrayStorage
:
2080 case ArrayWithSlowPutArrayStorage
: {
2081 // No own property present in the vector, but there might be in the sparse map!
2082 SparseArrayValueMap
* map
= arrayStorage()->m_sparseMap
.get();
2083 if (!(map
&& map
->contains(i
)) && attemptToInterceptPutByIndexOnHole(exec
, i
, value
, shouldThrow
))
2088 case NonArrayWithArrayStorage
:
2089 case ArrayWithArrayStorage
:
2090 putByIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, shouldThrow
, arrayStorage());
2094 RELEASE_ASSERT_NOT_REACHED();
2098 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
, ArrayStorage
* storage
)
2100 VM
& vm
= exec
->vm();
2102 // i should be a valid array index that is outside of the current vector.
2103 ASSERT(hasAnyArrayStorage(indexingType()));
2104 ASSERT(arrayStorage() == storage
);
2105 ASSERT(i
>= storage
->vectorLength() || attributes
);
2106 ASSERT(i
<= MAX_ARRAY_INDEX
);
2108 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
2110 // First, handle cases where we don't currently have a sparse map.
2112 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2113 ASSERT(isExtensible());
2115 // Update m_length if necessary.
2116 if (i
>= storage
->length())
2117 storage
->setLength(i
+ 1);
2119 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2122 && (isDenseEnoughForVector(i
, storage
->m_numValuesInVector
))
2123 && !indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength()))
2124 && increaseVectorLength(vm
, i
+ 1)) {
2125 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2126 storage
= arrayStorage();
2127 storage
->m_vector
[i
].set(vm
, this, value
);
2128 ++storage
->m_numValuesInVector
;
2131 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2132 map
= allocateSparseIndexMap(exec
->vm());
2133 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2136 // Update m_length if necessary.
2137 unsigned length
= storage
->length();
2139 if (mode
!= PutDirectIndexLikePutDirect
) {
2140 // Prohibit growing the array if length is not writable.
2141 if (map
->lengthIsReadOnly())
2142 return reject(exec
, mode
== PutDirectIndexShouldThrow
, StrictModeReadonlyPropertyWriteError
);
2143 if (!isExtensible())
2144 return reject(exec
, mode
== PutDirectIndexShouldThrow
, "Attempting to define property on object that is not extensible.");
2147 storage
->setLength(length
);
2150 // We are currently using a map - check whether we still want to be doing so.
2151 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2152 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2153 if (map
->sparseMode() || attributes
|| !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
))
2154 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2156 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2157 storage
= arrayStorage();
2158 storage
->m_numValuesInVector
= numValuesInArray
;
2160 // Copy all values from the map into the vector, and delete the map.
2161 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2162 SparseArrayValueMap::const_iterator end
= map
->end();
2163 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2164 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2165 deallocateSparseIndexMap();
2167 // Store the new property into the vector.
2168 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2170 ++storage
->m_numValuesInVector
;
2171 valueSlot
.set(vm
, this, value
);
2175 bool JSObject::putDirectIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
)
2177 VM
& vm
= exec
->vm();
2179 // i should be a valid array index that is outside of the current vector.
2180 ASSERT(i
<= MAX_ARRAY_INDEX
);
2182 if (attributes
& (ReadOnly
| Accessor
))
2183 notifyPresenceOfIndexedAccessors(vm
);
2185 switch (indexingType()) {
2186 case ALL_BLANK_INDEXING_TYPES
: {
2187 if (indexingShouldBeSparse() || attributes
) {
2188 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2189 exec
, i
, value
, attributes
, mode
,
2190 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2192 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
2193 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2194 exec
, i
, value
, attributes
, mode
, createArrayStorage(vm
, 0, 0));
2196 if (structure(vm
)->needsSlowPutIndexing()) {
2197 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2198 storage
->m_vector
[i
].set(vm
, this, value
);
2199 storage
->m_numValuesInVector
++;
2203 createInitialForValueAndSet(vm
, i
, value
);
2207 case ALL_UNDECIDED_INDEXING_TYPES
: {
2208 convertUndecidedForValue(exec
->vm(), value
);
2210 return putDirectIndex(exec
, i
, value
, attributes
, mode
);
2213 case ALL_INT32_INDEXING_TYPES
: {
2214 if (attributes
& (ReadOnly
| Accessor
)) {
2215 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2216 exec
, i
, value
, attributes
, mode
, convertInt32ToArrayStorage(vm
));
2218 if (!value
.isInt32()) {
2219 convertInt32ForValue(vm
, value
);
2220 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2222 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2226 case ALL_DOUBLE_INDEXING_TYPES
: {
2227 if (attributes
& (ReadOnly
| Accessor
)) {
2228 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2229 exec
, i
, value
, attributes
, mode
, convertDoubleToArrayStorage(vm
));
2231 if (!value
.isNumber()) {
2232 convertDoubleToContiguous(vm
);
2233 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2235 double valueAsDouble
= value
.asNumber();
2236 if (valueAsDouble
!= valueAsDouble
) {
2237 convertDoubleToContiguous(vm
);
2238 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2240 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2244 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2245 if (attributes
& (ReadOnly
| Accessor
)) {
2246 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2247 exec
, i
, value
, attributes
, mode
, convertContiguousToArrayStorage(vm
));
2249 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2253 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
2254 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, attributes
, mode
, arrayStorage());
2257 RELEASE_ASSERT_NOT_REACHED();
2262 void JSObject::putDirectNativeFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2264 StringImpl
* name
= propertyName
.publicName();
2266 name
= vm
.propertyNames
->anonymous
.impl();
2269 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2270 putDirect(vm
, propertyName
, function
, attributes
);
2273 JSFunction
* JSObject::putDirectBuiltinFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2275 StringImpl
* name
= propertyName
.publicName();
2277 name
= vm
.propertyNames
->anonymous
.impl();
2279 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2280 putDirect(vm
, propertyName
, function
, attributes
);
2284 JSFunction
* JSObject::putDirectBuiltinFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2286 StringImpl
* name
= propertyName
.publicName();
2288 name
= vm
.propertyNames
->anonymous
.impl();
2290 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2291 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2295 void JSObject::putDirectNativeFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2297 StringImpl
* name
= propertyName
.publicName();
2300 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2301 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2304 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned currentVectorLength
, unsigned currentLength
, unsigned desiredLength
)
2306 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
2308 unsigned increasedLength
;
2309 unsigned maxInitLength
= std::min(currentLength
, 100000U);
2311 if (desiredLength
< maxInitLength
)
2312 increasedLength
= maxInitLength
;
2313 else if (!currentVectorLength
)
2314 increasedLength
= std::max(desiredLength
, lastArraySize
);
2316 increasedLength
= timesThreePlusOneDividedByTwo(desiredLength
);
2319 ASSERT(increasedLength
>= desiredLength
);
2321 lastArraySize
= std::min(increasedLength
, FIRST_VECTOR_GROW
);
2323 return std::min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
2326 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned desiredLength
)
2328 unsigned vectorLength
;
2331 if (hasIndexedProperties(indexingType())) {
2332 vectorLength
= m_butterfly
->vectorLength();
2333 length
= m_butterfly
->publicLength();
2339 return getNewVectorLength(vectorLength
, length
, desiredLength
);
2342 template<IndexingType indexingShape
>
2343 unsigned JSObject::countElements(Butterfly
* butterfly
)
2345 unsigned numValues
= 0;
2346 for (unsigned i
= butterfly
->publicLength(); i
--;) {
2347 switch (indexingShape
) {
2349 case ContiguousShape
:
2350 if (butterfly
->contiguous()[i
])
2355 double value
= butterfly
->contiguousDouble()[i
];
2368 unsigned JSObject::countElements()
2370 switch (indexingType()) {
2371 case ALL_BLANK_INDEXING_TYPES
:
2372 case ALL_UNDECIDED_INDEXING_TYPES
:
2375 case ALL_INT32_INDEXING_TYPES
:
2376 return countElements
<Int32Shape
>(butterfly());
2378 case ALL_DOUBLE_INDEXING_TYPES
:
2379 return countElements
<DoubleShape
>(butterfly());
2381 case ALL_CONTIGUOUS_INDEXING_TYPES
:
2382 return countElements
<ContiguousShape
>(butterfly());
2390 bool JSObject::increaseVectorLength(VM
& vm
, unsigned newLength
)
2392 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2393 // to the vector. Callers have to account for that, because they can do it more efficiently.
2394 if (newLength
> MAX_STORAGE_VECTOR_LENGTH
)
2397 ArrayStorage
* storage
= arrayStorage();
2399 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
2400 && !isDenseEnoughForVector(newLength
, storage
->m_numValuesInVector
))
2403 unsigned indexBias
= storage
->m_indexBias
;
2404 unsigned vectorLength
= storage
->vectorLength();
2405 ASSERT(newLength
> vectorLength
);
2406 unsigned newVectorLength
= getNewVectorLength(newLength
);
2408 // Fast case - there is no precapacity. In these cases a realloc makes sense.
2409 Structure
* structure
= this->structure(vm
);
2410 if (LIKELY(!indexBias
)) {
2411 DeferGC
deferGC(vm
.heap
);
2412 Butterfly
* newButterfly
= storage
->butterfly()->growArrayRight(
2413 vm
, this, structure
, structure
->outOfLineCapacity(), true,
2414 ArrayStorage::sizeFor(vectorLength
), ArrayStorage::sizeFor(newVectorLength
));
2417 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2418 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2422 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2423 DeferGC
deferGC(vm
.heap
);
2424 unsigned newIndexBias
= std::min(indexBias
>> 1, MAX_STORAGE_VECTOR_LENGTH
- newVectorLength
);
2425 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(
2427 structure
->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength
),
2428 newIndexBias
, true, ArrayStorage::sizeFor(newVectorLength
));
2431 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2432 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
2433 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2437 void JSObject::ensureLengthSlow(VM
& vm
, unsigned length
)
2439 ASSERT(length
< MAX_ARRAY_INDEX
);
2440 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2441 ASSERT(length
> m_butterfly
->vectorLength());
2443 unsigned newVectorLength
= std::min(
2445 MAX_STORAGE_VECTOR_LENGTH
);
2446 unsigned oldVectorLength
= m_butterfly
->vectorLength();
2447 DeferGC
deferGC(vm
.heap
);
2448 m_butterfly
.set(vm
, this, m_butterfly
->growArrayRight(
2449 vm
, this, structure(), structure()->outOfLineCapacity(), true,
2450 oldVectorLength
* sizeof(EncodedJSValue
),
2451 newVectorLength
* sizeof(EncodedJSValue
)));
2453 m_butterfly
->setVectorLength(newVectorLength
);
2455 if (hasDouble(indexingType())) {
2456 for (unsigned i
= oldVectorLength
; i
< newVectorLength
; ++i
)
2457 m_butterfly
->contiguousDouble().data()[i
] = PNaN
;
2461 Butterfly
* JSObject::growOutOfLineStorage(VM
& vm
, size_t oldSize
, size_t newSize
)
2463 ASSERT(newSize
> oldSize
);
2465 // It's important that this function not rely on structure(), for the property
2466 // capacity, since we might have already mutated the structure in-place.
2468 return Butterfly::createOrGrowPropertyStorage(m_butterfly
.get(), vm
, this, structure(vm
), oldSize
, newSize
);
2471 bool JSObject::getOwnPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
2473 JSC::PropertySlot
slot(this);
2474 if (!methodTable(exec
->vm())->getOwnPropertySlot(this, exec
, propertyName
, slot
))
2476 /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */
2477 if (slot
.slotBase() != this && slot
.slotBase() && slot
.slotBase()->methodTable(exec
->vm())->toThis(slot
.slotBase(), exec
, NotStrictMode
) != this)
2479 if (slot
.isAccessor())
2480 descriptor
.setAccessorDescriptor(slot
.getterSetter(), slot
.attributes());
2481 else if (slot
.attributes() & CustomAccessor
)
2482 descriptor
.setCustomDescriptor(slot
.attributes());
2484 descriptor
.setDescriptor(slot
.getValue(exec
, propertyName
), slot
.attributes());
2488 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
2490 VM
& vm
= exec
->vm();
2491 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
2492 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
2493 GetterSetter
* accessor
= GetterSetter::create(vm
);
2494 if (oldDescriptor
.getterPresent())
2495 accessor
->setGetter(vm
, oldDescriptor
.getterObject());
2496 if (oldDescriptor
.setterPresent())
2497 accessor
->setSetter(vm
, oldDescriptor
.setterObject());
2498 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2501 JSValue newValue
= jsUndefined();
2502 if (descriptor
.value())
2503 newValue
= descriptor
.value();
2504 else if (oldDescriptor
.value())
2505 newValue
= oldDescriptor
.value();
2506 target
->putDirect(vm
, propertyName
, newValue
, attributes
& ~Accessor
);
2507 if (attributes
& ReadOnly
)
2508 target
->structure(vm
)->setContainsReadOnlyProperties();
2511 attributes
&= ~ReadOnly
;
2512 GetterSetter
* accessor
= GetterSetter::create(vm
);
2514 if (descriptor
.getterPresent())
2515 accessor
->setGetter(vm
, descriptor
.getterObject());
2516 else if (oldDescriptor
.getterPresent())
2517 accessor
->setGetter(vm
, oldDescriptor
.getterObject());
2518 if (descriptor
.setterPresent())
2519 accessor
->setSetter(vm
, descriptor
.setterObject());
2520 else if (oldDescriptor
.setterPresent())
2521 accessor
->setSetter(vm
, oldDescriptor
.setterObject());
2523 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2527 void JSObject::putDirectMayBeIndex(ExecState
* exec
, PropertyName propertyName
, JSValue value
)
2529 unsigned asIndex
= propertyName
.asIndex();
2530 if (asIndex
== PropertyName::NotAnIndex
)
2531 putDirect(exec
->vm(), propertyName
, value
);
2533 putDirectIndex(exec
, asIndex
, value
);
2536 class DefineOwnPropertyScope
{
2538 DefineOwnPropertyScope(ExecState
* exec
)
2541 m_vm
.setInDefineOwnProperty(true);
2544 ~DefineOwnPropertyScope()
2546 m_vm
.setInDefineOwnProperty(false);
2553 bool JSObject::defineOwnNonIndexProperty(ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2555 // Track on the globaldata that we're in define property.
2556 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2557 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2558 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2559 DefineOwnPropertyScope
scope(exec
);
2561 // If we have a new property we can just put it on normally
2562 PropertyDescriptor current
;
2563 if (!getOwnPropertyDescriptor(exec
, propertyName
, current
)) {
2564 // unless extensions are prevented!
2565 if (!isExtensible()) {
2567 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to define property on object that is not extensible.")));
2570 PropertyDescriptor oldDescriptor
;
2571 oldDescriptor
.setValue(jsUndefined());
2572 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
2575 if (descriptor
.isEmpty())
2578 if (current
.equalTo(exec
, descriptor
))
2581 // Filter out invalid changes
2582 if (!current
.configurable()) {
2583 if (descriptor
.configurable()) {
2585 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
2588 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
2590 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
2595 // A generic descriptor is simply changing the attributes of an existing property
2596 if (descriptor
.isGenericDescriptor()) {
2597 if (!current
.attributesEqual(descriptor
)) {
2598 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2599 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2604 // Changing between a normal property or an accessor property
2605 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
2606 if (!current
.configurable()) {
2608 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2611 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2612 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2615 // Changing the value and attributes of an existing property
2616 if (descriptor
.isDataDescriptor()) {
2617 if (!current
.configurable()) {
2618 if (!current
.writable() && descriptor
.writable()) {
2620 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
2623 if (!current
.writable()) {
2624 if (descriptor
.value() && !sameValue(exec
, current
.value(), descriptor
.value())) {
2626 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
2631 if (current
.attributesEqual(descriptor
) && !descriptor
.value())
2633 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2634 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2637 // Changing the accessor functions of an existing accessor property
2638 ASSERT(descriptor
.isAccessorDescriptor());
2639 if (!current
.configurable()) {
2640 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
2642 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
2645 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
2647 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2650 if (current
.attributes() & CustomAccessor
) {
2652 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2656 JSValue accessor
= getDirect(exec
->vm(), propertyName
);
2659 GetterSetter
* getterSetter
;
2660 if (accessor
.isCustomGetterSetter())
2661 getterSetter
= GetterSetter::create(exec
->vm());
2663 ASSERT(accessor
.isGetterSetter());
2664 getterSetter
= asGetterSetter(accessor
);
2666 if (descriptor
.setterPresent())
2667 getterSetter
->setSetter(exec
->vm(), descriptor
.setterObject());
2668 if (descriptor
.getterPresent())
2669 getterSetter
->setGetter(exec
->vm(), descriptor
.getterObject());
2670 if (current
.attributesEqual(descriptor
))
2672 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2673 unsigned attrs
= descriptor
.attributesOverridingCurrent(current
);
2674 putDirectAccessor(exec
, propertyName
, getterSetter
, attrs
| Accessor
);
2678 bool JSObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2680 // If it's an array index, then use the indexed property storage.
2681 unsigned index
= propertyName
.asIndex();
2682 if (index
!= PropertyName::NotAnIndex
) {
2683 // 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.
2684 // d. Reject if succeeded is false.
2685 // e. If index >= oldLen
2686 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2687 // 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.
2689 return object
->defineOwnIndexedProperty(exec
, index
, descriptor
, throwException
);
2692 return object
->defineOwnNonIndexProperty(exec
, propertyName
, descriptor
, throwException
);
2695 JSObject
* throwTypeError(ExecState
* exec
, const String
& message
)
2697 return exec
->vm().throwException(exec
, createTypeError(exec
, message
));
2700 void JSObject::shiftButterflyAfterFlattening(VM
& vm
, size_t outOfLineCapacityBefore
, size_t outOfLineCapacityAfter
)
2702 Butterfly
* butterfly
= this->butterfly();
2703 size_t preCapacity
= this->butterflyPreCapacity();
2704 void* currentBase
= butterfly
->base(preCapacity
, outOfLineCapacityAfter
);
2705 void* newBase
= butterfly
->base(preCapacity
, outOfLineCapacityBefore
);
2707 memmove(newBase
, currentBase
, this->butterflyTotalSize());
2708 setButterflyWithoutChangingStructure(vm
, Butterfly::fromBase(newBase
, preCapacity
, outOfLineCapacityAfter
));