]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - runtime/JSObject.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.cpp
... / ...
CommitLineData
1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2006, 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6 *
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.
11 *
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.
16 *
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.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
27#include "ButterflyInlines.h"
28#include "CopiedSpaceInlines.h"
29#include "CopyVisitor.h"
30#include "CopyVisitorInlines.h"
31#include "CustomGetterSetter.h"
32#include "DatePrototype.h"
33#include "ErrorConstructor.h"
34#include "Exception.h"
35#include "Executable.h"
36#include "GetterSetter.h"
37#include "IndexingHeaderInlines.h"
38#include "JSCatchScope.h"
39#include "JSFunction.h"
40#include "JSFunctionNameScope.h"
41#include "JSGlobalObject.h"
42#include "Lookup.h"
43#include "NativeErrorConstructor.h"
44#include "Nodes.h"
45#include "ObjectPrototype.h"
46#include "JSCInlines.h"
47#include "PropertyDescriptor.h"
48#include "PropertyNameArray.h"
49#include "Reject.h"
50#include "SlotVisitorInlines.h"
51#include <math.h>
52#include <wtf/Assertions.h>
53
54namespace JSC {
55
56// We keep track of the size of the last array after it was grown. We use this
57// as a simple heuristic for as the value to grow the next array from size 0.
58// This value is capped by the constant FIRST_VECTOR_GROW defined in
59// ArrayConventions.h.
60static unsigned lastArraySize = 0;
61
62STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
63STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
64
65const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
66
67const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) };
68
69const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) };
70
71static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify)
72{
73 VM& vm = exec->vm();
74
75 // Add properties from the static hashtables of properties
76 for (; classInfo; classInfo = classInfo->parentClass) {
77 const HashTable* table = classInfo->staticPropHashTable;
78 if (!table)
79 continue;
80
81 for (auto iter = table->begin(); iter != table->end(); ++iter) {
82 if ((!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties()) && !((iter->attributes() & BuiltinOrFunctionOrAccessor) && didReify))
83 propertyNames.add(Identifier::fromString(&vm, iter.key()));
84 }
85 }
86}
87
88ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butterfly, size_t storageSize)
89{
90 ASSERT(butterfly);
91
92 Structure* structure = this->structure();
93
94 size_t propertyCapacity = structure->outOfLineCapacity();
95 size_t preCapacity;
96 size_t indexingPayloadSizeInBytes;
97 bool hasIndexingHeader = this->hasIndexingHeader();
98 if (UNLIKELY(hasIndexingHeader)) {
99 preCapacity = butterfly->indexingHeader()->preCapacity(structure);
100 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
101 } else {
102 preCapacity = 0;
103 indexingPayloadSizeInBytes = 0;
104 }
105 size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
106 if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity))) {
107 Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
108
109 // Copy the properties.
110 PropertyStorage currentTarget = newButterfly->propertyStorage();
111 PropertyStorage currentSource = butterfly->propertyStorage();
112 for (size_t count = storageSize; count--;)
113 (--currentTarget)->setWithoutWriteBarrier((--currentSource)->get());
114
115 if (UNLIKELY(hasIndexingHeader)) {
116 *newButterfly->indexingHeader() = *butterfly->indexingHeader();
117
118 // Copy the array if appropriate.
119
120 WriteBarrier<Unknown>* currentTarget;
121 WriteBarrier<Unknown>* currentSource;
122 size_t count;
123
124 switch (this->indexingType()) {
125 case ALL_UNDECIDED_INDEXING_TYPES:
126 case ALL_CONTIGUOUS_INDEXING_TYPES:
127 case ALL_INT32_INDEXING_TYPES:
128 case ALL_DOUBLE_INDEXING_TYPES: {
129 currentTarget = newButterfly->contiguous().data();
130 currentSource = butterfly->contiguous().data();
131 RELEASE_ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength());
132 count = newButterfly->vectorLength();
133 break;
134 }
135
136 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
137 newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage());
138 currentTarget = newButterfly->arrayStorage()->m_vector;
139 currentSource = butterfly->arrayStorage()->m_vector;
140 count = newButterfly->arrayStorage()->vectorLength();
141 break;
142 }
143
144 default:
145 currentTarget = 0;
146 currentSource = 0;
147 count = 0;
148 break;
149 }
150
151 memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue));
152 }
153
154 m_butterfly.setWithoutWriteBarrier(newButterfly);
155 visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
156 }
157}
158
159ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize)
160{
161 ASSERT(butterfly);
162
163 Structure* structure = this->structure(visitor.vm());
164
165 size_t propertyCapacity = structure->outOfLineCapacity();
166 size_t preCapacity;
167 size_t indexingPayloadSizeInBytes;
168 bool hasIndexingHeader = this->hasIndexingHeader();
169 if (UNLIKELY(hasIndexingHeader)) {
170 preCapacity = butterfly->indexingHeader()->preCapacity(structure);
171 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
172 } else {
173 preCapacity = 0;
174 indexingPayloadSizeInBytes = 0;
175 }
176 size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
177
178 // Mark the properties.
179 visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize);
180 visitor.copyLater(
181 this, ButterflyCopyToken,
182 butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
183
184 // Mark the array if appropriate.
185 switch (this->indexingType()) {
186 case ALL_CONTIGUOUS_INDEXING_TYPES:
187 visitor.appendValues(butterfly->contiguous().data(), butterfly->publicLength());
188 break;
189 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
190 visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
191 if (butterfly->arrayStorage()->m_sparseMap)
192 visitor.append(&butterfly->arrayStorage()->m_sparseMap);
193 break;
194 default:
195 break;
196 }
197}
198
199void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
200{
201 JSObject* thisObject = jsCast<JSObject*>(cell);
202 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
203#if !ASSERT_DISABLED
204 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
205 visitor.m_isCheckingForDefaultMarkViolation = false;
206#endif
207
208 JSCell::visitChildren(thisObject, visitor);
209
210 Butterfly* butterfly = thisObject->butterfly();
211 if (butterfly)
212 thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm())->outOfLineSize());
213
214#if !ASSERT_DISABLED
215 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
216#endif
217}
218
219void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
220{
221 JSObject* thisObject = jsCast<JSObject*>(cell);
222 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
223
224 if (token != ButterflyCopyToken)
225 return;
226
227 Butterfly* butterfly = thisObject->butterfly();
228 if (butterfly)
229 thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize());
230}
231
232void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
233{
234 JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
235 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
236#if !ASSERT_DISABLED
237 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
238 visitor.m_isCheckingForDefaultMarkViolation = false;
239#endif
240
241 JSCell::visitChildren(thisObject, visitor);
242
243 Structure* structure = thisObject->structure();
244 Butterfly* butterfly = thisObject->butterfly();
245 if (butterfly)
246 thisObject->visitButterfly(visitor, butterfly, structure->outOfLineSize());
247
248 size_t storageSize = structure->inlineSize();
249 visitor.appendValues(thisObject->inlineStorage(), storageSize);
250
251#if !ASSERT_DISABLED
252 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
253#endif
254}
255
256String JSObject::className(const JSObject* object)
257{
258 const ClassInfo* info = object->classInfo();
259 ASSERT(info);
260 return info->className;
261}
262
263String JSObject::calculatedClassName(JSObject* object)
264{
265 String prototypeFunctionName;
266 ExecState* exec = object->globalObject()->globalExec();
267 PropertySlot slot(object->structure()->storedPrototype());
268 PropertyName constructor(exec->propertyNames().constructor);
269 if (object->getPropertySlot(exec, constructor, slot)) {
270 if (slot.isValue()) {
271 JSValue constructorValue = slot.getValue(exec, constructor);
272 if (constructorValue.isCell()) {
273 if (JSCell* constructorCell = constructorValue.asCell()) {
274 if (JSObject* ctorObject = constructorCell->getObject()) {
275 if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(ctorObject))
276 prototypeFunctionName = constructorFunction->calculatedDisplayName(exec);
277 else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(ctorObject))
278 prototypeFunctionName = constructorFunction->calculatedDisplayName(exec);
279 }
280 }
281 }
282 }
283 }
284
285 if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
286 String tableClassName = object->methodTable()->className(object);
287 if (!tableClassName.isNull() && tableClassName != "Object")
288 return tableClassName;
289
290 String classInfoName = object->classInfo()->className;
291 if (!classInfoName.isNull())
292 return classInfoName;
293
294 if (prototypeFunctionName.isNull())
295 return ASCIILiteral("Object");
296 }
297
298 return prototypeFunctionName;
299}
300
301bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
302{
303 // NB. The fact that we're directly consulting our indexed storage implies that it is not
304 // legal for anyone to override getOwnPropertySlot() without also overriding
305 // getOwnPropertySlotByIndex().
306
307 if (i > MAX_ARRAY_INDEX)
308 return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
309
310 switch (thisObject->indexingType()) {
311 case ALL_BLANK_INDEXING_TYPES:
312 case ALL_UNDECIDED_INDEXING_TYPES:
313 break;
314
315 case ALL_INT32_INDEXING_TYPES:
316 case ALL_CONTIGUOUS_INDEXING_TYPES: {
317 Butterfly* butterfly = thisObject->butterfly();
318 if (i >= butterfly->vectorLength())
319 return false;
320
321 JSValue value = butterfly->contiguous()[i].get();
322 if (value) {
323 slot.setValue(thisObject, None, value);
324 return true;
325 }
326
327 return false;
328 }
329
330 case ALL_DOUBLE_INDEXING_TYPES: {
331 Butterfly* butterfly = thisObject->butterfly();
332 if (i >= butterfly->vectorLength())
333 return false;
334
335 double value = butterfly->contiguousDouble()[i];
336 if (value == value) {
337 slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value));
338 return true;
339 }
340
341 return false;
342 }
343
344 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
345 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
346 if (i >= storage->length())
347 return false;
348
349 if (i < storage->vectorLength()) {
350 JSValue value = storage->m_vector[i].get();
351 if (value) {
352 slot.setValue(thisObject, None, value);
353 return true;
354 }
355 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
356 SparseArrayValueMap::iterator it = map->find(i);
357 if (it != map->notFound()) {
358 it->value.get(thisObject, slot);
359 return true;
360 }
361 }
362 break;
363 }
364
365 default:
366 RELEASE_ASSERT_NOT_REACHED();
367 break;
368 }
369
370 return false;
371}
372
373// ECMA 8.6.2.2
374void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
375{
376 JSObject* thisObject = jsCast<JSObject*>(cell);
377 ASSERT(value);
378 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
379 VM& vm = exec->vm();
380
381 // Try indexed put first. This is required for correctness, since loads on property names that appear like
382 // valid indices will never look in the named property storage.
383 if (Optional<uint32_t> index = parseIndex(propertyName)) {
384 putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
385 return;
386 }
387
388 // Check if there are any setters or getters in the prototype chain
389 JSValue prototype;
390 if (propertyName != exec->propertyNames().underscoreProto) {
391 for (JSObject* obj = thisObject; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
392 prototype = obj->prototype();
393 if (prototype.isNull()) {
394 ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
395 if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot)
396 && slot.isStrictMode())
397 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
398 return;
399 }
400 }
401 }
402
403 JSObject* obj;
404 for (obj = thisObject; ; obj = asObject(prototype)) {
405 unsigned attributes;
406 PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
407 if (isValidOffset(offset)) {
408 if (attributes & ReadOnly) {
409 ASSERT(thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
410 if (slot.isStrictMode())
411 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)));
412 return;
413 }
414
415 JSValue gs = obj->getDirect(offset);
416 if (gs.isGetterSetter()) {
417 callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
418 if (!thisObject->structure()->isDictionary())
419 slot.setCacheableSetter(obj, offset);
420 return;
421 }
422 if (gs.isCustomGetterSetter()) {
423 callCustomSetter(exec, gs, obj, slot.thisValue(), value);
424 slot.setCustomProperty(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
425 return;
426 }
427 ASSERT(!(attributes & Accessor));
428
429 // If there's an existing property on the object or one of its
430 // prototypes it should be replaced, so break here.
431 break;
432 }
433 const ClassInfo* info = obj->classInfo();
434 if (info->hasStaticSetterOrReadonlyProperties()) {
435 if (const HashTableValue* entry = obj->findPropertyHashEntry(propertyName)) {
436 if (!obj->staticFunctionsReified() || !(entry->attributes() & BuiltinOrFunctionOrAccessor)) {
437 putEntry(exec, entry, obj, propertyName, value, slot);
438 return;
439 }
440 }
441 }
442 prototype = obj->prototype();
443 if (prototype.isNull())
444 break;
445 }
446
447 ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
448 if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot) && slot.isStrictMode())
449 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
450 return;
451}
452
453void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
454{
455 JSObject* thisObject = jsCast<JSObject*>(cell);
456
457 if (propertyName > MAX_ARRAY_INDEX) {
458 PutPropertySlot slot(cell, shouldThrow);
459 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
460 return;
461 }
462
463 switch (thisObject->indexingType()) {
464 case ALL_BLANK_INDEXING_TYPES:
465 break;
466
467 case ALL_UNDECIDED_INDEXING_TYPES: {
468 thisObject->convertUndecidedForValue(exec->vm(), value);
469 // Reloop.
470 putByIndex(cell, exec, propertyName, value, shouldThrow);
471 return;
472 }
473
474 case ALL_INT32_INDEXING_TYPES: {
475 if (!value.isInt32()) {
476 thisObject->convertInt32ForValue(exec->vm(), value);
477 putByIndex(cell, exec, propertyName, value, shouldThrow);
478 return;
479 }
480 FALLTHROUGH;
481 }
482
483 case ALL_CONTIGUOUS_INDEXING_TYPES: {
484 Butterfly* butterfly = thisObject->butterfly();
485 if (propertyName >= butterfly->vectorLength())
486 break;
487 butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value);
488 if (propertyName >= butterfly->publicLength())
489 butterfly->setPublicLength(propertyName + 1);
490 return;
491 }
492
493 case ALL_DOUBLE_INDEXING_TYPES: {
494 if (!value.isNumber()) {
495 thisObject->convertDoubleToContiguous(exec->vm());
496 // Reloop.
497 putByIndex(cell, exec, propertyName, value, shouldThrow);
498 return;
499 }
500 double valueAsDouble = value.asNumber();
501 if (valueAsDouble != valueAsDouble) {
502 thisObject->convertDoubleToContiguous(exec->vm());
503 // Reloop.
504 putByIndex(cell, exec, propertyName, value, shouldThrow);
505 return;
506 }
507 Butterfly* butterfly = thisObject->butterfly();
508 if (propertyName >= butterfly->vectorLength())
509 break;
510 butterfly->contiguousDouble()[propertyName] = valueAsDouble;
511 if (propertyName >= butterfly->publicLength())
512 butterfly->setPublicLength(propertyName + 1);
513 return;
514 }
515
516 case NonArrayWithArrayStorage:
517 case ArrayWithArrayStorage: {
518 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
519
520 if (propertyName >= storage->vectorLength())
521 break;
522
523 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
524 unsigned length = storage->length();
525
526 // Update length & m_numValuesInVector as necessary.
527 if (propertyName >= length) {
528 length = propertyName + 1;
529 storage->setLength(length);
530 ++storage->m_numValuesInVector;
531 } else if (!valueSlot)
532 ++storage->m_numValuesInVector;
533
534 valueSlot.set(exec->vm(), thisObject, value);
535 return;
536 }
537
538 case NonArrayWithSlowPutArrayStorage:
539 case ArrayWithSlowPutArrayStorage: {
540 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
541
542 if (propertyName >= storage->vectorLength())
543 break;
544
545 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
546 unsigned length = storage->length();
547
548 // Update length & m_numValuesInVector as necessary.
549 if (propertyName >= length) {
550 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
551 return;
552 length = propertyName + 1;
553 storage->setLength(length);
554 ++storage->m_numValuesInVector;
555 } else if (!valueSlot) {
556 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
557 return;
558 ++storage->m_numValuesInVector;
559 }
560
561 valueSlot.set(exec->vm(), thisObject, value);
562 return;
563 }
564
565 default:
566 RELEASE_ASSERT_NOT_REACHED();
567 }
568
569 thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
570}
571
572ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
573{
574 SparseArrayValueMap* map = storage->m_sparseMap.get();
575
576 if (!map)
577 map = allocateSparseIndexMap(vm);
578
579 if (map->sparseMode())
580 return storage;
581
582 map->setSparseMode();
583
584 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
585 for (unsigned i = 0; i < usedVectorLength; ++i) {
586 JSValue value = storage->m_vector[i].get();
587 // This will always be a new entry in the map, so no need to check we can write,
588 // and attributes are default so no need to set them.
589 if (value)
590 map->add(this, i).iterator->value.set(vm, map, value);
591 }
592
593 DeferGC deferGC(vm.heap);
594 Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
595 RELEASE_ASSERT(newButterfly);
596 newButterfly->arrayStorage()->m_indexBias = 0;
597 newButterfly->arrayStorage()->setVectorLength(0);
598 newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
599 setButterflyWithoutChangingStructure(vm, newButterfly);
600
601 return newButterfly->arrayStorage();
602}
603
604void JSObject::enterDictionaryIndexingMode(VM& vm)
605{
606 switch (indexingType()) {
607 case ALL_BLANK_INDEXING_TYPES:
608 case ALL_UNDECIDED_INDEXING_TYPES:
609 case ALL_INT32_INDEXING_TYPES:
610 case ALL_DOUBLE_INDEXING_TYPES:
611 case ALL_CONTIGUOUS_INDEXING_TYPES:
612 // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
613 // this case if we ever cared.
614 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, ensureArrayStorageSlow(vm));
615 break;
616 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
617 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
618 break;
619
620 default:
621 break;
622 }
623}
624
625void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
626{
627 if (mayInterceptIndexedAccesses())
628 return;
629
630 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors));
631
632 if (!vm.prototypeMap.isPrototype(this))
633 return;
634
635 globalObject()->haveABadTime(vm);
636}
637
638Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize)
639{
640 ASSERT(length < MAX_ARRAY_INDEX);
641 IndexingType oldType = indexingType();
642 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
643 ASSERT(!structure()->needsSlowPutIndexing());
644 ASSERT(!indexingShouldBeSparse());
645 unsigned vectorLength = std::max(length, BASE_VECTOR_LEN);
646 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
647 m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0,
648 elementSize * vectorLength);
649 newButterfly->setPublicLength(length);
650 newButterfly->setVectorLength(vectorLength);
651 return newButterfly;
652}
653
654Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
655{
656 DeferGC deferGC(vm.heap);
657 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
658 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided);
659 setStructureAndButterfly(vm, newStructure, newButterfly);
660 return newButterfly;
661}
662
663ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
664{
665 DeferGC deferGC(vm.heap);
666 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
667 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32);
668 setStructureAndButterfly(vm, newStructure, newButterfly);
669 return newButterfly->contiguousInt32();
670}
671
672ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
673{
674 DeferGC deferGC(vm.heap);
675 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
676 for (unsigned i = newButterfly->vectorLength(); i--;)
677 newButterfly->contiguousDouble()[i] = PNaN;
678 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);
679 setStructureAndButterfly(vm, newStructure, newButterfly);
680 return newButterfly->contiguousDouble();
681}
682
683ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
684{
685 DeferGC deferGC(vm.heap);
686 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
687 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous);
688 setStructureAndButterfly(vm, newStructure, newButterfly);
689 return newButterfly->contiguous();
690}
691
692ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
693{
694 DeferGC deferGC(vm.heap);
695 Structure* structure = this->structure(vm);
696 IndexingType oldType = indexingType();
697 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
698 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
699 m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0,
700 ArrayStorage::sizeFor(vectorLength));
701 RELEASE_ASSERT(newButterfly);
702
703 ArrayStorage* result = newButterfly->arrayStorage();
704 result->setLength(length);
705 result->setVectorLength(vectorLength);
706 result->m_sparseMap.clear();
707 result->m_numValuesInVector = 0;
708 result->m_indexBias = 0;
709 Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition());
710 setStructureAndButterfly(vm, newStructure, newButterfly);
711 return result;
712}
713
714ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
715{
716 return createArrayStorage(vm, 0, BASE_VECTOR_LEN);
717}
718
719ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
720{
721 ASSERT(hasUndecided(indexingType()));
722 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32));
723 return m_butterfly->contiguousInt32();
724}
725
726ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
727{
728 ASSERT(hasUndecided(indexingType()));
729
730 for (unsigned i = m_butterfly->vectorLength(); i--;)
731 m_butterfly->contiguousDouble()[i] = PNaN;
732
733 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
734 return m_butterfly->contiguousDouble();
735}
736
737ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
738{
739 ASSERT(hasUndecided(indexingType()));
740 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
741 return m_butterfly->contiguous();
742}
743
744ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
745{
746 Structure* structure = this->structure(vm);
747 unsigned publicLength = m_butterfly->publicLength();
748 unsigned propertyCapacity = structure->outOfLineCapacity();
749 unsigned propertySize = structure->outOfLineSize();
750
751 Butterfly* newButterfly = Butterfly::createUninitialized(
752 vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
753
754 memcpy(
755 newButterfly->propertyStorage() - propertySize,
756 m_butterfly->propertyStorage() - propertySize,
757 propertySize * sizeof(EncodedJSValue));
758
759 ArrayStorage* newStorage = newButterfly->arrayStorage();
760 newStorage->setVectorLength(neededLength);
761 newStorage->setLength(publicLength);
762 newStorage->m_sparseMap.clear();
763 newStorage->m_indexBias = 0;
764 newStorage->m_numValuesInVector = 0;
765
766 return newStorage;
767}
768
769ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
770{
771 DeferGC deferGC(vm.heap);
772 ASSERT(hasUndecided(indexingType()));
773
774 unsigned vectorLength = m_butterfly->vectorLength();
775 ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
776 // No need to copy elements.
777
778 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
779 setStructureAndButterfly(vm, newStructure, storage->butterfly());
780 return storage;
781}
782
783ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
784{
785 return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
786}
787
788ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
789{
790 ASSERT(hasInt32(indexingType()));
791
792 for (unsigned i = m_butterfly->vectorLength(); i--;) {
793 WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i];
794 double* currentAsDouble = bitwise_cast<double*>(current);
795 JSValue v = current->get();
796 if (!v) {
797 *currentAsDouble = PNaN;
798 continue;
799 }
800 ASSERT(v.isInt32());
801 *currentAsDouble = v.asInt32();
802 }
803
804 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
805 return m_butterfly->contiguousDouble();
806}
807
808ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
809{
810 ASSERT(hasInt32(indexingType()));
811
812 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
813 return m_butterfly->contiguous();
814}
815
816ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
817{
818 DeferGC deferGC(vm.heap);
819 ASSERT(hasInt32(indexingType()));
820
821 unsigned vectorLength = m_butterfly->vectorLength();
822 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
823 for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
824 JSValue v = m_butterfly->contiguous()[i].get();
825 if (v) {
826 newStorage->m_vector[i].setWithoutWriteBarrier(v);
827 newStorage->m_numValuesInVector++;
828 } else
829 ASSERT(newStorage->m_vector[i].get().isEmpty());
830 }
831
832 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
833 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
834 return newStorage;
835}
836
837ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
838{
839 return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
840}
841
842ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
843{
844 ASSERT(hasDouble(indexingType()));
845
846 for (unsigned i = m_butterfly->vectorLength(); i--;) {
847 double* current = &m_butterfly->contiguousDouble()[i];
848 WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
849 double value = *current;
850 if (value != value) {
851 currentAsValue->clear();
852 continue;
853 }
854 JSValue v = JSValue(JSValue::EncodeAsDouble, value);
855 currentAsValue->setWithoutWriteBarrier(v);
856 }
857
858 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
859 return m_butterfly->contiguous();
860}
861
862ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
863{
864 DeferGC deferGC(vm.heap);
865 ASSERT(hasDouble(indexingType()));
866
867 unsigned vectorLength = m_butterfly->vectorLength();
868 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
869 for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
870 double value = m_butterfly->contiguousDouble()[i];
871 if (value == value) {
872 newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
873 newStorage->m_numValuesInVector++;
874 } else
875 ASSERT(newStorage->m_vector[i].get().isEmpty());
876 }
877
878 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
879 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
880 return newStorage;
881}
882
883ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
884{
885 return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
886}
887
888ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
889{
890 DeferGC deferGC(vm.heap);
891 ASSERT(hasContiguous(indexingType()));
892
893 unsigned vectorLength = m_butterfly->vectorLength();
894 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
895 for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
896 JSValue v = m_butterfly->contiguous()[i].get();
897 if (v) {
898 newStorage->m_vector[i].setWithoutWriteBarrier(v);
899 newStorage->m_numValuesInVector++;
900 } else
901 ASSERT(newStorage->m_vector[i].get().isEmpty());
902 }
903
904 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
905 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
906 return newStorage;
907}
908
909ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
910{
911 return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
912}
913
914void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
915{
916 if (value.isInt32()) {
917 convertUndecidedToInt32(vm);
918 return;
919 }
920
921 if (value.isDouble() && value.asNumber() == value.asNumber()) {
922 convertUndecidedToDouble(vm);
923 return;
924 }
925
926 convertUndecidedToContiguous(vm);
927}
928
929void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
930{
931 if (value.isInt32()) {
932 createInitialInt32(vm, index + 1)[index].set(vm, this, value);
933 return;
934 }
935
936 if (value.isDouble()) {
937 double doubleValue = value.asNumber();
938 if (doubleValue == doubleValue) {
939 createInitialDouble(vm, index + 1)[index] = doubleValue;
940 return;
941 }
942 }
943
944 createInitialContiguous(vm, index + 1)[index].set(vm, this, value);
945}
946
947void JSObject::convertInt32ForValue(VM& vm, JSValue value)
948{
949 ASSERT(!value.isInt32());
950
951 if (value.isDouble() && !std::isnan(value.asDouble())) {
952 convertInt32ToDouble(vm);
953 return;
954 }
955
956 convertInt32ToContiguous(vm);
957}
958
959void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
960{
961 ASSERT(index < m_butterfly->publicLength());
962 ASSERT(index < m_butterfly->vectorLength());
963 convertUndecidedForValue(vm, value);
964 setIndexQuickly(vm, index, value);
965}
966
967void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
968{
969 ASSERT(!value.isInt32());
970 convertInt32ForValue(vm, value);
971 setIndexQuickly(vm, index, value);
972}
973
974void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
975{
976 ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
977 convertDoubleToContiguous(vm);
978 setIndexQuickly(vm, index, value);
979}
980
981ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
982{
983 ASSERT(inherits(info()));
984
985 switch (indexingType()) {
986 case ALL_BLANK_INDEXING_TYPES:
987 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
988 return ContiguousJSValues();
989 return createInitialInt32(vm, 0);
990
991 case ALL_UNDECIDED_INDEXING_TYPES:
992 return convertUndecidedToInt32(vm);
993
994 case ALL_DOUBLE_INDEXING_TYPES:
995 case ALL_CONTIGUOUS_INDEXING_TYPES:
996 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
997 return ContiguousJSValues();
998
999 default:
1000 CRASH();
1001 return ContiguousJSValues();
1002 }
1003}
1004
1005ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
1006{
1007 ASSERT(inherits(info()));
1008
1009 switch (indexingType()) {
1010 case ALL_BLANK_INDEXING_TYPES:
1011 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1012 return ContiguousDoubles();
1013 return createInitialDouble(vm, 0);
1014
1015 case ALL_UNDECIDED_INDEXING_TYPES:
1016 return convertUndecidedToDouble(vm);
1017
1018 case ALL_INT32_INDEXING_TYPES:
1019 return convertInt32ToDouble(vm);
1020
1021 case ALL_CONTIGUOUS_INDEXING_TYPES:
1022 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1023 return ContiguousDoubles();
1024
1025 default:
1026 CRASH();
1027 return ContiguousDoubles();
1028 }
1029}
1030
1031ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
1032{
1033 ASSERT(inherits(info()));
1034
1035 switch (indexingType()) {
1036 case ALL_BLANK_INDEXING_TYPES:
1037 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1038 return ContiguousJSValues();
1039 return createInitialContiguous(vm, 0);
1040
1041 case ALL_UNDECIDED_INDEXING_TYPES:
1042 return convertUndecidedToContiguous(vm);
1043
1044 case ALL_INT32_INDEXING_TYPES:
1045 return convertInt32ToContiguous(vm);
1046
1047 case ALL_DOUBLE_INDEXING_TYPES:
1048 return convertDoubleToContiguous(vm);
1049
1050 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1051 return ContiguousJSValues();
1052
1053 default:
1054 CRASH();
1055 return ContiguousJSValues();
1056 }
1057}
1058
1059ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1060{
1061 ASSERT(inherits(info()));
1062
1063 switch (indexingType()) {
1064 case ALL_BLANK_INDEXING_TYPES:
1065 if (UNLIKELY(indexingShouldBeSparse()))
1066 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1067 return createInitialArrayStorage(vm);
1068
1069 case ALL_UNDECIDED_INDEXING_TYPES:
1070 ASSERT(!indexingShouldBeSparse());
1071 ASSERT(!structure(vm)->needsSlowPutIndexing());
1072 return convertUndecidedToArrayStorage(vm);
1073
1074 case ALL_INT32_INDEXING_TYPES:
1075 ASSERT(!indexingShouldBeSparse());
1076 ASSERT(!structure(vm)->needsSlowPutIndexing());
1077 return convertInt32ToArrayStorage(vm);
1078
1079 case ALL_DOUBLE_INDEXING_TYPES:
1080 ASSERT(!indexingShouldBeSparse());
1081 ASSERT(!structure(vm)->needsSlowPutIndexing());
1082 return convertDoubleToArrayStorage(vm);
1083
1084 case ALL_CONTIGUOUS_INDEXING_TYPES:
1085 ASSERT(!indexingShouldBeSparse());
1086 ASSERT(!structure(vm)->needsSlowPutIndexing());
1087 return convertContiguousToArrayStorage(vm);
1088
1089 default:
1090 RELEASE_ASSERT_NOT_REACHED();
1091 return 0;
1092 }
1093}
1094
1095ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1096{
1097 switch (indexingType()) {
1098 case ALL_BLANK_INDEXING_TYPES: {
1099 createArrayStorage(vm, 0, 0);
1100 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1101 map->setSparseMode();
1102 return arrayStorage();
1103 }
1104
1105 case ALL_UNDECIDED_INDEXING_TYPES:
1106 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1107
1108 case ALL_INT32_INDEXING_TYPES:
1109 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1110
1111 case ALL_DOUBLE_INDEXING_TYPES:
1112 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1113
1114 case ALL_CONTIGUOUS_INDEXING_TYPES:
1115 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1116
1117 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1118 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1119
1120 default:
1121 CRASH();
1122 return 0;
1123 }
1124}
1125
1126void JSObject::switchToSlowPutArrayStorage(VM& vm)
1127{
1128 switch (indexingType()) {
1129 case ALL_UNDECIDED_INDEXING_TYPES:
1130 convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage);
1131 break;
1132
1133 case ALL_INT32_INDEXING_TYPES:
1134 convertInt32ToArrayStorage(vm, AllocateSlowPutArrayStorage);
1135 break;
1136
1137 case ALL_DOUBLE_INDEXING_TYPES:
1138 convertDoubleToArrayStorage(vm, AllocateSlowPutArrayStorage);
1139 break;
1140
1141 case ALL_CONTIGUOUS_INDEXING_TYPES:
1142 convertContiguousToArrayStorage(vm, AllocateSlowPutArrayStorage);
1143 break;
1144
1145 case NonArrayWithArrayStorage:
1146 case ArrayWithArrayStorage: {
1147 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage);
1148 setStructure(vm, newStructure);
1149 break;
1150 }
1151
1152 default:
1153 CRASH();
1154 break;
1155 }
1156}
1157
1158void JSObject::setPrototype(VM& vm, JSValue prototype)
1159{
1160 ASSERT(prototype);
1161 if (prototype.isObject())
1162 vm.prototypeMap.addPrototype(asObject(prototype));
1163
1164 Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype);
1165 setStructure(vm, newStructure);
1166
1167 if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
1168 return;
1169
1170 if (vm.prototypeMap.isPrototype(this)) {
1171 newStructure->globalObject()->haveABadTime(vm);
1172 return;
1173 }
1174
1175 if (!hasIndexedProperties(indexingType()))
1176 return;
1177
1178 if (shouldUseSlowPut(indexingType()))
1179 return;
1180
1181 switchToSlowPutArrayStorage(vm);
1182}
1183
1184bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype)
1185{
1186 ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this);
1187 JSValue nextPrototype = prototype;
1188 while (nextPrototype && nextPrototype.isObject()) {
1189 if (nextPrototype == this)
1190 return false;
1191 nextPrototype = asObject(nextPrototype)->prototype();
1192 }
1193 setPrototype(exec->vm(), prototype);
1194 return true;
1195}
1196
1197bool JSObject::allowsAccessFrom(ExecState* exec)
1198{
1199 JSGlobalObject* globalObject = this->globalObject();
1200 return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
1201}
1202
1203void JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter)
1204{
1205 PropertyDescriptor descriptor;
1206 descriptor.setGetter(getter);
1207 descriptor.setEnumerable(true);
1208 descriptor.setConfigurable(true);
1209 defineOwnProperty(this, exec, propertyName, descriptor, false);
1210}
1211
1212void JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter)
1213{
1214 PropertyDescriptor descriptor;
1215 descriptor.setSetter(setter);
1216 descriptor.setEnumerable(true);
1217 descriptor.setConfigurable(true);
1218 defineOwnProperty(this, exec, propertyName, descriptor, false);
1219}
1220
1221void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
1222{
1223 ASSERT(value.isGetterSetter() && (attributes & Accessor));
1224
1225 if (Optional<uint32_t> index = parseIndex(propertyName)) {
1226 putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect);
1227 return;
1228 }
1229
1230 putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
1231}
1232
1233void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1234{
1235 ASSERT(!parseIndex(propertyName));
1236
1237 PutPropertySlot slot(this);
1238 putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1239
1240 ASSERT(slot.type() == PutPropertySlot::NewProperty);
1241
1242 Structure* structure = this->structure(vm);
1243 if (attributes & ReadOnly)
1244 structure->setContainsReadOnlyProperties();
1245 structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1246}
1247
1248void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1249{
1250 PutPropertySlot slot(this);
1251 putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1252
1253 Structure* structure = this->structure(vm);
1254 if (attributes & ReadOnly)
1255 structure->setContainsReadOnlyProperties();
1256
1257 structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1258}
1259
1260bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1261{
1262 PropertySlot slot(this);
1263 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1264}
1265
1266bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1267{
1268 PropertySlot slot(this);
1269 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1270}
1271
1272// ECMA 8.6.2.5
1273bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1274{
1275 JSObject* thisObject = jsCast<JSObject*>(cell);
1276
1277 if (Optional<uint32_t> index = parseIndex(propertyName))
1278 return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, index.value());
1279
1280 if (!thisObject->staticFunctionsReified())
1281 thisObject->reifyStaticFunctionsForDelete(exec);
1282
1283 unsigned attributes;
1284 VM& vm = exec->vm();
1285 if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes))) {
1286 if (attributes & DontDelete && !vm.isInDefineOwnProperty())
1287 return false;
1288 thisObject->removeDirect(vm, propertyName);
1289 return true;
1290 }
1291
1292 // Look in the static hashtable of properties
1293 const HashTableValue* entry = thisObject->findPropertyHashEntry(propertyName);
1294 if (entry) {
1295 if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty())
1296 return false; // this builtin property can't be deleted
1297
1298 PutPropertySlot slot(thisObject);
1299 if (!(entry->attributes() & BuiltinOrFunctionOrAccessor)) {
1300 ASSERT(thisObject->staticFunctionsReified());
1301 putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot);
1302 }
1303 }
1304
1305 return true;
1306}
1307
1308bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
1309{
1310 PropertySlot slot(this);
1311 return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
1312}
1313
1314bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
1315{
1316 PropertySlot slot(this);
1317 return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot);
1318}
1319
1320bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
1321{
1322 JSObject* thisObject = jsCast<JSObject*>(cell);
1323
1324 if (i > MAX_ARRAY_INDEX)
1325 return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
1326
1327 switch (thisObject->indexingType()) {
1328 case ALL_BLANK_INDEXING_TYPES:
1329 case ALL_UNDECIDED_INDEXING_TYPES:
1330 return true;
1331
1332 case ALL_INT32_INDEXING_TYPES:
1333 case ALL_CONTIGUOUS_INDEXING_TYPES: {
1334 Butterfly* butterfly = thisObject->butterfly();
1335 if (i >= butterfly->vectorLength())
1336 return true;
1337 butterfly->contiguous()[i].clear();
1338 return true;
1339 }
1340
1341 case ALL_DOUBLE_INDEXING_TYPES: {
1342 Butterfly* butterfly = thisObject->butterfly();
1343 if (i >= butterfly->vectorLength())
1344 return true;
1345 butterfly->contiguousDouble()[i] = PNaN;
1346 return true;
1347 }
1348
1349 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1350 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
1351
1352 if (i < storage->vectorLength()) {
1353 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
1354 if (valueSlot) {
1355 valueSlot.clear();
1356 --storage->m_numValuesInVector;
1357 }
1358 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1359 SparseArrayValueMap::iterator it = map->find(i);
1360 if (it != map->notFound()) {
1361 if (it->value.attributes & DontDelete)
1362 return false;
1363 map->remove(it);
1364 }
1365 }
1366
1367 return true;
1368 }
1369
1370 default:
1371 RELEASE_ASSERT_NOT_REACHED();
1372 return false;
1373 }
1374}
1375
1376static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName)
1377{
1378 JSValue function = object->get(exec, propertyName);
1379 CallData callData;
1380 CallType callType = getCallData(function, callData);
1381 if (callType == CallTypeNone)
1382 return exec->exception();
1383
1384 // Prevent "toString" and "valueOf" from observing execution if an exception
1385 // is pending.
1386 if (exec->hadException())
1387 return exec->exception();
1388
1389 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
1390 ASSERT(!result.isGetterSetter());
1391 if (exec->hadException())
1392 return exec->exception();
1393 if (result.isObject())
1394 return JSValue();
1395 return result;
1396}
1397
1398bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
1399{
1400 result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber);
1401 number = result.toNumber(exec);
1402 return !result.isString();
1403}
1404
1405// ECMA 8.6.2.6
1406JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
1407{
1408 // Make sure that whatever default value methods there are on object's prototype chain are
1409 // being watched.
1410 object->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec->vm());
1411
1412 // Must call toString first for Date objects.
1413 if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
1414 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
1415 if (value)
1416 return value;
1417 value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
1418 if (value)
1419 return value;
1420 } else {
1421 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
1422 if (value)
1423 return value;
1424 value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
1425 if (value)
1426 return value;
1427 }
1428
1429 ASSERT(!exec->hadException());
1430
1431 return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value")));
1432}
1433
1434const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const
1435{
1436 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1437 if (const HashTable* propHashTable = info->staticPropHashTable) {
1438 if (const HashTableValue* entry = propHashTable->entry(propertyName))
1439 return entry;
1440 }
1441 }
1442 return 0;
1443}
1444
1445bool JSObject::hasInstance(ExecState* exec, JSValue value)
1446{
1447 VM& vm = exec->vm();
1448 TypeInfo info = structure(vm)->typeInfo();
1449 if (info.implementsDefaultHasInstance())
1450 return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
1451 if (info.implementsHasInstance())
1452 return methodTable(vm)->customHasInstance(this, exec, value);
1453 vm.throwException(exec, createInvalidInstanceofParameterError(exec, this));
1454 return false;
1455}
1456
1457bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
1458{
1459 if (!value.isObject())
1460 return false;
1461
1462 if (!proto.isObject()) {
1463 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
1464 return false;
1465 }
1466
1467 JSObject* object = asObject(value);
1468 while ((object = object->prototype().getObject())) {
1469 if (proto == object)
1470 return true;
1471 }
1472 return false;
1473}
1474
1475void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1476{
1477 object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode);
1478
1479 if (object->prototype().isNull())
1480 return;
1481
1482 VM& vm = exec->vm();
1483 JSObject* prototype = asObject(object->prototype());
1484 while(1) {
1485 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
1486 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
1487 break;
1488 }
1489 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
1490 JSValue nextProto = prototype->prototype();
1491 if (nextProto.isNull())
1492 break;
1493 prototype = asObject(nextProto);
1494 }
1495}
1496
1497void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1498{
1499 if (!mode.includeJSObjectProperties()) {
1500 // We still have to get non-indexed properties from any subclasses of JSObject that have them.
1501 object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1502 return;
1503 }
1504
1505 // Add numeric properties first. That appears to be the accepted convention.
1506 // FIXME: Filling PropertyNameArray with an identifier for every integer
1507 // is incredibly inefficient for large arrays. We need a different approach,
1508 // which almost certainly means a different structure for PropertyNameArray.
1509 switch (object->indexingType()) {
1510 case ALL_BLANK_INDEXING_TYPES:
1511 case ALL_UNDECIDED_INDEXING_TYPES:
1512 break;
1513
1514 case ALL_INT32_INDEXING_TYPES:
1515 case ALL_CONTIGUOUS_INDEXING_TYPES: {
1516 Butterfly* butterfly = object->butterfly();
1517 unsigned usedLength = butterfly->publicLength();
1518 for (unsigned i = 0; i < usedLength; ++i) {
1519 if (!butterfly->contiguous()[i])
1520 continue;
1521 propertyNames.add(i);
1522 }
1523 break;
1524 }
1525
1526 case ALL_DOUBLE_INDEXING_TYPES: {
1527 Butterfly* butterfly = object->butterfly();
1528 unsigned usedLength = butterfly->publicLength();
1529 for (unsigned i = 0; i < usedLength; ++i) {
1530 double value = butterfly->contiguousDouble()[i];
1531 if (value != value)
1532 continue;
1533 propertyNames.add(i);
1534 }
1535 break;
1536 }
1537
1538 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1539 ArrayStorage* storage = object->m_butterfly->arrayStorage();
1540
1541 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
1542 for (unsigned i = 0; i < usedVectorLength; ++i) {
1543 if (storage->m_vector[i])
1544 propertyNames.add(i);
1545 }
1546
1547 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1548 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
1549 keys.reserveInitialCapacity(map->size());
1550
1551 SparseArrayValueMap::const_iterator end = map->end();
1552 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
1553 if (mode.includeDontEnumProperties() || !(it->value.attributes & DontEnum))
1554 keys.uncheckedAppend(static_cast<unsigned>(it->key));
1555 }
1556
1557 std::sort(keys.begin(), keys.end());
1558 for (unsigned i = 0; i < keys.size(); ++i)
1559 propertyNames.add(keys[i]);
1560 }
1561 break;
1562 }
1563
1564 default:
1565 RELEASE_ASSERT_NOT_REACHED();
1566 }
1567
1568 object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1569}
1570
1571void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1572{
1573 getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
1574
1575 if (!mode.includeJSObjectProperties())
1576 return;
1577
1578 VM& vm = exec->vm();
1579 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
1580}
1581
1582double JSObject::toNumber(ExecState* exec) const
1583{
1584 JSValue primitive = toPrimitive(exec, PreferNumber);
1585 if (exec->hadException()) // should be picked up soon in Nodes.cpp
1586 return 0.0;
1587 return primitive.toNumber(exec);
1588}
1589
1590JSString* JSObject::toString(ExecState* exec) const
1591{
1592 JSValue primitive = toPrimitive(exec, PreferString);
1593 if (exec->hadException())
1594 return jsEmptyString(exec);
1595 return primitive.toString(exec);
1596}
1597
1598JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
1599{
1600 return jsCast<JSObject*>(cell);
1601}
1602
1603bool JSObject::isCatchScopeObject() const
1604{
1605 return inherits(JSCatchScope::info());
1606}
1607
1608bool JSObject::isFunctionNameScopeObject() const
1609{
1610 return inherits(JSFunctionNameScope::info());
1611}
1612
1613void JSObject::seal(VM& vm)
1614{
1615 if (isSealed(vm))
1616 return;
1617 preventExtensions(vm);
1618 setStructure(vm, Structure::sealTransition(vm, structure(vm)));
1619}
1620
1621void JSObject::freeze(VM& vm)
1622{
1623 if (isFrozen(vm))
1624 return;
1625 preventExtensions(vm);
1626 setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
1627}
1628
1629void JSObject::preventExtensions(VM& vm)
1630{
1631 enterDictionaryIndexingMode(vm);
1632 if (isExtensible())
1633 setStructure(vm, Structure::preventExtensionsTransition(vm, structure(vm)));
1634}
1635
1636// This presently will flatten to an uncachable dictionary; this is suitable
1637// for use in delete, we may want to do something different elsewhere.
1638void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
1639{
1640 ASSERT(!staticFunctionsReified());
1641 VM& vm = exec->vm();
1642
1643 // If this object's ClassInfo has no static properties, then nothing to reify!
1644 // We can safely set the flag to avoid the expensive check again in the future.
1645 if (!classInfo()->hasStaticProperties()) {
1646 structure(vm)->setStaticFunctionsReified(true);
1647 return;
1648 }
1649
1650 if (!structure(vm)->isUncacheableDictionary())
1651 setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm)));
1652
1653 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1654 const HashTable* hashTable = info->staticPropHashTable;
1655 if (!hashTable)
1656 continue;
1657 PropertySlot slot(this);
1658 for (auto iter = hashTable->begin(); iter != hashTable->end(); ++iter) {
1659 if (iter->attributes() & BuiltinOrFunctionOrAccessor)
1660 setUpStaticFunctionSlot(globalObject()->globalExec(), iter.value(), this, Identifier::fromString(&vm, iter.key()), slot);
1661 }
1662 }
1663
1664 structure(vm)->setStaticFunctionsReified(true);
1665}
1666
1667bool JSObject::removeDirect(VM& vm, PropertyName propertyName)
1668{
1669 Structure* structure = this->structure(vm);
1670 if (!isValidOffset(structure->get(vm, propertyName)))
1671 return false;
1672
1673 PropertyOffset offset;
1674 if (structure->isUncacheableDictionary()) {
1675 offset = structure->removePropertyWithoutTransition(vm, propertyName);
1676 if (offset == invalidOffset)
1677 return false;
1678 putDirectUndefined(offset);
1679 return true;
1680 }
1681
1682 setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
1683 if (offset == invalidOffset)
1684 return false;
1685 putDirectUndefined(offset);
1686 return true;
1687}
1688
1689NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset)
1690{
1691 if (structure()->isDictionary()) {
1692 slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
1693 return;
1694 }
1695 slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
1696}
1697
1698void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
1699{
1700 VM& vm = exec->vm();
1701 auto map = m_butterfly->arrayStorage()->m_sparseMap.get();
1702
1703 if (descriptor.isDataDescriptor()) {
1704 if (descriptor.value())
1705 entryInMap->set(vm, map, descriptor.value());
1706 else if (oldDescriptor.isAccessorDescriptor())
1707 entryInMap->set(vm, map, jsUndefined());
1708 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
1709 return;
1710 }
1711
1712 if (descriptor.isAccessorDescriptor()) {
1713 JSObject* getter = 0;
1714 if (descriptor.getterPresent())
1715 getter = descriptor.getterObject();
1716 else if (oldDescriptor.isAccessorDescriptor())
1717 getter = oldDescriptor.getterObject();
1718 JSObject* setter = 0;
1719 if (descriptor.setterPresent())
1720 setter = descriptor.setterObject();
1721 else if (oldDescriptor.isAccessorDescriptor())
1722 setter = oldDescriptor.setterObject();
1723
1724 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
1725 if (getter)
1726 accessor->setGetter(vm, exec->lexicalGlobalObject(), getter);
1727 if (setter)
1728 accessor->setSetter(vm, exec->lexicalGlobalObject(), setter);
1729
1730 entryInMap->set(vm, map, accessor);
1731 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
1732 return;
1733 }
1734
1735 ASSERT(descriptor.isGenericDescriptor());
1736 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
1737}
1738
1739// Defined in ES5.1 8.12.9
1740bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
1741{
1742 ASSERT(index <= MAX_ARRAY_INDEX);
1743
1744 if (!inSparseIndexingMode()) {
1745 // Fast case: we're putting a regular property to a regular array
1746 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1747 // however if the property currently exists missing attributes will override from their current 'true'
1748 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1749 if (!descriptor.attributes()) {
1750 ASSERT(!descriptor.isAccessorDescriptor());
1751 return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
1752 }
1753
1754 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm());
1755 }
1756
1757 if (descriptor.attributes() & (ReadOnly | Accessor))
1758 notifyPresenceOfIndexedAccessors(exec->vm());
1759
1760 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
1761 RELEASE_ASSERT(map);
1762
1763 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1764 SparseArrayValueMap::AddResult result = map->add(this, index);
1765 SparseArrayEntry* entryInMap = &result.iterator->value;
1766
1767 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1768 // 3. If current is undefined and extensible is false, then Reject.
1769 // 4. If current is undefined and extensible is true, then
1770 if (result.isNewEntry) {
1771 if (!isExtensible()) {
1772 map->remove(result.iterator);
1773 return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
1774 }
1775
1776 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1777 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1778 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1779 // created property is set to its default value.
1780 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1781 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1782 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1783 // is set to its default value.
1784 // 4.c. Return true.
1785
1786 PropertyDescriptor defaults;
1787 entryInMap->setWithoutWriteBarrier(jsUndefined());
1788 entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
1789 entryInMap->get(defaults);
1790
1791 putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
1792 if (index >= m_butterfly->arrayStorage()->length())
1793 m_butterfly->arrayStorage()->setLength(index + 1);
1794 return true;
1795 }
1796
1797 // 5. Return true, if every field in Desc is absent.
1798 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
1799 PropertyDescriptor current;
1800 entryInMap->get(current);
1801 if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
1802 return true;
1803
1804 // 7. If the [[Configurable]] field of current is false then
1805 if (!current.configurable()) {
1806 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1807 if (descriptor.configurablePresent() && descriptor.configurable())
1808 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
1809 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
1810 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
1811 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
1812 }
1813
1814 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1815 if (!descriptor.isGenericDescriptor()) {
1816 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1817 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
1818 // 9.a. Reject, if the [[Configurable]] field of current is false.
1819 if (!current.configurable())
1820 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
1821 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1822 // data property to an accessor property. Preserve the existing values of the converted property's
1823 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1824 // their default values.
1825 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1826 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1827 // attributes and set the rest of the property's attributes to their default values.
1828 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
1829 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1830 // 10.a. If the [[Configurable]] field of current is false, then
1831 if (!current.configurable() && !current.writable()) {
1832 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1833 if (descriptor.writable())
1834 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
1835 // 10.a.ii. If the [[Writable]] field of current is false, then
1836 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1837 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
1838 return reject(exec, throwException, "Attempting to change value of a readonly property.");
1839 }
1840 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1841 } else {
1842 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
1843 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1844 if (!current.configurable()) {
1845 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1846 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
1847 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
1848 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1849 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
1850 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
1851 }
1852 }
1853 }
1854
1855 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
1856 putIndexedDescriptor(exec, entryInMap, descriptor, current);
1857 // 13. Return true.
1858 return true;
1859}
1860
1861SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
1862{
1863 SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
1864 arrayStorage()->m_sparseMap.set(vm, this, result);
1865 return result;
1866}
1867
1868void JSObject::deallocateSparseIndexMap()
1869{
1870 if (ArrayStorage* arrayStorage = arrayStorageOrNull())
1871 arrayStorage->m_sparseMap.clear();
1872}
1873
1874bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow)
1875{
1876 for (JSObject* current = this; ;) {
1877 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1878 // allows a prototype to intercept a put if (a) the prototype declares the property
1879 // we're after rather than intercepting it via an override of JSObject::put(), and
1880 // (b) that property is declared as ReadOnly or Accessor.
1881
1882 ArrayStorage* storage = current->arrayStorageOrNull();
1883 if (storage && storage->m_sparseMap) {
1884 SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
1885 if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) {
1886 iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
1887 return true;
1888 }
1889 }
1890
1891 JSValue prototypeValue = current->prototype();
1892 if (prototypeValue.isNull())
1893 return false;
1894
1895 current = asObject(prototypeValue);
1896 }
1897}
1898
1899bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
1900{
1901 JSValue prototypeValue = prototype();
1902 if (prototypeValue.isNull())
1903 return false;
1904
1905 return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow);
1906}
1907
1908template<IndexingType indexingShape>
1909void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
1910{
1911 ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
1912 ASSERT(!indexingShouldBeSparse());
1913
1914 // For us to get here, the index is either greater than the public length, or greater than
1915 // or equal to the vector length.
1916 ASSERT(i >= m_butterfly->vectorLength());
1917
1918 VM& vm = exec->vm();
1919
1920 if (i >= MAX_ARRAY_INDEX - 1
1921 || (i >= MIN_SPARSE_ARRAY_INDEX
1922 && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly())))
1923 || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) {
1924 ASSERT(i <= MAX_ARRAY_INDEX);
1925 ensureArrayStorageSlow(vm);
1926 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1927 map->putEntry(exec, this, i, value, false);
1928 ASSERT(i >= arrayStorage()->length());
1929 arrayStorage()->setLength(i + 1);
1930 return;
1931 }
1932
1933 ensureLength(vm, i + 1);
1934
1935 RELEASE_ASSERT(i < m_butterfly->vectorLength());
1936 switch (indexingShape) {
1937 case Int32Shape:
1938 ASSERT(value.isInt32());
1939 m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value);
1940 break;
1941
1942 case DoubleShape: {
1943 ASSERT(value.isNumber());
1944 double valueAsDouble = value.asNumber();
1945 ASSERT(valueAsDouble == valueAsDouble);
1946 m_butterfly->contiguousDouble()[i] = valueAsDouble;
1947 break;
1948 }
1949
1950 case ContiguousShape:
1951 m_butterfly->contiguous()[i].set(vm, this, value);
1952 break;
1953
1954 default:
1955 CRASH();
1956 }
1957}
1958
1959void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
1960{
1961 VM& vm = exec->vm();
1962
1963 // i should be a valid array index that is outside of the current vector.
1964 ASSERT(i <= MAX_ARRAY_INDEX);
1965 ASSERT(i >= storage->vectorLength());
1966
1967 SparseArrayValueMap* map = storage->m_sparseMap.get();
1968
1969 // First, handle cases where we don't currently have a sparse map.
1970 if (LIKELY(!map)) {
1971 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
1972 ASSERT(isExtensible());
1973
1974 // Update m_length if necessary.
1975 if (i >= storage->length())
1976 storage->setLength(i + 1);
1977
1978 // Check that it is sensible to still be using a vector, and then try to grow the vector.
1979 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())
1980 && isDenseEnoughForVector(i, storage->m_numValuesInVector)
1981 && increaseVectorLength(vm, i + 1))) {
1982 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1983 storage = arrayStorage();
1984 storage->m_vector[i].set(vm, this, value);
1985 ++storage->m_numValuesInVector;
1986 return;
1987 }
1988 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1989 map = allocateSparseIndexMap(exec->vm());
1990 map->putEntry(exec, this, i, value, shouldThrow);
1991 return;
1992 }
1993
1994 // Update m_length if necessary.
1995 unsigned length = storage->length();
1996 if (i >= length) {
1997 // Prohibit growing the array if length is not writable.
1998 if (map->lengthIsReadOnly() || !isExtensible()) {
1999 if (shouldThrow)
2000 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
2001 return;
2002 }
2003 length = i + 1;
2004 storage->setLength(length);
2005 }
2006
2007 // We are currently using a map - check whether we still want to be doing so.
2008 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2009 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2010 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length)) {
2011 map->putEntry(exec, this, i, value, shouldThrow);
2012 return;
2013 }
2014
2015 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2016 storage = arrayStorage();
2017 storage->m_numValuesInVector = numValuesInArray;
2018
2019 // Copy all values from the map into the vector, and delete the map.
2020 WriteBarrier<Unknown>* vector = storage->m_vector;
2021 SparseArrayValueMap::const_iterator end = map->end();
2022 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2023 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2024 deallocateSparseIndexMap();
2025
2026 // Store the new property into the vector.
2027 WriteBarrier<Unknown>& valueSlot = vector[i];
2028 if (!valueSlot)
2029 ++storage->m_numValuesInVector;
2030 valueSlot.set(vm, this, value);
2031}
2032
2033void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2034{
2035 VM& vm = exec->vm();
2036
2037 // i should be a valid array index that is outside of the current vector.
2038 ASSERT(i <= MAX_ARRAY_INDEX);
2039
2040 switch (indexingType()) {
2041 case ALL_BLANK_INDEXING_TYPES: {
2042 if (indexingShouldBeSparse()) {
2043 putByIndexBeyondVectorLengthWithArrayStorage(
2044 exec, i, value, shouldThrow,
2045 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2046 break;
2047 }
2048 if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2049 putByIndexBeyondVectorLengthWithArrayStorage(
2050 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2051 break;
2052 }
2053 if (structure(vm)->needsSlowPutIndexing()) {
2054 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
2055 storage->m_vector[i].set(vm, this, value);
2056 storage->m_numValuesInVector++;
2057 break;
2058 }
2059
2060 createInitialForValueAndSet(vm, i, value);
2061 break;
2062 }
2063
2064 case ALL_UNDECIDED_INDEXING_TYPES: {
2065 CRASH();
2066 break;
2067 }
2068
2069 case ALL_INT32_INDEXING_TYPES: {
2070 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2071 break;
2072 }
2073
2074 case ALL_DOUBLE_INDEXING_TYPES: {
2075 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2076 break;
2077 }
2078
2079 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2080 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2081 break;
2082 }
2083
2084 case NonArrayWithSlowPutArrayStorage:
2085 case ArrayWithSlowPutArrayStorage: {
2086 // No own property present in the vector, but there might be in the sparse map!
2087 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2088 if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow))
2089 return;
2090 FALLTHROUGH;
2091 }
2092
2093 case NonArrayWithArrayStorage:
2094 case ArrayWithArrayStorage:
2095 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2096 break;
2097
2098 default:
2099 RELEASE_ASSERT_NOT_REACHED();
2100 }
2101}
2102
2103bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2104{
2105 VM& vm = exec->vm();
2106
2107 // i should be a valid array index that is outside of the current vector.
2108 ASSERT(hasAnyArrayStorage(indexingType()));
2109 ASSERT(arrayStorage() == storage);
2110 ASSERT(i >= storage->vectorLength() || attributes);
2111 ASSERT(i <= MAX_ARRAY_INDEX);
2112
2113 SparseArrayValueMap* map = storage->m_sparseMap.get();
2114
2115 // First, handle cases where we don't currently have a sparse map.
2116 if (LIKELY(!map)) {
2117 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2118 ASSERT(isExtensible());
2119
2120 // Update m_length if necessary.
2121 if (i >= storage->length())
2122 storage->setLength(i + 1);
2123
2124 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2125 if (LIKELY(
2126 !attributes
2127 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2128 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2129 && increaseVectorLength(vm, i + 1)) {
2130 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2131 storage = arrayStorage();
2132 storage->m_vector[i].set(vm, this, value);
2133 ++storage->m_numValuesInVector;
2134 return true;
2135 }
2136 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2137 map = allocateSparseIndexMap(exec->vm());
2138 return map->putDirect(exec, this, i, value, attributes, mode);
2139 }
2140
2141 // Update m_length if necessary.
2142 unsigned length = storage->length();
2143 if (i >= length) {
2144 if (mode != PutDirectIndexLikePutDirect) {
2145 // Prohibit growing the array if length is not writable.
2146 if (map->lengthIsReadOnly())
2147 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
2148 if (!isExtensible())
2149 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
2150 }
2151 length = i + 1;
2152 storage->setLength(length);
2153 }
2154
2155 // We are currently using a map - check whether we still want to be doing so.
2156 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2157 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2158 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length))
2159 return map->putDirect(exec, this, i, value, attributes, mode);
2160
2161 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2162 storage = arrayStorage();
2163 storage->m_numValuesInVector = numValuesInArray;
2164
2165 // Copy all values from the map into the vector, and delete the map.
2166 WriteBarrier<Unknown>* vector = storage->m_vector;
2167 SparseArrayValueMap::const_iterator end = map->end();
2168 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2169 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2170 deallocateSparseIndexMap();
2171
2172 // Store the new property into the vector.
2173 WriteBarrier<Unknown>& valueSlot = vector[i];
2174 if (!valueSlot)
2175 ++storage->m_numValuesInVector;
2176 valueSlot.set(vm, this, value);
2177 return true;
2178}
2179
2180bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
2181{
2182 VM& vm = exec->vm();
2183
2184 // i should be a valid array index that is outside of the current vector.
2185 ASSERT(i <= MAX_ARRAY_INDEX);
2186
2187 if (attributes & (ReadOnly | Accessor))
2188 notifyPresenceOfIndexedAccessors(vm);
2189
2190 switch (indexingType()) {
2191 case ALL_BLANK_INDEXING_TYPES: {
2192 if (indexingShouldBeSparse() || attributes) {
2193 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2194 exec, i, value, attributes, mode,
2195 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2196 }
2197 if (i >= MIN_SPARSE_ARRAY_INDEX) {
2198 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2199 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
2200 }
2201 if (structure(vm)->needsSlowPutIndexing()) {
2202 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
2203 storage->m_vector[i].set(vm, this, value);
2204 storage->m_numValuesInVector++;
2205 return true;
2206 }
2207
2208 createInitialForValueAndSet(vm, i, value);
2209 return true;
2210 }
2211
2212 case ALL_UNDECIDED_INDEXING_TYPES: {
2213 convertUndecidedForValue(exec->vm(), value);
2214 // Reloop.
2215 return putDirectIndex(exec, i, value, attributes, mode);
2216 }
2217
2218 case ALL_INT32_INDEXING_TYPES: {
2219 if (attributes & (ReadOnly | Accessor)) {
2220 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2221 exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm));
2222 }
2223 if (!value.isInt32()) {
2224 convertInt32ForValue(vm, value);
2225 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2226 }
2227 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2228 return true;
2229 }
2230
2231 case ALL_DOUBLE_INDEXING_TYPES: {
2232 if (attributes & (ReadOnly | Accessor)) {
2233 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2234 exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm));
2235 }
2236 if (!value.isNumber()) {
2237 convertDoubleToContiguous(vm);
2238 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2239 }
2240 double valueAsDouble = value.asNumber();
2241 if (valueAsDouble != valueAsDouble) {
2242 convertDoubleToContiguous(vm);
2243 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2244 }
2245 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2246 return true;
2247 }
2248
2249 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2250 if (attributes & (ReadOnly | Accessor)) {
2251 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2252 exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm));
2253 }
2254 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2255 return true;
2256 }
2257
2258 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
2259 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
2260
2261 default:
2262 RELEASE_ASSERT_NOT_REACHED();
2263 return false;
2264 }
2265}
2266
2267void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2268{
2269 StringImpl* name = propertyName.publicName();
2270 if (!name)
2271 name = vm.propertyNames->anonymous.impl();
2272 ASSERT(name);
2273
2274 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2275 putDirect(vm, propertyName, function, attributes);
2276}
2277
2278JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2279{
2280 StringImpl* name = propertyName.publicName();
2281 if (!name)
2282 name = vm.propertyNames->anonymous.impl();
2283 ASSERT(name);
2284 JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2285 putDirect(vm, propertyName, function, attributes);
2286 return function;
2287}
2288
2289JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2290{
2291 StringImpl* name = propertyName.publicName();
2292 if (!name)
2293 name = vm.propertyNames->anonymous.impl();
2294 ASSERT(name);
2295 JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2296 putDirectWithoutTransition(vm, propertyName, function, attributes);
2297 return function;
2298}
2299
2300void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2301{
2302 StringImpl* name = propertyName.publicName();
2303 ASSERT(name);
2304
2305 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2306 putDirectWithoutTransition(vm, propertyName, function, attributes);
2307}
2308
2309ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
2310{
2311 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
2312
2313 unsigned increasedLength;
2314 unsigned maxInitLength = std::min(currentLength, 100000U);
2315
2316 if (desiredLength < maxInitLength)
2317 increasedLength = maxInitLength;
2318 else if (!currentVectorLength)
2319 increasedLength = std::max(desiredLength, lastArraySize);
2320 else {
2321 increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
2322 }
2323
2324 ASSERT(increasedLength >= desiredLength);
2325
2326 lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW);
2327
2328 return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
2329}
2330
2331ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
2332{
2333 unsigned vectorLength;
2334 unsigned length;
2335
2336 if (hasIndexedProperties(indexingType())) {
2337 vectorLength = m_butterfly->vectorLength();
2338 length = m_butterfly->publicLength();
2339 } else {
2340 vectorLength = 0;
2341 length = 0;
2342 }
2343
2344 return getNewVectorLength(vectorLength, length, desiredLength);
2345}
2346
2347template<IndexingType indexingShape>
2348unsigned JSObject::countElements(Butterfly* butterfly)
2349{
2350 unsigned numValues = 0;
2351 for (unsigned i = butterfly->publicLength(); i--;) {
2352 switch (indexingShape) {
2353 case Int32Shape:
2354 case ContiguousShape:
2355 if (butterfly->contiguous()[i])
2356 numValues++;
2357 break;
2358
2359 case DoubleShape: {
2360 double value = butterfly->contiguousDouble()[i];
2361 if (value == value)
2362 numValues++;
2363 break;
2364 }
2365
2366 default:
2367 CRASH();
2368 }
2369 }
2370 return numValues;
2371}
2372
2373unsigned JSObject::countElements()
2374{
2375 switch (indexingType()) {
2376 case ALL_BLANK_INDEXING_TYPES:
2377 case ALL_UNDECIDED_INDEXING_TYPES:
2378 return 0;
2379
2380 case ALL_INT32_INDEXING_TYPES:
2381 return countElements<Int32Shape>(butterfly());
2382
2383 case ALL_DOUBLE_INDEXING_TYPES:
2384 return countElements<DoubleShape>(butterfly());
2385
2386 case ALL_CONTIGUOUS_INDEXING_TYPES:
2387 return countElements<ContiguousShape>(butterfly());
2388
2389 default:
2390 CRASH();
2391 return 0;
2392 }
2393}
2394
2395bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
2396{
2397 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2398 // to the vector. Callers have to account for that, because they can do it more efficiently.
2399 if (newLength > MAX_STORAGE_VECTOR_LENGTH)
2400 return false;
2401
2402 ArrayStorage* storage = arrayStorage();
2403
2404 if (newLength >= MIN_SPARSE_ARRAY_INDEX
2405 && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
2406 return false;
2407
2408 unsigned indexBias = storage->m_indexBias;
2409 unsigned vectorLength = storage->vectorLength();
2410 ASSERT(newLength > vectorLength);
2411 unsigned newVectorLength = getNewVectorLength(newLength);
2412
2413 // Fast case - there is no precapacity. In these cases a realloc makes sense.
2414 Structure* structure = this->structure(vm);
2415 if (LIKELY(!indexBias)) {
2416 DeferGC deferGC(vm.heap);
2417 Butterfly* newButterfly = storage->butterfly()->growArrayRight(
2418 vm, this, structure, structure->outOfLineCapacity(), true,
2419 ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
2420 if (!newButterfly)
2421 return false;
2422 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2423 setButterflyWithoutChangingStructure(vm, newButterfly);
2424 return true;
2425 }
2426
2427 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2428 DeferGC deferGC(vm.heap);
2429 unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
2430 Butterfly* newButterfly = storage->butterfly()->resizeArray(
2431 vm, this,
2432 structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
2433 newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
2434 if (!newButterfly)
2435 return false;
2436 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2437 newButterfly->arrayStorage()->m_indexBias = newIndexBias;
2438 setButterflyWithoutChangingStructure(vm, newButterfly);
2439 return true;
2440}
2441
2442void JSObject::ensureLengthSlow(VM& vm, unsigned length)
2443{
2444 ASSERT(length < MAX_ARRAY_INDEX);
2445 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2446 ASSERT(length > m_butterfly->vectorLength());
2447
2448 unsigned newVectorLength = std::min(
2449 length << 1,
2450 MAX_STORAGE_VECTOR_LENGTH);
2451 unsigned oldVectorLength = m_butterfly->vectorLength();
2452 DeferGC deferGC(vm.heap);
2453 m_butterfly.set(vm, this, m_butterfly->growArrayRight(
2454 vm, this, structure(), structure()->outOfLineCapacity(), true,
2455 oldVectorLength * sizeof(EncodedJSValue),
2456 newVectorLength * sizeof(EncodedJSValue)));
2457
2458 m_butterfly->setVectorLength(newVectorLength);
2459
2460 if (hasDouble(indexingType())) {
2461 for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
2462 m_butterfly->contiguousDouble().data()[i] = PNaN;
2463 }
2464}
2465
2466void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
2467{
2468 ASSERT(length < MAX_ARRAY_INDEX);
2469 ASSERT(length < MAX_STORAGE_VECTOR_LENGTH);
2470 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2471 ASSERT(m_butterfly->vectorLength() > length);
2472 ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure()));
2473
2474 DeferGC deferGC(vm.heap);
2475 Butterfly* newButterfly = m_butterfly->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length));
2476 m_butterfly.set(vm, this, newButterfly);
2477 m_butterfly->setVectorLength(length);
2478 m_butterfly->setPublicLength(length);
2479}
2480
2481Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
2482{
2483 ASSERT(newSize > oldSize);
2484
2485 // It's important that this function not rely on structure(), for the property
2486 // capacity, since we might have already mutated the structure in-place.
2487
2488 return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize);
2489}
2490
2491bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
2492{
2493 JSC::PropertySlot slot(this);
2494 if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot))
2495 return false;
2496 /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */
2497 if (slot.slotBase() != this && slot.slotBase() && slot.slotBase()->methodTable(exec->vm())->toThis(slot.slotBase(), exec, NotStrictMode) != this)
2498 return false;
2499 if (slot.isAccessor())
2500 descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
2501 else if (slot.attributes() & CustomAccessor)
2502 descriptor.setCustomDescriptor(slot.attributes());
2503 else
2504 descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes());
2505 return true;
2506}
2507
2508static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
2509{
2510 VM& vm = exec->vm();
2511 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
2512 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
2513 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2514 if (oldDescriptor.getterPresent())
2515 accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2516 if (oldDescriptor.setterPresent())
2517 accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2518 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2519 return true;
2520 }
2521 JSValue newValue = jsUndefined();
2522 if (descriptor.value())
2523 newValue = descriptor.value();
2524 else if (oldDescriptor.value())
2525 newValue = oldDescriptor.value();
2526 target->putDirect(vm, propertyName, newValue, attributes & ~Accessor);
2527 if (attributes & ReadOnly)
2528 target->structure(vm)->setContainsReadOnlyProperties();
2529 return true;
2530 }
2531 attributes &= ~ReadOnly;
2532 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2533
2534 if (descriptor.getterPresent())
2535 accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
2536 else if (oldDescriptor.getterPresent())
2537 accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2538 if (descriptor.setterPresent())
2539 accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
2540 else if (oldDescriptor.setterPresent())
2541 accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2542
2543 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2544 return true;
2545}
2546
2547void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
2548{
2549 if (Optional<uint32_t> index = parseIndex(propertyName))
2550 putDirectIndex(exec, index.value(), value);
2551 else
2552 putDirect(exec->vm(), propertyName, value);
2553}
2554
2555class DefineOwnPropertyScope {
2556public:
2557 DefineOwnPropertyScope(ExecState* exec)
2558 : m_vm(exec->vm())
2559 {
2560 m_vm.setInDefineOwnProperty(true);
2561 }
2562
2563 ~DefineOwnPropertyScope()
2564 {
2565 m_vm.setInDefineOwnProperty(false);
2566 }
2567
2568private:
2569 VM& m_vm;
2570};
2571
2572bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
2573{
2574 // Track on the globaldata that we're in define property.
2575 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2576 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2577 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2578 DefineOwnPropertyScope scope(exec);
2579
2580 // If we have a new property we can just put it on normally
2581 PropertyDescriptor current;
2582 if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
2583 // unless extensions are prevented!
2584 if (!isExtensible()) {
2585 if (throwException)
2586 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible.")));
2587 return false;
2588 }
2589 PropertyDescriptor oldDescriptor;
2590 oldDescriptor.setValue(jsUndefined());
2591 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
2592 }
2593
2594 if (descriptor.isEmpty())
2595 return true;
2596
2597 if (current.equalTo(exec, descriptor))
2598 return true;
2599
2600 // Filter out invalid changes
2601 if (!current.configurable()) {
2602 if (descriptor.configurable()) {
2603 if (throwException)
2604 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property.")));
2605 return false;
2606 }
2607 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
2608 if (throwException)
2609 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
2610 return false;
2611 }
2612 }
2613
2614 // A generic descriptor is simply changing the attributes of an existing property
2615 if (descriptor.isGenericDescriptor()) {
2616 if (!current.attributesEqual(descriptor)) {
2617 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
2618 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2619 }
2620 return true;
2621 }
2622
2623 // Changing between a normal property or an accessor property
2624 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
2625 if (!current.configurable()) {
2626 if (throwException)
2627 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2628 return false;
2629 }
2630 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
2631 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2632 }
2633
2634 // Changing the value and attributes of an existing property
2635 if (descriptor.isDataDescriptor()) {
2636 if (!current.configurable()) {
2637 if (!current.writable() && descriptor.writable()) {
2638 if (throwException)
2639 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
2640 return false;
2641 }
2642 if (!current.writable()) {
2643 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) {
2644 if (throwException)
2645 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property.")));
2646 return false;
2647 }
2648 }
2649 }
2650 if (current.attributesEqual(descriptor) && !descriptor.value())
2651 return true;
2652 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
2653 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2654 }
2655
2656 // Changing the accessor functions of an existing accessor property
2657 ASSERT(descriptor.isAccessorDescriptor());
2658 if (!current.configurable()) {
2659 if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
2660 if (throwException)
2661 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
2662 return false;
2663 }
2664 if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
2665 if (throwException)
2666 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2667 return false;
2668 }
2669 if (current.attributes() & CustomAccessor) {
2670 if (throwException)
2671 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
2672 return false;
2673 }
2674 }
2675 JSValue accessor = getDirect(exec->vm(), propertyName);
2676 if (!accessor)
2677 return false;
2678 GetterSetter* getterSetter;
2679 bool getterSetterChanged = false;
2680 if (accessor.isCustomGetterSetter())
2681 getterSetter = GetterSetter::create(exec->vm(), exec->lexicalGlobalObject());
2682 else {
2683 ASSERT(accessor.isGetterSetter());
2684 getterSetter = asGetterSetter(accessor);
2685 }
2686 if (descriptor.setterPresent()) {
2687 getterSetter = getterSetter->withSetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.setterObject());
2688 getterSetterChanged = true;
2689 }
2690 if (descriptor.getterPresent()) {
2691 getterSetter = getterSetter->withGetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.getterObject());
2692 getterSetterChanged = true;
2693 }
2694 if (current.attributesEqual(descriptor) && !getterSetterChanged)
2695 return true;
2696 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
2697 unsigned attrs = descriptor.attributesOverridingCurrent(current);
2698 putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
2699 return true;
2700}
2701
2702bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
2703{
2704 // If it's an array index, then use the indexed property storage.
2705 if (Optional<uint32_t> index = parseIndex(propertyName)) {
2706 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
2707 // d. Reject if succeeded is false.
2708 // e. If index >= oldLen
2709 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2710 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
2711 // f. Return true.
2712 return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
2713 }
2714
2715 return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
2716}
2717
2718JSObject* throwTypeError(ExecState* exec, const String& message)
2719{
2720 return exec->vm().throwException(exec, createTypeError(exec, message));
2721}
2722
2723void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter)
2724{
2725 Butterfly* butterfly = this->butterfly();
2726 size_t preCapacity = this->butterflyPreCapacity();
2727 void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter);
2728 void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore);
2729
2730 memmove(newBase, currentBase, this->butterflyTotalSize());
2731 setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter));
2732}
2733
2734uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
2735{
2736 VM& vm = exec->vm();
2737 Structure* structure = object->structure(vm);
2738 if (structure->holesMustForwardToPrototype(vm))
2739 return 0;
2740 switch (object->indexingType()) {
2741 case ALL_BLANK_INDEXING_TYPES:
2742 case ALL_UNDECIDED_INDEXING_TYPES:
2743 return 0;
2744
2745 case ALL_INT32_INDEXING_TYPES:
2746 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2747 Butterfly* butterfly = object->butterfly();
2748 unsigned usedLength = butterfly->publicLength();
2749 for (unsigned i = 0; i < usedLength; ++i) {
2750 if (!butterfly->contiguous()[i])
2751 return 0;
2752 }
2753 return usedLength;
2754 }
2755
2756 case ALL_DOUBLE_INDEXING_TYPES: {
2757 Butterfly* butterfly = object->butterfly();
2758 unsigned usedLength = butterfly->publicLength();
2759 for (unsigned i = 0; i < usedLength; ++i) {
2760 double value = butterfly->contiguousDouble()[i];
2761 if (value != value)
2762 return 0;
2763 }
2764 return usedLength;
2765 }
2766
2767 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2768 ArrayStorage* storage = object->m_butterfly->arrayStorage();
2769 if (storage->m_sparseMap.get())
2770 return 0;
2771
2772 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
2773 for (unsigned i = 0; i < usedVectorLength; ++i) {
2774 if (!storage->m_vector[i])
2775 return 0;
2776 }
2777 return usedVectorLength;
2778 }
2779
2780 default:
2781 RELEASE_ASSERT_NOT_REACHED();
2782 return 0;
2783 }
2784}
2785
2786void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2787{
2788 VM& vm = exec->vm();
2789 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
2790}
2791
2792void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2793{
2794 VM& vm = exec->vm();
2795 object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude));
2796
2797 if (object->prototype().isNull())
2798 return;
2799
2800 JSObject* prototype = asObject(object->prototype());
2801 while (true) {
2802 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
2803 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
2804 break;
2805 }
2806 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
2807 JSValue nextProto = prototype->prototype();
2808 if (nextProto.isNull())
2809 break;
2810 prototype = asObject(nextProto);
2811 }
2812}
2813
2814} // namespace JSC