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 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 "ClassInfo.h"
28 #include "CommonIdentifiers.h"
29 #include "Completion.h"
30 #include "CallFrame.h"
31 #include "JSCell.h"
32 #include "MarkStack.h"
33 #include "PropertySlot.h"
34 #include "PutPropertySlot.h"
35 #include "ScopeChain.h"
36 #include "Structure.h"
37 #include "JSGlobalData.h"
38 #include "JSString.h"
39 #include <wtf/StdLibExtras.h>
40
41 namespace JSC {
42
getJSFunction(JSGlobalData & globalData,JSValue value)43 inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value)
44 {
45 if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr))
46 return value.asCell();
47 return 0;
48 }
49
50 class HashEntry;
51 class InternalFunction;
52 class PropertyDescriptor;
53 class PropertyNameArray;
54 class Structure;
55 struct HashTable;
56
57 JSObject* throwTypeError(ExecState*, const UString&);
58 extern const char* StrictModeReadonlyPropertyWriteError;
59
60 // ECMA 262-3 8.6.1
61 // Property attributes
62 enum Attribute {
63 None = 0,
64 ReadOnly = 1 << 1, // property can be only read, not written
65 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..)
66 DontDelete = 1 << 3, // property can't be deleted
67 Function = 1 << 4, // property is a function - only used by static hashtables
68 Getter = 1 << 5, // property is a getter
69 Setter = 1 << 6 // property is a setter
70 };
71
72 typedef WriteBarrierBase<Unknown>* PropertyStorage;
73 typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage;
74
75 class JSObject : public JSCell {
76 friend class BatchedTransitionOptimizer;
77 friend class JIT;
78 friend class JSCell;
79 friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
80
81 public:
82 virtual void markChildren(MarkStack&);
83 ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack);
84
85 // The inline virtual destructor cannot be the first virtual function declared
86 // in the class as it results in the vtable being generated as a weak symbol
87 virtual ~JSObject();
88
89 JSValue prototype() const;
90 void setPrototype(JSGlobalData&, JSValue prototype);
91 bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
92
93 void setStructure(JSGlobalData&, Structure*);
94 Structure* inheritorID(JSGlobalData&);
95
96 virtual UString className() const;
97
98 JSValue get(ExecState*, const Identifier& propertyName) const;
99 JSValue get(ExecState*, unsigned propertyName) const;
100
101 bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
102 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
103 bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
104
105 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
106 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
107 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
108
109 virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&);
110 virtual void put(ExecState*, unsigned propertyName, JSValue value);
111
112 virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
113 virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes);
114 virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes);
115 virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
116 virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes);
117 virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes);
118
119 bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
120
121 bool hasProperty(ExecState*, const Identifier& propertyName) const;
122 bool hasProperty(ExecState*, unsigned propertyName) const;
123 bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;
124
125 virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
126 virtual bool deleteProperty(ExecState*, unsigned propertyName);
127
128 virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const;
129
130 virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty);
131
132 virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
133 virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
134
135 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
136 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
137 virtual bool toBoolean(ExecState*) const;
138 virtual double toNumber(ExecState*) const;
139 virtual UString toString(ExecState*) const;
140 virtual JSObject* toObject(ExecState*, JSGlobalObject*) const;
141
142 virtual JSObject* toThisObject(ExecState*) const;
143 virtual JSValue toStrictThisObject(ExecState*) const;
144 virtual JSObject* unwrappedObject();
145
146 bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const;
147
148 // This get function only looks at the property map.
getDirect(JSGlobalData & globalData,const Identifier & propertyName)149 JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const
150 {
151 size_t offset = m_structure->get(globalData, propertyName);
152 return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
153 }
154
getDirectLocation(JSGlobalData & globalData,const Identifier & propertyName)155 WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName)
156 {
157 size_t offset = m_structure->get(globalData, propertyName);
158 return offset != WTF::notFound ? locationForOffset(offset) : 0;
159 }
160
getDirectLocation(JSGlobalData & globalData,const Identifier & propertyName,unsigned & attributes)161 WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes)
162 {
163 JSCell* specificFunction;
164 size_t offset = m_structure->get(globalData, propertyName, attributes, specificFunction);
165 return offset != WTF::notFound ? locationForOffset(offset) : 0;
166 }
167
offsetForLocation(WriteBarrierBase<Unknown> * location)168 size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
169 {
170 return location - propertyStorage();
171 }
172
173 void transitionTo(JSGlobalData&, Structure*);
174
175 void removeDirect(JSGlobalData&, const Identifier& propertyName);
hasCustomProperties()176 bool hasCustomProperties() { return !m_structure->isEmpty(); }
hasGetterSetterProperties()177 bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); }
178
179 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
180 void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
181 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&);
182
183 void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr = 0);
184 void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr, bool checkReadOnly, PutPropertySlot&);
185 void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0);
186 void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0);
187
188 void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
189 void putDirectFunctionWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSCell* value, unsigned attr = 0);
190 void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0);
191 void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0);
192
193 // Fast access to known property offsets.
getDirectOffset(size_t offset)194 JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); }
putDirectOffset(JSGlobalData & globalData,size_t offset,JSValue value)195 void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
putUndefinedAtDirectOffset(size_t offset)196 void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
197
198 void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
199
200 virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
201 virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
202 virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
203 virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
204 virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
205
isGlobalObject()206 virtual bool isGlobalObject() const { return false; }
isVariableObject()207 virtual bool isVariableObject() const { return false; }
isActivationObject()208 virtual bool isActivationObject() const { return false; }
isStrictModeFunction()209 virtual bool isStrictModeFunction() const { return false; }
isErrorInstance()210 virtual bool isErrorInstance() const { return false; }
211
212 void seal(JSGlobalData&);
213 void freeze(JSGlobalData&);
214 void preventExtensions(JSGlobalData&);
isSealed(JSGlobalData & globalData)215 bool isSealed(JSGlobalData& globalData) { return m_structure->isSealed(globalData); }
isFrozen(JSGlobalData & globalData)216 bool isFrozen(JSGlobalData& globalData) { return m_structure->isFrozen(globalData); }
isExtensible()217 bool isExtensible() { return m_structure->isExtensible(); }
218
exceptionType()219 virtual ComplType exceptionType() const { return Throw; }
220
221 void allocatePropertyStorage(size_t oldSize, size_t newSize);
isUsingInlineStorage()222 bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage) == static_cast<const void*>(this + 1); }
223
224 static const unsigned baseExternalStorageCapacity = 16;
225
flattenDictionaryObject(JSGlobalData & globalData)226 void flattenDictionaryObject(JSGlobalData& globalData)
227 {
228 m_structure->flattenDictionaryStructure(globalData, this);
229 }
230
putAnonymousValue(JSGlobalData & globalData,unsigned index,JSValue value)231 void putAnonymousValue(JSGlobalData& globalData, unsigned index, JSValue value)
232 {
233 ASSERT(index < m_structure->anonymousSlotCount());
234 locationForOffset(index)->set(globalData, this, value);
235 }
clearAnonymousValue(unsigned index)236 void clearAnonymousValue(unsigned index)
237 {
238 ASSERT(index < m_structure->anonymousSlotCount());
239 locationForOffset(index)->clear();
240 }
getAnonymousValue(unsigned index)241 JSValue getAnonymousValue(unsigned index) const
242 {
243 ASSERT(index < m_structure->anonymousSlotCount());
244 return locationForOffset(index)->get();
245 }
246
247 static size_t offsetOfInlineStorage();
248
249 static JS_EXPORTDATA const ClassInfo s_info;
250
251 protected:
createStructure(JSGlobalData & globalData,JSValue prototype)252 static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
253 {
254 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
255 }
256
257 static const unsigned StructureFlags = 0;
258
putThisToAnonymousValue(unsigned index)259 void putThisToAnonymousValue(unsigned index)
260 {
261 locationForOffset(index)->setWithoutWriteBarrier(this);
262 }
263
264 // To instantiate objects you likely want JSFinalObject, below.
265 // To create derived types you likely want JSNonFinalObject, below.
266 JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
JSObject(VPtrStealingHackType,PropertyStorage inlineStorage)267 JSObject(VPtrStealingHackType, PropertyStorage inlineStorage)
268 : JSCell(VPtrStealingHack)
269 , m_propertyStorage(inlineStorage)
270 {
271 }
272
273 private:
274 // Nobody should ever ask any of these questions on something already known to be a JSObject.
275 using JSCell::isAPIValueWrapper;
276 using JSCell::isGetterSetter;
277 using JSCell::toObject;
278 void getObject();
279 void getString(ExecState* exec);
280 void isObject();
281 void isString();
282
propertyStorage()283 ConstPropertyStorage propertyStorage() const { return m_propertyStorage; }
propertyStorage()284 PropertyStorage propertyStorage() { return m_propertyStorage; }
285
locationForOffset(size_t offset)286 const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
287 {
288 return &propertyStorage()[offset];
289 }
290
locationForOffset(size_t offset)291 WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
292 {
293 return &propertyStorage()[offset];
294 }
295
296 bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
297 bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
298 void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0);
299
300 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
301
302 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
303 Structure* createInheritorID(JSGlobalData&);
304
305 PropertyStorage m_propertyStorage;
306 WriteBarrier<Structure> m_inheritorID;
307 };
308
309
310 #if USE(JSVALUE32_64)
311 #define JSNonFinalObject_inlineStorageCapacity 4
312 #define JSFinalObject_inlineStorageCapacity 6
313 #else
314 #define JSNonFinalObject_inlineStorageCapacity 2
315 #define JSFinalObject_inlineStorageCapacity 4
316 #endif
317
318 COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
319
320 // JSNonFinalObject is a type of JSObject that has some internal storage,
321 // but also preserves some space in the collector cell for additional
322 // data members in derived types.
323 class JSNonFinalObject : public JSObject {
324 friend class JSObject;
325
326 public:
createStructure(JSGlobalData & globalData,JSValue prototype)327 static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
328 {
329 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
330 }
331
332 protected:
JSNonFinalObject(VPtrStealingHackType)333 explicit JSNonFinalObject(VPtrStealingHackType)
334 : JSObject(VPtrStealingHack, m_inlineStorage)
335 {
336 }
337
JSNonFinalObject(JSGlobalData & globalData,Structure * structure)338 explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
339 : JSObject(globalData, structure, m_inlineStorage)
340 {
341 ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
342 ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
343 }
344
345 private:
346 WriteBarrierBase<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
347 };
348
349 // JSFinalObject is a type of JSObject that contains sufficent internal
350 // storage to fully make use of the colloctor cell containing it.
351 class JSFinalObject : public JSObject {
352 friend class JSObject;
353
354 public:
create(ExecState * exec,Structure * structure)355 static JSFinalObject* create(ExecState* exec, Structure* structure)
356 {
357 return new (exec) JSFinalObject(exec->globalData(), structure);
358 }
359
createStructure(JSGlobalData & globalData,JSValue prototype)360 static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
361 {
362 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
363 }
364
365 private:
JSFinalObject(JSGlobalData & globalData,Structure * structure)366 explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
367 : JSObject(globalData, structure, m_inlineStorage)
368 {
369 ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double) == 0);
370 ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
371 }
372
373 static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject;
374
375 WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
376 };
377
offsetOfInlineStorage()378 inline size_t JSObject::offsetOfInlineStorage()
379 {
380 ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
381 return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
382 }
383
constructEmptyObject(ExecState * exec,Structure * structure)384 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
385 {
386 return JSFinalObject::create(exec, structure);
387 }
388
createEmptyObjectStructure(JSGlobalData & globalData,JSValue prototype)389 inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSValue prototype)
390 {
391 return JSFinalObject::createStructure(globalData, prototype);
392 }
393
asObject(JSCell * cell)394 inline JSObject* asObject(JSCell* cell)
395 {
396 ASSERT(cell->isObject());
397 return static_cast<JSObject*>(cell);
398 }
399
asObject(JSValue value)400 inline JSObject* asObject(JSValue value)
401 {
402 return asObject(value.asCell());
403 }
404
JSObject(JSGlobalData & globalData,Structure * structure,PropertyStorage inlineStorage)405 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
406 : JSCell(globalData, structure)
407 , m_propertyStorage(inlineStorage)
408 {
409 ASSERT(inherits(&s_info));
410 ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity);
411 ASSERT(m_structure->isEmpty());
412 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
413 ASSERT(static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
414 ASSERT(m_structure->typeInfo().type() == ObjectType);
415 }
416
~JSObject()417 inline JSObject::~JSObject()
418 {
419 if (!isUsingInlineStorage())
420 delete [] m_propertyStorage;
421 }
422
prototype()423 inline JSValue JSObject::prototype() const
424 {
425 return m_structure->storedPrototype();
426 }
427
setPrototypeWithCycleCheck(JSGlobalData & globalData,JSValue prototype)428 inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
429 {
430 JSValue nextPrototypeValue = prototype;
431 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
432 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
433 if (nextPrototype == this)
434 return false;
435 nextPrototypeValue = nextPrototype->prototype();
436 }
437 setPrototype(globalData, prototype);
438 return true;
439 }
440
setPrototype(JSGlobalData & globalData,JSValue prototype)441 inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
442 {
443 ASSERT(prototype);
444 setStructure(globalData, Structure::changePrototypeTransition(globalData, m_structure.get(), prototype));
445 }
446
setStructure(JSGlobalData & globalData,Structure * structure)447 inline void JSObject::setStructure(JSGlobalData& globalData, Structure* structure)
448 {
449 m_structure.set(globalData, this, structure);
450 }
451
inheritorID(JSGlobalData & globalData)452 inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
453 {
454 if (m_inheritorID) {
455 ASSERT(m_inheritorID->isEmpty());
456 return m_inheritorID.get();
457 }
458 return createInheritorID(globalData);
459 }
460
isUsingInlineStorage()461 inline bool Structure::isUsingInlineStorage() const
462 {
463 return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
464 }
465
inherits(const ClassInfo * info)466 inline bool JSCell::inherits(const ClassInfo* info) const
467 {
468 for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
469 if (ci == info)
470 return true;
471 }
472 return false;
473 }
474
475 // this method is here to be after the inline declaration of JSCell::inherits
inherits(const ClassInfo * classInfo)476 inline bool JSValue::inherits(const ClassInfo* classInfo) const
477 {
478 return isCell() && asCell()->inherits(classInfo);
479 }
480
inlineGetOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)481 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
482 {
483 if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
484 if (m_structure->hasGetterSetterProperties() && location->isGetterSetter())
485 fillGetterPropertySlot(slot, location);
486 else
487 slot.setValue(this, location->get(), offsetForLocation(location));
488 return true;
489 }
490
491 // non-standard Netscape extension
492 if (propertyName == exec->propertyNames().underscoreProto) {
493 slot.setValue(prototype());
494 return true;
495 }
496
497 return false;
498 }
499
500 // It may seem crazy to inline a function this large, especially a virtual function,
501 // but it makes a big difference to property lookup that derived classes can inline their
502 // base class call to this.
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)503 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
504 {
505 return inlineGetOwnPropertySlot(exec, propertyName, slot);
506 }
507
fastGetOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)508 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
509 {
510 if (!structure()->typeInfo().overridesGetOwnPropertySlot())
511 return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
512 return getOwnPropertySlot(exec, propertyName, slot);
513 }
514
515 // It may seem crazy to inline a function this large but it makes a big difference
516 // since this is function very hot in variable lookup
getPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)517 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
518 {
519 JSObject* object = this;
520 while (true) {
521 if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
522 return true;
523 JSValue prototype = object->prototype();
524 if (!prototype.isObject())
525 return false;
526 object = asObject(prototype);
527 }
528 }
529
getPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)530 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
531 {
532 JSObject* object = this;
533 while (true) {
534 if (object->getOwnPropertySlot(exec, propertyName, slot))
535 return true;
536 JSValue prototype = object->prototype();
537 if (!prototype.isObject())
538 return false;
539 object = asObject(prototype);
540 }
541 }
542
get(ExecState * exec,const Identifier & propertyName)543 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
544 {
545 PropertySlot slot(this);
546 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
547 return slot.getValue(exec, propertyName);
548
549 return jsUndefined();
550 }
551
get(ExecState * exec,unsigned propertyName)552 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
553 {
554 PropertySlot slot(this);
555 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
556 return slot.getValue(exec, propertyName);
557
558 return jsUndefined();
559 }
560
putDirectInternal(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes,bool checkReadOnly,PutPropertySlot & slot,JSCell * specificFunction)561 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
562 {
563 ASSERT(value);
564 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
565
566 if (m_structure->isDictionary()) {
567 unsigned currentAttributes;
568 JSCell* currentSpecificFunction;
569 size_t offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
570 if (offset != WTF::notFound) {
571 // If there is currently a specific function, and there now either isn't,
572 // or the new value is different, then despecify.
573 if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
574 m_structure->despecifyDictionaryFunction(globalData, propertyName);
575 if (checkReadOnly && currentAttributes & ReadOnly)
576 return false;
577
578 putDirectOffset(globalData, offset, value);
579 // At this point, the objects structure only has a specific value set if previously there
580 // had been one set, and if the new value being specified is the same (otherwise we would
581 // have despecified, above). So, if currentSpecificFunction is not set, or if the new
582 // value is different (or there is no new value), then the slot now has no value - and
583 // as such it is cachable.
584 // If there was previously a value, and the new value is the same, then we cannot cache.
585 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
586 slot.setExistingProperty(this, offset);
587 return true;
588 }
589
590 if (!isExtensible())
591 return false;
592
593 size_t currentCapacity = m_structure->propertyStorageCapacity();
594 offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
595 if (currentCapacity != m_structure->propertyStorageCapacity())
596 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
597
598 ASSERT(offset < m_structure->propertyStorageCapacity());
599 putDirectOffset(globalData, offset, value);
600 // See comment on setNewProperty call below.
601 if (!specificFunction)
602 slot.setNewProperty(this, offset);
603 return true;
604 }
605
606 size_t offset;
607 size_t currentCapacity = m_structure->propertyStorageCapacity();
608 if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(m_structure.get(), propertyName, attributes, specificFunction, offset)) {
609 if (currentCapacity != structure->propertyStorageCapacity())
610 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
611
612 ASSERT(offset < structure->propertyStorageCapacity());
613 setStructure(globalData, structure);
614 putDirectOffset(globalData, offset, value);
615 // This is a new property; transitions with specific values are not currently cachable,
616 // so leave the slot in an uncachable state.
617 if (!specificFunction)
618 slot.setNewProperty(this, offset);
619 return true;
620 }
621
622 unsigned currentAttributes;
623 JSCell* currentSpecificFunction;
624 offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
625 if (offset != WTF::notFound) {
626 if (checkReadOnly && currentAttributes & ReadOnly)
627 return false;
628
629 // There are three possibilities here:
630 // (1) There is an existing specific value set, and we're overwriting with *the same value*.
631 // * Do nothing - no need to despecify, but that means we can't cache (a cached
632 // put could write a different value). Leave the slot in an uncachable state.
633 // (2) There is a specific value currently set, but we're writing a different value.
634 // * First, we have to despecify. Having done so, this is now a regular slot
635 // with no specific value, so go ahead & cache like normal.
636 // (3) Normal case, there is no specific value set.
637 // * Go ahead & cache like normal.
638 if (currentSpecificFunction) {
639 // case (1) Do the put, then return leaving the slot uncachable.
640 if (specificFunction == currentSpecificFunction) {
641 putDirectOffset(globalData, offset, value);
642 return true;
643 }
644 // case (2) Despecify, fall through to (3).
645 setStructure(globalData, Structure::despecifyFunctionTransition(globalData, m_structure.get(), propertyName));
646 }
647
648 // case (3) set the slot, do the put, return.
649 slot.setExistingProperty(this, offset);
650 putDirectOffset(globalData, offset, value);
651 return true;
652 }
653
654 if (!isExtensible())
655 return false;
656
657 Structure* structure = Structure::addPropertyTransition(globalData, m_structure.get(), propertyName, attributes, specificFunction, offset);
658
659 if (currentCapacity != structure->propertyStorageCapacity())
660 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
661
662 ASSERT(offset < structure->propertyStorageCapacity());
663 setStructure(globalData, structure);
664 putDirectOffset(globalData, offset, value);
665 // This is a new property; transitions with specific values are not currently cachable,
666 // so leave the slot in an uncachable state.
667 if (!specificFunction)
668 slot.setNewProperty(this, offset);
669 return true;
670 }
671
putDirectInternal(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes,bool checkReadOnly,PutPropertySlot & slot)672 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
673 {
674 ASSERT(value);
675 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
676
677 return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
678 }
679
putDirectInternal(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes)680 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
681 {
682 PutPropertySlot slot;
683 putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value));
684 }
685
putDirect(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes,bool checkReadOnly,PutPropertySlot & slot)686 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
687 {
688 ASSERT(value);
689 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
690
691 return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0);
692 }
693
putDirect(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes)694 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
695 {
696 PutPropertySlot slot;
697 putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0);
698 }
699
putDirect(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)700 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
701 {
702 return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0);
703 }
704
putDirectFunction(JSGlobalData & globalData,const Identifier & propertyName,JSCell * value,unsigned attributes,bool checkReadOnly,PutPropertySlot & slot)705 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
706 {
707 putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value);
708 }
709
putDirectFunction(JSGlobalData & globalData,const Identifier & propertyName,JSCell * value,unsigned attr)710 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr)
711 {
712 PutPropertySlot slot;
713 putDirectInternal(globalData, propertyName, value, attr, false, slot, value);
714 }
715
putDirectWithoutTransition(JSGlobalData & globalData,const Identifier & propertyName,JSValue value,unsigned attributes)716 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
717 {
718 size_t currentCapacity = m_structure->propertyStorageCapacity();
719 size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, 0);
720 if (currentCapacity != m_structure->propertyStorageCapacity())
721 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
722 putDirectOffset(globalData, offset, value);
723 }
724
putDirectFunctionWithoutTransition(JSGlobalData & globalData,const Identifier & propertyName,JSCell * value,unsigned attributes)725 inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes)
726 {
727 size_t currentCapacity = m_structure->propertyStorageCapacity();
728 size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, value);
729 if (currentCapacity != m_structure->propertyStorageCapacity())
730 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
731 putDirectOffset(globalData, offset, value);
732 }
733
transitionTo(JSGlobalData & globalData,Structure * newStructure)734 inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
735 {
736 if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
737 allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
738 setStructure(globalData, newStructure);
739 }
740
toPrimitive(ExecState * exec,PreferredPrimitiveType preferredType)741 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
742 {
743 return defaultValue(exec, preferredType);
744 }
745
get(ExecState * exec,const Identifier & propertyName)746 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
747 {
748 PropertySlot slot(asValue());
749 return get(exec, propertyName, slot);
750 }
751
get(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)752 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
753 {
754 if (UNLIKELY(!isCell())) {
755 JSObject* prototype = synthesizePrototype(exec);
756 if (propertyName == exec->propertyNames().underscoreProto)
757 return prototype;
758 if (!prototype->getPropertySlot(exec, propertyName, slot))
759 return jsUndefined();
760 return slot.getValue(exec, propertyName);
761 }
762 JSCell* cell = asCell();
763 while (true) {
764 if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
765 return slot.getValue(exec, propertyName);
766 JSValue prototype = asObject(cell)->prototype();
767 if (!prototype.isObject())
768 return jsUndefined();
769 cell = asObject(prototype);
770 }
771 }
772
get(ExecState * exec,unsigned propertyName)773 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
774 {
775 PropertySlot slot(asValue());
776 return get(exec, propertyName, slot);
777 }
778
get(ExecState * exec,unsigned propertyName,PropertySlot & slot)779 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
780 {
781 if (UNLIKELY(!isCell())) {
782 JSObject* prototype = synthesizePrototype(exec);
783 if (!prototype->getPropertySlot(exec, propertyName, slot))
784 return jsUndefined();
785 return slot.getValue(exec, propertyName);
786 }
787 JSCell* cell = const_cast<JSCell*>(asCell());
788 while (true) {
789 if (cell->getOwnPropertySlot(exec, propertyName, slot))
790 return slot.getValue(exec, propertyName);
791 JSValue prototype = asObject(cell)->prototype();
792 if (!prototype.isObject())
793 return jsUndefined();
794 cell = prototype.asCell();
795 }
796 }
797
put(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)798 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
799 {
800 if (UNLIKELY(!isCell())) {
801 synthesizeObject(exec)->put(exec, propertyName, value, slot);
802 return;
803 }
804 asCell()->put(exec, propertyName, value, slot);
805 }
806
putDirect(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)807 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
808 {
809 ASSERT(isCell() && isObject());
810 if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
811 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
812 }
813
put(ExecState * exec,unsigned propertyName,JSValue value)814 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
815 {
816 if (UNLIKELY(!isCell())) {
817 synthesizeObject(exec)->put(exec, propertyName, value);
818 return;
819 }
820 asCell()->put(exec, propertyName, value);
821 }
822
markChildrenDirect(MarkStack & markStack)823 ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack)
824 {
825 JSCell::markChildren(markStack);
826
827 PropertyStorage storage = propertyStorage();
828 size_t storageSize = m_structure->propertyStorageSize();
829 markStack.appendValues(storage, storageSize);
830 if (m_inheritorID)
831 markStack.append(&m_inheritorID);
832 }
833
834 // --- JSValue inlines ----------------------------
835
toThisString(ExecState * exec)836 ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const
837 {
838 return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec);
839 }
840
toThisJSString(ExecState * exec)841 inline JSString* JSValue::toThisJSString(ExecState* exec) const
842 {
843 return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec));
844 }
845
toStrictThisObject(ExecState * exec)846 inline JSValue JSValue::toStrictThisObject(ExecState* exec) const
847 {
848 if (!isObject())
849 return *this;
850 return asObject(asCell())->toStrictThisObject(exec);
851 }
852
function()853 ALWAYS_INLINE JSObject* Register::function() const
854 {
855 if (!jsValue())
856 return 0;
857 return asObject(jsValue());
858 }
859
withCallee(JSObject * callee)860 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
861 {
862 Register r;
863 r = JSValue(callee);
864 return r;
865 }
866
867 } // namespace JSC
868
869 #endif // JSObject_h
870