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