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, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23#ifndef JSObject_h 24#define JSObject_h 25 26#include "ArgList.h" 27#include "ArrayConventions.h" 28#include "ArrayStorage.h" 29#include "Butterfly.h" 30#include "CallFrame.h" 31#include "ClassInfo.h" 32#include "CommonIdentifiers.h" 33#include "CopyWriteBarrier.h" 34#include "CustomGetterSetter.h" 35#include "DeferGC.h" 36#include "Heap.h" 37#include "HeapInlines.h" 38#include "IndexingHeaderInlines.h" 39#include "JSCell.h" 40#include "PropertySlot.h" 41#include "PropertyStorage.h" 42#include "PutDirectIndexMode.h" 43#include "PutPropertySlot.h" 44 45#include "Structure.h" 46#include "VM.h" 47#include "JSString.h" 48#include "SparseArrayValueMap.h" 49#include <wtf/StdLibExtras.h> 50 51namespace JSC { 52 53inline JSCell* getJSFunction(JSValue value) 54{ 55 if (value.isCell() && (value.asCell()->type() == JSFunctionType)) 56 return value.asCell(); 57 return 0; 58} 59 60JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*); 61 62inline JSCell* getCallableObject(JSValue value) 63{ 64 if (!value.isCell()) 65 return 0; 66 return getCallableObjectSlow(value.asCell()); 67} 68 69class GetterSetter; 70class InternalFunction; 71class JSFunction; 72class LLIntOffsetsExtractor; 73class MarkedBlock; 74class PropertyDescriptor; 75class PropertyNameArray; 76class Structure; 77struct HashTable; 78struct HashTableValue; 79 80JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&); 81extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; 82 83COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute); 84COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute); 85COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute); 86COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute); 87COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute); 88COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute); 89 90class JSFinalObject; 91 92class JSObject : public JSCell { 93 friend class BatchedTransitionOptimizer; 94 friend class JIT; 95 friend class JSCell; 96 friend class JSFinalObject; 97 friend class MarkedBlock; 98 JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); 99 100 enum PutMode { 101 PutModePut, 102 PutModeDefineOwnProperty, 103 }; 104 105public: 106 typedef JSCell Base; 107 108 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); 109 JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); 110 111 JS_EXPORT_PRIVATE static String className(const JSObject*); 112 113 JSValue prototype() const; 114 void setPrototype(VM&, JSValue prototype); 115 bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype); 116 117 bool mayInterceptIndexedAccesses() 118 { 119 return structure()->mayInterceptIndexedAccesses(); 120 } 121 122 JSValue get(ExecState*, PropertyName) const; 123 JSValue get(ExecState*, unsigned propertyName) const; 124 125 bool fastGetOwnPropertySlot(ExecState*, VM&, Structure&, PropertyName, PropertySlot&); 126 bool getPropertySlot(ExecState*, PropertyName, PropertySlot&); 127 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 128 129 static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); 130 JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); 131 132 // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot 133 // currently returns incorrect results for the DOM window (with non-own properties) 134 // being returned. Once this is fixed we should migrate code & remove this method. 135 bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); 136 137 bool allowsAccessFrom(ExecState*); 138 139 unsigned getArrayLength() const 140 { 141 if (!hasIndexedProperties(indexingType())) 142 return 0; 143 return m_butterfly->publicLength(); 144 } 145 146 unsigned getVectorLength() 147 { 148 if (!hasIndexedProperties(indexingType())) 149 return 0; 150 return m_butterfly->vectorLength(); 151 } 152 153 JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); 154 JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 155 156 void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) 157 { 158 if (canSetIndexQuickly(propertyName)) { 159 setIndexQuickly(exec->vm(), propertyName, value); 160 return; 161 } 162 methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow); 163 } 164 165 // This is similar to the putDirect* methods: 166 // - the prototype chain is not consulted 167 // - accessors are not called. 168 // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default). 169 // This method creates a property with attributes writable, enumerable and configurable all set to true. 170 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode) 171 { 172 if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) { 173 setIndexQuickly(exec->vm(), propertyName, value); 174 return true; 175 } 176 return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode); 177 } 178 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value) 179 { 180 return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect); 181 } 182 183 // A non-throwing version of putDirect and putDirectIndex. 184 JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue); 185 186 bool hasIndexingHeader() const 187 { 188 return structure()->hasIndexingHeader(this); 189 } 190 191 bool canGetIndexQuickly(unsigned i) 192 { 193 switch (indexingType()) { 194 case ALL_BLANK_INDEXING_TYPES: 195 case ALL_UNDECIDED_INDEXING_TYPES: 196 return false; 197 case ALL_INT32_INDEXING_TYPES: 198 case ALL_CONTIGUOUS_INDEXING_TYPES: 199 return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; 200 case ALL_DOUBLE_INDEXING_TYPES: { 201 if (i >= m_butterfly->vectorLength()) 202 return false; 203 double value = m_butterfly->contiguousDouble()[i]; 204 if (value != value) 205 return false; 206 return true; 207 } 208 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 209 return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; 210 default: 211 RELEASE_ASSERT_NOT_REACHED(); 212 return false; 213 } 214 } 215 216 JSValue getIndexQuickly(unsigned i) 217 { 218 switch (indexingType()) { 219 case ALL_INT32_INDEXING_TYPES: 220 return jsNumber(m_butterfly->contiguous()[i].get().asInt32()); 221 case ALL_CONTIGUOUS_INDEXING_TYPES: 222 return m_butterfly->contiguous()[i].get(); 223 case ALL_DOUBLE_INDEXING_TYPES: 224 return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); 225 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 226 return m_butterfly->arrayStorage()->m_vector[i].get(); 227 default: 228 RELEASE_ASSERT_NOT_REACHED(); 229 return JSValue(); 230 } 231 } 232 233 JSValue tryGetIndexQuickly(unsigned i) 234 { 235 switch (indexingType()) { 236 case ALL_BLANK_INDEXING_TYPES: 237 case ALL_UNDECIDED_INDEXING_TYPES: 238 break; 239 case ALL_INT32_INDEXING_TYPES: 240 if (i < m_butterfly->publicLength()) { 241 JSValue result = m_butterfly->contiguous()[i].get(); 242 ASSERT(result.isInt32() || !result); 243 return result; 244 } 245 break; 246 case ALL_CONTIGUOUS_INDEXING_TYPES: 247 if (i < m_butterfly->publicLength()) 248 return m_butterfly->contiguous()[i].get(); 249 break; 250 case ALL_DOUBLE_INDEXING_TYPES: { 251 if (i >= m_butterfly->publicLength()) 252 break; 253 double result = m_butterfly->contiguousDouble()[i]; 254 if (result != result) 255 break; 256 return JSValue(JSValue::EncodeAsDouble, result); 257 } 258 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 259 if (i < m_butterfly->arrayStorage()->vectorLength()) 260 return m_butterfly->arrayStorage()->m_vector[i].get(); 261 break; 262 default: 263 RELEASE_ASSERT_NOT_REACHED(); 264 break; 265 } 266 return JSValue(); 267 } 268 269 JSValue getDirectIndex(ExecState* exec, unsigned i) 270 { 271 if (JSValue result = tryGetIndexQuickly(i)) 272 return result; 273 PropertySlot slot(this); 274 if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) 275 return slot.getValue(exec, i); 276 return JSValue(); 277 } 278 279 JSValue getIndex(ExecState* exec, unsigned i) 280 { 281 if (JSValue result = tryGetIndexQuickly(i)) 282 return result; 283 return get(exec, i); 284 } 285 286 bool canSetIndexQuickly(unsigned i) 287 { 288 switch (indexingType()) { 289 case ALL_BLANK_INDEXING_TYPES: 290 case ALL_UNDECIDED_INDEXING_TYPES: 291 return false; 292 case ALL_INT32_INDEXING_TYPES: 293 case ALL_DOUBLE_INDEXING_TYPES: 294 case ALL_CONTIGUOUS_INDEXING_TYPES: 295 case NonArrayWithArrayStorage: 296 case ArrayWithArrayStorage: 297 return i < m_butterfly->vectorLength(); 298 case NonArrayWithSlowPutArrayStorage: 299 case ArrayWithSlowPutArrayStorage: 300 return i < m_butterfly->arrayStorage()->vectorLength() 301 && !!m_butterfly->arrayStorage()->m_vector[i]; 302 default: 303 RELEASE_ASSERT_NOT_REACHED(); 304 return false; 305 } 306 } 307 308 bool canSetIndexQuicklyForPutDirect(unsigned i) 309 { 310 switch (indexingType()) { 311 case ALL_BLANK_INDEXING_TYPES: 312 case ALL_UNDECIDED_INDEXING_TYPES: 313 return false; 314 case ALL_INT32_INDEXING_TYPES: 315 case ALL_DOUBLE_INDEXING_TYPES: 316 case ALL_CONTIGUOUS_INDEXING_TYPES: 317 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 318 return i < m_butterfly->vectorLength(); 319 default: 320 RELEASE_ASSERT_NOT_REACHED(); 321 return false; 322 } 323 } 324 325 void setIndexQuickly(VM& vm, unsigned i, JSValue v) 326 { 327 switch (indexingType()) { 328 case ALL_INT32_INDEXING_TYPES: { 329 ASSERT(i < m_butterfly->vectorLength()); 330 if (!v.isInt32()) { 331 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); 332 return; 333 } 334 FALLTHROUGH; 335 } 336 case ALL_CONTIGUOUS_INDEXING_TYPES: { 337 ASSERT(i < m_butterfly->vectorLength()); 338 m_butterfly->contiguous()[i].set(vm, this, v); 339 if (i >= m_butterfly->publicLength()) 340 m_butterfly->setPublicLength(i + 1); 341 break; 342 } 343 case ALL_DOUBLE_INDEXING_TYPES: { 344 ASSERT(i < m_butterfly->vectorLength()); 345 if (!v.isNumber()) { 346 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); 347 return; 348 } 349 double value = v.asNumber(); 350 if (value != value) { 351 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); 352 return; 353 } 354 m_butterfly->contiguousDouble()[i] = value; 355 if (i >= m_butterfly->publicLength()) 356 m_butterfly->setPublicLength(i + 1); 357 break; 358 } 359 case ALL_ARRAY_STORAGE_INDEXING_TYPES: { 360 ArrayStorage* storage = m_butterfly->arrayStorage(); 361 WriteBarrier<Unknown>& x = storage->m_vector[i]; 362 JSValue old = x.get(); 363 x.set(vm, this, v); 364 if (!old) { 365 ++storage->m_numValuesInVector; 366 if (i >= storage->length()) 367 storage->setLength(i + 1); 368 } 369 break; 370 } 371 default: 372 RELEASE_ASSERT_NOT_REACHED(); 373 } 374 } 375 376 void initializeIndex(VM& vm, unsigned i, JSValue v) 377 { 378 switch (indexingType()) { 379 case ALL_UNDECIDED_INDEXING_TYPES: { 380 setIndexQuicklyToUndecided(vm, i, v); 381 break; 382 } 383 case ALL_INT32_INDEXING_TYPES: { 384 ASSERT(i < m_butterfly->publicLength()); 385 ASSERT(i < m_butterfly->vectorLength()); 386 if (!v.isInt32()) { 387 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); 388 break; 389 } 390 FALLTHROUGH; 391 } 392 case ALL_CONTIGUOUS_INDEXING_TYPES: { 393 ASSERT(i < m_butterfly->publicLength()); 394 ASSERT(i < m_butterfly->vectorLength()); 395 m_butterfly->contiguous()[i].set(vm, this, v); 396 break; 397 } 398 case ALL_DOUBLE_INDEXING_TYPES: { 399 ASSERT(i < m_butterfly->publicLength()); 400 ASSERT(i < m_butterfly->vectorLength()); 401 if (!v.isNumber()) { 402 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); 403 return; 404 } 405 double value = v.asNumber(); 406 if (value != value) { 407 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); 408 return; 409 } 410 m_butterfly->contiguousDouble()[i] = value; 411 break; 412 } 413 case ALL_ARRAY_STORAGE_INDEXING_TYPES: { 414 ArrayStorage* storage = m_butterfly->arrayStorage(); 415 ASSERT(i < storage->length()); 416 ASSERT(i < storage->m_numValuesInVector); 417 storage->m_vector[i].set(vm, this, v); 418 break; 419 } 420 default: 421 RELEASE_ASSERT_NOT_REACHED(); 422 } 423 } 424 425 bool hasSparseMap() 426 { 427 switch (indexingType()) { 428 case ALL_BLANK_INDEXING_TYPES: 429 case ALL_UNDECIDED_INDEXING_TYPES: 430 case ALL_INT32_INDEXING_TYPES: 431 case ALL_DOUBLE_INDEXING_TYPES: 432 case ALL_CONTIGUOUS_INDEXING_TYPES: 433 return false; 434 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 435 return m_butterfly->arrayStorage()->m_sparseMap; 436 default: 437 RELEASE_ASSERT_NOT_REACHED(); 438 return false; 439 } 440 } 441 442 bool inSparseIndexingMode() 443 { 444 switch (indexingType()) { 445 case ALL_BLANK_INDEXING_TYPES: 446 case ALL_UNDECIDED_INDEXING_TYPES: 447 case ALL_INT32_INDEXING_TYPES: 448 case ALL_DOUBLE_INDEXING_TYPES: 449 case ALL_CONTIGUOUS_INDEXING_TYPES: 450 return false; 451 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 452 return m_butterfly->arrayStorage()->inSparseMode(); 453 default: 454 RELEASE_ASSERT_NOT_REACHED(); 455 return false; 456 } 457 } 458 459 void enterDictionaryIndexingMode(VM&); 460 461 // putDirect is effectively an unchecked vesion of 'defineOwnProperty': 462 // - the prototype chain is not consulted 463 // - accessors are not called. 464 // - attributes will be respected (after the call the property will exist with the given attributes) 465 // - the property name is assumed to not be an index. 466 void putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); 467 void putDirect(VM&, PropertyName, JSValue, PutPropertySlot&); 468 void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0); 469 void putDirectNonIndexAccessor(VM&, PropertyName, JSValue, unsigned attributes); 470 void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes); 471 JS_EXPORT_PRIVATE void putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes); 472 473 JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const; 474 JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; 475 bool hasOwnProperty(ExecState*, PropertyName) const; 476 477 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); 478 JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); 479 480 JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); 481 482 bool hasInstance(ExecState*, JSValue); 483 static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); 484 485 JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 486 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 487 JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 488 489 JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; 490 bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; 491 JS_EXPORT_PRIVATE double toNumber(ExecState*) const; 492 JS_EXPORT_PRIVATE JSString* toString(ExecState*) const; 493 494 JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode); 495 496 bool getPropertySpecificValue(ExecState*, PropertyName, JSCell*& specificFunction) const; 497 498 // This get function only looks at the property map. 499 JSValue getDirect(VM& vm, PropertyName propertyName) const 500 { 501 Structure* structure = this->structure(vm); 502 PropertyOffset offset = structure->get(vm, propertyName); 503 checkOffset(offset, structure->inlineCapacity()); 504 return offset != invalidOffset ? getDirect(offset) : JSValue(); 505 } 506 507 JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const 508 { 509 JSCell* specific; 510 Structure* structure = this->structure(vm); 511 PropertyOffset offset = structure->get(vm, propertyName, attributes, specific); 512 checkOffset(offset, structure->inlineCapacity()); 513 return offset != invalidOffset ? getDirect(offset) : JSValue(); 514 } 515 516 PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) 517 { 518 Structure* structure = this->structure(vm); 519 PropertyOffset offset = structure->get(vm, propertyName); 520 checkOffset(offset, structure->inlineCapacity()); 521 return offset; 522 } 523 524 PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) 525 { 526 JSCell* specific; 527 Structure* structure = this->structure(vm); 528 PropertyOffset offset = structure->get(vm, propertyName, attributes, specific); 529 checkOffset(offset, structure->inlineCapacity()); 530 return offset; 531 } 532 533 bool hasInlineStorage() const { return structure()->hasInlineStorage(); } 534 ConstPropertyStorage inlineStorageUnsafe() const 535 { 536 return bitwise_cast<ConstPropertyStorage>(this + 1); 537 } 538 PropertyStorage inlineStorageUnsafe() 539 { 540 return bitwise_cast<PropertyStorage>(this + 1); 541 } 542 ConstPropertyStorage inlineStorage() const 543 { 544 ASSERT(hasInlineStorage()); 545 return inlineStorageUnsafe(); 546 } 547 PropertyStorage inlineStorage() 548 { 549 ASSERT(hasInlineStorage()); 550 return inlineStorageUnsafe(); 551 } 552 553 const Butterfly* butterfly() const { return m_butterfly.get(); } 554 Butterfly* butterfly() { return m_butterfly.get(); } 555 556 ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } 557 PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } 558 559 const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const 560 { 561 if (isInlineOffset(offset)) 562 return &inlineStorage()[offsetInInlineStorage(offset)]; 563 return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; 564 } 565 566 WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) 567 { 568 if (isInlineOffset(offset)) 569 return &inlineStorage()[offsetInInlineStorage(offset)]; 570 return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; 571 } 572 573 void transitionTo(VM&, Structure*); 574 575 JS_EXPORT_PRIVATE bool removeDirect(VM&, PropertyName); // Return true if anything is removed. 576 bool hasCustomProperties() { return structure()->didTransition(); } 577 bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); } 578 bool hasCustomGetterSetterProperties() { return structure()->hasCustomGetterSetterProperties(); } 579 580 // putOwnDataProperty has 'put' like semantics, however this method: 581 // - assumes the object contains no own getter/setter properties. 582 // - provides no special handling for __proto__ 583 // - does not walk the prototype chain (to check for accessors or non-writable properties). 584 // This is used by JSActivation. 585 bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); 586 587 // Fast access to known property offsets. 588 JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); } 589 void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); } 590 void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); } 591 592 JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); 593 JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); 594 JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); 595 void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); 596 597 JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); 598 599 bool isGlobalObject() const; 600 bool isVariableObject() const; 601 bool isStaticScopeObject() const; 602 bool isNameScopeObject() const; 603 bool isActivationObject() const; 604 bool isErrorInstance() const; 605 606 void seal(VM&); 607 void freeze(VM&); 608 JS_EXPORT_PRIVATE void preventExtensions(VM&); 609 bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); } 610 bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); } 611 bool isExtensible() { return structure()->isExtensible(); } 612 bool indexingShouldBeSparse() 613 { 614 return !isExtensible() 615 || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); 616 } 617 618 bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } 619 void reifyStaticFunctionsForDelete(ExecState* exec); 620 621 JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize); 622 void setButterflyWithoutChangingStructure(VM&, Butterfly*); 623 624 void setStructure(VM&, Structure*); 625 void setStructureAndButterfly(VM&, Structure*, Butterfly*); 626 void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*); 627 void setStructureAndReallocateStorageIfNecessary(VM&, Structure*); 628 629 void convertToDictionary(VM& vm) 630 { 631 setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm))); 632 } 633 634 void flattenDictionaryObject(VM& vm) 635 { 636 structure(vm)->flattenDictionaryStructure(vm, this); 637 } 638 void shiftButterflyAfterFlattening(VM&, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter); 639 640 JSGlobalObject* globalObject() const 641 { 642 ASSERT(structure()->globalObject()); 643 ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); 644 return structure()->globalObject(); 645 } 646 647 void switchToSlowPutArrayStorage(VM&); 648 649 // The receiver is the prototype in this case. The following: 650 // 651 // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...) 652 // 653 // is equivalent to: 654 // 655 // foo->attemptToInterceptPutByIndexOnHole(...); 656 bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); 657 658 // Returns 0 if int32 storage cannot be created - either because 659 // indexing should be sparse, we're having a bad time, or because 660 // we already have a more general form of storage (double, 661 // contiguous, array storage). 662 ContiguousJSValues ensureInt32(VM& vm) 663 { 664 if (LIKELY(hasInt32(indexingType()))) 665 return m_butterfly->contiguousInt32(); 666 667 return ensureInt32Slow(vm); 668 } 669 670 // Returns 0 if double storage cannot be created - either because 671 // indexing should be sparse, we're having a bad time, or because 672 // we already have a more general form of storage (contiguous, 673 // or array storage). 674 ContiguousDoubles ensureDouble(VM& vm) 675 { 676 if (LIKELY(hasDouble(indexingType()))) 677 return m_butterfly->contiguousDouble(); 678 679 return ensureDoubleSlow(vm); 680 } 681 682 // Returns 0 if contiguous storage cannot be created - either because 683 // indexing should be sparse or because we're having a bad time. 684 ContiguousJSValues ensureContiguous(VM& vm) 685 { 686 if (LIKELY(hasContiguous(indexingType()))) 687 return m_butterfly->contiguous(); 688 689 return ensureContiguousSlow(vm); 690 } 691 692 // Same as ensureContiguous(), except that if the indexed storage is in 693 // double mode, then it does a rage conversion to contiguous: it 694 // attempts to convert each double to an int32. 695 ContiguousJSValues rageEnsureContiguous(VM& vm) 696 { 697 if (LIKELY(hasContiguous(indexingType()))) 698 return m_butterfly->contiguous(); 699 700 return rageEnsureContiguousSlow(vm); 701 } 702 703 // Ensure that the object is in a mode where it has array storage. Use 704 // this if you're about to perform actions that would have required the 705 // object to be converted to have array storage, if it didn't have it 706 // already. 707 ArrayStorage* ensureArrayStorage(VM& vm) 708 { 709 if (LIKELY(hasAnyArrayStorage(indexingType()))) 710 return m_butterfly->arrayStorage(); 711 712 return ensureArrayStorageSlow(vm); 713 } 714 715 static size_t offsetOfInlineStorage(); 716 717 static ptrdiff_t butterflyOffset() 718 { 719 return OBJECT_OFFSETOF(JSObject, m_butterfly); 720 } 721 722 void* butterflyAddress() 723 { 724 return &m_butterfly; 725 } 726 727 DECLARE_EXPORT_INFO; 728 729protected: 730 void finishCreation(VM& vm) 731 { 732 Base::finishCreation(vm); 733 ASSERT(inherits(info())); 734 ASSERT(!structure()->outOfLineCapacity()); 735 ASSERT(structure()->isEmpty()); 736 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); 737 ASSERT(structure()->isObject()); 738 ASSERT(classInfo()); 739 } 740 741 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 742 { 743 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 744 } 745 746 // To instantiate objects you likely want JSFinalObject, below. 747 // To create derived types you likely want JSNonFinalObject, below. 748 JSObject(VM&, Structure*, Butterfly* = 0); 749 750 void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); 751 void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize); 752 753 // Call this if you know that the object is in a mode where it has array 754 // storage. This will assert otherwise. 755 ArrayStorage* arrayStorage() 756 { 757 ASSERT(hasAnyArrayStorage(indexingType())); 758 return m_butterfly->arrayStorage(); 759 } 760 761 // Call this if you want to predicate some actions on whether or not the 762 // object is in a mode where it has array storage. 763 ArrayStorage* arrayStorageOrNull() 764 { 765 switch (indexingType()) { 766 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 767 return m_butterfly->arrayStorage(); 768 769 default: 770 return 0; 771 } 772 } 773 774 size_t butterflyTotalSize(); 775 size_t butterflyPreCapacity(); 776 777 Butterfly* createInitialUndecided(VM&, unsigned length); 778 ContiguousJSValues createInitialInt32(VM&, unsigned length); 779 ContiguousDoubles createInitialDouble(VM&, unsigned length); 780 ContiguousJSValues createInitialContiguous(VM&, unsigned length); 781 782 void convertUndecidedForValue(VM&, JSValue); 783 void createInitialForValueAndSet(VM&, unsigned index, JSValue); 784 void convertInt32ForValue(VM&, JSValue); 785 786 ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength); 787 ArrayStorage* createInitialArrayStorage(VM&); 788 789 ContiguousJSValues convertUndecidedToInt32(VM&); 790 ContiguousDoubles convertUndecidedToDouble(VM&); 791 ContiguousJSValues convertUndecidedToContiguous(VM&); 792 ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); 793 ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition); 794 ArrayStorage* convertUndecidedToArrayStorage(VM&); 795 796 ContiguousDoubles convertInt32ToDouble(VM&); 797 ContiguousJSValues convertInt32ToContiguous(VM&); 798 ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); 799 ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition); 800 ArrayStorage* convertInt32ToArrayStorage(VM&); 801 802 ContiguousJSValues convertDoubleToContiguous(VM&); 803 ContiguousJSValues rageConvertDoubleToContiguous(VM&); 804 ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); 805 ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition); 806 ArrayStorage* convertDoubleToArrayStorage(VM&); 807 808 ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); 809 ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition); 810 ArrayStorage* convertContiguousToArrayStorage(VM&); 811 812 813 ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&); 814 815 bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); 816 817 template<IndexingType indexingShape> 818 void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); 819 void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); 820 821 bool increaseVectorLength(VM&, unsigned newLength); 822 void deallocateSparseIndexMap(); 823 bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException); 824 SparseArrayValueMap* allocateSparseIndexMap(VM&); 825 826 void notifyPresenceOfIndexedAccessors(VM&); 827 828 bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); 829 830 // Call this if you want setIndexQuickly to succeed and you're sure that 831 // the array is contiguous. 832 void ensureLength(VM& vm, unsigned length) 833 { 834 ASSERT(length < MAX_ARRAY_INDEX); 835 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); 836 837 if (m_butterfly->vectorLength() < length) 838 ensureLengthSlow(vm, length); 839 840 if (m_butterfly->publicLength() < length) 841 m_butterfly->setPublicLength(length); 842 } 843 844 template<IndexingType indexingShape> 845 unsigned countElements(Butterfly*); 846 847 // This is relevant to undecided, int32, double, and contiguous. 848 unsigned countElements(); 849 850 // This strange method returns a pointer to the start of the indexed data 851 // as if it contained JSValues. But it won't always contain JSValues. 852 // Make sure you cast this to the appropriate type before using. 853 template<IndexingType indexingType> 854 ContiguousJSValues indexingData() 855 { 856 switch (indexingType) { 857 case ALL_INT32_INDEXING_TYPES: 858 case ALL_DOUBLE_INDEXING_TYPES: 859 case ALL_CONTIGUOUS_INDEXING_TYPES: 860 return m_butterfly->contiguous(); 861 862 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 863 return m_butterfly->arrayStorage()->vector(); 864 865 default: 866 CRASH(); 867 return ContiguousJSValues(); 868 } 869 } 870 871 ContiguousJSValues currentIndexingData() 872 { 873 switch (indexingType()) { 874 case ALL_INT32_INDEXING_TYPES: 875 case ALL_CONTIGUOUS_INDEXING_TYPES: 876 return m_butterfly->contiguous(); 877 878 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 879 return m_butterfly->arrayStorage()->vector(); 880 881 default: 882 CRASH(); 883 return ContiguousJSValues(); 884 } 885 } 886 887 JSValue getHolyIndexQuickly(unsigned i) 888 { 889 ASSERT(i < m_butterfly->vectorLength()); 890 switch (indexingType()) { 891 case ALL_INT32_INDEXING_TYPES: 892 case ALL_CONTIGUOUS_INDEXING_TYPES: 893 return m_butterfly->contiguous()[i].get(); 894 case ALL_DOUBLE_INDEXING_TYPES: { 895 double value = m_butterfly->contiguousDouble()[i]; 896 if (value == value) 897 return JSValue(JSValue::EncodeAsDouble, value); 898 return JSValue(); 899 } 900 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 901 return m_butterfly->arrayStorage()->m_vector[i].get(); 902 default: 903 CRASH(); 904 return JSValue(); 905 } 906 } 907 908 template<IndexingType indexingType> 909 unsigned relevantLength() 910 { 911 switch (indexingType) { 912 case ALL_INT32_INDEXING_TYPES: 913 case ALL_DOUBLE_INDEXING_TYPES: 914 case ALL_CONTIGUOUS_INDEXING_TYPES: 915 return m_butterfly->publicLength(); 916 917 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 918 return std::min( 919 m_butterfly->arrayStorage()->length(), 920 m_butterfly->arrayStorage()->vectorLength()); 921 922 default: 923 CRASH(); 924 return 0; 925 } 926 } 927 928 unsigned currentRelevantLength() 929 { 930 switch (indexingType()) { 931 case ALL_INT32_INDEXING_TYPES: 932 case ALL_DOUBLE_INDEXING_TYPES: 933 case ALL_CONTIGUOUS_INDEXING_TYPES: 934 return m_butterfly->publicLength(); 935 936 case ALL_ARRAY_STORAGE_INDEXING_TYPES: 937 return std::min( 938 m_butterfly->arrayStorage()->length(), 939 m_butterfly->arrayStorage()->vectorLength()); 940 941 default: 942 CRASH(); 943 return 0; 944 } 945 } 946 947private: 948 friend class LLIntOffsetsExtractor; 949 950 // Nobody should ever ask any of these questions on something already known to be a JSObject. 951 using JSCell::isAPIValueWrapper; 952 using JSCell::isGetterSetter; 953 void getObject(); 954 void getString(ExecState* exec); 955 void isObject(); 956 void isString(); 957 958 Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize); 959 960 ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); 961 962 template<PutMode> 963 bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*); 964 965 bool inlineGetOwnPropertySlot(VM&, Structure&, PropertyName, PropertySlot&); 966 JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset); 967 void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure&); 968 969 const HashTableValue* findPropertyHashEntry(VM&, PropertyName) const; 970 971 void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); 972 973 void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 974 bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*); 975 JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode); 976 977 unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); 978 unsigned getNewVectorLength(unsigned desiredLength); 979 980 ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength); 981 982 JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue); 983 JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); 984 JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); 985 986 void ensureLengthSlow(VM&, unsigned length); 987 988 ContiguousJSValues ensureInt32Slow(VM&); 989 ContiguousDoubles ensureDoubleSlow(VM&); 990 ContiguousJSValues ensureContiguousSlow(VM&); 991 ContiguousJSValues rageEnsureContiguousSlow(VM&); 992 ArrayStorage* ensureArrayStorageSlow(VM&); 993 994 enum DoubleToContiguousMode { EncodeValueAsDouble, RageConvertDoubleToValue }; 995 template<DoubleToContiguousMode mode> 996 ContiguousJSValues genericConvertDoubleToContiguous(VM&); 997 ContiguousJSValues ensureContiguousSlow(VM&, DoubleToContiguousMode); 998 999protected: 1000 CopyWriteBarrier<Butterfly> m_butterfly; 1001#if USE(JSVALUE32_64) 1002private: 1003 uint32_t m_padding; 1004#endif 1005}; 1006 1007// JSNonFinalObject is a type of JSObject that has some internal storage, 1008// but also preserves some space in the collector cell for additional 1009// data members in derived types. 1010class JSNonFinalObject : public JSObject { 1011 friend class JSObject; 1012 1013public: 1014 typedef JSObject Base; 1015 1016 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 1017 { 1018 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 1019 } 1020 1021protected: 1022 explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0) 1023 : JSObject(vm, structure, butterfly) 1024 { 1025 } 1026 1027 void finishCreation(VM& vm) 1028 { 1029 Base::finishCreation(vm); 1030 ASSERT(!this->structure()->totalStorageCapacity()); 1031 ASSERT(classInfo()); 1032 } 1033}; 1034 1035class JSFinalObject; 1036 1037// JSFinalObject is a type of JSObject that contains sufficent internal 1038// storage to fully make use of the colloctor cell containing it. 1039class JSFinalObject : public JSObject { 1040 friend class JSObject; 1041 1042public: 1043 typedef JSObject Base; 1044 1045 static size_t allocationSize(size_t inlineCapacity) 1046 { 1047 return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>); 1048 } 1049 1050 static const unsigned defaultSize = 64; 1051 static inline unsigned defaultInlineCapacity() 1052 { 1053 return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); 1054 } 1055 1056 static const unsigned maxSize = 512; 1057 static inline unsigned maxInlineCapacity() 1058 { 1059 return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); 1060 } 1061 1062 static JSFinalObject* create(ExecState*, Structure*); 1063 static JSFinalObject* create(VM&, Structure*); 1064 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity) 1065 { 1066 return Structure::create(vm, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), info(), NonArray, inlineCapacity); 1067 } 1068 1069 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); 1070 1071 DECLARE_EXPORT_INFO; 1072 1073protected: 1074 void visitChildrenCommon(SlotVisitor&); 1075 1076 void finishCreation(VM& vm) 1077 { 1078 Base::finishCreation(vm); 1079 ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity()); 1080 ASSERT(classInfo()); 1081 } 1082 1083private: 1084 friend class LLIntOffsetsExtractor; 1085 1086 explicit JSFinalObject(VM& vm, Structure* structure) 1087 : JSObject(vm, structure) 1088 { 1089 } 1090 1091 static const unsigned StructureFlags = JSObject::StructureFlags; 1092}; 1093 1094inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure) 1095{ 1096 JSFinalObject* finalObject = new ( 1097 NotNull, 1098 allocateCell<JSFinalObject>( 1099 *exec->heap(), 1100 allocationSize(structure->inlineCapacity()) 1101 ) 1102 ) JSFinalObject(exec->vm(), structure); 1103 finalObject->finishCreation(exec->vm()); 1104 return finalObject; 1105} 1106 1107inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure) 1108{ 1109 JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure); 1110 finalObject->finishCreation(vm); 1111 return finalObject; 1112} 1113 1114inline bool isJSFinalObject(JSCell* cell) 1115{ 1116 return cell->classInfo() == JSFinalObject::info(); 1117} 1118 1119inline bool isJSFinalObject(JSValue value) 1120{ 1121 return value.isCell() && isJSFinalObject(value.asCell()); 1122} 1123 1124inline size_t JSObject::offsetOfInlineStorage() 1125{ 1126 return sizeof(JSObject); 1127} 1128 1129inline bool JSObject::isGlobalObject() const 1130{ 1131 return type() == GlobalObjectType; 1132} 1133 1134inline bool JSObject::isVariableObject() const 1135{ 1136 return type() == GlobalObjectType || type() == ActivationObjectType; 1137} 1138 1139inline bool JSObject::isStaticScopeObject() const 1140{ 1141 JSType type = this->type(); 1142 return type == NameScopeObjectType || type == ActivationObjectType; 1143} 1144 1145 1146inline bool JSObject::isNameScopeObject() const 1147{ 1148 return type() == NameScopeObjectType; 1149} 1150 1151inline bool JSObject::isActivationObject() const 1152{ 1153 return type() == ActivationObjectType; 1154} 1155 1156inline bool JSObject::isErrorInstance() const 1157{ 1158 return type() == ErrorInstanceType; 1159} 1160 1161inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly) 1162{ 1163 ASSERT(structure); 1164 ASSERT(!butterfly == (!structure->outOfLineCapacity() && !structure->hasIndexingHeader(this))); 1165 m_butterfly.set(vm, this, butterfly); 1166 setStructure(vm, structure); 1167} 1168 1169inline void JSObject::setStructure(VM& vm, Structure* structure) 1170{ 1171 ASSERT(structure); 1172 ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this))); 1173 JSCell::setStructure(vm, structure); 1174} 1175 1176inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly) 1177{ 1178 m_butterfly.set(vm, this, butterfly); 1179} 1180 1181inline CallType getCallData(JSValue value, CallData& callData) 1182{ 1183 CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone; 1184 ASSERT(result == CallTypeNone || value.isValidCallee()); 1185 return result; 1186} 1187 1188inline ConstructType getConstructData(JSValue value, ConstructData& constructData) 1189{ 1190 ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone; 1191 ASSERT(result == ConstructTypeNone || value.isValidCallee()); 1192 return result; 1193} 1194 1195inline JSObject* asObject(JSCell* cell) 1196{ 1197 ASSERT(cell->isObject()); 1198 return jsCast<JSObject*>(cell); 1199} 1200 1201inline JSObject* asObject(JSValue value) 1202{ 1203 return asObject(value.asCell()); 1204} 1205 1206inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly) 1207 : JSCell(vm, structure) 1208 , m_butterfly(vm, this, butterfly) 1209{ 1210 vm.heap.ascribeOwner(this, butterfly); 1211} 1212 1213inline JSValue JSObject::prototype() const 1214{ 1215 return structure()->storedPrototype(); 1216} 1217 1218ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) 1219{ 1220 unsigned attributes; 1221 JSCell* specific; 1222 PropertyOffset offset = structure.get(vm, propertyName, attributes, specific); 1223 if (!isValidOffset(offset)) 1224 return false; 1225 1226 JSValue value = getDirect(offset); 1227 if (structure.hasGetterSetterProperties() && value.isGetterSetter()) 1228 fillGetterPropertySlot(slot, value, attributes, offset); 1229 else if (structure.hasCustomGetterSetterProperties() && value.isCustomGetterSetter()) 1230 fillCustomGetterPropertySlot(slot, value, attributes, structure); 1231 else 1232 slot.setValue(this, attributes, value, offset); 1233 1234 return true; 1235} 1236 1237ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JSValue customGetterSetter, unsigned attributes, Structure& structure) 1238{ 1239 if (structure.isDictionary()) { 1240 slot.setCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); 1241 return; 1242 } 1243 slot.setCacheableCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); 1244} 1245 1246// It may seem crazy to inline a function this large, especially a virtual function, 1247// but it makes a big difference to property lookup that derived classes can inline their 1248// base class call to this. 1249ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) 1250{ 1251 VM& vm = exec->vm(); 1252 Structure& structure = *object->structure(vm); 1253 if (object->inlineGetOwnPropertySlot(vm, structure, propertyName, slot)) 1254 return true; 1255 unsigned index = propertyName.asIndex(); 1256 if (index != PropertyName::NotAnIndex) 1257 return getOwnPropertySlotByIndex(object, exec, index, slot); 1258 return false; 1259} 1260 1261ALWAYS_INLINE bool JSObject::fastGetOwnPropertySlot(ExecState* exec, VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) 1262{ 1263 if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(inlineTypeFlags()))) 1264 return inlineGetOwnPropertySlot(vm, structure, propertyName, slot); 1265 return structure.classInfo()->methodTable.getOwnPropertySlot(this, exec, propertyName, slot); 1266} 1267 1268// It may seem crazy to inline a function this large but it makes a big difference 1269// since this is function very hot in variable lookup 1270ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) 1271{ 1272 VM& vm = exec->vm(); 1273 auto& structureIDTable = vm.heap.structureIDTable(); 1274 JSObject* object = this; 1275 while (true) { 1276 Structure& structure = *structureIDTable.get(object->structureID()); 1277 if (object->fastGetOwnPropertySlot(exec, vm, structure, propertyName, slot)) 1278 return true; 1279 JSValue prototype = structure.storedPrototype(); 1280 if (!prototype.isObject()) 1281 break; 1282 object = asObject(prototype); 1283 } 1284 1285 unsigned index = propertyName.asIndex(); 1286 if (index != PropertyName::NotAnIndex) 1287 return getPropertySlot(exec, index, slot); 1288 return false; 1289} 1290 1291ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 1292{ 1293 VM& vm = exec->vm(); 1294 auto& structureIDTable = vm.heap.structureIDTable(); 1295 JSObject* object = this; 1296 while (true) { 1297 Structure& structure = *structureIDTable.get(object->structureID()); 1298 if (structure.classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot)) 1299 return true; 1300 JSValue prototype = structure.storedPrototype(); 1301 if (!prototype.isObject()) 1302 return false; 1303 object = asObject(prototype); 1304 } 1305} 1306 1307inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const 1308{ 1309 PropertySlot slot(this); 1310 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) 1311 return slot.getValue(exec, propertyName); 1312 1313 return jsUndefined(); 1314} 1315 1316inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const 1317{ 1318 PropertySlot slot(this); 1319 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) 1320 return slot.getValue(exec, propertyName); 1321 1322 return jsUndefined(); 1323} 1324 1325template<JSObject::PutMode mode> 1326inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot, JSCell* specificFunction) 1327{ 1328 ASSERT(value); 1329 ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); 1330 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 1331 ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex); 1332 1333 Structure* structure = this->structure(vm); 1334 if (structure->isDictionary()) { 1335 unsigned currentAttributes; 1336 JSCell* currentSpecificFunction; 1337 PropertyOffset offset = structure->get(vm, propertyName, currentAttributes, currentSpecificFunction); 1338 if (offset != invalidOffset) { 1339 // If there is currently a specific function, and there now either isn't, 1340 // or the new value is different, then despecify. 1341 if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) 1342 structure->despecifyDictionaryFunction(vm, propertyName); 1343 if ((mode == PutModePut) && currentAttributes & ReadOnly) 1344 return false; 1345 1346 putDirect(vm, offset, value); 1347 // At this point, the objects structure only has a specific value set if previously there 1348 // had been one set, and if the new value being specified is the same (otherwise we would 1349 // have despecified, above). So, if currentSpecificFunction is not set, or if the new 1350 // value is different (or there is no new value), then the slot now has no value - and 1351 // as such it is cachable. 1352 // If there was previously a value, and the new value is the same, then we cannot cache. 1353 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) 1354 slot.setExistingProperty(this, offset); 1355 return true; 1356 } 1357 1358 if ((mode == PutModePut) && !isExtensible()) 1359 return false; 1360 1361 DeferGC deferGC(vm.heap); 1362 Butterfly* newButterfly = butterfly(); 1363 if (this->structure()->putWillGrowOutOfLineStorage()) 1364 newButterfly = growOutOfLineStorage(vm, this->structure()->outOfLineCapacity(), this->structure()->suggestedNewOutOfLineStorageCapacity()); 1365 offset = this->structure()->addPropertyWithoutTransition(vm, propertyName, attributes, specificFunction); 1366 setStructureAndButterfly(vm, this->structure(), newButterfly); 1367 1368 validateOffset(offset); 1369 ASSERT(this->structure()->isValidOffset(offset)); 1370 putDirect(vm, offset, value); 1371 // See comment on setNewProperty call below. 1372 if (!specificFunction) 1373 slot.setNewProperty(this, offset); 1374 if (attributes & ReadOnly) 1375 this->structure()->setContainsReadOnlyProperties(); 1376 return true; 1377 } 1378 1379 PropertyOffset offset; 1380 size_t currentCapacity = this->structure()->outOfLineCapacity(); 1381 if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { 1382 DeferGC deferGC(vm.heap); 1383 Butterfly* newButterfly = butterfly(); 1384 if (currentCapacity != structure->outOfLineCapacity()) { 1385 ASSERT(structure != this->structure()); 1386 newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); 1387 } 1388 1389 validateOffset(offset); 1390 ASSERT(structure->isValidOffset(offset)); 1391 setStructureAndButterfly(vm, structure, newButterfly); 1392 putDirect(vm, offset, value); 1393 // This is a new property; transitions with specific values are not currently cachable, 1394 // so leave the slot in an uncachable state. 1395 if (!specificFunction) 1396 slot.setNewProperty(this, offset); 1397 return true; 1398 } 1399 1400 unsigned currentAttributes; 1401 JSCell* currentSpecificFunction; 1402 offset = structure->get(vm, propertyName, currentAttributes, currentSpecificFunction); 1403 if (offset != invalidOffset) { 1404 if ((mode == PutModePut) && currentAttributes & ReadOnly) 1405 return false; 1406 1407 // There are three possibilities here: 1408 // (1) There is an existing specific value set, and we're overwriting with *the same value*. 1409 // * Do nothing - no need to despecify, but that means we can't cache (a cached 1410 // put could write a different value). Leave the slot in an uncachable state. 1411 // (2) There is a specific value currently set, but we're writing a different value. 1412 // * First, we have to despecify. Having done so, this is now a regular slot 1413 // with no specific value, so go ahead & cache like normal. 1414 // (3) Normal case, there is no specific value set. 1415 // * Go ahead & cache like normal. 1416 if (currentSpecificFunction) { 1417 // case (1) Do the put, then return leaving the slot uncachable. 1418 if (specificFunction == currentSpecificFunction) { 1419 putDirect(vm, offset, value); 1420 return true; 1421 } 1422 // case (2) Despecify, fall through to (3). 1423 setStructure(vm, Structure::despecifyFunctionTransition(vm, structure, propertyName)); 1424 } 1425 1426 // case (3) set the slot, do the put, return. 1427 slot.setExistingProperty(this, offset); 1428 putDirect(vm, offset, value); 1429 return true; 1430 } 1431 1432 if ((mode == PutModePut) && !isExtensible()) 1433 return false; 1434 1435 structure = Structure::addPropertyTransition(vm, structure, propertyName, attributes, specificFunction, offset, slot.context()); 1436 1437 validateOffset(offset); 1438 ASSERT(structure->isValidOffset(offset)); 1439 setStructureAndReallocateStorageIfNecessary(vm, structure); 1440 1441 putDirect(vm, offset, value); 1442 // This is a new property; transitions with specific values are not currently cachable, 1443 // so leave the slot in an uncachable state. 1444 if (!specificFunction) 1445 slot.setNewProperty(this, offset); 1446 if (attributes & ReadOnly) 1447 structure->setContainsReadOnlyProperties(); 1448 return true; 1449} 1450 1451inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure) 1452{ 1453 ASSERT(oldCapacity <= newStructure->outOfLineCapacity()); 1454 1455 if (oldCapacity == newStructure->outOfLineCapacity()) { 1456 setStructure(vm, newStructure); 1457 return; 1458 } 1459 1460 DeferGC deferGC(vm.heap); 1461 Butterfly* newButterfly = growOutOfLineStorage( 1462 vm, oldCapacity, newStructure->outOfLineCapacity()); 1463 setStructureAndButterfly(vm, newStructure, newButterfly); 1464} 1465 1466inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure) 1467{ 1468 setStructureAndReallocateStorageIfNecessary( 1469 vm, structure(vm)->outOfLineCapacity(), newStructure); 1470} 1471 1472inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 1473{ 1474 ASSERT(value); 1475 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 1476 ASSERT(!structure()->hasGetterSetterProperties()); 1477 ASSERT(!structure()->hasCustomGetterSetterProperties()); 1478 1479 return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)); 1480} 1481 1482inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) 1483{ 1484 ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); 1485 ASSERT(!value.isCustomGetterSetter()); 1486 PutPropertySlot slot(this); 1487 putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value)); 1488} 1489 1490inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 1491{ 1492 ASSERT(!value.isGetterSetter()); 1493 ASSERT(!value.isCustomGetterSetter()); 1494 putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot, getCallableObject(value)); 1495} 1496 1497inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) 1498{ 1499 DeferGC deferGC(vm.heap); 1500 ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); 1501 ASSERT(!value.isCustomGetterSetter()); 1502 Butterfly* newButterfly = m_butterfly.get(); 1503 if (structure()->putWillGrowOutOfLineStorage()) 1504 newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); 1505 PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes, getCallableObject(value)); 1506 setStructureAndButterfly(vm, structure(), newButterfly); 1507 putDirect(vm, offset, value); 1508} 1509 1510inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const 1511{ 1512 return methodTable()->defaultValue(this, exec, preferredType); 1513} 1514 1515ALWAYS_INLINE JSObject* Register::function() const 1516{ 1517 if (!jsValue()) 1518 return 0; 1519 return asObject(jsValue()); 1520} 1521 1522ALWAYS_INLINE Register Register::withCallee(JSObject* callee) 1523{ 1524 Register r; 1525 r = JSValue(callee); 1526 return r; 1527} 1528 1529inline size_t offsetInButterfly(PropertyOffset offset) 1530{ 1531 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); 1532} 1533 1534inline size_t JSObject::butterflyPreCapacity() 1535{ 1536 if (UNLIKELY(hasIndexingHeader())) 1537 return butterfly()->indexingHeader()->preCapacity(structure()); 1538 return 0; 1539} 1540 1541inline size_t JSObject::butterflyTotalSize() 1542{ 1543 Structure* structure = this->structure(); 1544 Butterfly* butterfly = this->butterfly(); 1545 size_t preCapacity; 1546 size_t indexingPayloadSizeInBytes; 1547 bool hasIndexingHeader = this->hasIndexingHeader(); 1548 1549 if (UNLIKELY(hasIndexingHeader)) { 1550 preCapacity = butterfly->indexingHeader()->preCapacity(structure); 1551 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); 1552 } else { 1553 preCapacity = 0; 1554 indexingPayloadSizeInBytes = 0; 1555 } 1556 1557 return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes); 1558} 1559 1560// Helpers for patching code where you want to emit a load or store and 1561// the base is: 1562// For inline offsets: a pointer to the out-of-line storage pointer. 1563// For out-of-line offsets: the base of the out-of-line storage. 1564inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset) 1565{ 1566 if (isOutOfLineOffset(offset)) 1567 return sizeof(EncodedJSValue) * offsetInButterfly(offset); 1568 return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); 1569} 1570 1571// Returns the maximum offset (away from zero) a load instruction will encode. 1572inline size_t maxOffsetRelativeToPatchedStorage(PropertyOffset offset) 1573{ 1574 ptrdiff_t addressOffset = static_cast<ptrdiff_t>(offsetRelativeToPatchedStorage(offset)); 1575#if USE(JSVALUE32_64) 1576 if (addressOffset >= 0) 1577 return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); 1578#endif 1579 return static_cast<size_t>(addressOffset); 1580} 1581 1582inline int indexRelativeToBase(PropertyOffset offset) 1583{ 1584 if (isOutOfLineOffset(offset)) 1585 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); 1586 ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue))); 1587 return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset); 1588} 1589 1590inline int offsetRelativeToBase(PropertyOffset offset) 1591{ 1592 if (isOutOfLineOffset(offset)) 1593 return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage(); 1594 return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); 1595} 1596 1597COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment); 1598 1599ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) 1600{ 1601 return Identifier(&vm, name); 1602} 1603 1604ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) 1605{ 1606 return name; 1607} 1608 1609// Helper for defining native functions, if you're not using a static hash table. 1610// Use this macro from within finishCreation() methods in prototypes. This assumes 1611// you've defined variables called exec, globalObject, and vm, and they 1612// have the expected meanings. 1613#define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \ 1614 putDirectNativeFunction(\ 1615 vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ 1616 (intrinsic), (attributes)) 1617 1618// As above, but this assumes that the function you're defining doesn't have an 1619// intrinsic. 1620#define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \ 1621 JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic) 1622 1623ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, PropertyName propertyName) const 1624{ 1625 if (m_propertyType == TypeValue) 1626 return JSValue::decode(m_data.value); 1627 if (m_propertyType == TypeGetter) 1628 return functionGetter(exec); 1629 return JSValue::decode(m_data.custom.getValue(exec, slotBase(), JSValue::encode(m_thisValue), propertyName)); 1630} 1631 1632ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, unsigned propertyName) const 1633{ 1634 if (m_propertyType == TypeValue) 1635 return JSValue::decode(m_data.value); 1636 if (m_propertyType == TypeGetter) 1637 return functionGetter(exec); 1638 return JSValue::decode(m_data.custom.getValue(exec, slotBase(), JSValue::encode(m_thisValue), Identifier::from(exec, propertyName))); 1639} 1640 1641} // namespace JSC 1642 1643#endif // JSObject_h 1644