]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSObject.h
JavaScriptCore-7600.1.4.11.8.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.h
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
51 namespace JSC {
52
53 inline JSCell* getJSFunction(JSValue value)
54 {
55 if (value.isCell() && (value.asCell()->type() == JSFunctionType))
56 return value.asCell();
57 return 0;
58 }
59
60 JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*);
61
62 inline JSCell* getCallableObject(JSValue value)
63 {
64 if (!value.isCell())
65 return 0;
66 return getCallableObjectSlow(value.asCell());
67 }
68
69 class GetterSetter;
70 class InternalFunction;
71 class JSFunction;
72 class LLIntOffsetsExtractor;
73 class MarkedBlock;
74 class PropertyDescriptor;
75 class PropertyNameArray;
76 class Structure;
77 struct HashTable;
78 struct HashTableValue;
79
80 JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&);
81 extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError;
82
83 COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
84 COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
85 COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
86 COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
87 COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute);
88 COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);
89
90 class JSFinalObject;
91
92 class 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
105 public:
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
729 protected:
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
947 private:
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
999 protected:
1000 CopyWriteBarrier<Butterfly> m_butterfly;
1001 #if USE(JSVALUE32_64)
1002 private:
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.
1010 class JSNonFinalObject : public JSObject {
1011 friend class JSObject;
1012
1013 public:
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
1021 protected:
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
1035 class 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.
1039 class JSFinalObject : public JSObject {
1040 friend class JSObject;
1041
1042 public:
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
1073 protected:
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
1083 private:
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
1094 inline 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
1107 inline 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
1114 inline bool isJSFinalObject(JSCell* cell)
1115 {
1116 return cell->classInfo() == JSFinalObject::info();
1117 }
1118
1119 inline bool isJSFinalObject(JSValue value)
1120 {
1121 return value.isCell() && isJSFinalObject(value.asCell());
1122 }
1123
1124 inline size_t JSObject::offsetOfInlineStorage()
1125 {
1126 return sizeof(JSObject);
1127 }
1128
1129 inline bool JSObject::isGlobalObject() const
1130 {
1131 return type() == GlobalObjectType;
1132 }
1133
1134 inline bool JSObject::isVariableObject() const
1135 {
1136 return type() == GlobalObjectType || type() == ActivationObjectType;
1137 }
1138
1139 inline bool JSObject::isStaticScopeObject() const
1140 {
1141 JSType type = this->type();
1142 return type == NameScopeObjectType || type == ActivationObjectType;
1143 }
1144
1145
1146 inline bool JSObject::isNameScopeObject() const
1147 {
1148 return type() == NameScopeObjectType;
1149 }
1150
1151 inline bool JSObject::isActivationObject() const
1152 {
1153 return type() == ActivationObjectType;
1154 }
1155
1156 inline bool JSObject::isErrorInstance() const
1157 {
1158 return type() == ErrorInstanceType;
1159 }
1160
1161 inline 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
1169 inline 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
1176 inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly)
1177 {
1178 m_butterfly.set(vm, this, butterfly);
1179 }
1180
1181 inline 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
1188 inline 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
1195 inline JSObject* asObject(JSCell* cell)
1196 {
1197 ASSERT(cell->isObject());
1198 return jsCast<JSObject*>(cell);
1199 }
1200
1201 inline JSObject* asObject(JSValue value)
1202 {
1203 return asObject(value.asCell());
1204 }
1205
1206 inline 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
1213 inline JSValue JSObject::prototype() const
1214 {
1215 return structure()->storedPrototype();
1216 }
1217
1218 ALWAYS_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
1237 ALWAYS_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.
1249 ALWAYS_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
1261 ALWAYS_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
1270 ALWAYS_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
1291 ALWAYS_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
1307 inline 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
1316 inline 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
1325 template<JSObject::PutMode mode>
1326 inline 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
1451 inline 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
1466 inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure)
1467 {
1468 setStructureAndReallocateStorageIfNecessary(
1469 vm, structure(vm)->outOfLineCapacity(), newStructure);
1470 }
1471
1472 inline 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
1482 inline 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
1490 inline 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
1497 inline 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
1510 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
1511 {
1512 return methodTable()->defaultValue(this, exec, preferredType);
1513 }
1514
1515 ALWAYS_INLINE JSObject* Register::function() const
1516 {
1517 if (!jsValue())
1518 return 0;
1519 return asObject(jsValue());
1520 }
1521
1522 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
1523 {
1524 Register r;
1525 r = JSValue(callee);
1526 return r;
1527 }
1528
1529 inline size_t offsetInButterfly(PropertyOffset offset)
1530 {
1531 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1532 }
1533
1534 inline size_t JSObject::butterflyPreCapacity()
1535 {
1536 if (UNLIKELY(hasIndexingHeader()))
1537 return butterfly()->indexingHeader()->preCapacity(structure());
1538 return 0;
1539 }
1540
1541 inline 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.
1564 inline 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.
1572 inline 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
1582 inline 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
1590 inline 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
1597 COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment);
1598
1599 ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name)
1600 {
1601 return Identifier(&vm, name);
1602 }
1603
1604 ALWAYS_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
1623 ALWAYS_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
1632 ALWAYS_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