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