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