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