]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSObject.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
93a37866 4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
9dae56ea
A
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
93a37866
A
27#include "ButterflyInlines.h"
28#include "CopiedSpaceInlines.h"
29#include "CopyVisitor.h"
30#include "CopyVisitorInlines.h"
81345200 31#include "CustomGetterSetter.h"
9dae56ea
A
32#include "DatePrototype.h"
33#include "ErrorConstructor.h"
93a37866 34#include "Executable.h"
9dae56ea 35#include "GetterSetter.h"
93a37866 36#include "IndexingHeaderInlines.h"
14957cd0 37#include "JSFunction.h"
9dae56ea 38#include "JSGlobalObject.h"
6fe7ccc8 39#include "Lookup.h"
9dae56ea 40#include "NativeErrorConstructor.h"
6fe7ccc8 41#include "Nodes.h"
9dae56ea 42#include "ObjectPrototype.h"
81345200 43#include "JSCInlines.h"
f9bf01c6 44#include "PropertyDescriptor.h"
9dae56ea 45#include "PropertyNameArray.h"
93a37866
A
46#include "Reject.h"
47#include "SlotVisitorInlines.h"
9dae56ea
A
48#include <math.h>
49#include <wtf/Assertions.h>
50
9dae56ea
A
51namespace JSC {
52
93a37866
A
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.
57static unsigned lastArraySize = 0;
58
59JSCell* getCallableObjectSlow(JSCell* cell)
60{
81345200 61 if (cell->type() == JSFunctionType)
93a37866 62 return cell;
81345200 63 if (cell->structure()->classInfo()->isSubClassOf(InternalFunction::info()))
93a37866
A
64 return cell;
65 return 0;
66}
14957cd0 67
81345200
A
68STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
69STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
6fe7ccc8 70
14957cd0
A
71const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
72
6fe7ccc8
A
73const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) };
74
75const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) };
9dae56ea 76
93a37866 77static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify)
f9bf01c6 78{
81345200
A
79 VM& vm = exec->vm();
80
f9bf01c6
A
81 // Add properties from the static hashtables of properties
82 for (; classInfo; classInfo = classInfo->parentClass) {
81345200 83 const HashTable* table = classInfo->propHashTable(vm);
f9bf01c6
A
84 if (!table)
85 continue;
81345200
A
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()));
f9bf01c6
A
90 }
91 }
92}
9dae56ea 93
93a37866
A
94ALWAYS_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;
81345200 103 bool hasIndexingHeader = this->hasIndexingHeader();
93a37866
A
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
81345200 130 switch (this->indexingType()) {
93a37866
A
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 }
81345200 149
93a37866 150 default:
93a37866
A
151 currentTarget = 0;
152 currentSource = 0;
153 count = 0;
154 break;
155 }
156
157 memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue));
158 }
159
81345200 160 m_butterfly.setWithoutWriteBarrier(newButterfly);
93a37866
A
161 visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
162 }
163}
164
165ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize)
166{
167 ASSERT(butterfly);
168
81345200 169 Structure* structure = this->structure(visitor.vm());
93a37866
A
170
171 size_t propertyCapacity = structure->outOfLineCapacity();
172 size_t preCapacity;
173 size_t indexingPayloadSizeInBytes;
81345200 174 bool hasIndexingHeader = this->hasIndexingHeader();
93a37866
A
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);
81345200
A
186 visitor.copyLater(
187 this, ButterflyCopyToken,
188 butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
93a37866
A
189
190 // Mark the array if appropriate.
81345200 191 switch (this->indexingType()) {
93a37866
A
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
6fe7ccc8 205void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
f9bf01c6 206{
6fe7ccc8 207 JSObject* thisObject = jsCast<JSObject*>(cell);
81345200 208 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
6fe7ccc8 209#if !ASSERT_DISABLED
14957cd0
A
210 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
211 visitor.m_isCheckingForDefaultMarkViolation = false;
f9bf01c6 212#endif
93a37866
A
213
214 JSCell::visitChildren(thisObject, visitor);
215
216 Butterfly* butterfly = thisObject->butterfly();
217 if (butterfly)
81345200 218 thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm())->outOfLineSize());
93a37866
A
219
220#if !ASSERT_DISABLED
221 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
222#endif
223}
ba379fdc 224
81345200 225void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
93a37866
A
226{
227 JSObject* thisObject = jsCast<JSObject*>(cell);
81345200
A
228 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
229
230 if (token != ButterflyCopyToken)
231 return;
93a37866
A
232
233 Butterfly* butterfly = thisObject->butterfly();
234 if (butterfly)
235 thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize());
236}
237
238void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
239{
240 JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
81345200 241 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
93a37866
A
242#if !ASSERT_DISABLED
243 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
244 visitor.m_isCheckingForDefaultMarkViolation = false;
245#endif
246
6fe7ccc8
A
247 JSCell::visitChildren(thisObject, visitor);
248
81345200 249 Structure* structure = thisObject->structure();
93a37866
A
250 Butterfly* butterfly = thisObject->butterfly();
251 if (butterfly)
81345200 252 thisObject->visitButterfly(visitor, butterfly, structure->outOfLineSize());
6fe7ccc8 253
81345200 254 size_t storageSize = structure->inlineSize();
93a37866 255 visitor.appendValues(thisObject->inlineStorage(), storageSize);
9dae56ea 256
6fe7ccc8 257#if !ASSERT_DISABLED
14957cd0 258 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
f9bf01c6 259#endif
9dae56ea
A
260}
261
93a37866 262String JSObject::className(const JSObject* object)
9dae56ea 263{
6fe7ccc8 264 const ClassInfo* info = object->classInfo();
14957cd0
A
265 ASSERT(info);
266 return info->className;
9dae56ea
A
267}
268
81345200 269bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
9dae56ea 270{
93a37866
A
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
93a37866 275 if (i > MAX_ARRAY_INDEX)
81345200 276 return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
93a37866 277
81345200 278 switch (thisObject->indexingType()) {
93a37866
A
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: {
81345200 285 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
286 if (i >= butterfly->vectorLength())
287 return false;
288
289 JSValue value = butterfly->contiguous()[i].get();
290 if (value) {
81345200 291 slot.setValue(thisObject, None, value);
93a37866
A
292 return true;
293 }
294
295 return false;
296 }
297
298 case ALL_DOUBLE_INDEXING_TYPES: {
81345200 299 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
300 if (i >= butterfly->vectorLength())
301 return false;
302
303 double value = butterfly->contiguousDouble()[i];
304 if (value == value) {
81345200 305 slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value));
93a37866
A
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) {
81345200 320 slot.setValue(thisObject, None, value);
93a37866
A
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()) {
81345200 326 it->value.get(thisObject, slot);
93a37866
A
327 return true;
328 }
329 }
330 break;
331 }
332
333 default:
334 RELEASE_ASSERT_NOT_REACHED();
335 break;
336 }
337
338 return false;
9dae56ea
A
339}
340
341// ECMA 8.6.2.2
93a37866 342void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
9dae56ea 343{
6fe7ccc8 344 JSObject* thisObject = jsCast<JSObject*>(cell);
9dae56ea 345 ASSERT(value);
6fe7ccc8 346 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
93a37866
A
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
9dae56ea 357 // Check if there are any setters or getters in the prototype chain
ba379fdc 358 JSValue prototype;
6fe7ccc8 359 if (propertyName != exec->propertyNames().underscoreProto) {
81345200 360 for (JSObject* obj = thisObject; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
6fe7ccc8
A
361 prototype = obj->prototype();
362 if (prototype.isNull()) {
81345200 363 ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
93a37866
A
364 if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value))
365 && slot.isStrictMode())
366 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
6fe7ccc8
A
367 return;
368 }
9dae56ea
A
369 }
370 }
9dae56ea 371
93a37866
A
372 JSObject* obj;
373 for (obj = thisObject; ; obj = asObject(prototype)) {
6fe7ccc8
A
374 unsigned attributes;
375 JSCell* specificValue;
81345200 376 PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes, specificValue);
93a37866 377 if (isValidOffset(offset)) {
6fe7ccc8 378 if (attributes & ReadOnly) {
81345200 379 ASSERT(thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
6fe7ccc8 380 if (slot.isStrictMode())
81345200 381 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)));
6fe7ccc8
A
382 return;
383 }
384
93a37866 385 JSValue gs = obj->getDirect(offset);
9dae56ea 386 if (gs.isGetterSetter()) {
81345200
A
387 callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
388 if (!thisObject->structure()->isDictionary())
389 slot.setCacheableSetter(obj, offset);
9dae56ea 390 return;
81345200
A
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));
9dae56ea
A
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 }
81345200
A
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 }
9dae56ea
A
410 prototype = obj->prototype();
411 if (prototype.isNull())
412 break;
413 }
6fe7ccc8 414
81345200 415 ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
93a37866
A
416 if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode())
417 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
9dae56ea
A
418 return;
419}
420
6fe7ccc8 421void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
9dae56ea 422{
6fe7ccc8 423 JSObject* thisObject = jsCast<JSObject*>(cell);
93a37866
A
424
425 if (propertyName > MAX_ARRAY_INDEX) {
81345200 426 PutPropertySlot slot(cell, shouldThrow);
93a37866
A
427 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
428 return;
429 }
430
81345200 431 switch (thisObject->indexingType()) {
93a37866
A
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 }
81345200 448 FALLTHROUGH;
93a37866
A
449 }
450
451 case ALL_CONTIGUOUS_INDEXING_TYPES: {
81345200 452 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
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 }
81345200 475 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
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
540ArrayStorage* 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, this, value);
559 }
560
81345200
A
561 DeferGC deferGC(vm.heap);
562 Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
93a37866 563 RELEASE_ASSERT(newButterfly);
93a37866
A
564 newButterfly->arrayStorage()->m_indexBias = 0;
565 newButterfly->arrayStorage()->setVectorLength(0);
566 newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
81345200 567 setButterflyWithoutChangingStructure(vm, newButterfly);
93a37866
A
568
569 return newButterfly->arrayStorage();
570}
571
572void JSObject::enterDictionaryIndexingMode(VM& vm)
573{
81345200 574 switch (indexingType()) {
93a37866
A
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
593void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
594{
595 if (mayInterceptIndexedAccesses())
596 return;
597
81345200 598 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors));
93a37866
A
599
600 if (!vm.prototypeMap.isPrototype(this))
601 return;
602
603 globalObject()->haveABadTime(vm);
604}
605
606Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize)
607{
608 ASSERT(length < MAX_ARRAY_INDEX);
81345200 609 IndexingType oldType = indexingType();
93a37866
A
610 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
611 ASSERT(!structure()->needsSlowPutIndexing());
612 ASSERT(!indexingShouldBeSparse());
613 unsigned vectorLength = std::max(length, BASE_VECTOR_LEN);
81345200
A
614 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
615 m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0,
93a37866
A
616 elementSize * vectorLength);
617 newButterfly->setPublicLength(length);
618 newButterfly->setVectorLength(vectorLength);
619 return newButterfly;
620}
621
622Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
623{
81345200 624 DeferGC deferGC(vm.heap);
93a37866 625 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
81345200
A
626 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided);
627 setStructureAndButterfly(vm, newStructure, newButterfly);
93a37866
A
628 return newButterfly;
629}
630
631ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
632{
81345200 633 DeferGC deferGC(vm.heap);
93a37866 634 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
81345200
A
635 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32);
636 setStructureAndButterfly(vm, newStructure, newButterfly);
93a37866
A
637 return newButterfly->contiguousInt32();
638}
639
640ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
641{
81345200 642 DeferGC deferGC(vm.heap);
93a37866
A
643 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
644 for (unsigned i = newButterfly->vectorLength(); i--;)
81345200
A
645 newButterfly->contiguousDouble()[i] = PNaN;
646 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);
647 setStructureAndButterfly(vm, newStructure, newButterfly);
93a37866
A
648 return newButterfly->contiguousDouble();
649}
650
651ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
652{
81345200 653 DeferGC deferGC(vm.heap);
93a37866 654 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
81345200
A
655 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous);
656 setStructureAndButterfly(vm, newStructure, newButterfly);
93a37866
A
657 return newButterfly->contiguous();
658}
659
660ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
661{
81345200
A
662 DeferGC deferGC(vm.heap);
663 Structure* structure = this->structure(vm);
664 IndexingType oldType = indexingType();
93a37866 665 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
81345200
A
666 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
667 m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0,
93a37866
A
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;
81345200
A
677 Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition());
678 setStructureAndButterfly(vm, newStructure, newButterfly);
93a37866
A
679 return result;
680}
681
682ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
683{
684 return createArrayStorage(vm, 0, BASE_VECTOR_LEN);
685}
686
687ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
688{
81345200
A
689 ASSERT(hasUndecided(indexingType()));
690 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32));
93a37866
A
691 return m_butterfly->contiguousInt32();
692}
693
694ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
695{
81345200 696 ASSERT(hasUndecided(indexingType()));
93a37866
A
697
698 for (unsigned i = m_butterfly->vectorLength(); i--;)
81345200 699 m_butterfly->contiguousDouble()[i] = PNaN;
93a37866 700
81345200 701 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
93a37866
A
702 return m_butterfly->contiguousDouble();
703}
704
705ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
706{
81345200
A
707 ASSERT(hasUndecided(indexingType()));
708 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
93a37866
A
709 return m_butterfly->contiguous();
710}
711
712ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
713{
81345200 714 Structure* structure = this->structure(vm);
93a37866 715 unsigned publicLength = m_butterfly->publicLength();
81345200
A
716 unsigned propertyCapacity = structure->outOfLineCapacity();
717 unsigned propertySize = structure->outOfLineSize();
93a37866
A
718
719 Butterfly* newButterfly = Butterfly::createUninitialized(
81345200 720 vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
93a37866
A
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
737ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
738{
81345200
A
739 DeferGC deferGC(vm.heap);
740 ASSERT(hasUndecided(indexingType()));
93a37866
A
741
742 ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
743 // No need to copy elements.
744
81345200
A
745 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
746 setStructureAndButterfly(vm, newStructure, storage->butterfly());
93a37866
A
747 return storage;
748}
749
750ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
751{
752 return convertUndecidedToArrayStorage(vm, transition, m_butterfly->vectorLength());
753}
754
755ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
756{
81345200 757 return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
9dae56ea
A
758}
759
93a37866
A
760ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
761{
81345200 762 ASSERT(hasInt32(indexingType()));
93a37866
A
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) {
81345200 769 *currentAsDouble = PNaN;
93a37866
A
770 continue;
771 }
772 ASSERT(v.isInt32());
773 *currentAsDouble = v.asInt32();
774 }
775
81345200 776 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
93a37866
A
777 return m_butterfly->contiguousDouble();
778}
779
780ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
781{
81345200 782 ASSERT(hasInt32(indexingType()));
93a37866 783
81345200 784 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
93a37866
A
785 return m_butterfly->contiguous();
786}
787
788ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
789{
81345200 790 ASSERT(hasInt32(indexingType()));
93a37866 791
81345200 792 DeferGC deferGC(vm.heap);
93a37866
A
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
81345200
A
802 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
803 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
93a37866
A
804 return newStorage;
805}
806
807ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
808{
809 return convertInt32ToArrayStorage(vm, transition, m_butterfly->vectorLength());
810}
811
812ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
813{
81345200 814 return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
93a37866
A
815}
816
817template<JSObject::DoubleToContiguousMode mode>
818ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
819{
81345200 820 ASSERT(hasDouble(indexingType()));
93a37866
A
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;
81345200
A
838 default:
839 v = JSValue();
840 RELEASE_ASSERT_NOT_REACHED();
841 break;
93a37866
A
842 }
843 ASSERT(v.isNumber());
844 currentAsValue->setWithoutWriteBarrier(v);
845 }
846
81345200 847 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
93a37866
A
848 return m_butterfly->contiguous();
849}
850
851ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
852{
853 return genericConvertDoubleToContiguous<EncodeValueAsDouble>(vm);
854}
855
856ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm)
857{
858 return genericConvertDoubleToContiguous<RageConvertDoubleToValue>(vm);
859}
860
861ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
862{
81345200
A
863 DeferGC deferGC(vm.heap);
864 ASSERT(hasDouble(indexingType()));
93a37866
A
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
81345200
A
875 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
876 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
93a37866
A
877 return newStorage;
878}
879
880ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
881{
882 return convertDoubleToArrayStorage(vm, transition, m_butterfly->vectorLength());
883}
884
885ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
886{
81345200 887 return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
93a37866
A
888}
889
890ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
891{
81345200
A
892 DeferGC deferGC(vm.heap);
893 ASSERT(hasContiguous(indexingType()));
93a37866
A
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
81345200
A
904 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
905 setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
93a37866
A
906 return newStorage;
907}
908
909ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
910{
911 return convertContiguousToArrayStorage(vm, transition, m_butterfly->vectorLength());
912}
913
914ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
915{
81345200 916 return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
93a37866
A
917}
918
919void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
920{
921 if (value.isInt32()) {
922 convertUndecidedToInt32(vm);
923 return;
924 }
925
81345200 926 if (value.isDouble() && value.asNumber() == value.asNumber()) {
93a37866
A
927 convertUndecidedToDouble(vm);
928 return;
929 }
930
931 convertUndecidedToContiguous(vm);
932}
933
81345200
A
934void 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
93a37866
A
952void 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
964void 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
972void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
973{
974 ASSERT(!value.isInt32());
975 convertInt32ForValue(vm, value);
976 setIndexQuickly(vm, index, value);
977}
978
979void 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
986ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
987{
81345200 988 ASSERT(inherits(info()));
93a37866 989
81345200 990 switch (indexingType()) {
93a37866 991 case ALL_BLANK_INDEXING_TYPES:
81345200 992 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
93a37866
A
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
1010ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
1011{
81345200 1012 ASSERT(inherits(info()));
93a37866 1013
81345200 1014 switch (indexingType()) {
93a37866 1015 case ALL_BLANK_INDEXING_TYPES:
81345200 1016 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
93a37866
A
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
1036ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm, DoubleToContiguousMode mode)
1037{
81345200 1038 ASSERT(inherits(info()));
93a37866 1039
81345200 1040 switch (indexingType()) {
93a37866 1041 case ALL_BLANK_INDEXING_TYPES:
81345200 1042 if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
93a37866
A
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
1066ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
1067{
1068 return ensureContiguousSlow(vm, EncodeValueAsDouble);
1069}
1070
1071ContiguousJSValues JSObject::rageEnsureContiguousSlow(VM& vm)
1072{
1073 return ensureContiguousSlow(vm, RageConvertDoubleToValue);
1074}
1075
1076ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1077{
81345200 1078 ASSERT(inherits(info()));
93a37866 1079
81345200 1080 switch (indexingType()) {
93a37866
A
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());
81345200 1088 ASSERT(!structure(vm)->needsSlowPutIndexing());
93a37866
A
1089 return convertUndecidedToArrayStorage(vm);
1090
1091 case ALL_INT32_INDEXING_TYPES:
1092 ASSERT(!indexingShouldBeSparse());
81345200 1093 ASSERT(!structure(vm)->needsSlowPutIndexing());
93a37866
A
1094 return convertInt32ToArrayStorage(vm);
1095
1096 case ALL_DOUBLE_INDEXING_TYPES:
1097 ASSERT(!indexingShouldBeSparse());
81345200 1098 ASSERT(!structure(vm)->needsSlowPutIndexing());
93a37866
A
1099 return convertDoubleToArrayStorage(vm);
1100
1101 case ALL_CONTIGUOUS_INDEXING_TYPES:
1102 ASSERT(!indexingShouldBeSparse());
81345200 1103 ASSERT(!structure(vm)->needsSlowPutIndexing());
93a37866
A
1104 return convertContiguousToArrayStorage(vm);
1105
1106 default:
1107 RELEASE_ASSERT_NOT_REACHED();
1108 return 0;
1109 }
1110}
1111
1112ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1113{
81345200 1114 switch (indexingType()) {
93a37866
A
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
1143void JSObject::switchToSlowPutArrayStorage(VM& vm)
1144{
81345200 1145 switch (indexingType()) {
93a37866
A
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: {
81345200
A
1164 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage);
1165 setStructure(vm, newStructure);
93a37866
A
1166 break;
1167 }
1168
1169 default:
1170 CRASH();
1171 break;
1172 }
1173}
1174
93a37866
A
1175void JSObject::setPrototype(VM& vm, JSValue prototype)
1176{
1177 ASSERT(prototype);
1178 if (prototype.isObject())
1179 vm.prototypeMap.addPrototype(asObject(prototype));
1180
81345200
A
1181 Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype);
1182 setStructure(vm, newStructure);
93a37866
A
1183
1184 if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
1185 return;
1186
1187 if (vm.prototypeMap.isPrototype(this)) {
1188 newStructure->globalObject()->haveABadTime(vm);
1189 return;
1190 }
1191
81345200 1192 if (!hasIndexedProperties(indexingType()))
93a37866
A
1193 return;
1194
81345200 1195 if (shouldUseSlowPut(indexingType()))
93a37866
A
1196 return;
1197
1198 switchToSlowPutArrayStorage(vm);
1199}
1200
81345200 1201bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype)
14957cd0 1202{
81345200 1203 ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this);
6fe7ccc8
A
1204 JSValue nextPrototype = prototype;
1205 while (nextPrototype && nextPrototype.isObject()) {
81345200 1206 if (nextPrototype == this)
6fe7ccc8
A
1207 return false;
1208 nextPrototype = asObject(nextPrototype)->prototype();
1209 }
81345200 1210 setPrototype(exec->vm(), prototype);
6fe7ccc8 1211 return true;
14957cd0
A
1212}
1213
6fe7ccc8 1214bool JSObject::allowsAccessFrom(ExecState* exec)
9dae56ea 1215{
93a37866 1216 JSGlobalObject* globalObject = this->globalObject();
6fe7ccc8 1217 return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
9dae56ea
A
1218}
1219
93a37866 1220void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
ba379fdc 1221{
6fe7ccc8 1222 ASSERT(value.isGetterSetter() && (attributes & Accessor));
ba379fdc 1223
93a37866
A
1224 unsigned index = propertyName.asIndex();
1225 if (index != PropertyName::NotAnIndex) {
1226 putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect);
1227 return;
1228 }
1229
81345200
A
1230 putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
1231}
93a37866 1232
81345200
A
1233void 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
1248void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1249{
1250 PutPropertySlot slot(this);
93a37866 1251 putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value));
6fe7ccc8
A
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)
81345200 1257 setStructure(vm, Structure::attributeChangeTransition(vm, structure(vm), propertyName, attributes));
6fe7ccc8 1258
81345200 1259 Structure* structure = this->structure(vm);
6fe7ccc8 1260 if (attributes & ReadOnly)
81345200 1261 structure->setContainsReadOnlyProperties();
6fe7ccc8 1262
81345200 1263 structure->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
9dae56ea
A
1264}
1265
93a37866 1266bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
9dae56ea 1267{
81345200 1268 PropertySlot slot(this);
9dae56ea
A
1269 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1270}
1271
1272bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1273{
81345200 1274 PropertySlot slot(this);
9dae56ea
A
1275 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1276}
1277
1278// ECMA 8.6.2.5
93a37866 1279bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
9dae56ea 1280{
6fe7ccc8 1281 JSObject* thisObject = jsCast<JSObject*>(cell);
93a37866
A
1282
1283 unsigned i = propertyName.asIndex();
1284 if (i != PropertyName::NotAnIndex)
81345200 1285 return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, i);
6fe7ccc8
A
1286
1287 if (!thisObject->staticFunctionsReified())
1288 thisObject->reifyStaticFunctionsForDelete(exec);
1289
9dae56ea 1290 unsigned attributes;
ba379fdc 1291 JSCell* specificValue;
81345200
A
1292 VM& vm = exec->vm();
1293 if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes, specificValue))) {
1294 if (attributes & DontDelete && !vm.isInDefineOwnProperty())
9dae56ea 1295 return false;
81345200 1296 thisObject->removeDirect(vm, propertyName);
9dae56ea
A
1297 return true;
1298 }
1299
1300 // Look in the static hashtable of properties
81345200 1301 const HashTableValue* entry = thisObject->findPropertyHashEntry(vm, propertyName);
93a37866 1302 if (entry) {
81345200 1303 if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty())
93a37866
A
1304 return false; // this builtin property can't be deleted
1305
81345200
A
1306 PutPropertySlot slot(thisObject);
1307 putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot);
93a37866 1308 }
9dae56ea 1309
9dae56ea
A
1310 return true;
1311}
1312
93a37866 1313bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
9dae56ea 1314{
81345200
A
1315 PropertySlot slot(this);
1316 return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
9dae56ea
A
1317}
1318
93a37866 1319bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
9dae56ea 1320{
6fe7ccc8 1321 JSObject* thisObject = jsCast<JSObject*>(cell);
93a37866
A
1322
1323 if (i > MAX_ARRAY_INDEX)
81345200 1324 return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
93a37866 1325
81345200 1326 switch (thisObject->indexingType()) {
93a37866
A
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: {
81345200 1333 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
1334 if (i >= butterfly->vectorLength())
1335 return true;
1336 butterfly->contiguous()[i].clear();
1337 return true;
1338 }
1339
1340 case ALL_DOUBLE_INDEXING_TYPES: {
81345200 1341 Butterfly* butterfly = thisObject->butterfly();
93a37866
A
1342 if (i >= butterfly->vectorLength())
1343 return true;
81345200 1344 butterfly->contiguousDouble()[i] = PNaN;
93a37866
A
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 }
9dae56ea
A
1373}
1374
93a37866 1375static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName)
9dae56ea 1376{
ba379fdc 1377 JSValue function = object->get(exec, propertyName);
9dae56ea 1378 CallData callData;
14957cd0 1379 CallType callType = getCallData(function, callData);
9dae56ea
A
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
ba379fdc 1388 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
9dae56ea
A
1389 ASSERT(!result.isGetterSetter());
1390 if (exec->hadException())
1391 return exec->exception();
1392 if (result.isObject())
ba379fdc 1393 return JSValue();
9dae56ea
A
1394 return result;
1395}
1396
6fe7ccc8 1397bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
9dae56ea 1398{
81345200 1399 result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber);
9dae56ea
A
1400 number = result.toNumber(exec);
1401 return !result.isString();
1402}
1403
1404// ECMA 8.6.2.6
6fe7ccc8 1405JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
9dae56ea
A
1406{
1407 // Must call toString first for Date objects.
6fe7ccc8
A
1408 if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
1409 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
9dae56ea
A
1410 if (value)
1411 return value;
6fe7ccc8 1412 value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
9dae56ea
A
1413 if (value)
1414 return value;
1415 } else {
6fe7ccc8 1416 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
9dae56ea
A
1417 if (value)
1418 return value;
6fe7ccc8 1419 value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
9dae56ea
A
1420 if (value)
1421 return value;
1422 }
1423
1424 ASSERT(!exec->hadException());
1425
81345200 1426 return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value")));
9dae56ea
A
1427}
1428
81345200 1429const HashTableValue* JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const
9dae56ea
A
1430{
1431 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
81345200
A
1432 if (const HashTable* propHashTable = info->propHashTable(vm)) {
1433 if (const HashTableValue* entry = propHashTable->entry(vm, propertyName))
9dae56ea
A
1434 return entry;
1435 }
1436 }
1437 return 0;
1438}
1439
93a37866
A
1440bool JSObject::hasInstance(ExecState* exec, JSValue value)
1441{
81345200
A
1442 VM& vm = exec->vm();
1443 TypeInfo info = structure(vm)->typeInfo();
93a37866
A
1444 if (info.implementsDefaultHasInstance())
1445 return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
1446 if (info.implementsHasInstance())
81345200
A
1447 return methodTable(vm)->customHasInstance(this, exec, value);
1448 vm.throwException(exec, createInvalidParameterError(exec, "instanceof" , this));
93a37866
A
1449 return false;
1450}
1451
1452bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
9dae56ea 1453{
ba379fdc
A
1454 if (!value.isObject())
1455 return false;
1456
9dae56ea 1457 if (!proto.isObject()) {
81345200 1458 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
9dae56ea
A
1459 return false;
1460 }
1461
9dae56ea
A
1462 JSObject* object = asObject(value);
1463 while ((object = object->prototype().getObject())) {
1464 if (proto == object)
1465 return true;
1466 }
1467 return false;
1468}
1469
93a37866 1470bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const
ba379fdc 1471{
81345200 1472 VM& vm = exec->vm();
ba379fdc 1473 unsigned attributes;
81345200 1474 if (isValidOffset(structure(vm)->get(vm, propertyName, attributes, specificValue)))
ba379fdc
A
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
6fe7ccc8 1484void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
9dae56ea 1485{
93a37866 1486 propertyNames.setBaseObject(object);
81345200 1487 object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode);
f9bf01c6 1488
6fe7ccc8 1489 if (object->prototype().isNull())
f9bf01c6
A
1490 return;
1491
81345200 1492 VM& vm = exec->vm();
6fe7ccc8 1493 JSObject* prototype = asObject(object->prototype());
f9bf01c6 1494 while(1) {
81345200
A
1495 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
1496 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
f9bf01c6
A
1497 break;
1498 }
81345200 1499 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
f9bf01c6
A
1500 JSValue nextProto = prototype->prototype();
1501 if (nextProto.isNull())
1502 break;
1503 prototype = asObject(nextProto);
1504 }
1505}
1506
93a37866
A
1507void 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.
81345200 1513 switch (object->indexingType()) {
93a37866
A
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: {
81345200 1520 Butterfly* butterfly = object->butterfly();
93a37866
A
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: {
81345200 1531 Butterfly* butterfly = object->butterfly();
93a37866
A
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
81345200 1572 object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
93a37866
A
1573}
1574
1575void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
f9bf01c6 1576{
93a37866 1577 getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
9dae56ea 1578
81345200 1579 VM& vm = exec->vm();
93a37866 1580 bool canCachePropertiesFromStructure = !propertyNames.size();
81345200 1581 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
93a37866
A
1582
1583 if (canCachePropertiesFromStructure)
1584 propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size());
9dae56ea
A
1585}
1586
1587double JSObject::toNumber(ExecState* exec) const
1588{
ba379fdc 1589 JSValue primitive = toPrimitive(exec, PreferNumber);
9dae56ea
A
1590 if (exec->hadException()) // should be picked up soon in Nodes.cpp
1591 return 0.0;
1592 return primitive.toNumber(exec);
1593}
1594
6fe7ccc8 1595JSString* JSObject::toString(ExecState* exec) const
9dae56ea 1596{
ba379fdc 1597 JSValue primitive = toPrimitive(exec, PreferString);
9dae56ea 1598 if (exec->hadException())
6fe7ccc8 1599 return jsEmptyString(exec);
9dae56ea
A
1600 return primitive.toString(exec);
1601}
1602
81345200 1603JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
14957cd0 1604{
6fe7ccc8 1605 return jsCast<JSObject*>(cell);
14957cd0
A
1606}
1607
93a37866 1608void JSObject::seal(VM& vm)
14957cd0 1609{
93a37866 1610 if (isSealed(vm))
14957cd0 1611 return;
93a37866 1612 preventExtensions(vm);
81345200 1613 setStructure(vm, Structure::sealTransition(vm, structure(vm)));
14957cd0
A
1614}
1615
93a37866 1616void JSObject::freeze(VM& vm)
14957cd0 1617{
93a37866 1618 if (isFrozen(vm))
14957cd0 1619 return;
93a37866 1620 preventExtensions(vm);
81345200 1621 setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
14957cd0
A
1622}
1623
93a37866 1624void JSObject::preventExtensions(VM& vm)
14957cd0 1625{
93a37866 1626 enterDictionaryIndexingMode(vm);
14957cd0 1627 if (isExtensible())
81345200 1628 setStructure(vm, Structure::preventExtensionsTransition(vm, structure(vm)));
6fe7ccc8
A
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.
1633void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
1634{
1635 ASSERT(!staticFunctionsReified());
93a37866 1636 VM& vm = exec->vm();
6fe7ccc8
A
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()) {
81345200 1641 structure(vm)->setStaticFunctionsReified();
6fe7ccc8
A
1642 return;
1643 }
1644
81345200
A
1645 if (!structure(vm)->isUncacheableDictionary())
1646 setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm)));
6fe7ccc8
A
1647
1648 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
81345200 1649 const HashTable* hashTable = info->propHashTable(vm);
6fe7ccc8
A
1650 if (!hashTable)
1651 continue;
81345200
A
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);
6fe7ccc8
A
1656 }
1657 }
1658
81345200 1659 structure(vm)->setStaticFunctionsReified();
14957cd0
A
1660}
1661
93a37866 1662bool JSObject::removeDirect(VM& vm, PropertyName propertyName)
9dae56ea 1663{
81345200
A
1664 Structure* structure = this->structure(vm);
1665 if (!isValidOffset(structure->get(vm, propertyName)))
93a37866 1666 return false;
14957cd0 1667
93a37866 1668 PropertyOffset offset;
81345200
A
1669 if (structure->isUncacheableDictionary()) {
1670 offset = structure->removePropertyWithoutTransition(vm, propertyName);
93a37866
A
1671 if (offset == invalidOffset)
1672 return false;
1673 putDirectUndefined(offset);
1674 return true;
9dae56ea
A
1675 }
1676
81345200 1677 setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
93a37866
A
1678 if (offset == invalidOffset)
1679 return false;
1680 putDirectUndefined(offset);
1681 return true;
9dae56ea
A
1682}
1683
81345200 1684NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset)
14957cd0 1685{
81345200
A
1686 if (structure()->isDictionary()) {
1687 slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
1688 return;
1689 }
1690 slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
9dae56ea
A
1691}
1692
81345200 1693void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
9dae56ea 1694{
81345200
A
1695 VM& vm = exec->vm();
1696
93a37866
A
1697 if (descriptor.isDataDescriptor()) {
1698 if (descriptor.value())
81345200 1699 entryInMap->set(vm, this, descriptor.value());
93a37866 1700 else if (oldDescriptor.isAccessorDescriptor())
81345200 1701 entryInMap->set(vm, this, jsUndefined());
93a37866
A
1702 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
1703 return;
1704 }
1705
1706 if (descriptor.isAccessorDescriptor()) {
1707 JSObject* getter = 0;
1708 if (descriptor.getterPresent())
1709 getter = descriptor.getterObject();
1710 else if (oldDescriptor.isAccessorDescriptor())
1711 getter = oldDescriptor.getterObject();
1712 JSObject* setter = 0;
1713 if (descriptor.setterPresent())
1714 setter = descriptor.setterObject();
1715 else if (oldDescriptor.isAccessorDescriptor())
1716 setter = oldDescriptor.setterObject();
1717
81345200 1718 GetterSetter* accessor = GetterSetter::create(vm);
93a37866 1719 if (getter)
81345200 1720 accessor->setGetter(vm, getter);
93a37866 1721 if (setter)
81345200 1722 accessor->setSetter(vm, setter);
93a37866 1723
81345200 1724 entryInMap->set(vm, this, accessor);
93a37866
A
1725 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
1726 return;
1727 }
1728
1729 ASSERT(descriptor.isGenericDescriptor());
1730 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
9dae56ea
A
1731}
1732
93a37866 1733// Defined in ES5.1 8.12.9
81345200 1734bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
9dae56ea 1735{
93a37866
A
1736 ASSERT(index <= MAX_ARRAY_INDEX);
1737
1738 if (!inSparseIndexingMode()) {
1739 // Fast case: we're putting a regular property to a regular array
1740 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1741 // however if the property currently exists missing attributes will override from their current 'true'
1742 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1743 if (!descriptor.attributes()) {
1744 ASSERT(!descriptor.isAccessorDescriptor());
1745 return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
1746 }
1747
1748 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm());
1749 }
14957cd0 1750
93a37866
A
1751 if (descriptor.attributes() & (ReadOnly | Accessor))
1752 notifyPresenceOfIndexedAccessors(exec->vm());
14957cd0 1753
93a37866
A
1754 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
1755 RELEASE_ASSERT(map);
14957cd0 1756
93a37866
A
1757 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
1758 SparseArrayValueMap::AddResult result = map->add(this, index);
1759 SparseArrayEntry* entryInMap = &result.iterator->value;
1760
1761 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
1762 // 3. If current is undefined and extensible is false, then Reject.
1763 // 4. If current is undefined and extensible is true, then
1764 if (result.isNewEntry) {
1765 if (!isExtensible()) {
1766 map->remove(result.iterator);
1767 return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
1768 }
1769
1770 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
1771 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
1772 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
1773 // created property is set to its default value.
1774 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
1775 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
1776 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
1777 // is set to its default value.
1778 // 4.c. Return true.
1779
1780 PropertyDescriptor defaults;
1781 entryInMap->setWithoutWriteBarrier(jsUndefined());
1782 entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
1783 entryInMap->get(defaults);
1784
1785 putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
1786 if (index >= m_butterfly->arrayStorage()->length())
1787 m_butterfly->arrayStorage()->setLength(index + 1);
1788 return true;
1789 }
1790
1791 // 5. Return true, if every field in Desc is absent.
1792 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
1793 PropertyDescriptor current;
1794 entryInMap->get(current);
1795 if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
1796 return true;
1797
1798 // 7. If the [[Configurable]] field of current is false then
1799 if (!current.configurable()) {
1800 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
1801 if (descriptor.configurablePresent() && descriptor.configurable())
1802 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
1803 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
1804 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
1805 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
1806 }
1807
1808 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
1809 if (!descriptor.isGenericDescriptor()) {
1810 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
1811 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
1812 // 9.a. Reject, if the [[Configurable]] field of current is false.
1813 if (!current.configurable())
1814 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
1815 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
1816 // data property to an accessor property. Preserve the existing values of the converted property's
1817 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
1818 // their default values.
1819 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
1820 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
1821 // attributes and set the rest of the property's attributes to their default values.
1822 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
1823 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
1824 // 10.a. If the [[Configurable]] field of current is false, then
1825 if (!current.configurable() && !current.writable()) {
1826 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
1827 if (descriptor.writable())
1828 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
1829 // 10.a.ii. If the [[Writable]] field of current is false, then
1830 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
1831 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
1832 return reject(exec, throwException, "Attempting to change value of a readonly property.");
1833 }
1834 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
1835 } else {
1836 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
1837 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
1838 if (!current.configurable()) {
1839 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
1840 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
1841 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
1842 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
1843 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
1844 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
1845 }
1846 }
1847 }
1848
1849 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
1850 putIndexedDescriptor(exec, entryInMap, descriptor, current);
1851 // 13. Return true.
1852 return true;
1853}
1854
1855SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
1856{
1857 SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
1858 arrayStorage()->m_sparseMap.set(vm, this, result);
1859 return result;
1860}
1861
1862void JSObject::deallocateSparseIndexMap()
1863{
1864 if (ArrayStorage* arrayStorage = arrayStorageOrNull())
1865 arrayStorage->m_sparseMap.clear();
1866}
1867
1868bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow)
1869{
1870 for (JSObject* current = this; ;) {
1871 // This has the same behavior with respect to prototypes as JSObject::put(). It only
1872 // allows a prototype to intercept a put if (a) the prototype declares the property
1873 // we're after rather than intercepting it via an override of JSObject::put(), and
1874 // (b) that property is declared as ReadOnly or Accessor.
1875
1876 ArrayStorage* storage = current->arrayStorageOrNull();
1877 if (storage && storage->m_sparseMap) {
1878 SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
1879 if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) {
1880 iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
1881 return true;
1882 }
1883 }
1884
1885 JSValue prototypeValue = current->prototype();
1886 if (prototypeValue.isNull())
1887 return false;
1888
1889 current = asObject(prototypeValue);
1890 }
1891}
1892
1893bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
1894{
1895 JSValue prototypeValue = prototype();
1896 if (prototypeValue.isNull())
1897 return false;
1898
1899 return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow);
1900}
1901
1902template<IndexingType indexingShape>
1903void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
1904{
81345200 1905 ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
93a37866
A
1906 ASSERT(!indexingShouldBeSparse());
1907
1908 // For us to get here, the index is either greater than the public length, or greater than
1909 // or equal to the vector length.
1910 ASSERT(i >= m_butterfly->vectorLength());
1911
1912 VM& vm = exec->vm();
1913
1914 if (i >= MAX_ARRAY_INDEX - 1
1915 || (i >= MIN_SPARSE_ARRAY_INDEX
81345200 1916 && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly())))
12899fa2 1917 || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) {
93a37866
A
1918 ASSERT(i <= MAX_ARRAY_INDEX);
1919 ensureArrayStorageSlow(vm);
1920 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1921 map->putEntry(exec, this, i, value, false);
1922 ASSERT(i >= arrayStorage()->length());
1923 arrayStorage()->setLength(i + 1);
1924 return;
1925 }
1926
1927 ensureLength(vm, i + 1);
1928
1929 RELEASE_ASSERT(i < m_butterfly->vectorLength());
1930 switch (indexingShape) {
1931 case Int32Shape:
1932 ASSERT(value.isInt32());
1933 m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value);
1934 break;
1935
1936 case DoubleShape: {
1937 ASSERT(value.isNumber());
1938 double valueAsDouble = value.asNumber();
1939 ASSERT(valueAsDouble == valueAsDouble);
1940 m_butterfly->contiguousDouble()[i] = valueAsDouble;
1941 break;
1942 }
1943
1944 case ContiguousShape:
1945 m_butterfly->contiguous()[i].set(vm, this, value);
1946 break;
1947
1948 default:
1949 CRASH();
1950 }
1951}
1952
1953void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
1954{
1955 VM& vm = exec->vm();
1956
1957 // i should be a valid array index that is outside of the current vector.
1958 ASSERT(i <= MAX_ARRAY_INDEX);
1959 ASSERT(i >= storage->vectorLength());
1960
1961 SparseArrayValueMap* map = storage->m_sparseMap.get();
1962
1963 // First, handle cases where we don't currently have a sparse map.
1964 if (LIKELY(!map)) {
12899fa2 1965 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
93a37866
A
1966 ASSERT(isExtensible());
1967
1968 // Update m_length if necessary.
1969 if (i >= storage->length())
1970 storage->setLength(i + 1);
1971
1972 // Check that it is sensible to still be using a vector, and then try to grow the vector.
12899fa2
A
1973 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())
1974 && isDenseEnoughForVector(i, storage->m_numValuesInVector)
1975 && increaseVectorLength(vm, i + 1))) {
93a37866
A
1976 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
1977 storage = arrayStorage();
1978 storage->m_vector[i].set(vm, this, value);
1979 ++storage->m_numValuesInVector;
1980 return;
1981 }
1982 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
1983 map = allocateSparseIndexMap(exec->vm());
1984 map->putEntry(exec, this, i, value, shouldThrow);
1985 return;
1986 }
1987
1988 // Update m_length if necessary.
1989 unsigned length = storage->length();
1990 if (i >= length) {
1991 // Prohibit growing the array if length is not writable.
1992 if (map->lengthIsReadOnly() || !isExtensible()) {
1993 if (shouldThrow)
1994 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
1995 return;
1996 }
1997 length = i + 1;
1998 storage->setLength(length);
1999 }
2000
2001 // We are currently using a map - check whether we still want to be doing so.
2002 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2003 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2004 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length)) {
2005 map->putEntry(exec, this, i, value, shouldThrow);
2006 return;
2007 }
2008
2009 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2010 storage = arrayStorage();
2011 storage->m_numValuesInVector = numValuesInArray;
2012
2013 // Copy all values from the map into the vector, and delete the map.
2014 WriteBarrier<Unknown>* vector = storage->m_vector;
2015 SparseArrayValueMap::const_iterator end = map->end();
2016 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2017 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2018 deallocateSparseIndexMap();
2019
2020 // Store the new property into the vector.
2021 WriteBarrier<Unknown>& valueSlot = vector[i];
2022 if (!valueSlot)
2023 ++storage->m_numValuesInVector;
2024 valueSlot.set(vm, this, value);
2025}
2026
2027void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2028{
2029 VM& vm = exec->vm();
2030
2031 // i should be a valid array index that is outside of the current vector.
2032 ASSERT(i <= MAX_ARRAY_INDEX);
2033
81345200 2034 switch (indexingType()) {
93a37866
A
2035 case ALL_BLANK_INDEXING_TYPES: {
2036 if (indexingShouldBeSparse()) {
2037 putByIndexBeyondVectorLengthWithArrayStorage(
2038 exec, i, value, shouldThrow,
2039 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2040 break;
2041 }
12899fa2 2042 if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
93a37866
A
2043 putByIndexBeyondVectorLengthWithArrayStorage(
2044 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2045 break;
2046 }
81345200 2047 if (structure(vm)->needsSlowPutIndexing()) {
93a37866
A
2048 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
2049 storage->m_vector[i].set(vm, this, value);
2050 storage->m_numValuesInVector++;
2051 break;
2052 }
81345200
A
2053
2054 createInitialForValueAndSet(vm, i, value);
93a37866
A
2055 break;
2056 }
2057
2058 case ALL_UNDECIDED_INDEXING_TYPES: {
2059 CRASH();
2060 break;
2061 }
2062
2063 case ALL_INT32_INDEXING_TYPES: {
2064 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2065 break;
2066 }
2067
2068 case ALL_DOUBLE_INDEXING_TYPES: {
2069 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2070 break;
2071 }
2072
2073 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2074 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2075 break;
2076 }
2077
2078 case NonArrayWithSlowPutArrayStorage:
2079 case ArrayWithSlowPutArrayStorage: {
2080 // No own property present in the vector, but there might be in the sparse map!
2081 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2082 if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow))
2083 return;
81345200 2084 FALLTHROUGH;
93a37866
A
2085 }
2086
2087 case NonArrayWithArrayStorage:
2088 case ArrayWithArrayStorage:
2089 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2090 break;
2091
2092 default:
2093 RELEASE_ASSERT_NOT_REACHED();
2094 }
2095}
2096
2097bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2098{
2099 VM& vm = exec->vm();
2100
2101 // i should be a valid array index that is outside of the current vector.
81345200 2102 ASSERT(hasAnyArrayStorage(indexingType()));
93a37866
A
2103 ASSERT(arrayStorage() == storage);
2104 ASSERT(i >= storage->vectorLength() || attributes);
2105 ASSERT(i <= MAX_ARRAY_INDEX);
2106
2107 SparseArrayValueMap* map = storage->m_sparseMap.get();
2108
2109 // First, handle cases where we don't currently have a sparse map.
2110 if (LIKELY(!map)) {
2111 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2112 ASSERT(isExtensible());
2113
2114 // Update m_length if necessary.
2115 if (i >= storage->length())
2116 storage->setLength(i + 1);
2117
2118 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2119 if (LIKELY(
2120 !attributes
2121 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
12899fa2
A
2122 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2123 && increaseVectorLength(vm, i + 1)) {
93a37866
A
2124 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2125 storage = arrayStorage();
2126 storage->m_vector[i].set(vm, this, value);
2127 ++storage->m_numValuesInVector;
2128 return true;
2129 }
2130 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2131 map = allocateSparseIndexMap(exec->vm());
2132 return map->putDirect(exec, this, i, value, attributes, mode);
2133 }
2134
2135 // Update m_length if necessary.
2136 unsigned length = storage->length();
2137 if (i >= length) {
2138 if (mode != PutDirectIndexLikePutDirect) {
2139 // Prohibit growing the array if length is not writable.
2140 if (map->lengthIsReadOnly())
2141 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
2142 if (!isExtensible())
2143 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
2144 }
2145 length = i + 1;
2146 storage->setLength(length);
2147 }
2148
2149 // We are currently using a map - check whether we still want to be doing so.
2150 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2151 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2152 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length))
2153 return map->putDirect(exec, this, i, value, attributes, mode);
2154
2155 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2156 storage = arrayStorage();
2157 storage->m_numValuesInVector = numValuesInArray;
2158
2159 // Copy all values from the map into the vector, and delete the map.
2160 WriteBarrier<Unknown>* vector = storage->m_vector;
2161 SparseArrayValueMap::const_iterator end = map->end();
2162 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2163 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2164 deallocateSparseIndexMap();
2165
2166 // Store the new property into the vector.
2167 WriteBarrier<Unknown>& valueSlot = vector[i];
2168 if (!valueSlot)
2169 ++storage->m_numValuesInVector;
2170 valueSlot.set(vm, this, value);
2171 return true;
2172}
2173
2174bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
2175{
2176 VM& vm = exec->vm();
2177
2178 // i should be a valid array index that is outside of the current vector.
2179 ASSERT(i <= MAX_ARRAY_INDEX);
2180
2181 if (attributes & (ReadOnly | Accessor))
2182 notifyPresenceOfIndexedAccessors(vm);
2183
81345200 2184 switch (indexingType()) {
93a37866
A
2185 case ALL_BLANK_INDEXING_TYPES: {
2186 if (indexingShouldBeSparse() || attributes) {
2187 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2188 exec, i, value, attributes, mode,
2189 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2190 }
2191 if (i >= MIN_SPARSE_ARRAY_INDEX) {
2192 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2193 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
2194 }
81345200 2195 if (structure(vm)->needsSlowPutIndexing()) {
93a37866
A
2196 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
2197 storage->m_vector[i].set(vm, this, value);
2198 storage->m_numValuesInVector++;
2199 return true;
2200 }
2201
81345200 2202 createInitialForValueAndSet(vm, i, value);
93a37866
A
2203 return true;
2204 }
2205
2206 case ALL_UNDECIDED_INDEXING_TYPES: {
2207 convertUndecidedForValue(exec->vm(), value);
2208 // Reloop.
2209 return putDirectIndex(exec, i, value, attributes, mode);
2210 }
2211
2212 case ALL_INT32_INDEXING_TYPES: {
2213 if (attributes & (ReadOnly | Accessor)) {
2214 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2215 exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm));
2216 }
2217 if (!value.isInt32()) {
2218 convertInt32ForValue(vm, value);
2219 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2220 }
2221 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2222 return true;
2223 }
2224
2225 case ALL_DOUBLE_INDEXING_TYPES: {
2226 if (attributes & (ReadOnly | Accessor)) {
2227 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2228 exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm));
2229 }
2230 if (!value.isNumber()) {
2231 convertDoubleToContiguous(vm);
2232 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2233 }
2234 double valueAsDouble = value.asNumber();
2235 if (valueAsDouble != valueAsDouble) {
2236 convertDoubleToContiguous(vm);
2237 return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2238 }
2239 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2240 return true;
2241 }
2242
2243 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2244 if (attributes & (ReadOnly | Accessor)) {
2245 return putDirectIndexBeyondVectorLengthWithArrayStorage(
2246 exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm));
2247 }
2248 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2249 return true;
2250 }
2251
2252 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
2253 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
2254
2255 default:
2256 RELEASE_ASSERT_NOT_REACHED();
2257 return false;
2258 }
2259}
2260
81345200 2261void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
93a37866
A
2262{
2263 StringImpl* name = propertyName.publicName();
81345200
A
2264 if (!name)
2265 name = vm.propertyNames->anonymous.impl();
93a37866 2266 ASSERT(name);
81345200
A
2267
2268 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2269 putDirect(vm, propertyName, function, attributes);
2270}
2271
2272JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2273{
2274 StringImpl* name = propertyName.publicName();
2275 if (!name)
2276 name = vm.propertyNames->anonymous.impl();
2277 ASSERT(name);
2278 JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2279 putDirect(vm, propertyName, function, attributes);
2280 return function;
2281}
2282
2283JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2284{
2285 StringImpl* name = propertyName.publicName();
2286 if (!name)
2287 name = vm.propertyNames->anonymous.impl();
2288 ASSERT(name);
2289 JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2290 putDirectWithoutTransition(vm, propertyName, function, attributes);
2291 return function;
2292}
2293
2294void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2295{
2296 StringImpl* name = propertyName.publicName();
2297 ASSERT(name);
2298
2299 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2300 putDirectWithoutTransition(vm, propertyName, function, attributes);
93a37866
A
2301}
2302
2303ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
2304{
2305 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
2306
2307 unsigned increasedLength;
2308 unsigned maxInitLength = std::min(currentLength, 100000U);
14957cd0 2309
93a37866
A
2310 if (desiredLength < maxInitLength)
2311 increasedLength = maxInitLength;
2312 else if (!currentVectorLength)
2313 increasedLength = std::max(desiredLength, lastArraySize);
2314 else {
2315 increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
2316 }
2317
2318 ASSERT(increasedLength >= desiredLength);
2319
2320 lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW);
2321
2322 return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
2323}
2324
2325ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
2326{
2327 unsigned vectorLength;
2328 unsigned length;
2329
81345200 2330 if (hasIndexedProperties(indexingType())) {
93a37866
A
2331 vectorLength = m_butterfly->vectorLength();
2332 length = m_butterfly->publicLength();
6fe7ccc8 2333 } else {
93a37866
A
2334 vectorLength = 0;
2335 length = 0;
2336 }
2337
2338 return getNewVectorLength(vectorLength, length, desiredLength);
2339}
2340
2341template<IndexingType indexingShape>
2342unsigned JSObject::countElements(Butterfly* butterfly)
2343{
2344 unsigned numValues = 0;
2345 for (unsigned i = butterfly->publicLength(); i--;) {
2346 switch (indexingShape) {
2347 case Int32Shape:
2348 case ContiguousShape:
2349 if (butterfly->contiguous()[i])
2350 numValues++;
2351 break;
2352
2353 case DoubleShape: {
2354 double value = butterfly->contiguousDouble()[i];
2355 if (value == value)
2356 numValues++;
2357 break;
2358 }
2359
2360 default:
6fe7ccc8 2361 CRASH();
93a37866
A
2362 }
2363 }
2364 return numValues;
2365}
2366
2367unsigned JSObject::countElements()
2368{
81345200 2369 switch (indexingType()) {
93a37866
A
2370 case ALL_BLANK_INDEXING_TYPES:
2371 case ALL_UNDECIDED_INDEXING_TYPES:
2372 return 0;
2373
2374 case ALL_INT32_INDEXING_TYPES:
81345200 2375 return countElements<Int32Shape>(butterfly());
93a37866
A
2376
2377 case ALL_DOUBLE_INDEXING_TYPES:
81345200 2378 return countElements<DoubleShape>(butterfly());
93a37866
A
2379
2380 case ALL_CONTIGUOUS_INDEXING_TYPES:
81345200 2381 return countElements<ContiguousShape>(butterfly());
93a37866
A
2382
2383 default:
2384 CRASH();
2385 return 0;
2386 }
2387}
2388
2389bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
2390{
2391 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2392 // to the vector. Callers have to account for that, because they can do it more efficiently.
2393 if (newLength > MAX_STORAGE_VECTOR_LENGTH)
2394 return false;
2395
2396 ArrayStorage* storage = arrayStorage();
2397
2398 if (newLength >= MIN_SPARSE_ARRAY_INDEX
2399 && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
2400 return false;
2401
2402 unsigned indexBias = storage->m_indexBias;
2403 unsigned vectorLength = storage->vectorLength();
2404 ASSERT(newLength > vectorLength);
2405 unsigned newVectorLength = getNewVectorLength(newLength);
2406
2407 // Fast case - there is no precapacity. In these cases a realloc makes sense.
81345200 2408 Structure* structure = this->structure(vm);
93a37866 2409 if (LIKELY(!indexBias)) {
81345200
A
2410 DeferGC deferGC(vm.heap);
2411 Butterfly* newButterfly = storage->butterfly()->growArrayRight(
2412 vm, this, structure, structure->outOfLineCapacity(), true,
2413 ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
93a37866
A
2414 if (!newButterfly)
2415 return false;
93a37866 2416 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
81345200 2417 setButterflyWithoutChangingStructure(vm, newButterfly);
93a37866
A
2418 return true;
2419 }
2420
2421 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
81345200 2422 DeferGC deferGC(vm.heap);
93a37866
A
2423 unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
2424 Butterfly* newButterfly = storage->butterfly()->resizeArray(
81345200
A
2425 vm, this,
2426 structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
93a37866
A
2427 newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
2428 if (!newButterfly)
2429 return false;
93a37866
A
2430 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2431 newButterfly->arrayStorage()->m_indexBias = newIndexBias;
81345200 2432 setButterflyWithoutChangingStructure(vm, newButterfly);
93a37866
A
2433 return true;
2434}
2435
2436void JSObject::ensureLengthSlow(VM& vm, unsigned length)
2437{
2438 ASSERT(length < MAX_ARRAY_INDEX);
81345200 2439 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
93a37866
A
2440 ASSERT(length > m_butterfly->vectorLength());
2441
2442 unsigned newVectorLength = std::min(
2443 length << 1,
2444 MAX_STORAGE_VECTOR_LENGTH);
2445 unsigned oldVectorLength = m_butterfly->vectorLength();
81345200
A
2446 DeferGC deferGC(vm.heap);
2447 m_butterfly.set(vm, this, m_butterfly->growArrayRight(
2448 vm, this, structure(), structure()->outOfLineCapacity(), true,
93a37866 2449 oldVectorLength * sizeof(EncodedJSValue),
81345200
A
2450 newVectorLength * sizeof(EncodedJSValue)));
2451
2452 m_butterfly->setVectorLength(newVectorLength);
2453
2454 if (hasDouble(indexingType())) {
93a37866 2455 for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
81345200 2456 m_butterfly->contiguousDouble().data()[i] = PNaN;
6fe7ccc8 2457 }
93a37866 2458}
14957cd0 2459
93a37866
A
2460Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
2461{
2462 ASSERT(newSize > oldSize);
2463
2464 // It's important that this function not rely on structure(), for the property
2465 // capacity, since we might have already mutated the structure in-place.
2466
81345200 2467 return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize);
9dae56ea
A
2468}
2469
81345200 2470bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
9dae56ea 2471{
81345200
A
2472 JSC::PropertySlot slot(this);
2473 if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot))
f9bf01c6 2474 return false;
81345200
A
2475 /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */
2476 if (slot.slotBase() != this && slot.slotBase() && slot.slotBase()->methodTable(exec->vm())->toThis(slot.slotBase(), exec, NotStrictMode) != this)
93a37866 2477 return false;
81345200
A
2478 if (slot.isAccessor())
2479 descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
2480 else if (slot.attributes() & CustomAccessor)
2481 descriptor.setCustomDescriptor(slot.attributes());
2482 else
2483 descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes());
2484 return true;
f9bf01c6
A
2485}
2486
81345200 2487static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
f9bf01c6 2488{
81345200 2489 VM& vm = exec->vm();
f9bf01c6 2490 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
14957cd0 2491 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
81345200 2492 GetterSetter* accessor = GetterSetter::create(vm);
6fe7ccc8 2493 if (oldDescriptor.getterPresent())
81345200 2494 accessor->setGetter(vm, oldDescriptor.getterObject());
6fe7ccc8 2495 if (oldDescriptor.setterPresent())
81345200 2496 accessor->setSetter(vm, oldDescriptor.setterObject());
93a37866 2497 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
14957cd0
A
2498 return true;
2499 }
2500 JSValue newValue = jsUndefined();
2501 if (descriptor.value())
2502 newValue = descriptor.value();
2503 else if (oldDescriptor.value())
2504 newValue = oldDescriptor.value();
81345200 2505 target->putDirect(vm, propertyName, newValue, attributes & ~Accessor);
6fe7ccc8 2506 if (attributes & ReadOnly)
81345200 2507 target->structure(vm)->setContainsReadOnlyProperties();
f9bf01c6
A
2508 return true;
2509 }
2510 attributes &= ~ReadOnly;
81345200 2511 GetterSetter* accessor = GetterSetter::create(vm);
6fe7ccc8
A
2512
2513 if (descriptor.getterPresent())
81345200 2514 accessor->setGetter(vm, descriptor.getterObject());
6fe7ccc8 2515 else if (oldDescriptor.getterPresent())
81345200 2516 accessor->setGetter(vm, oldDescriptor.getterObject());
6fe7ccc8 2517 if (descriptor.setterPresent())
81345200 2518 accessor->setSetter(vm, descriptor.setterObject());
6fe7ccc8 2519 else if (oldDescriptor.setterPresent())
81345200 2520 accessor->setSetter(vm, oldDescriptor.setterObject());
6fe7ccc8 2521
93a37866 2522 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
6fe7ccc8 2523 return true;
f9bf01c6
A
2524}
2525
93a37866
A
2526void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
2527{
2528 unsigned asIndex = propertyName.asIndex();
2529 if (asIndex == PropertyName::NotAnIndex)
2530 putDirect(exec->vm(), propertyName, value);
2531 else
2532 putDirectIndex(exec, asIndex, value);
2533}
2534
6fe7ccc8
A
2535class DefineOwnPropertyScope {
2536public:
2537 DefineOwnPropertyScope(ExecState* exec)
93a37866 2538 : m_vm(exec->vm())
6fe7ccc8 2539 {
93a37866 2540 m_vm.setInDefineOwnProperty(true);
6fe7ccc8
A
2541 }
2542
2543 ~DefineOwnPropertyScope()
2544 {
93a37866 2545 m_vm.setInDefineOwnProperty(false);
6fe7ccc8
A
2546 }
2547
2548private:
93a37866 2549 VM& m_vm;
6fe7ccc8
A
2550};
2551
81345200 2552bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
f9bf01c6 2553{
6fe7ccc8
A
2554 // Track on the globaldata that we're in define property.
2555 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
2556 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
2557 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
2558 DefineOwnPropertyScope scope(exec);
93a37866 2559
f9bf01c6
A
2560 // If we have a new property we can just put it on normally
2561 PropertyDescriptor current;
81345200 2562 if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
14957cd0 2563 // unless extensions are prevented!
93a37866 2564 if (!isExtensible()) {
14957cd0 2565 if (throwException)
81345200 2566 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible.")));
14957cd0
A
2567 return false;
2568 }
2569 PropertyDescriptor oldDescriptor;
2570 oldDescriptor.setValue(jsUndefined());
93a37866 2571 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
14957cd0 2572 }
f9bf01c6
A
2573
2574 if (descriptor.isEmpty())
2575 return true;
2576
2577 if (current.equalTo(exec, descriptor))
2578 return true;
2579
2580 // Filter out invalid changes
2581 if (!current.configurable()) {
2582 if (descriptor.configurable()) {
2583 if (throwException)
81345200 2584 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
f9bf01c6
A
2585 return false;
2586 }
2587 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
2588 if (throwException)
81345200 2589 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
f9bf01c6
A
2590 return false;
2591 }
2592 }
2593
2594 // A generic descriptor is simply changing the attributes of an existing property
2595 if (descriptor.isGenericDescriptor()) {
2596 if (!current.attributesEqual(descriptor)) {
81345200 2597 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
93a37866 2598 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
f9bf01c6
A
2599 }
2600 return true;
2601 }
2602
2603 // Changing between a normal property or an accessor property
2604 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
2605 if (!current.configurable()) {
2606 if (throwException)
81345200 2607 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
f9bf01c6
A
2608 return false;
2609 }
81345200 2610 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
93a37866 2611 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
f9bf01c6
A
2612 }
2613
2614 // Changing the value and attributes of an existing property
2615 if (descriptor.isDataDescriptor()) {
2616 if (!current.configurable()) {
2617 if (!current.writable() && descriptor.writable()) {
2618 if (throwException)
81345200 2619 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
f9bf01c6
A
2620 return false;
2621 }
2622 if (!current.writable()) {
6fe7ccc8 2623 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) {
f9bf01c6 2624 if (throwException)
81345200 2625 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property.")));
f9bf01c6
A
2626 return false;
2627 }
2628 }
f9bf01c6 2629 }
6fe7ccc8
A
2630 if (current.attributesEqual(descriptor) && !descriptor.value())
2631 return true;
81345200 2632 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
93a37866 2633 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
f9bf01c6
A
2634 }
2635
2636 // Changing the accessor functions of an existing accessor property
2637 ASSERT(descriptor.isAccessorDescriptor());
2638 if (!current.configurable()) {
14957cd0 2639 if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
f9bf01c6 2640 if (throwException)
81345200 2641 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the setter of an unconfigurable property.")));
f9bf01c6
A
2642 return false;
2643 }
14957cd0 2644 if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
f9bf01c6 2645 if (throwException)
81345200
A
2646 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the getter of an unconfigurable property.")));
2647 return false;
2648 }
2649 if (current.attributes() & CustomAccessor) {
2650 if (throwException)
2651 exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
f9bf01c6
A
2652 return false;
2653 }
2654 }
93a37866 2655 JSValue accessor = getDirect(exec->vm(), propertyName);
f9bf01c6
A
2656 if (!accessor)
2657 return false;
81345200
A
2658 GetterSetter* getterSetter;
2659 if (accessor.isCustomGetterSetter())
2660 getterSetter = GetterSetter::create(exec->vm());
2661 else {
2662 ASSERT(accessor.isGetterSetter());
2663 getterSetter = asGetterSetter(accessor);
2664 }
6fe7ccc8 2665 if (descriptor.setterPresent())
93a37866 2666 getterSetter->setSetter(exec->vm(), descriptor.setterObject());
6fe7ccc8 2667 if (descriptor.getterPresent())
93a37866 2668 getterSetter->setGetter(exec->vm(), descriptor.getterObject());
6fe7ccc8 2669 if (current.attributesEqual(descriptor))
f9bf01c6 2670 return true;
81345200 2671 methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
6fe7ccc8 2672 unsigned attrs = descriptor.attributesOverridingCurrent(current);
93a37866 2673 putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
f9bf01c6 2674 return true;
9dae56ea
A
2675}
2676
81345200 2677bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
93a37866
A
2678{
2679 // If it's an array index, then use the indexed property storage.
2680 unsigned index = propertyName.asIndex();
2681 if (index != PropertyName::NotAnIndex) {
2682 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
2683 // d. Reject if succeeded is false.
2684 // e. If index >= oldLen
2685 // e.i. Set oldLenDesc.[[Value]] to index + 1.
2686 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
2687 // f. Return true.
2688 return object->defineOwnIndexedProperty(exec, index, descriptor, throwException);
2689 }
2690
2691 return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
2692}
2693
81345200 2694JSObject* throwTypeError(ExecState* exec, const String& message)
93a37866 2695{
81345200 2696 return exec->vm().throwException(exec, createTypeError(exec, message));
93a37866
A
2697}
2698
81345200 2699void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter)
14957cd0 2700{
81345200
A
2701 Butterfly* butterfly = this->butterfly();
2702 size_t preCapacity = this->butterflyPreCapacity();
2703 void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter);
2704 void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore);
2705
2706 memmove(newBase, currentBase, this->butterflyTotalSize());
2707 setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter));
14957cd0
A
2708}
2709
9dae56ea 2710} // namespace JSC