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 "DatePrototype.h"
32 #include "ErrorConstructor.h"
33 #include "Executable.h"
34 #include "GetterSetter.h"
35 #include "IndexingHeaderInlines.h"
36 #include "JSFunction.h"
37 #include "JSGlobalObject.h"
39 #include "NativeErrorConstructor.h"
41 #include "ObjectPrototype.h"
42 #include "Operations.h"
43 #include "PropertyDescriptor.h"
44 #include "PropertyNameArray.h"
46 #include "SlotVisitorInlines.h"
48 #include <wtf/Assertions.h>
52 // We keep track of the size of the last array after it was grown. We use this
53 // as a simple heuristic for as the value to grow the next array from size 0.
54 // This value is capped by the constant FIRST_VECTOR_GROW defined in
55 // ArrayConventions.h.
56 static unsigned lastArraySize
= 0;
58 JSCell
* getCallableObjectSlow(JSCell
* cell
)
60 Structure
* structure
= cell
->structure();
61 if (structure
->typeInfo().type() == JSFunctionType
)
63 if (structure
->classInfo()->isSubClassOf(&InternalFunction::s_info
))
68 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject
);
69 ASSERT_HAS_TRIVIAL_DESTRUCTOR(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
)
79 // Add properties from the static hashtables of properties
80 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
81 const HashTable
* table
= classInfo
->propHashTable(exec
);
84 table
->initializeIfNeeded(exec
);
87 int hashSizeMask
= table
->compactSize
- 1;
88 const HashEntry
* entry
= table
->table
;
89 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
90 if (entry
->key() && (!(entry
->attributes() & DontEnum
) || (mode
== IncludeDontEnumProperties
)) && !((entry
->attributes() & Function
) && didReify
))
91 propertyNames
.add(entry
->key());
96 ALWAYS_INLINE
void JSObject::copyButterfly(CopyVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
100 Structure
* structure
= this->structure();
102 size_t propertyCapacity
= structure
->outOfLineCapacity();
104 size_t indexingPayloadSizeInBytes
;
105 bool hasIndexingHeader
= JSC::hasIndexingHeader(structure
->indexingType());
106 if (UNLIKELY(hasIndexingHeader
)) {
107 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
108 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
111 indexingPayloadSizeInBytes
= 0;
113 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
114 if (visitor
.checkIfShouldCopy(butterfly
->base(preCapacity
, propertyCapacity
))) {
115 Butterfly
* newButterfly
= Butterfly::createUninitializedDuringCollection(visitor
, preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
117 // Copy the properties.
118 PropertyStorage currentTarget
= newButterfly
->propertyStorage();
119 PropertyStorage currentSource
= butterfly
->propertyStorage();
120 for (size_t count
= storageSize
; count
--;)
121 (--currentTarget
)->setWithoutWriteBarrier((--currentSource
)->get());
123 if (UNLIKELY(hasIndexingHeader
)) {
124 *newButterfly
->indexingHeader() = *butterfly
->indexingHeader();
126 // Copy the array if appropriate.
128 WriteBarrier
<Unknown
>* currentTarget
;
129 WriteBarrier
<Unknown
>* currentSource
;
132 switch (structure
->indexingType()) {
133 case ALL_UNDECIDED_INDEXING_TYPES
:
134 case ALL_CONTIGUOUS_INDEXING_TYPES
:
135 case ALL_INT32_INDEXING_TYPES
:
136 case ALL_DOUBLE_INDEXING_TYPES
: {
137 currentTarget
= newButterfly
->contiguous().data();
138 currentSource
= butterfly
->contiguous().data();
139 RELEASE_ASSERT(newButterfly
->publicLength() <= newButterfly
->vectorLength());
140 count
= newButterfly
->vectorLength();
144 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
145 newButterfly
->arrayStorage()->copyHeaderFromDuringGC(*butterfly
->arrayStorage());
146 currentTarget
= newButterfly
->arrayStorage()->m_vector
;
147 currentSource
= butterfly
->arrayStorage()->m_vector
;
148 count
= newButterfly
->arrayStorage()->vectorLength();
159 memcpy(currentTarget
, currentSource
, count
* sizeof(EncodedJSValue
));
162 m_butterfly
= newButterfly
;
163 visitor
.didCopy(butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
167 ALWAYS_INLINE
void JSObject::visitButterfly(SlotVisitor
& visitor
, Butterfly
* butterfly
, size_t storageSize
)
171 Structure
* structure
= this->structure();
173 size_t propertyCapacity
= structure
->outOfLineCapacity();
175 size_t indexingPayloadSizeInBytes
;
176 bool hasIndexingHeader
= JSC::hasIndexingHeader(structure
->indexingType());
177 if (UNLIKELY(hasIndexingHeader
)) {
178 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
179 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
182 indexingPayloadSizeInBytes
= 0;
184 size_t capacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
186 // Mark the properties.
187 visitor
.appendValues(butterfly
->propertyStorage() - storageSize
, storageSize
);
188 visitor
.copyLater(this, butterfly
->base(preCapacity
, propertyCapacity
), capacityInBytes
);
190 // Mark the array if appropriate.
191 switch (structure
->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
, &s_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()->outOfLineSize());
221 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
225 void JSObject::copyBackingStore(JSCell
* cell
, CopyVisitor
& visitor
)
227 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
228 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
230 Butterfly
* butterfly
= thisObject
->butterfly();
232 thisObject
->copyButterfly(visitor
, butterfly
, thisObject
->structure()->outOfLineSize());
235 void JSFinalObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
237 JSFinalObject
* thisObject
= jsCast
<JSFinalObject
*>(cell
);
238 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
240 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
241 visitor
.m_isCheckingForDefaultMarkViolation
= false;
244 JSCell::visitChildren(thisObject
, visitor
);
246 Butterfly
* butterfly
= thisObject
->butterfly();
248 thisObject
->visitButterfly(visitor
, butterfly
, thisObject
->structure()->outOfLineSize());
250 size_t storageSize
= thisObject
->structure()->inlineSize();
251 visitor
.appendValues(thisObject
->inlineStorage(), storageSize
);
254 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
258 String
JSObject::className(const JSObject
* object
)
260 const ClassInfo
* info
= object
->classInfo();
262 return info
->className
;
265 bool JSObject::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
267 // NB. The fact that we're directly consulting our indexed storage implies that it is not
268 // legal for anyone to override getOwnPropertySlot() without also overriding
269 // getOwnPropertySlotByIndex().
271 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
273 if (i
> MAX_ARRAY_INDEX
)
274 return thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
276 switch (thisObject
->structure()->indexingType()) {
277 case ALL_BLANK_INDEXING_TYPES
:
278 case ALL_UNDECIDED_INDEXING_TYPES
:
281 case ALL_INT32_INDEXING_TYPES
:
282 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
283 Butterfly
* butterfly
= thisObject
->m_butterfly
;
284 if (i
>= butterfly
->vectorLength())
287 JSValue value
= butterfly
->contiguous()[i
].get();
289 slot
.setValue(value
);
296 case ALL_DOUBLE_INDEXING_TYPES
: {
297 Butterfly
* butterfly
= thisObject
->m_butterfly
;
298 if (i
>= butterfly
->vectorLength())
301 double value
= butterfly
->contiguousDouble()[i
];
302 if (value
== value
) {
303 slot
.setValue(JSValue(JSValue::EncodeAsDouble
, value
));
310 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
311 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
312 if (i
>= storage
->length())
315 if (i
< storage
->vectorLength()) {
316 JSValue value
= storage
->m_vector
[i
].get();
318 slot
.setValue(value
);
321 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
322 SparseArrayValueMap::iterator it
= map
->find(i
);
323 if (it
!= map
->notFound()) {
332 RELEASE_ASSERT_NOT_REACHED();
340 void JSObject::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
342 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
344 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(thisObject
));
347 // Try indexed put first. This is required for correctness, since loads on property names that appear like
348 // valid indices will never look in the named property storage.
349 unsigned i
= propertyName
.asIndex();
350 if (i
!= PropertyName::NotAnIndex
) {
351 putByIndex(thisObject
, exec
, i
, value
, slot
.isStrictMode());
355 // Check if there are any setters or getters in the prototype chain
357 if (propertyName
!= exec
->propertyNames().underscoreProto
) {
358 for (JSObject
* obj
= thisObject
; !obj
->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj
= asObject(prototype
)) {
359 prototype
= obj
->prototype();
360 if (prototype
.isNull()) {
361 ASSERT(!thisObject
->structure()->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
));
362 if (!thisObject
->putDirectInternal
<PutModePut
>(vm
, propertyName
, value
, 0, slot
, getCallableObject(value
))
363 && slot
.isStrictMode())
364 throwTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
));
371 for (obj
= thisObject
; ; obj
= asObject(prototype
)) {
373 JSCell
* specificValue
;
374 PropertyOffset offset
= obj
->structure()->get(vm
, propertyName
, attributes
, specificValue
);
375 if (isValidOffset(offset
)) {
376 if (attributes
& ReadOnly
) {
377 ASSERT(thisObject
->structure()->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
378 if (slot
.isStrictMode())
379 throwError(exec
, createTypeError(exec
, ASCIILiteral(StrictModeReadonlyPropertyWriteError
)));
383 JSValue gs
= obj
->getDirect(offset
);
384 if (gs
.isGetterSetter()) {
385 ASSERT(attributes
& Accessor
);
386 ASSERT(thisObject
->structure()->prototypeChainMayInterceptStoreTo(exec
->vm(), propertyName
) || obj
== thisObject
);
387 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
389 if (slot
.isStrictMode())
390 throwError(exec
, createTypeError(exec
, ASCIILiteral("setting a property that has only a getter")));
395 CallType callType
= setterFunc
->methodTable()->getCallData(setterFunc
, callData
);
396 MarkedArgumentBuffer args
;
399 // If this is WebCore's global object then we need to substitute the shell.
400 call(exec
, setterFunc
, callType
, callData
, thisObject
->methodTable()->toThisObject(thisObject
, exec
), args
);
403 ASSERT(!(attributes
& Accessor
));
405 // If there's an existing property on the object or one of its
406 // prototypes it should be replaced, so break here.
410 prototype
= obj
->prototype();
411 if (prototype
.isNull())
415 ASSERT(!thisObject
->structure()->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(shouldThrow
);
427 thisObject
->methodTable()->put(thisObject
, exec
, Identifier::from(exec
, propertyName
), value
, slot
);
431 switch (thisObject
->structure()->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
->m_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
->m_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 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(vm
, structure(), 0, ArrayStorage::sizeFor(0));
562 RELEASE_ASSERT(newButterfly
);
564 m_butterfly
= newButterfly
;
565 newButterfly
->arrayStorage()->m_indexBias
= 0;
566 newButterfly
->arrayStorage()->setVectorLength(0);
567 newButterfly
->arrayStorage()->m_sparseMap
.set(vm
, this, map
);
569 return newButterfly
->arrayStorage();
572 void JSObject::enterDictionaryIndexingMode(VM
& vm
)
574 switch (structure()->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(), 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
= structure()->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(m_butterfly
,
615 vm
, 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 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
625 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), AllocateUndecided
);
626 setButterfly(vm
, newButterfly
, newStructure
);
630 ContiguousJSValues
JSObject::createInitialInt32(VM
& vm
, unsigned length
)
632 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
633 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), AllocateInt32
);
634 setButterfly(vm
, newButterfly
, newStructure
);
635 return newButterfly
->contiguousInt32();
638 ContiguousDoubles
JSObject::createInitialDouble(VM
& vm
, unsigned length
)
640 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(double));
641 for (unsigned i
= newButterfly
->vectorLength(); i
--;)
642 newButterfly
->contiguousDouble()[i
] = QNaN
;
643 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), AllocateDouble
);
644 setButterfly(vm
, newButterfly
, newStructure
);
645 return newButterfly
->contiguousDouble();
648 ContiguousJSValues
JSObject::createInitialContiguous(VM
& vm
, unsigned length
)
650 Butterfly
* newButterfly
= createInitialIndexedStorage(vm
, length
, sizeof(EncodedJSValue
));
651 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), AllocateContiguous
);
652 setButterfly(vm
, newButterfly
, newStructure
);
653 return newButterfly
->contiguous();
656 ArrayStorage
* JSObject::createArrayStorage(VM
& vm
, unsigned length
, unsigned vectorLength
)
658 IndexingType oldType
= structure()->indexingType();
659 ASSERT_UNUSED(oldType
, !hasIndexedProperties(oldType
));
660 Butterfly
* newButterfly
= Butterfly::createOrGrowArrayRight(m_butterfly
,
661 vm
, structure(), structure()->outOfLineCapacity(), false, 0,
662 ArrayStorage::sizeFor(vectorLength
));
663 RELEASE_ASSERT(newButterfly
);
665 ArrayStorage
* result
= newButterfly
->arrayStorage();
666 result
->setLength(length
);
667 result
->setVectorLength(vectorLength
);
668 result
->m_sparseMap
.clear();
669 result
->m_numValuesInVector
= 0;
670 result
->m_indexBias
= 0;
671 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), structure()->suggestedArrayStorageTransition());
672 setButterfly(vm
, newButterfly
, newStructure
);
676 ArrayStorage
* JSObject::createInitialArrayStorage(VM
& vm
)
678 return createArrayStorage(vm
, 0, BASE_VECTOR_LEN
);
681 ContiguousJSValues
JSObject::convertUndecidedToInt32(VM
& vm
)
683 ASSERT(hasUndecided(structure()->indexingType()));
684 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateInt32
));
685 return m_butterfly
->contiguousInt32();
688 ContiguousDoubles
JSObject::convertUndecidedToDouble(VM
& vm
)
690 ASSERT(hasUndecided(structure()->indexingType()));
692 for (unsigned i
= m_butterfly
->vectorLength(); i
--;)
693 m_butterfly
->contiguousDouble()[i
] = QNaN
;
695 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateDouble
));
696 return m_butterfly
->contiguousDouble();
699 ContiguousJSValues
JSObject::convertUndecidedToContiguous(VM
& vm
)
701 ASSERT(hasUndecided(structure()->indexingType()));
702 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateContiguous
));
703 return m_butterfly
->contiguous();
706 ArrayStorage
* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM
& vm
, unsigned neededLength
)
708 unsigned publicLength
= m_butterfly
->publicLength();
709 unsigned propertyCapacity
= structure()->outOfLineCapacity();
710 unsigned propertySize
= structure()->outOfLineSize();
712 Butterfly
* newButterfly
= Butterfly::createUninitialized(
713 vm
, 0, propertyCapacity
, true, ArrayStorage::sizeFor(neededLength
));
716 newButterfly
->propertyStorage() - propertySize
,
717 m_butterfly
->propertyStorage() - propertySize
,
718 propertySize
* sizeof(EncodedJSValue
));
720 ArrayStorage
* newStorage
= newButterfly
->arrayStorage();
721 newStorage
->setVectorLength(neededLength
);
722 newStorage
->setLength(publicLength
);
723 newStorage
->m_sparseMap
.clear();
724 newStorage
->m_indexBias
= 0;
725 newStorage
->m_numValuesInVector
= 0;
730 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
732 ASSERT(hasUndecided(structure()->indexingType()));
734 ArrayStorage
* storage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
735 // No need to copy elements.
737 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), transition
);
738 setButterfly(vm
, storage
->butterfly(), newStructure
);
742 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
744 return convertUndecidedToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
747 ArrayStorage
* JSObject::convertUndecidedToArrayStorage(VM
& vm
)
749 return convertUndecidedToArrayStorage(vm
, structure()->suggestedArrayStorageTransition());
752 ContiguousDoubles
JSObject::convertInt32ToDouble(VM
& vm
)
754 ASSERT(hasInt32(structure()->indexingType()));
756 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
757 WriteBarrier
<Unknown
>* current
= &m_butterfly
->contiguousInt32()[i
];
758 double* currentAsDouble
= bitwise_cast
<double*>(current
);
759 JSValue v
= current
->get();
761 *currentAsDouble
= QNaN
;
765 *currentAsDouble
= v
.asInt32();
768 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateDouble
));
769 return m_butterfly
->contiguousDouble();
772 ContiguousJSValues
JSObject::convertInt32ToContiguous(VM
& vm
)
774 ASSERT(hasInt32(structure()->indexingType()));
776 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateContiguous
));
777 return m_butterfly
->contiguous();
780 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
782 ASSERT(hasInt32(structure()->indexingType()));
784 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
785 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
786 JSValue v
= m_butterfly
->contiguous()[i
].get();
789 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
790 newStorage
->m_numValuesInVector
++;
793 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), transition
);
794 setButterfly(vm
, newStorage
->butterfly(), newStructure
);
798 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
800 return convertInt32ToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
803 ArrayStorage
* JSObject::convertInt32ToArrayStorage(VM
& vm
)
805 return convertInt32ToArrayStorage(vm
, structure()->suggestedArrayStorageTransition());
808 template<JSObject::DoubleToContiguousMode mode
>
809 ContiguousJSValues
JSObject::genericConvertDoubleToContiguous(VM
& vm
)
811 ASSERT(hasDouble(structure()->indexingType()));
813 for (unsigned i
= m_butterfly
->vectorLength(); i
--;) {
814 double* current
= &m_butterfly
->contiguousDouble()[i
];
815 WriteBarrier
<Unknown
>* currentAsValue
= bitwise_cast
<WriteBarrier
<Unknown
>*>(current
);
816 double value
= *current
;
817 if (value
!= value
) {
818 currentAsValue
->clear();
823 case EncodeValueAsDouble
:
824 v
= JSValue(JSValue::EncodeAsDouble
, value
);
826 case RageConvertDoubleToValue
:
830 ASSERT(v
.isNumber());
831 currentAsValue
->setWithoutWriteBarrier(v
);
834 setStructure(vm
, Structure::nonPropertyTransition(vm
, structure(), AllocateContiguous
));
835 return m_butterfly
->contiguous();
838 ContiguousJSValues
JSObject::convertDoubleToContiguous(VM
& vm
)
840 return genericConvertDoubleToContiguous
<EncodeValueAsDouble
>(vm
);
843 ContiguousJSValues
JSObject::rageConvertDoubleToContiguous(VM
& vm
)
845 return genericConvertDoubleToContiguous
<RageConvertDoubleToValue
>(vm
);
848 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
850 ASSERT(hasDouble(structure()->indexingType()));
852 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
853 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
854 double value
= m_butterfly
->contiguousDouble()[i
];
857 newStorage
->m_vector
[i
].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble
, value
));
858 newStorage
->m_numValuesInVector
++;
861 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), transition
);
862 setButterfly(vm
, newStorage
->butterfly(), newStructure
);
866 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
868 return convertDoubleToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
871 ArrayStorage
* JSObject::convertDoubleToArrayStorage(VM
& vm
)
873 return convertDoubleToArrayStorage(vm
, structure()->suggestedArrayStorageTransition());
876 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
, NonPropertyTransition transition
, unsigned neededLength
)
878 ASSERT(hasContiguous(structure()->indexingType()));
880 ArrayStorage
* newStorage
= constructConvertedArrayStorageWithoutCopyingElements(vm
, neededLength
);
881 for (unsigned i
= m_butterfly
->publicLength(); i
--;) {
882 JSValue v
= m_butterfly
->contiguous()[i
].get();
885 newStorage
->m_vector
[i
].setWithoutWriteBarrier(v
);
886 newStorage
->m_numValuesInVector
++;
889 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), transition
);
890 setButterfly(vm
, newStorage
->butterfly(), newStructure
);
894 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
, NonPropertyTransition transition
)
896 return convertContiguousToArrayStorage(vm
, transition
, m_butterfly
->vectorLength());
899 ArrayStorage
* JSObject::convertContiguousToArrayStorage(VM
& vm
)
901 return convertContiguousToArrayStorage(vm
, structure()->suggestedArrayStorageTransition());
904 void JSObject::convertUndecidedForValue(VM
& vm
, JSValue value
)
906 if (value
.isInt32()) {
907 convertUndecidedToInt32(vm
);
911 if (value
.isDouble()) {
912 convertUndecidedToDouble(vm
);
916 convertUndecidedToContiguous(vm
);
919 void JSObject::convertInt32ForValue(VM
& vm
, JSValue value
)
921 ASSERT(!value
.isInt32());
923 if (value
.isDouble()) {
924 convertInt32ToDouble(vm
);
928 convertInt32ToContiguous(vm
);
931 void JSObject::setIndexQuicklyToUndecided(VM
& vm
, unsigned index
, JSValue value
)
933 ASSERT(index
< m_butterfly
->publicLength());
934 ASSERT(index
< m_butterfly
->vectorLength());
935 convertUndecidedForValue(vm
, value
);
936 setIndexQuickly(vm
, index
, value
);
939 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
941 ASSERT(!value
.isInt32());
942 convertInt32ForValue(vm
, value
);
943 setIndexQuickly(vm
, index
, value
);
946 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM
& vm
, unsigned index
, JSValue value
)
948 ASSERT(!value
.isNumber() || value
.asNumber() != value
.asNumber());
949 convertDoubleToContiguous(vm
);
950 setIndexQuickly(vm
, index
, value
);
953 ContiguousJSValues
JSObject::ensureInt32Slow(VM
& vm
)
955 ASSERT(inherits(&s_info
));
957 switch (structure()->indexingType()) {
958 case ALL_BLANK_INDEXING_TYPES
:
959 if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing()))
960 return ContiguousJSValues();
961 return createInitialInt32(vm
, 0);
963 case ALL_UNDECIDED_INDEXING_TYPES
:
964 return convertUndecidedToInt32(vm
);
966 case ALL_DOUBLE_INDEXING_TYPES
:
967 case ALL_CONTIGUOUS_INDEXING_TYPES
:
968 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
969 return ContiguousJSValues();
973 return ContiguousJSValues();
977 ContiguousDoubles
JSObject::ensureDoubleSlow(VM
& vm
)
979 ASSERT(inherits(&s_info
));
981 switch (structure()->indexingType()) {
982 case ALL_BLANK_INDEXING_TYPES
:
983 if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing()))
984 return ContiguousDoubles();
985 return createInitialDouble(vm
, 0);
987 case ALL_UNDECIDED_INDEXING_TYPES
:
988 return convertUndecidedToDouble(vm
);
990 case ALL_INT32_INDEXING_TYPES
:
991 return convertInt32ToDouble(vm
);
993 case ALL_CONTIGUOUS_INDEXING_TYPES
:
994 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
995 return ContiguousDoubles();
999 return ContiguousDoubles();
1003 ContiguousJSValues
JSObject::ensureContiguousSlow(VM
& vm
, DoubleToContiguousMode mode
)
1005 ASSERT(inherits(&s_info
));
1007 switch (structure()->indexingType()) {
1008 case ALL_BLANK_INDEXING_TYPES
:
1009 if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing()))
1010 return ContiguousJSValues();
1011 return createInitialContiguous(vm
, 0);
1013 case ALL_UNDECIDED_INDEXING_TYPES
:
1014 return convertUndecidedToContiguous(vm
);
1016 case ALL_INT32_INDEXING_TYPES
:
1017 return convertInt32ToContiguous(vm
);
1019 case ALL_DOUBLE_INDEXING_TYPES
:
1020 if (mode
== RageConvertDoubleToValue
)
1021 return rageConvertDoubleToContiguous(vm
);
1022 return convertDoubleToContiguous(vm
);
1024 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1025 return ContiguousJSValues();
1029 return ContiguousJSValues();
1033 ContiguousJSValues
JSObject::ensureContiguousSlow(VM
& vm
)
1035 return ensureContiguousSlow(vm
, EncodeValueAsDouble
);
1038 ContiguousJSValues
JSObject::rageEnsureContiguousSlow(VM
& vm
)
1040 return ensureContiguousSlow(vm
, RageConvertDoubleToValue
);
1043 ArrayStorage
* JSObject::ensureArrayStorageSlow(VM
& vm
)
1045 ASSERT(inherits(&s_info
));
1047 switch (structure()->indexingType()) {
1048 case ALL_BLANK_INDEXING_TYPES
:
1049 if (UNLIKELY(indexingShouldBeSparse()))
1050 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
);
1051 return createInitialArrayStorage(vm
);
1053 case ALL_UNDECIDED_INDEXING_TYPES
:
1054 ASSERT(!indexingShouldBeSparse());
1055 ASSERT(!structure()->needsSlowPutIndexing());
1056 return convertUndecidedToArrayStorage(vm
);
1058 case ALL_INT32_INDEXING_TYPES
:
1059 ASSERT(!indexingShouldBeSparse());
1060 ASSERT(!structure()->needsSlowPutIndexing());
1061 return convertInt32ToArrayStorage(vm
);
1063 case ALL_DOUBLE_INDEXING_TYPES
:
1064 ASSERT(!indexingShouldBeSparse());
1065 ASSERT(!structure()->needsSlowPutIndexing());
1066 return convertDoubleToArrayStorage(vm
);
1068 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1069 ASSERT(!indexingShouldBeSparse());
1070 ASSERT(!structure()->needsSlowPutIndexing());
1071 return convertContiguousToArrayStorage(vm
);
1074 RELEASE_ASSERT_NOT_REACHED();
1079 ArrayStorage
* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM
& vm
)
1081 switch (structure()->indexingType()) {
1082 case ALL_BLANK_INDEXING_TYPES
: {
1083 createArrayStorage(vm
, 0, 0);
1084 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1085 map
->setSparseMode();
1086 return arrayStorage();
1089 case ALL_UNDECIDED_INDEXING_TYPES
:
1090 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertUndecidedToArrayStorage(vm
));
1092 case ALL_INT32_INDEXING_TYPES
:
1093 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertInt32ToArrayStorage(vm
));
1095 case ALL_DOUBLE_INDEXING_TYPES
:
1096 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertDoubleToArrayStorage(vm
));
1098 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1099 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, convertContiguousToArrayStorage(vm
));
1101 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
1102 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm
, m_butterfly
->arrayStorage());
1110 void JSObject::switchToSlowPutArrayStorage(VM
& vm
)
1112 switch (structure()->indexingType()) {
1113 case ALL_UNDECIDED_INDEXING_TYPES
:
1114 convertUndecidedToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1117 case ALL_INT32_INDEXING_TYPES
:
1118 convertInt32ToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1121 case ALL_DOUBLE_INDEXING_TYPES
:
1122 convertDoubleToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1125 case ALL_CONTIGUOUS_INDEXING_TYPES
:
1126 convertContiguousToArrayStorage(vm
, AllocateSlowPutArrayStorage
);
1129 case NonArrayWithArrayStorage
:
1130 case ArrayWithArrayStorage
: {
1131 Structure
* newStructure
= Structure::nonPropertyTransition(vm
, structure(), SwitchToSlowPutArrayStorage
);
1132 setStructure(vm
, newStructure
);
1142 void JSObject::putDirectVirtual(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1144 ASSERT(!value
.isGetterSetter() && !(attributes
& Accessor
));
1145 PutPropertySlot slot
;
1146 object
->putDirectInternal
<PutModeDefineOwnProperty
>(exec
->vm(), propertyName
, value
, attributes
, slot
, getCallableObject(value
));
1149 void JSObject::setPrototype(VM
& vm
, JSValue prototype
)
1152 if (prototype
.isObject())
1153 vm
.prototypeMap
.addPrototype(asObject(prototype
));
1155 Structure
* newStructure
= Structure::changePrototypeTransition(vm
, structure(), prototype
);
1156 setStructure(vm
, newStructure
);
1158 if (!newStructure
->anyObjectInChainMayInterceptIndexedAccesses())
1161 if (vm
.prototypeMap
.isPrototype(this)) {
1162 newStructure
->globalObject()->haveABadTime(vm
);
1166 if (!hasIndexingHeader(structure()->indexingType()))
1169 if (shouldUseSlowPut(structure()->indexingType()))
1172 switchToSlowPutArrayStorage(vm
);
1175 bool JSObject::setPrototypeWithCycleCheck(VM
& vm
, JSValue prototype
)
1177 JSValue checkFor
= this;
1178 if (this->isGlobalObject())
1179 checkFor
= jsCast
<JSGlobalObject
*>(this)->globalExec()->thisValue();
1181 JSValue nextPrototype
= prototype
;
1182 while (nextPrototype
&& nextPrototype
.isObject()) {
1183 if (nextPrototype
== checkFor
)
1185 nextPrototype
= asObject(nextPrototype
)->prototype();
1187 setPrototype(vm
, prototype
);
1191 bool JSObject::allowsAccessFrom(ExecState
* exec
)
1193 JSGlobalObject
* globalObject
= this->globalObject();
1194 return globalObject
->globalObjectMethodTable()->allowsAccessFrom(globalObject
, exec
);
1197 void JSObject::putDirectAccessor(ExecState
* exec
, PropertyName propertyName
, JSValue value
, unsigned attributes
)
1199 ASSERT(value
.isGetterSetter() && (attributes
& Accessor
));
1201 unsigned index
= propertyName
.asIndex();
1202 if (index
!= PropertyName::NotAnIndex
) {
1203 putDirectIndex(exec
, index
, value
, attributes
, PutDirectIndexLikePutDirect
);
1207 VM
& vm
= exec
->vm();
1209 PutPropertySlot slot
;
1210 putDirectInternal
<PutModeDefineOwnProperty
>(vm
, propertyName
, value
, attributes
, slot
, getCallableObject(value
));
1212 // putDirect will change our Structure if we add a new property. For
1213 // getters and setters, though, we also need to change our Structure
1214 // if we override an existing non-getter or non-setter.
1215 if (slot
.type() != PutPropertySlot::NewProperty
)
1216 setStructure(vm
, Structure::attributeChangeTransition(vm
, structure(), propertyName
, attributes
));
1218 if (attributes
& ReadOnly
)
1219 structure()->setContainsReadOnlyProperties();
1221 structure()->setHasGetterSetterProperties(propertyName
== vm
.propertyNames
->underscoreProto
);
1224 bool JSObject::hasProperty(ExecState
* exec
, PropertyName propertyName
) const
1227 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1230 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
1233 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
1237 bool JSObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
1239 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1241 unsigned i
= propertyName
.asIndex();
1242 if (i
!= PropertyName::NotAnIndex
)
1243 return thisObject
->methodTable()->deletePropertyByIndex(thisObject
, exec
, i
);
1245 if (!thisObject
->staticFunctionsReified())
1246 thisObject
->reifyStaticFunctionsForDelete(exec
);
1248 unsigned attributes
;
1249 JSCell
* specificValue
;
1250 if (isValidOffset(thisObject
->structure()->get(exec
->vm(), propertyName
, attributes
, specificValue
))) {
1251 if (attributes
& DontDelete
&& !exec
->vm().isInDefineOwnProperty())
1253 thisObject
->removeDirect(exec
->vm(), propertyName
);
1257 // Look in the static hashtable of properties
1258 const HashEntry
* entry
= thisObject
->findPropertyHashEntry(exec
, propertyName
);
1260 if (entry
->attributes() & DontDelete
&& !exec
->vm().isInDefineOwnProperty())
1261 return false; // this builtin property can't be deleted
1263 putEntry(exec
, entry
, propertyName
, jsUndefined(), thisObject
);
1269 bool JSObject::hasOwnProperty(ExecState
* exec
, PropertyName propertyName
) const
1272 return const_cast<JSObject
*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject
*>(this), exec
, propertyName
, slot
);
1275 bool JSObject::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
1277 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
1279 if (i
> MAX_ARRAY_INDEX
)
1280 return thisObject
->methodTable()->deleteProperty(thisObject
, exec
, Identifier::from(exec
, i
));
1282 switch (thisObject
->structure()->indexingType()) {
1283 case ALL_BLANK_INDEXING_TYPES
:
1284 case ALL_UNDECIDED_INDEXING_TYPES
:
1287 case ALL_INT32_INDEXING_TYPES
:
1288 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1289 Butterfly
* butterfly
= thisObject
->m_butterfly
;
1290 if (i
>= butterfly
->vectorLength())
1292 butterfly
->contiguous()[i
].clear();
1296 case ALL_DOUBLE_INDEXING_TYPES
: {
1297 Butterfly
* butterfly
= thisObject
->m_butterfly
;
1298 if (i
>= butterfly
->vectorLength())
1300 butterfly
->contiguousDouble()[i
] = QNaN
;
1304 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1305 ArrayStorage
* storage
= thisObject
->m_butterfly
->arrayStorage();
1307 if (i
< storage
->vectorLength()) {
1308 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
1311 --storage
->m_numValuesInVector
;
1313 } else if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1314 SparseArrayValueMap::iterator it
= map
->find(i
);
1315 if (it
!= map
->notFound()) {
1316 if (it
->value
.attributes
& DontDelete
)
1326 RELEASE_ASSERT_NOT_REACHED();
1331 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, PropertyName propertyName
)
1333 JSValue function
= object
->get(exec
, propertyName
);
1335 CallType callType
= getCallData(function
, callData
);
1336 if (callType
== CallTypeNone
)
1337 return exec
->exception();
1339 // Prevent "toString" and "valueOf" from observing execution if an exception
1341 if (exec
->hadException())
1342 return exec
->exception();
1344 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
1345 ASSERT(!result
.isGetterSetter());
1346 if (exec
->hadException())
1347 return exec
->exception();
1348 if (result
.isObject())
1353 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
1355 result
= methodTable()->defaultValue(this, exec
, PreferNumber
);
1356 number
= result
.toNumber(exec
);
1357 return !result
.isString();
1361 JSValue
JSObject::defaultValue(const JSObject
* object
, ExecState
* exec
, PreferredPrimitiveType hint
)
1363 // Must call toString first for Date objects.
1364 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& object
->prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
1365 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1368 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1372 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
1375 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
1380 ASSERT(!exec
->hadException());
1382 return throwError(exec
, createTypeError(exec
, ASCIILiteral("No default value")));
1385 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, PropertyName propertyName
) const
1387 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1388 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
1389 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
1396 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
)
1398 TypeInfo info
= structure()->typeInfo();
1399 if (info
.implementsDefaultHasInstance())
1400 return defaultHasInstance(exec
, value
, get(exec
, exec
->propertyNames().prototype
));
1401 if (info
.implementsHasInstance())
1402 return methodTable()->customHasInstance(this, exec
, value
);
1403 throwError(exec
, createInvalidParamError(exec
, "instanceof" , this));
1407 bool JSObject::defaultHasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
1409 if (!value
.isObject())
1412 if (!proto
.isObject()) {
1413 throwError(exec
, createTypeError(exec
, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
1417 JSObject
* object
= asObject(value
);
1418 while ((object
= object
->prototype().getObject())) {
1419 if (proto
== object
)
1425 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
1427 PropertyDescriptor descriptor
;
1428 if (!const_cast<JSObject
*>(this)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject
*>(this), exec
, propertyName
, descriptor
))
1430 return descriptor
.enumerable();
1433 bool JSObject::getPropertySpecificValue(ExecState
* exec
, PropertyName propertyName
, JSCell
*& specificValue
) const
1435 unsigned attributes
;
1436 if (isValidOffset(structure()->get(exec
->vm(), propertyName
, attributes
, specificValue
)))
1439 // This could be a function within the static table? - should probably
1440 // also look in the hash? This currently should not be a problem, since
1441 // we've currently always call 'get' first, which should have populated
1442 // the normal storage.
1446 void JSObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1448 propertyNames
.setBaseObject(object
);
1449 object
->methodTable()->getOwnPropertyNames(object
, exec
, propertyNames
, mode
);
1451 if (object
->prototype().isNull())
1454 JSObject
* prototype
= asObject(object
->prototype());
1456 if (prototype
->structure()->typeInfo().overridesGetPropertyNames()) {
1457 prototype
->methodTable()->getPropertyNames(prototype
, exec
, propertyNames
, mode
);
1460 prototype
->methodTable()->getOwnPropertyNames(prototype
, exec
, propertyNames
, mode
);
1461 JSValue nextProto
= prototype
->prototype();
1462 if (nextProto
.isNull())
1464 prototype
= asObject(nextProto
);
1468 void JSObject::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1470 // Add numeric properties first. That appears to be the accepted convention.
1471 // FIXME: Filling PropertyNameArray with an identifier for every integer
1472 // is incredibly inefficient for large arrays. We need a different approach,
1473 // which almost certainly means a different structure for PropertyNameArray.
1474 switch (object
->structure()->indexingType()) {
1475 case ALL_BLANK_INDEXING_TYPES
:
1476 case ALL_UNDECIDED_INDEXING_TYPES
:
1479 case ALL_INT32_INDEXING_TYPES
:
1480 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
1481 Butterfly
* butterfly
= object
->m_butterfly
;
1482 unsigned usedLength
= butterfly
->publicLength();
1483 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1484 if (!butterfly
->contiguous()[i
])
1486 propertyNames
.add(Identifier::from(exec
, i
));
1491 case ALL_DOUBLE_INDEXING_TYPES
: {
1492 Butterfly
* butterfly
= object
->m_butterfly
;
1493 unsigned usedLength
= butterfly
->publicLength();
1494 for (unsigned i
= 0; i
< usedLength
; ++i
) {
1495 double value
= butterfly
->contiguousDouble()[i
];
1498 propertyNames
.add(Identifier::from(exec
, i
));
1503 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
1504 ArrayStorage
* storage
= object
->m_butterfly
->arrayStorage();
1506 unsigned usedVectorLength
= std::min(storage
->length(), storage
->vectorLength());
1507 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
1508 if (storage
->m_vector
[i
])
1509 propertyNames
.add(Identifier::from(exec
, i
));
1512 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
1513 Vector
<unsigned, 0, UnsafeVectorOverflow
> keys
;
1514 keys
.reserveInitialCapacity(map
->size());
1516 SparseArrayValueMap::const_iterator end
= map
->end();
1517 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
1518 if (mode
== IncludeDontEnumProperties
|| !(it
->value
.attributes
& DontEnum
))
1519 keys
.uncheckedAppend(static_cast<unsigned>(it
->key
));
1522 std::sort(keys
.begin(), keys
.end());
1523 for (unsigned i
= 0; i
< keys
.size(); ++i
)
1524 propertyNames
.add(Identifier::from(exec
, keys
[i
]));
1530 RELEASE_ASSERT_NOT_REACHED();
1533 object
->methodTable()->getOwnNonIndexPropertyNames(object
, exec
, propertyNames
, mode
);
1536 void JSObject::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1538 getClassPropertyNames(exec
, object
->classInfo(), propertyNames
, mode
, object
->staticFunctionsReified());
1540 bool canCachePropertiesFromStructure
= !propertyNames
.size();
1541 object
->structure()->getPropertyNamesFromStructure(exec
->vm(), propertyNames
, mode
);
1543 if (canCachePropertiesFromStructure
)
1544 propertyNames
.setNumCacheableSlotsForObject(object
, propertyNames
.size());
1547 double JSObject::toNumber(ExecState
* exec
) const
1549 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
1550 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
1552 return primitive
.toNumber(exec
);
1555 JSString
* JSObject::toString(ExecState
* exec
) const
1557 JSValue primitive
= toPrimitive(exec
, PreferString
);
1558 if (exec
->hadException())
1559 return jsEmptyString(exec
);
1560 return primitive
.toString(exec
);
1563 JSObject
* JSObject::toThisObject(JSCell
* cell
, ExecState
*)
1565 return jsCast
<JSObject
*>(cell
);
1568 void JSObject::seal(VM
& vm
)
1572 preventExtensions(vm
);
1573 setStructure(vm
, Structure::sealTransition(vm
, structure()));
1576 void JSObject::freeze(VM
& vm
)
1580 preventExtensions(vm
);
1581 setStructure(vm
, Structure::freezeTransition(vm
, structure()));
1584 void JSObject::preventExtensions(VM
& vm
)
1586 enterDictionaryIndexingMode(vm
);
1588 setStructure(vm
, Structure::preventExtensionsTransition(vm
, structure()));
1591 // This presently will flatten to an uncachable dictionary; this is suitable
1592 // for use in delete, we may want to do something different elsewhere.
1593 void JSObject::reifyStaticFunctionsForDelete(ExecState
* exec
)
1595 ASSERT(!staticFunctionsReified());
1596 VM
& vm
= exec
->vm();
1598 // If this object's ClassInfo has no static properties, then nothing to reify!
1599 // We can safely set the flag to avoid the expensive check again in the future.
1600 if (!classInfo()->hasStaticProperties()) {
1601 structure()->setStaticFunctionsReified();
1605 if (!structure()->isUncacheableDictionary())
1606 setStructure(vm
, Structure::toUncacheableDictionaryTransition(vm
, structure()));
1608 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
1609 const HashTable
* hashTable
= info
->propHashTable(globalObject()->globalExec());
1613 for (HashTable::ConstIterator iter
= hashTable
->begin(vm
); iter
!= hashTable
->end(vm
); ++iter
) {
1614 if (iter
->attributes() & Function
)
1615 setUpStaticFunctionSlot(globalObject()->globalExec(), *iter
, this, Identifier(&vm
, iter
->key()), slot
);
1619 structure()->setStaticFunctionsReified();
1622 bool JSObject::removeDirect(VM
& vm
, PropertyName propertyName
)
1624 if (!isValidOffset(structure()->get(vm
, propertyName
)))
1627 PropertyOffset offset
;
1628 if (structure()->isUncacheableDictionary()) {
1629 offset
= structure()->removePropertyWithoutTransition(vm
, propertyName
);
1630 if (offset
== invalidOffset
)
1632 putDirectUndefined(offset
);
1636 setStructure(vm
, Structure::removePropertyTransition(vm
, structure(), propertyName
, offset
));
1637 if (offset
== invalidOffset
)
1639 putDirectUndefined(offset
);
1643 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, PropertyOffset offset
)
1645 if (JSObject
* getterFunction
= asGetterSetter(getDirect(offset
))->getter()) {
1646 if (!structure()->isDictionary())
1647 slot
.setCacheableGetterSlot(this, getterFunction
, offset
);
1649 slot
.setGetterSlot(getterFunction
);
1651 slot
.setUndefined();
1654 void JSObject::putIndexedDescriptor(ExecState
* exec
, SparseArrayEntry
* entryInMap
, PropertyDescriptor
& descriptor
, PropertyDescriptor
& oldDescriptor
)
1656 if (descriptor
.isDataDescriptor()) {
1657 if (descriptor
.value())
1658 entryInMap
->set(exec
->vm(), this, descriptor
.value());
1659 else if (oldDescriptor
.isAccessorDescriptor())
1660 entryInMap
->set(exec
->vm(), this, jsUndefined());
1661 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~Accessor
;
1665 if (descriptor
.isAccessorDescriptor()) {
1666 JSObject
* getter
= 0;
1667 if (descriptor
.getterPresent())
1668 getter
= descriptor
.getterObject();
1669 else if (oldDescriptor
.isAccessorDescriptor())
1670 getter
= oldDescriptor
.getterObject();
1671 JSObject
* setter
= 0;
1672 if (descriptor
.setterPresent())
1673 setter
= descriptor
.setterObject();
1674 else if (oldDescriptor
.isAccessorDescriptor())
1675 setter
= oldDescriptor
.setterObject();
1677 GetterSetter
* accessor
= GetterSetter::create(exec
);
1679 accessor
->setGetter(exec
->vm(), getter
);
1681 accessor
->setSetter(exec
->vm(), setter
);
1683 entryInMap
->set(exec
->vm(), this, accessor
);
1684 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~ReadOnly
;
1688 ASSERT(descriptor
.isGenericDescriptor());
1689 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
);
1692 // Defined in ES5.1 8.12.9
1693 bool JSObject::defineOwnIndexedProperty(ExecState
* exec
, unsigned index
, PropertyDescriptor
& descriptor
, bool throwException
)
1695 ASSERT(index
<= MAX_ARRAY_INDEX
);
1697 if (!inSparseIndexingMode()) {
1698 // Fast case: we're putting a regular property to a regular array
1699 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1700 // however if the property currently exists missing attributes will override from their current 'true'
1701 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1702 if (!descriptor
.attributes()) {
1703 ASSERT(!descriptor
.isAccessorDescriptor());
1704 return putDirectIndex(exec
, index
, descriptor
.value(), 0, throwException
? PutDirectIndexShouldThrow
: PutDirectIndexShouldNotThrow
);
1707 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec
->vm());
1710 if (descriptor
.attributes() & (ReadOnly
| Accessor
))
1711 notifyPresenceOfIndexedAccessors(exec
->vm());
1713 SparseArrayValueMap
* map
= m_butterfly
->arrayStorage()->m_sparseMap
.get();
1714 RELEASE_ASSERT(map
);
1716 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1717 SparseArrayValueMap::AddResult result
= map
->add(this, index
);
1718 SparseArrayEntry
* entryInMap
= &result
.iterator
->value
;
1720 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1721 // 3. If current is undefined and extensible is false, then Reject.
1722 // 4. If current is undefined and extensible is true, then
1723 if (result
.isNewEntry
) {
1724 if (!isExtensible()) {
1725 map
->remove(result
.iterator
);
1726 return reject(exec
, throwException
, "Attempting to define property on object that is not extensible.");
1729 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1730 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1731 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1732 // created property is set to its default value.
1733 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1734 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1735 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1736 // is set to its default value.
1737 // 4.c. Return true.
1739 PropertyDescriptor defaults
;
1740 entryInMap
->setWithoutWriteBarrier(jsUndefined());
1741 entryInMap
->attributes
= DontDelete
| DontEnum
| ReadOnly
;
1742 entryInMap
->get(defaults
);
1744 putIndexedDescriptor(exec
, entryInMap
, descriptor
, defaults
);
1745 if (index
>= m_butterfly
->arrayStorage()->length())
1746 m_butterfly
->arrayStorage()->setLength(index
+ 1);
1750 // 5. Return true, if every field in Desc is absent.
1751 // 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).
1752 PropertyDescriptor current
;
1753 entryInMap
->get(current
);
1754 if (descriptor
.isEmpty() || descriptor
.equalTo(exec
, current
))
1757 // 7. If the [[Configurable]] field of current is false then
1758 if (!current
.configurable()) {
1759 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1760 if (descriptor
.configurablePresent() && descriptor
.configurable())
1761 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
1762 // 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.
1763 if (descriptor
.enumerablePresent() && current
.enumerable() != descriptor
.enumerable())
1764 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
1767 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1768 if (!descriptor
.isGenericDescriptor()) {
1769 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1770 if (current
.isDataDescriptor() != descriptor
.isDataDescriptor()) {
1771 // 9.a. Reject, if the [[Configurable]] field of current is false.
1772 if (!current
.configurable())
1773 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
1774 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1775 // data property to an accessor property. Preserve the existing values of the converted property's
1776 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1777 // their default values.
1778 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1779 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1780 // attributes and set the rest of the property's attributes to their default values.
1781 } else if (current
.isDataDescriptor() && descriptor
.isDataDescriptor()) {
1782 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1783 // 10.a. If the [[Configurable]] field of current is false, then
1784 if (!current
.configurable() && !current
.writable()) {
1785 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1786 if (descriptor
.writable())
1787 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
1788 // 10.a.ii. If the [[Writable]] field of current is false, then
1789 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1790 if (descriptor
.value() && !sameValue(exec
, descriptor
.value(), current
.value()))
1791 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
1793 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1795 ASSERT(current
.isAccessorDescriptor() && current
.getterPresent() && current
.setterPresent());
1796 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1797 if (!current
.configurable()) {
1798 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1799 if (descriptor
.setterPresent() && descriptor
.setter() != current
.setter())
1800 return reject(exec
, throwException
, "Attempting to change the setter of an unconfigurable property.");
1801 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1802 if (descriptor
.getterPresent() && descriptor
.getter() != current
.getter())
1803 return reject(exec
, throwException
, "Attempting to change the getter of an unconfigurable property.");
1808 // 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.
1809 putIndexedDescriptor(exec
, entryInMap
, descriptor
, current
);
1814 SparseArrayValueMap
* JSObject::allocateSparseIndexMap(VM
& vm
)
1816 SparseArrayValueMap
* result
= SparseArrayValueMap::create(vm
);
1817 arrayStorage()->m_sparseMap
.set(vm
, this, result
);
1821 void JSObject::deallocateSparseIndexMap()
1823 if (ArrayStorage
* arrayStorage
= arrayStorageOrNull())
1824 arrayStorage
->m_sparseMap
.clear();
1827 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState
* exec
, JSValue thisValue
, unsigned i
, JSValue value
, bool shouldThrow
)
1829 for (JSObject
* current
= this; ;) {
1830 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1831 // allows a prototype to intercept a put if (a) the prototype declares the property
1832 // we're after rather than intercepting it via an override of JSObject::put(), and
1833 // (b) that property is declared as ReadOnly or Accessor.
1835 ArrayStorage
* storage
= current
->arrayStorageOrNull();
1836 if (storage
&& storage
->m_sparseMap
) {
1837 SparseArrayValueMap::iterator iter
= storage
->m_sparseMap
->find(i
);
1838 if (iter
!= storage
->m_sparseMap
->notFound() && (iter
->value
.attributes
& (Accessor
| ReadOnly
))) {
1839 iter
->value
.put(exec
, thisValue
, storage
->m_sparseMap
.get(), value
, shouldThrow
);
1844 JSValue prototypeValue
= current
->prototype();
1845 if (prototypeValue
.isNull())
1848 current
= asObject(prototypeValue
);
1852 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
1854 JSValue prototypeValue
= prototype();
1855 if (prototypeValue
.isNull())
1858 return asObject(prototypeValue
)->attemptToInterceptPutByIndexOnHoleForPrototype(exec
, this, i
, value
, shouldThrow
);
1861 template<IndexingType indexingShape
>
1862 void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState
* exec
, unsigned i
, JSValue value
)
1864 ASSERT((structure()->indexingType() & IndexingShapeMask
) == indexingShape
);
1865 ASSERT(!indexingShouldBeSparse());
1867 // For us to get here, the index is either greater than the public length, or greater than
1868 // or equal to the vector length.
1869 ASSERT(i
>= m_butterfly
->vectorLength());
1871 VM
& vm
= exec
->vm();
1873 if (i
>= MAX_ARRAY_INDEX
- 1
1874 || (i
>= MIN_SPARSE_ARRAY_INDEX
1875 && !isDenseEnoughForVector(i
, countElements
<indexingShape
>(m_butterfly
)))) {
1876 ASSERT(i
<= MAX_ARRAY_INDEX
);
1877 ensureArrayStorageSlow(vm
);
1878 SparseArrayValueMap
* map
= allocateSparseIndexMap(vm
);
1879 map
->putEntry(exec
, this, i
, value
, false);
1880 ASSERT(i
>= arrayStorage()->length());
1881 arrayStorage()->setLength(i
+ 1);
1885 ensureLength(vm
, i
+ 1);
1887 RELEASE_ASSERT(i
< m_butterfly
->vectorLength());
1888 switch (indexingShape
) {
1890 ASSERT(value
.isInt32());
1891 m_butterfly
->contiguousInt32()[i
].setWithoutWriteBarrier(value
);
1895 ASSERT(value
.isNumber());
1896 double valueAsDouble
= value
.asNumber();
1897 ASSERT(valueAsDouble
== valueAsDouble
);
1898 m_butterfly
->contiguousDouble()[i
] = valueAsDouble
;
1902 case ContiguousShape
:
1903 m_butterfly
->contiguous()[i
].set(vm
, this, value
);
1911 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
, ArrayStorage
* storage
)
1913 VM
& vm
= exec
->vm();
1915 // i should be a valid array index that is outside of the current vector.
1916 ASSERT(i
<= MAX_ARRAY_INDEX
);
1917 ASSERT(i
>= storage
->vectorLength());
1919 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
1921 // First, handle cases where we don't currently have a sparse map.
1923 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
1924 ASSERT(isExtensible());
1926 // Update m_length if necessary.
1927 if (i
>= storage
->length())
1928 storage
->setLength(i
+ 1);
1930 // Check that it is sensible to still be using a vector, and then try to grow the vector.
1931 if (LIKELY((isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)) && increaseVectorLength(vm
, i
+ 1))) {
1932 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1933 storage
= arrayStorage();
1934 storage
->m_vector
[i
].set(vm
, this, value
);
1935 ++storage
->m_numValuesInVector
;
1938 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1939 map
= allocateSparseIndexMap(exec
->vm());
1940 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
1944 // Update m_length if necessary.
1945 unsigned length
= storage
->length();
1947 // Prohibit growing the array if length is not writable.
1948 if (map
->lengthIsReadOnly() || !isExtensible()) {
1950 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
1954 storage
->setLength(length
);
1957 // We are currently using a map - check whether we still want to be doing so.
1958 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
1959 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
1960 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
)) {
1961 map
->putEntry(exec
, this, i
, value
, shouldThrow
);
1965 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
1966 storage
= arrayStorage();
1967 storage
->m_numValuesInVector
= numValuesInArray
;
1969 // Copy all values from the map into the vector, and delete the map.
1970 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
1971 SparseArrayValueMap::const_iterator end
= map
->end();
1972 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
1973 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
1974 deallocateSparseIndexMap();
1976 // Store the new property into the vector.
1977 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
1979 ++storage
->m_numValuesInVector
;
1980 valueSlot
.set(vm
, this, value
);
1983 void JSObject::putByIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
1985 VM
& vm
= exec
->vm();
1987 // i should be a valid array index that is outside of the current vector.
1988 ASSERT(i
<= MAX_ARRAY_INDEX
);
1990 switch (structure()->indexingType()) {
1991 case ALL_BLANK_INDEXING_TYPES
: {
1992 if (indexingShouldBeSparse()) {
1993 putByIndexBeyondVectorLengthWithArrayStorage(
1994 exec
, i
, value
, shouldThrow
,
1995 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
1998 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
1999 putByIndexBeyondVectorLengthWithArrayStorage(
2000 exec
, i
, value
, shouldThrow
, createArrayStorage(vm
, 0, 0));
2003 if (structure()->needsSlowPutIndexing()) {
2004 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2005 storage
->m_vector
[i
].set(vm
, this, value
);
2006 storage
->m_numValuesInVector
++;
2010 createInitialContiguous(vm
, i
+ 1)[i
].set(vm
, this, value
);
2014 case ALL_UNDECIDED_INDEXING_TYPES
: {
2019 case ALL_INT32_INDEXING_TYPES
: {
2020 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2024 case ALL_DOUBLE_INDEXING_TYPES
: {
2025 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2029 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2030 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2034 case NonArrayWithSlowPutArrayStorage
:
2035 case ArrayWithSlowPutArrayStorage
: {
2036 // No own property present in the vector, but there might be in the sparse map!
2037 SparseArrayValueMap
* map
= arrayStorage()->m_sparseMap
.get();
2038 if (!(map
&& map
->contains(i
)) && attemptToInterceptPutByIndexOnHole(exec
, i
, value
, shouldThrow
))
2040 // Otherwise, fall though.
2043 case NonArrayWithArrayStorage
:
2044 case ArrayWithArrayStorage
:
2045 putByIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, shouldThrow
, arrayStorage());
2049 RELEASE_ASSERT_NOT_REACHED();
2053 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
, ArrayStorage
* storage
)
2055 VM
& vm
= exec
->vm();
2057 // i should be a valid array index that is outside of the current vector.
2058 ASSERT(hasArrayStorage(structure()->indexingType()));
2059 ASSERT(arrayStorage() == storage
);
2060 ASSERT(i
>= storage
->vectorLength() || attributes
);
2061 ASSERT(i
<= MAX_ARRAY_INDEX
);
2063 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
2065 // First, handle cases where we don't currently have a sparse map.
2067 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2068 ASSERT(isExtensible());
2070 // Update m_length if necessary.
2071 if (i
>= storage
->length())
2072 storage
->setLength(i
+ 1);
2074 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2077 && (isDenseEnoughForVector(i
, storage
->m_numValuesInVector
))
2078 && increaseVectorLength(vm
, i
+ 1))) {
2079 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2080 storage
= arrayStorage();
2081 storage
->m_vector
[i
].set(vm
, this, value
);
2082 ++storage
->m_numValuesInVector
;
2085 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2086 map
= allocateSparseIndexMap(exec
->vm());
2087 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2090 // Update m_length if necessary.
2091 unsigned length
= storage
->length();
2093 if (mode
!= PutDirectIndexLikePutDirect
) {
2094 // Prohibit growing the array if length is not writable.
2095 if (map
->lengthIsReadOnly())
2096 return reject(exec
, mode
== PutDirectIndexShouldThrow
, StrictModeReadonlyPropertyWriteError
);
2097 if (!isExtensible())
2098 return reject(exec
, mode
== PutDirectIndexShouldThrow
, "Attempting to define property on object that is not extensible.");
2101 storage
->setLength(length
);
2104 // We are currently using a map - check whether we still want to be doing so.
2105 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2106 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
2107 if (map
->sparseMode() || attributes
|| !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->vm(), length
))
2108 return map
->putDirect(exec
, this, i
, value
, attributes
, mode
);
2110 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2111 storage
= arrayStorage();
2112 storage
->m_numValuesInVector
= numValuesInArray
;
2114 // Copy all values from the map into the vector, and delete the map.
2115 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
2116 SparseArrayValueMap::const_iterator end
= map
->end();
2117 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
2118 vector
[it
->key
].set(vm
, this, it
->value
.getNonSparseMode());
2119 deallocateSparseIndexMap();
2121 // Store the new property into the vector.
2122 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
2124 ++storage
->m_numValuesInVector
;
2125 valueSlot
.set(vm
, this, value
);
2129 bool JSObject::putDirectIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, unsigned attributes
, PutDirectIndexMode mode
)
2131 VM
& vm
= exec
->vm();
2133 // i should be a valid array index that is outside of the current vector.
2134 ASSERT(i
<= MAX_ARRAY_INDEX
);
2136 if (attributes
& (ReadOnly
| Accessor
))
2137 notifyPresenceOfIndexedAccessors(vm
);
2139 switch (structure()->indexingType()) {
2140 case ALL_BLANK_INDEXING_TYPES
: {
2141 if (indexingShouldBeSparse() || attributes
) {
2142 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2143 exec
, i
, value
, attributes
, mode
,
2144 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm
));
2146 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
2147 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2148 exec
, i
, value
, attributes
, mode
, createArrayStorage(vm
, 0, 0));
2150 if (structure()->needsSlowPutIndexing()) {
2151 ArrayStorage
* storage
= createArrayStorage(vm
, i
+ 1, getNewVectorLength(0, 0, i
+ 1));
2152 storage
->m_vector
[i
].set(vm
, this, value
);
2153 storage
->m_numValuesInVector
++;
2157 createInitialContiguous(vm
, i
+ 1)[i
].set(vm
, this, value
);
2161 case ALL_UNDECIDED_INDEXING_TYPES
: {
2162 convertUndecidedForValue(exec
->vm(), value
);
2164 return putDirectIndex(exec
, i
, value
, attributes
, mode
);
2167 case ALL_INT32_INDEXING_TYPES
: {
2168 if (attributes
& (ReadOnly
| Accessor
)) {
2169 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2170 exec
, i
, value
, attributes
, mode
, convertInt32ToArrayStorage(vm
));
2172 if (!value
.isInt32()) {
2173 convertInt32ForValue(vm
, value
);
2174 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2176 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, i
, value
);
2180 case ALL_DOUBLE_INDEXING_TYPES
: {
2181 if (attributes
& (ReadOnly
| Accessor
)) {
2182 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2183 exec
, i
, value
, attributes
, mode
, convertDoubleToArrayStorage(vm
));
2185 if (!value
.isNumber()) {
2186 convertDoubleToContiguous(vm
);
2187 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2189 double valueAsDouble
= value
.asNumber();
2190 if (valueAsDouble
!= valueAsDouble
) {
2191 convertDoubleToContiguous(vm
);
2192 return putDirectIndexBeyondVectorLength(exec
, i
, value
, attributes
, mode
);
2194 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, i
, value
);
2198 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2199 if (attributes
& (ReadOnly
| Accessor
)) {
2200 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2201 exec
, i
, value
, attributes
, mode
, convertContiguousToArrayStorage(vm
));
2203 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, i
, value
);
2207 case ALL_ARRAY_STORAGE_INDEXING_TYPES
:
2208 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec
, i
, value
, attributes
, mode
, arrayStorage());
2211 RELEASE_ASSERT_NOT_REACHED();
2216 void JSObject::putDirectNativeFunction(ExecState
* exec
, JSGlobalObject
* globalObject
, const PropertyName
& propertyName
, unsigned functionLength
, NativeFunction nativeFunction
, Intrinsic intrinsic
, unsigned attributes
)
2218 StringImpl
* name
= propertyName
.publicName();
2221 JSFunction
* function
=
2222 JSFunction::create(exec
, globalObject
, functionLength
, name
, nativeFunction
, intrinsic
);
2223 putDirect(exec
->vm(), propertyName
, function
, attributes
);
2226 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned currentVectorLength
, unsigned currentLength
, unsigned desiredLength
)
2228 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
2230 unsigned increasedLength
;
2231 unsigned maxInitLength
= std::min(currentLength
, 100000U);
2233 if (desiredLength
< maxInitLength
)
2234 increasedLength
= maxInitLength
;
2235 else if (!currentVectorLength
)
2236 increasedLength
= std::max(desiredLength
, lastArraySize
);
2238 increasedLength
= timesThreePlusOneDividedByTwo(desiredLength
);
2241 ASSERT(increasedLength
>= desiredLength
);
2243 lastArraySize
= std::min(increasedLength
, FIRST_VECTOR_GROW
);
2245 return std::min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
2248 ALWAYS_INLINE
unsigned JSObject::getNewVectorLength(unsigned desiredLength
)
2250 unsigned vectorLength
;
2253 if (hasIndexedProperties(structure()->indexingType())) {
2254 vectorLength
= m_butterfly
->vectorLength();
2255 length
= m_butterfly
->publicLength();
2261 return getNewVectorLength(vectorLength
, length
, desiredLength
);
2264 template<IndexingType indexingShape
>
2265 unsigned JSObject::countElements(Butterfly
* butterfly
)
2267 unsigned numValues
= 0;
2268 for (unsigned i
= butterfly
->publicLength(); i
--;) {
2269 switch (indexingShape
) {
2271 case ContiguousShape
:
2272 if (butterfly
->contiguous()[i
])
2277 double value
= butterfly
->contiguousDouble()[i
];
2290 unsigned JSObject::countElements()
2292 switch (structure()->indexingType()) {
2293 case ALL_BLANK_INDEXING_TYPES
:
2294 case ALL_UNDECIDED_INDEXING_TYPES
:
2297 case ALL_INT32_INDEXING_TYPES
:
2298 return countElements
<Int32Shape
>(m_butterfly
);
2300 case ALL_DOUBLE_INDEXING_TYPES
:
2301 return countElements
<DoubleShape
>(m_butterfly
);
2303 case ALL_CONTIGUOUS_INDEXING_TYPES
:
2304 return countElements
<ContiguousShape
>(m_butterfly
);
2312 bool JSObject::increaseVectorLength(VM
& vm
, unsigned newLength
)
2314 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2315 // to the vector. Callers have to account for that, because they can do it more efficiently.
2316 if (newLength
> MAX_STORAGE_VECTOR_LENGTH
)
2319 ArrayStorage
* storage
= arrayStorage();
2321 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
2322 && !isDenseEnoughForVector(newLength
, storage
->m_numValuesInVector
))
2325 unsigned indexBias
= storage
->m_indexBias
;
2326 unsigned vectorLength
= storage
->vectorLength();
2327 ASSERT(newLength
> vectorLength
);
2328 unsigned newVectorLength
= getNewVectorLength(newLength
);
2330 // Fast case - there is no precapacity. In these cases a realloc makes sense.
2331 if (LIKELY(!indexBias
)) {
2332 Butterfly
* newButterfly
= storage
->butterfly()->growArrayRight(vm
, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength
), ArrayStorage::sizeFor(newVectorLength
));
2335 m_butterfly
= newButterfly
;
2336 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2340 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2341 unsigned newIndexBias
= std::min(indexBias
>> 1, MAX_STORAGE_VECTOR_LENGTH
- newVectorLength
);
2342 Butterfly
* newButterfly
= storage
->butterfly()->resizeArray(
2344 structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength
),
2345 newIndexBias
, true, ArrayStorage::sizeFor(newVectorLength
));
2349 m_butterfly
= newButterfly
;
2350 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
2351 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
2355 void JSObject::ensureLengthSlow(VM
& vm
, unsigned length
)
2357 ASSERT(length
< MAX_ARRAY_INDEX
);
2358 ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType()));
2359 ASSERT(length
> m_butterfly
->vectorLength());
2361 unsigned newVectorLength
= std::min(
2363 MAX_STORAGE_VECTOR_LENGTH
);
2364 unsigned oldVectorLength
= m_butterfly
->vectorLength();
2365 m_butterfly
= m_butterfly
->growArrayRight(
2366 vm
, structure(), structure()->outOfLineCapacity(), true,
2367 oldVectorLength
* sizeof(EncodedJSValue
),
2368 newVectorLength
* sizeof(EncodedJSValue
));
2369 if (hasDouble(structure()->indexingType())) {
2370 for (unsigned i
= oldVectorLength
; i
< newVectorLength
; ++i
)
2371 m_butterfly
->contiguousDouble().data()[i
] = QNaN
;
2373 m_butterfly
->setVectorLength(newVectorLength
);
2376 Butterfly
* JSObject::growOutOfLineStorage(VM
& vm
, size_t oldSize
, size_t newSize
)
2378 ASSERT(newSize
> oldSize
);
2380 // It's important that this function not rely on structure(), for the property
2381 // capacity, since we might have already mutated the structure in-place.
2383 return m_butterfly
->growPropertyStorage(vm
, structure(), oldSize
, newSize
);
2386 bool JSObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
2388 unsigned attributes
= 0;
2390 PropertyOffset offset
= object
->structure()->get(exec
->vm(), propertyName
, attributes
, cell
);
2391 if (isValidOffset(offset
)) {
2392 descriptor
.setDescriptor(object
->getDirect(offset
), attributes
);
2396 unsigned i
= propertyName
.asIndex();
2397 if (i
== PropertyName::NotAnIndex
)
2400 switch (object
->structure()->indexingType()) {
2401 case ALL_BLANK_INDEXING_TYPES
:
2402 case ALL_UNDECIDED_INDEXING_TYPES
:
2405 case ALL_INT32_INDEXING_TYPES
:
2406 case ALL_CONTIGUOUS_INDEXING_TYPES
: {
2407 Butterfly
* butterfly
= object
->m_butterfly
;
2408 if (i
>= butterfly
->vectorLength())
2410 JSValue value
= butterfly
->contiguous()[i
].get();
2413 descriptor
.setDescriptor(value
, 0);
2417 case ALL_DOUBLE_INDEXING_TYPES
: {
2418 Butterfly
* butterfly
= object
->m_butterfly
;
2419 if (i
>= butterfly
->vectorLength())
2421 double value
= butterfly
->contiguousDouble()[i
];
2424 descriptor
.setDescriptor(JSValue(JSValue::EncodeAsDouble
, value
), 0);
2428 case ALL_ARRAY_STORAGE_INDEXING_TYPES
: {
2429 ArrayStorage
* storage
= object
->m_butterfly
->arrayStorage();
2430 if (i
>= storage
->length())
2432 if (i
< storage
->vectorLength()) {
2433 WriteBarrier
<Unknown
>& value
= storage
->m_vector
[i
];
2436 descriptor
.setDescriptor(value
.get(), 0);
2439 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
2440 SparseArrayValueMap::iterator it
= map
->find(i
);
2441 if (it
== map
->notFound())
2443 it
->value
.get(descriptor
);
2450 RELEASE_ASSERT_NOT_REACHED();
2455 bool JSObject::getPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
2457 JSObject
* object
= this;
2459 if (object
->methodTable()->getOwnPropertyDescriptor(object
, exec
, propertyName
, descriptor
))
2461 JSValue prototype
= object
->prototype();
2462 if (!prototype
.isObject())
2464 object
= asObject(prototype
);
2468 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
2470 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
2471 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
2472 GetterSetter
* accessor
= GetterSetter::create(exec
);
2473 if (oldDescriptor
.getterPresent())
2474 accessor
->setGetter(exec
->vm(), oldDescriptor
.getterObject());
2475 if (oldDescriptor
.setterPresent())
2476 accessor
->setSetter(exec
->vm(), oldDescriptor
.setterObject());
2477 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2480 JSValue newValue
= jsUndefined();
2481 if (descriptor
.value())
2482 newValue
= descriptor
.value();
2483 else if (oldDescriptor
.value())
2484 newValue
= oldDescriptor
.value();
2485 target
->putDirect(exec
->vm(), propertyName
, newValue
, attributes
& ~Accessor
);
2486 if (attributes
& ReadOnly
)
2487 target
->structure()->setContainsReadOnlyProperties();
2490 attributes
&= ~ReadOnly
;
2491 GetterSetter
* accessor
= GetterSetter::create(exec
);
2493 if (descriptor
.getterPresent())
2494 accessor
->setGetter(exec
->vm(), descriptor
.getterObject());
2495 else if (oldDescriptor
.getterPresent())
2496 accessor
->setGetter(exec
->vm(), oldDescriptor
.getterObject());
2497 if (descriptor
.setterPresent())
2498 accessor
->setSetter(exec
->vm(), descriptor
.setterObject());
2499 else if (oldDescriptor
.setterPresent())
2500 accessor
->setSetter(exec
->vm(), oldDescriptor
.setterObject());
2502 target
->putDirectAccessor(exec
, propertyName
, accessor
, attributes
| Accessor
);
2506 void JSObject::putDirectMayBeIndex(ExecState
* exec
, PropertyName propertyName
, JSValue value
)
2508 unsigned asIndex
= propertyName
.asIndex();
2509 if (asIndex
== PropertyName::NotAnIndex
)
2510 putDirect(exec
->vm(), propertyName
, value
);
2512 putDirectIndex(exec
, asIndex
, value
);
2515 class DefineOwnPropertyScope
{
2517 DefineOwnPropertyScope(ExecState
* exec
)
2520 m_vm
.setInDefineOwnProperty(true);
2523 ~DefineOwnPropertyScope()
2525 m_vm
.setInDefineOwnProperty(false);
2532 bool JSObject::defineOwnNonIndexProperty(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
2534 // Track on the globaldata that we're in define property.
2535 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2536 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2537 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2538 DefineOwnPropertyScope
scope(exec
);
2540 // If we have a new property we can just put it on normally
2541 PropertyDescriptor current
;
2542 if (!methodTable()->getOwnPropertyDescriptor(this, exec
, propertyName
, current
)) {
2543 // unless extensions are prevented!
2544 if (!isExtensible()) {
2546 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to define property on object that is not extensible.")));
2549 PropertyDescriptor oldDescriptor
;
2550 oldDescriptor
.setValue(jsUndefined());
2551 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
2554 if (descriptor
.isEmpty())
2557 if (current
.equalTo(exec
, descriptor
))
2560 // Filter out invalid changes
2561 if (!current
.configurable()) {
2562 if (descriptor
.configurable()) {
2564 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
2567 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
2569 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
2574 // A generic descriptor is simply changing the attributes of an existing property
2575 if (descriptor
.isGenericDescriptor()) {
2576 if (!current
.attributesEqual(descriptor
)) {
2577 methodTable()->deleteProperty(this, exec
, propertyName
);
2578 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2583 // Changing between a normal property or an accessor property
2584 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
2585 if (!current
.configurable()) {
2587 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2590 methodTable()->deleteProperty(this, exec
, propertyName
);
2591 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2594 // Changing the value and attributes of an existing property
2595 if (descriptor
.isDataDescriptor()) {
2596 if (!current
.configurable()) {
2597 if (!current
.writable() && descriptor
.writable()) {
2599 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
2602 if (!current
.writable()) {
2603 if (descriptor
.value() && !sameValue(exec
, current
.value(), descriptor
.value())) {
2605 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change value of a readonly property.")));
2610 if (current
.attributesEqual(descriptor
) && !descriptor
.value())
2612 methodTable()->deleteProperty(this, exec
, propertyName
);
2613 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
2616 // Changing the accessor functions of an existing accessor property
2617 ASSERT(descriptor
.isAccessorDescriptor());
2618 if (!current
.configurable()) {
2619 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
2621 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
2624 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
2626 throwError(exec
, createTypeError(exec
, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2630 JSValue accessor
= getDirect(exec
->vm(), propertyName
);
2633 GetterSetter
* getterSetter
= asGetterSetter(accessor
);
2634 if (descriptor
.setterPresent())
2635 getterSetter
->setSetter(exec
->vm(), descriptor
.setterObject());
2636 if (descriptor
.getterPresent())
2637 getterSetter
->setGetter(exec
->vm(), descriptor
.getterObject());
2638 if (current
.attributesEqual(descriptor
))
2640 methodTable()->deleteProperty(this, exec
, propertyName
);
2641 unsigned attrs
= descriptor
.attributesOverridingCurrent(current
);
2642 putDirectAccessor(exec
, propertyName
, getterSetter
, attrs
| Accessor
);
2646 bool JSObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
2648 // If it's an array index, then use the indexed property storage.
2649 unsigned index
= propertyName
.asIndex();
2650 if (index
!= PropertyName::NotAnIndex
) {
2651 // 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.
2652 // d. Reject if succeeded is false.
2653 // e. If index >= oldLen
2654 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2655 // 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.
2657 return object
->defineOwnIndexedProperty(exec
, index
, descriptor
, throwException
);
2660 return object
->defineOwnNonIndexProperty(exec
, propertyName
, descriptor
, throwException
);
2663 bool JSObject::getOwnPropertySlotSlow(ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
2665 unsigned i
= propertyName
.asIndex();
2666 if (i
!= PropertyName::NotAnIndex
)
2667 return getOwnPropertySlotByIndex(this, exec
, i
, slot
);
2671 JSObject
* throwTypeError(ExecState
* exec
, const String
& message
)
2673 return throwError(exec
, createTypeError(exec
, message
));