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
, this, 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();
1697 if (descriptor
.isDataDescriptor()) {
1698 if (descriptor
.value())
1699 entryInMap
->set(vm
, this, descriptor
.value());
1700 else if (oldDescriptor
.isAccessorDescriptor())
1701 entryInMap
->set(vm
, this, jsUndefined());
1702 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~Accessor
;
1706 if (descriptor
.isAccessorDescriptor()) {
1707 JSObject
* getter
= 0;
1708 if (descriptor
.getterPresent())
1709 getter
= descriptor
.getterObject();
1710 else if (oldDescriptor
.isAccessorDescriptor())
1711 getter
= oldDescriptor
.getterObject();
1712 JSObject
* setter
= 0;
1713 if (descriptor
.setterPresent())
1714 setter
= descriptor
.setterObject();
1715 else if (oldDescriptor
.isAccessorDescriptor())
1716 setter
= oldDescriptor
.setterObject();
1718 GetterSetter
* accessor
= GetterSetter::create(vm
);
1720 accessor
->setGetter(vm
, getter
);
1722 accessor
->setSetter(vm
, setter
);
1724 entryInMap
->set(vm
, this, accessor
);
1725 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~ReadOnly
;
1729 ASSERT(descriptor
.isGenericDescriptor());
1730 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
);
1733 // Defined in ES5.1 8.12.9
1734 bool JSObject::defineOwnIndexedProperty(ExecState
* exec
, unsigned index
, const PropertyDescriptor
& descriptor
, bool throwException
)
1736 ASSERT(index
<= MAX_ARRAY_INDEX
);
1738 if (!inSparseIndexingMode()) {
1739 // Fast case: we're putting a regular property to a regular array
1740 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1741 // however if the property currently exists missing attributes will override from their current 'true'
1742 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1743 if (!descriptor
.attributes()) {
1744 ASSERT(!descriptor
.isAccessorDescriptor());
1745 return putDirectIndex(exec
, index
, descriptor
.value(), 0, throwException
? PutDirectIndexShouldThrow
: PutDirectIndexShouldNotThrow
);
1748 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec
->vm());
1751 if (descriptor
.attributes() & (ReadOnly
| Accessor
))
1752 notifyPresenceOfIndexedAccessors(exec
->vm());
1754 SparseArrayValueMap
* map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1755 RELEASE_ASSERT(map
);
1757 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1758 SparseArrayValueMap::AddResult result
= map
->add(this, index
);
1759 SparseArrayEntry
* entryInMap
= &result
.iterator
->value
;
1761 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1762 // 3. If current is undefined and extensible is false, then Reject.
1763 // 4. If current is undefined and extensible is true, then
1764 if (result
.isNewEntry
) {
1765 if (!isExtensible()) {
1766 map
->remove(result
.iterator
);
1767 return reject(exec
, throwException
, "Attempting to define property on object that is not extensible.");
1770 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1771 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1772 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1773 // created property is set to its default value.
1774 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1775 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1776 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1777 // is set to its default value.
1778 // 4.c. Return true.
1780 PropertyDescriptor defaults
;
1781 entryInMap
->setWithoutWriteBarrier(jsUndefined());
1782 entryInMap
->attributes
= DontDelete
| DontEnum
| ReadOnly
;
1783 entryInMap
->get(defaults
);
1785 putIndexedDescriptor(exec
, entryInMap
, descriptor
, defaults
);
1786 if (index
>= m_butterfly
->arrayStorage()->length())
1787 m_butterfly
->arrayStorage()->setLength(index
+ 1);
1791 // 5. Return true, if every field in Desc is absent.
1792 // 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).
1793 PropertyDescriptor current
;
1794 entryInMap
->get(current
);
1795 if (descriptor
.isEmpty() || descriptor
.equalTo(exec
, current
))
1798 // 7. If the [[Configurable]] field of current is false then
1799 if (!current
.configurable()) {
1800 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1801 if (descriptor
.configurablePresent() && descriptor
.configurable())
1802 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
1803 // 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.
1804 if (descriptor
.enumerablePresent() && current
.enumerable() != descriptor
.enumerable())
1805 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
1808 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1809 if (!descriptor
.isGenericDescriptor()) {
1810 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1811 if (current
.isDataDescriptor() != descriptor
.isDataDescriptor()) {
1812 // 9.a. Reject, if the [[Configurable]] field of current is false.
1813 if (!current
.configurable())
1814 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
1815 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1816 // data property to an accessor property. Preserve the existing values of the converted property's
1817 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1818 // their default values.
1819 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1820 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1821 // attributes and set the rest of the property's attributes to their default values.
1822 } else if (current
.isDataDescriptor() && descriptor
.isDataDescriptor()) {
1823 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1824 // 10.a. If the [[Configurable]] field of current is false, then
1825 if (!current
.configurable() && !current
.writable()) {
1826 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1827 if (descriptor
.writable())
1828 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
1829 // 10.a.ii. If the [[Writable]] field of current is false, then
1830 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1831 if (descriptor
.value() && !sameValue(exec
, descriptor
.value(), current
.value()))
1832 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
1834 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1836 ASSERT(current
.isAccessorDescriptor() && current
.getterPresent() && current
.setterPresent());
1837 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1838 if (!current
.configurable()) {
1839 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1840 if (descriptor
.setterPresent() && descriptor
.setter() != current
.setter())
1841 return reject(exec
, throwException
, "Attempting to change the setter of an unconfigurable property.");
1842 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1843 if (descriptor
.getterPresent() && descriptor
.getter() != current
.getter())
1844 return reject(exec
, throwException
, "Attempting to change the getter of an unconfigurable property.");
1849 // 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.
1850 putIndexedDescriptor(exec
, entryInMap
, descriptor
, current
);
1855 SparseArrayValueMap
* JSObject::allocateSparseIndexMap(VM
& vm
)
1857 SparseArrayValueMap
* result
= SparseArrayValueMap::create(vm
);
1858 arrayStorage()->m_sparseMap
.set(vm
, this, result
);
1862 void JSObject::deallocateSparseIndexMap()
1864 if (ArrayStorage
* arrayStorage
= arrayStorageOrNull())
1865 arrayStorage
->m_sparseMap
.clear();
1868 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState
* exec
, JSValue thisValue
, unsigned i
, JSValue value
, bool shouldThrow
)
1870 for (JSObject
* current
= this; ;) {
1871 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1872 // allows a prototype to intercept a put if (a) the prototype declares the property
1873 // we're after rather than intercepting it via an override of JSObject::put(), and
1874 // (b) that property is declared as ReadOnly or Accessor.
1876 ArrayStorage
* storage
= current
->arrayStorageOrNull();
1877 if (storage
&& storage
->m_sparseMap
) {
1878 SparseArrayValueMap::iterator iter
= storage
->m_sparseMap
->find(i
);
1879 if (iter
!= storage
->m_sparseMap
->notFound() && (iter
->value
.attributes
& (Accessor
| ReadOnly
))) {
1880 iter
->value
.put(exec
, thisValue
, storage
->m_sparseMap
.get(), value
, shouldThrow
);
1885 JSValue prototypeValue
= current
->prototype();
1886 if (prototypeValue
.isNull())
1889 current
= asObject(prototypeValue
);
1893 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
1895 JSValue prototypeValue
= prototype();
1896 if (prototypeValue
.isNull())
1899 return asObject(prototypeValue
)->attemptToInterceptPutByIndexOnHoleForPrototype(exec
, this, i
, value
, shouldThrow
);
1902 template<IndexingType indexingShape
>
1903 void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState
* exec
, unsigned i
, JSValue value
)
1905 ASSERT((indexingType() & IndexingShapeMask
) == indexingShape
);
1906 ASSERT(!indexingShouldBeSparse());
1908 // For us to get here, the index is either greater than the public length, or greater than
1909 // or equal to the vector length.
1910 ASSERT(i
>= m_butterfly
->vectorLength());
1912 VM
& vm
= exec
->vm();
1914 if (i
>= MAX_ARRAY_INDEX
- 1
1915 || (i
>= MIN_SPARSE_ARRAY_INDEX
1916 && !isDenseEnoughForVector(i
, countElements
<indexingShape
>(butterfly())))
1917 || indexIsSufficientlyBeyondLengthForSparseMap(i
, m_butterfly
->vectorLength())) {
1918 ASSERT(i
<= MAX_ARRAY_INDEX
);
1919 ensureArrayStorageSlow(vm
);
1920 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1921 map
->putEntry(exec
, this, i
, value
, false);
1922 ASSERT(i
>= arrayStorage()->length());
1923 arrayStorage()->setLength(i
+ 1);
1927 ensureLength(vm
, i
+ 1);
1929 RELEASE_ASSERT(i
< m_butterfly
->vectorLength());
1930 switch (indexingShape
) {
1932 ASSERT(value
.isInt32());
1933 m_butterfly
->contiguousInt32()[i
].setWithoutWriteBarrier(value
);
1937 ASSERT(value
.isNumber());
1938 double valueAsDouble
= value
.asNumber();
1939 ASSERT(valueAsDouble
== valueAsDouble
);
1940 m_butterfly
->contiguousDouble()[i
] = valueAsDouble
;
1944 case ContiguousShape
:
1945 m_butterfly
->contiguous()[i
].set(vm
, this, value
);
1953 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
, ArrayStorage
* storage
)
1955 VM
& vm
= exec
->vm();
1957 // i should be a valid array index that is outside of the current vector.
1958 ASSERT(i
<= MAX_ARRAY_INDEX
);
1959 ASSERT(i
>= storage
->vectorLength());
1961 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
1963 // First, handle cases where we don't currently have a sparse map.
1965 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
1966 ASSERT(isExtensible());
1968 // Update m_length if necessary.
1969 if (i
>= storage
->length())
1970 storage
->setLength(i
+ 1);
1972 // Check that it is sensible to still be using a vector, and then try to grow the vector.
1973 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength())
1974 && isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)
1975 && increaseVectorLength(vm
, i
+ 1))) {
1976 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1977 storage
= arrayStorage();
1978 storage
->m_vector
[i
].set(vm
, this, value
);
1979 ++storage
->m_numValuesInVector
;
1982 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1983 map
= allocateSparseIndexMap(exec
->vm());
1984 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
1988 // Update m_length if necessary.
1989 unsigned length
= storage
->length();
1991 // Prohibit growing the array if length is not writable.
1992 if (map
->lengthIsReadOnly() || !isExtensible()) {
1994 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
1998 storage
->setLength(length
);
2001 // We are currently using a map - check whether we still want to be doing so.
2002 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2003 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2004 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
)) {
2005 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
2009 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2010 storage
= arrayStorage();
2011 storage
->m_numValuesInVector
= numValuesInArray
;
2013 // Copy all values from the map into the vector, and delete the map.
2014 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2015 SparseArrayValueMap::const_iterator end
= map
->end();
2016 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2017 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2018 deallocateSparseIndexMap();
2020 // Store the new property into the vector.
2021 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2023 ++storage
->m_numValuesInVector
;
2024 valueSlot
.set(vm
, this, value
);
2027 void JSObject::putByIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
2029 VM
& vm
= exec
->vm();
2031 // i should be a valid array index that is outside of the current vector.
2032 ASSERT(i
<= MAX_ARRAY_INDEX
);
2034 switch (indexingType()) {
2035 case ALL_BLANK_INDEXING_TYPES
: {
2036 if (indexingShouldBeSparse()) {
2037 putByIndexBeyondVectorLengthWithArrayStorage(
2038 exec
, i
, value
, shouldThrow
,
2039 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2042 if (indexIsSufficientlyBeyondLengthForSparseMap(i
, 0) || i
>= MIN_SPARSE_ARRAY_INDEX
) {
2043 putByIndexBeyondVectorLengthWithArrayStorage(
2044 exec
, i
, value
, shouldThrow
, createArrayStorage(vm
, 0, 0));
2047 if (structure(vm
)->needsSlowPutIndexing()) {
2048 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2049 storage
->m_vector
[i
].set(vm
, this, value
);
2050 storage
->m_numValuesInVector
++;
2054 createInitialForValueAndSet(vm
, i
, value
);
2058 case ALL_UNDECIDED_INDEXING_TYPES
: {
2063 case ALL_INT32_INDEXING_TYPES
: {
2064 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2068 case ALL_DOUBLE_INDEXING_TYPES
: {
2069 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2073 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2074 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2078 case NonArrayWithSlowPutArrayStorage
:
2079 case ArrayWithSlowPutArrayStorage
: {
2080 // No own property present in the vector, but there might be in the sparse map!
2081 SparseArrayValueMap
* map
= arrayStorage()->m_sparseMap
.get();
2082 if (!(map
&& map
->contains(i
)) && attemptToInterceptPutByIndexOnHole(exec
, i
, value
, shouldThrow
))
2087 case NonArrayWithArrayStorage
:
2088 case ArrayWithArrayStorage
:
2089 putByIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, shouldThrow
, arrayStorage());
2093 RELEASE_ASSERT_NOT_REACHED();
2097 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
, ArrayStorage
* storage
)
2099 VM
& vm
= exec
->vm();
2101 // i should be a valid array index that is outside of the current vector.
2102 ASSERT(hasAnyArrayStorage(indexingType()));
2103 ASSERT(arrayStorage() == storage
);
2104 ASSERT(i
>= storage
->vectorLength() || attributes
);
2105 ASSERT(i
<= MAX_ARRAY_INDEX
);
2107 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
2109 // First, handle cases where we don't currently have a sparse map.
2111 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2112 ASSERT(isExtensible());
2114 // Update m_length if necessary.
2115 if (i
>= storage
->length())
2116 storage
->setLength(i
+ 1);
2118 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2121 && (isDenseEnoughForVector(i
, storage
->m_numValuesInVector
))
2122 && !indexIsSufficientlyBeyondLengthForSparseMap(i
, storage
->vectorLength()))
2123 && increaseVectorLength(vm
, i
+ 1)) {
2124 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2125 storage
= arrayStorage();
2126 storage
->m_vector
[i
].set(vm
, this, value
);
2127 ++storage
->m_numValuesInVector
;
2130 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2131 map
= allocateSparseIndexMap(exec
->vm());
2132 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2135 // Update m_length if necessary.
2136 unsigned length
= storage
->length();
2138 if (mode
!= PutDirectIndexLikePutDirect
) {
2139 // Prohibit growing the array if length is not writable.
2140 if (map
->lengthIsReadOnly())
2141 return reject(exec
, mode
== PutDirectIndexShouldThrow
, StrictModeReadonlyPropertyWriteError
);
2142 if (!isExtensible())
2143 return reject(exec
, mode
== PutDirectIndexShouldThrow
, "Attempting to define property on object that is not extensible.");
2146 storage
->setLength(length
);
2149 // We are currently using a map - check whether we still want to be doing so.
2150 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2151 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2152 if (map
->sparseMode() || attributes
|| !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
))
2153 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2155 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2156 storage
= arrayStorage();
2157 storage
->m_numValuesInVector
= numValuesInArray
;
2159 // Copy all values from the map into the vector, and delete the map.
2160 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2161 SparseArrayValueMap::const_iterator end
= map
->end();
2162 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2163 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2164 deallocateSparseIndexMap();
2166 // Store the new property into the vector.
2167 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2169 ++storage
->m_numValuesInVector
;
2170 valueSlot
.set(vm
, this, value
);
2174 bool JSObject::putDirectIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
)
2176 VM
& vm
= exec
->vm();
2178 // i should be a valid array index that is outside of the current vector.
2179 ASSERT(i
<= MAX_ARRAY_INDEX
);
2181 if (attributes
& (ReadOnly
| Accessor
))
2182 notifyPresenceOfIndexedAccessors(vm
);
2184 switch (indexingType()) {
2185 case ALL_BLANK_INDEXING_TYPES
: {
2186 if (indexingShouldBeSparse() || attributes
) {
2187 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2188 exec
, i
, value
, attributes
, mode
,
2189 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2191 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
2192 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2193 exec
, i
, value
, attributes
, mode
, createArrayStorage(vm
, 0, 0));
2195 if (structure(vm
)->needsSlowPutIndexing()) {
2196 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2197 storage
->m_vector
[i
].set(vm
, this, value
);
2198 storage
->m_numValuesInVector
++;
2202 createInitialForValueAndSet(vm
, i
, value
);
2206 case ALL_UNDECIDED_INDEXING_TYPES
: {
2207 convertUndecidedForValue(exec
->vm(), value
);
2209 return putDirectIndex(exec
, i
, value
, attributes
, mode
);
2212 case ALL_INT32_INDEXING_TYPES
: {
2213 if (attributes
& (ReadOnly
| Accessor
)) {
2214 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2215 exec
, i
, value
, attributes
, mode
, convertInt32ToArrayStorage(vm
));
2217 if (!value
.isInt32()) {
2218 convertInt32ForValue(vm
, value
);
2219 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2221 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2225 case ALL_DOUBLE_INDEXING_TYPES
: {
2226 if (attributes
& (ReadOnly
| Accessor
)) {
2227 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2228 exec
, i
, value
, attributes
, mode
, convertDoubleToArrayStorage(vm
));
2230 if (!value
.isNumber()) {
2231 convertDoubleToContiguous(vm
);
2232 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2234 double valueAsDouble
= value
.asNumber();
2235 if (valueAsDouble
!= valueAsDouble
) {
2236 convertDoubleToContiguous(vm
);
2237 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2239 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2243 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2244 if (attributes
& (ReadOnly
| Accessor
)) {
2245 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2246 exec
, i
, value
, attributes
, mode
, convertContiguousToArrayStorage(vm
));
2248 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2252 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
2253 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, attributes
, mode
, arrayStorage());
2256 RELEASE_ASSERT_NOT_REACHED();
2261 void JSObject::putDirectNativeFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2263 StringImpl
* name
= propertyName
.publicName();
2265 name
= vm
.propertyNames
->anonymous
.impl();
2268 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2269 putDirect(vm
, propertyName
, function
, attributes
);
2272 JSFunction
* JSObject::putDirectBuiltinFunction(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2274 StringImpl
* name
= propertyName
.publicName();
2276 name
= vm
.propertyNames
->anonymous
.impl();
2278 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2279 putDirect(vm
, propertyName
, function
, attributes
);
2283 JSFunction
* JSObject::putDirectBuiltinFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, FunctionExecutable
* functionExecutable
, unsigned attributes
)
2285 StringImpl
* name
= propertyName
.publicName();
2287 name
= vm
.propertyNames
->anonymous
.impl();
2289 JSFunction
* function
= JSFunction::createBuiltinFunction(vm
, static_cast<FunctionExecutable
*>(functionExecutable
), globalObject
);
2290 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2294 void JSObject::putDirectNativeFunctionWithoutTransition(VM
& vm
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2296 StringImpl
* name
= propertyName
.publicName();
2299 JSFunction
* function
= JSFunction::create(vm
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2300 putDirectWithoutTransition(vm
, propertyName
, function
, attributes
);
2303 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned currentVectorLength
, unsigned currentLength
, unsigned desiredLength
)
2305 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
2307 unsigned increasedLength
;
2308 unsigned maxInitLength
= std::min(currentLength
, 100000U);
2310 if (desiredLength
< maxInitLength
)
2311 increasedLength
= maxInitLength
;
2312 else if (!currentVectorLength
)
2313 increasedLength
= std::max(desiredLength
, lastArraySize
);
2315 increasedLength
= timesThreePlusOneDividedByTwo(desiredLength
);
2318 ASSERT(increasedLength
>= desiredLength
);
2320 lastArraySize
= std::min(increasedLength
, FIRST_VECTOR_GROW
);
2322 return std::min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
2325 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned desiredLength
)
2327 unsigned vectorLength
;
2330 if (hasIndexedProperties(indexingType())) {
2331 vectorLength
= m_butterfly
->vectorLength();
2332 length
= m_butterfly
->publicLength();
2338 return getNewVectorLength(vectorLength
, length
, desiredLength
);
2341 template<IndexingType indexingShape
>
2342 unsigned JSObject::countElements(Butterfly
* butterfly
)
2344 unsigned numValues
= 0;
2345 for (unsigned i
= butterfly
->publicLength(); i
--;) {
2346 switch (indexingShape
) {
2348 case ContiguousShape
:
2349 if (butterfly
->contiguous()[i
])
2354 double value
= butterfly
->contiguousDouble()[i
];
2367 unsigned JSObject::countElements()
2369 switch (indexingType()) {
2370 case ALL_BLANK_INDEXING_TYPES
:
2371 case ALL_UNDECIDED_INDEXING_TYPES
:
2374 case ALL_INT32_INDEXING_TYPES
:
2375 return countElements
<Int32Shape
>(butterfly());
2377 case ALL_DOUBLE_INDEXING_TYPES
:
2378 return countElements
<DoubleShape
>(butterfly());
2380 case ALL_CONTIGUOUS_INDEXING_TYPES
:
2381 return countElements
<ContiguousShape
>(butterfly());
2389 bool JSObject::increaseVectorLength(VM
& vm
, unsigned newLength
)
2391 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2392 // to the vector. Callers have to account for that, because they can do it more efficiently.
2393 if (newLength
> MAX_STORAGE_VECTOR_LENGTH
)
2396 ArrayStorage
* storage
= arrayStorage();
2398 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
2399 && !isDenseEnoughForVector(newLength
, storage
->m_numValuesInVector
))
2402 unsigned indexBias
= storage
->m_indexBias
;
2403 unsigned vectorLength
= storage
->vectorLength();
2404 ASSERT(newLength
> vectorLength
);
2405 unsigned newVectorLength
= getNewVectorLength(newLength
);
2407 // Fast case - there is no precapacity. In these cases a realloc makes sense.
2408 Structure
* structure
= this->structure(vm
);
2409 if (LIKELY(!indexBias
)) {
2410 DeferGC
deferGC(vm
.heap
);
2411 Butterfly
* newButterfly
= storage
->butterfly()->growArrayRight(
2412 vm
, this, structure
, structure
->outOfLineCapacity(), true,
2413 ArrayStorage::sizeFor(vectorLength
), ArrayStorage::sizeFor(newVectorLength
));
2416 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2417 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2421 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2422 DeferGC
deferGC(vm
.heap
);
2423 unsigned newIndexBias
= std::min(indexBias
>> 1, MAX_STORAGE_VECTOR_LENGTH
- newVectorLength
);
2424 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(
2426 structure
->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength
),
2427 newIndexBias
, true, ArrayStorage::sizeFor(newVectorLength
));
2430 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2431 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
2432 setButterflyWithoutChangingStructure(vm
, newButterfly
);
2436 void JSObject::ensureLengthSlow(VM
& vm
, unsigned length
)
2438 ASSERT(length
< MAX_ARRAY_INDEX
);
2439 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2440 ASSERT(length
> m_butterfly
->vectorLength());
2442 unsigned newVectorLength
= std::min(
2444 MAX_STORAGE_VECTOR_LENGTH
);
2445 unsigned oldVectorLength
= m_butterfly
->vectorLength();
2446 DeferGC
deferGC(vm
.heap
);
2447 m_butterfly
.set(vm
, this, m_butterfly
->growArrayRight(
2448 vm
, this, structure(), structure()->outOfLineCapacity(), true,
2449 oldVectorLength
* sizeof(EncodedJSValue
),
2450 newVectorLength
* sizeof(EncodedJSValue
)));
2452 m_butterfly
->setVectorLength(newVectorLength
);
2454 if (hasDouble(indexingType())) {
2455 for (unsigned i
= oldVectorLength
; i
< newVectorLength
; ++i
)
2456 m_butterfly
->contiguousDouble().data()[i
] = PNaN
;
2460 Butterfly
* JSObject::growOutOfLineStorage(VM
& vm
, size_t oldSize
, size_t newSize
)
2462 ASSERT(newSize
> oldSize
);
2464 // It's important that this function not rely on structure(), for the property
2465 // capacity, since we might have already mutated the structure in-place.
2467 return Butterfly::createOrGrowPropertyStorage(m_butterfly
.get(), vm
, this, structure(vm
), oldSize
, newSize
);
2470 bool JSObject::getOwnPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
2472 JSC::PropertySlot
slot(this);
2473 if (!methodTable(exec
->vm())->getOwnPropertySlot(this, exec
, propertyName
, slot
))
2475 /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */
2476 if (slot
.slotBase() != this && slot
.slotBase() && slot
.slotBase()->methodTable(exec
->vm())->toThis(slot
.slotBase(), exec
, NotStrictMode
) != this)
2478 if (slot
.isAccessor())
2479 descriptor
.setAccessorDescriptor(slot
.getterSetter(), slot
.attributes());
2480 else if (slot
.attributes() & CustomAccessor
)
2481 descriptor
.setCustomDescriptor(slot
.attributes());
2483 descriptor
.setDescriptor(slot
.getValue(exec
, propertyName
), slot
.attributes());
2487 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
2489 VM
& vm
= exec
->vm();
2490 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
2491 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
2492 GetterSetter
* accessor
= GetterSetter::create(vm
);
2493 if (oldDescriptor
.getterPresent())
2494 accessor
->setGetter(vm
, oldDescriptor
.getterObject());
2495 if (oldDescriptor
.setterPresent())
2496 accessor
->setSetter(vm
, oldDescriptor
.setterObject());
2497 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2500 JSValue newValue
= jsUndefined();
2501 if (descriptor
.value())
2502 newValue
= descriptor
.value();
2503 else if (oldDescriptor
.value())
2504 newValue
= oldDescriptor
.value();
2505 target
->putDirect(vm
, propertyName
, newValue
, attributes
& ~Accessor
);
2506 if (attributes
& ReadOnly
)
2507 target
->structure(vm
)->setContainsReadOnlyProperties();
2510 attributes
&= ~ReadOnly
;
2511 GetterSetter
* accessor
= GetterSetter::create(vm
);
2513 if (descriptor
.getterPresent())
2514 accessor
->setGetter(vm
, descriptor
.getterObject());
2515 else if (oldDescriptor
.getterPresent())
2516 accessor
->setGetter(vm
, oldDescriptor
.getterObject());
2517 if (descriptor
.setterPresent())
2518 accessor
->setSetter(vm
, descriptor
.setterObject());
2519 else if (oldDescriptor
.setterPresent())
2520 accessor
->setSetter(vm
, oldDescriptor
.setterObject());
2522 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2526 void JSObject::putDirectMayBeIndex(ExecState
* exec
, PropertyName propertyName
, JSValue value
)
2528 unsigned asIndex
= propertyName
.asIndex();
2529 if (asIndex
== PropertyName::NotAnIndex
)
2530 putDirect(exec
->vm(), propertyName
, value
);
2532 putDirectIndex(exec
, asIndex
, value
);
2535 class DefineOwnPropertyScope
{
2537 DefineOwnPropertyScope(ExecState
* exec
)
2540 m_vm
.setInDefineOwnProperty(true);
2543 ~DefineOwnPropertyScope()
2545 m_vm
.setInDefineOwnProperty(false);
2552 bool JSObject::defineOwnNonIndexProperty(ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2554 // Track on the globaldata that we're in define property.
2555 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2556 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2557 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2558 DefineOwnPropertyScope
scope(exec
);
2560 // If we have a new property we can just put it on normally
2561 PropertyDescriptor current
;
2562 if (!getOwnPropertyDescriptor(exec
, propertyName
, current
)) {
2563 // unless extensions are prevented!
2564 if (!isExtensible()) {
2566 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to define property on object that is not extensible.")));
2569 PropertyDescriptor oldDescriptor
;
2570 oldDescriptor
.setValue(jsUndefined());
2571 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
2574 if (descriptor
.isEmpty())
2577 if (current
.equalTo(exec
, descriptor
))
2580 // Filter out invalid changes
2581 if (!current
.configurable()) {
2582 if (descriptor
.configurable()) {
2584 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
2587 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
2589 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
2594 // A generic descriptor is simply changing the attributes of an existing property
2595 if (descriptor
.isGenericDescriptor()) {
2596 if (!current
.attributesEqual(descriptor
)) {
2597 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2598 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2603 // Changing between a normal property or an accessor property
2604 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
2605 if (!current
.configurable()) {
2607 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2610 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2611 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2614 // Changing the value and attributes of an existing property
2615 if (descriptor
.isDataDescriptor()) {
2616 if (!current
.configurable()) {
2617 if (!current
.writable() && descriptor
.writable()) {
2619 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
2622 if (!current
.writable()) {
2623 if (descriptor
.value() && !sameValue(exec
, current
.value(), descriptor
.value())) {
2625 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
2630 if (current
.attributesEqual(descriptor
) && !descriptor
.value())
2632 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2633 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2636 // Changing the accessor functions of an existing accessor property
2637 ASSERT(descriptor
.isAccessorDescriptor());
2638 if (!current
.configurable()) {
2639 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
2641 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
2644 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
2646 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2649 if (current
.attributes() & CustomAccessor
) {
2651 exec
->vm().throwException(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2655 JSValue accessor
= getDirect(exec
->vm(), propertyName
);
2658 GetterSetter
* getterSetter
;
2659 if (accessor
.isCustomGetterSetter())
2660 getterSetter
= GetterSetter::create(exec
->vm());
2662 ASSERT(accessor
.isGetterSetter());
2663 getterSetter
= asGetterSetter(accessor
);
2665 if (descriptor
.setterPresent())
2666 getterSetter
->setSetter(exec
->vm(), descriptor
.setterObject());
2667 if (descriptor
.getterPresent())
2668 getterSetter
->setGetter(exec
->vm(), descriptor
.getterObject());
2669 if (current
.attributesEqual(descriptor
))
2671 methodTable(exec
->vm())->deleteProperty(this, exec
, propertyName
);
2672 unsigned attrs
= descriptor
.attributesOverridingCurrent(current
);
2673 putDirectAccessor(exec
, propertyName
, getterSetter
, attrs
| Accessor
);
2677 bool JSObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
2679 // If it's an array index, then use the indexed property storage.
2680 unsigned index
= propertyName
.asIndex();
2681 if (index
!= PropertyName::NotAnIndex
) {
2682 // 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.
2683 // d. Reject if succeeded is false.
2684 // e. If index >= oldLen
2685 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2686 // 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.
2688 return object
->defineOwnIndexedProperty(exec
, index
, descriptor
, throwException
);
2691 return object
->defineOwnNonIndexProperty(exec
, propertyName
, descriptor
, throwException
);
2694 JSObject
* throwTypeError(ExecState
* exec
, const String
& message
)
2696 return exec
->vm().throwException(exec
, createTypeError(exec
, message
));
2699 void JSObject::shiftButterflyAfterFlattening(VM
& vm
, size_t outOfLineCapacityBefore
, size_t outOfLineCapacityAfter
)
2701 Butterfly
* butterfly
= this->butterfly();
2702 size_t preCapacity
= this->butterflyPreCapacity();
2703 void* currentBase
= butterfly
->base(preCapacity
, outOfLineCapacityAfter
);
2704 void* newBase
= butterfly
->base(preCapacity
, outOfLineCapacityBefore
);
2706 memmove(newBase
, currentBase
, this->butterflyTotalSize());
2707 setButterflyWithoutChangingStructure(vm
, Butterfly::fromBase(newBase
, preCapacity
, outOfLineCapacityAfter
));