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