|
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 "JSNumberCell.h" |
|
33 #include "MarkStack.h" |
|
34 #include "PropertySlot.h" |
|
35 #include "PutPropertySlot.h" |
|
36 #include "ScopeChain.h" |
|
37 #include "Structure.h" |
|
38 #include "JSGlobalData.h" |
|
39 #include "JSString.h" |
|
40 #include <wtf/StdLibExtras.h> |
|
41 |
|
42 namespace JSC { |
|
43 |
|
44 inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value) |
|
45 { |
|
46 if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr)) |
|
47 return value.asCell(); |
|
48 return 0; |
|
49 } |
|
50 |
|
51 class HashEntry; |
|
52 class InternalFunction; |
|
53 class PropertyDescriptor; |
|
54 class PropertyNameArray; |
|
55 class Structure; |
|
56 struct HashTable; |
|
57 |
|
58 // ECMA 262-3 8.6.1 |
|
59 // Property attributes |
|
60 enum Attribute { |
|
61 None = 0, |
|
62 ReadOnly = 1 << 1, // property can be only read, not written |
|
63 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) |
|
64 DontDelete = 1 << 3, // property can't be deleted |
|
65 Function = 1 << 4, // property is a function - only used by static hashtables |
|
66 Getter = 1 << 5, // property is a getter |
|
67 Setter = 1 << 6 // property is a setter |
|
68 }; |
|
69 |
|
70 typedef EncodedJSValue* PropertyStorage; |
|
71 typedef const EncodedJSValue* ConstPropertyStorage; |
|
72 |
|
73 class JSObject : public JSCell { |
|
74 friend class BatchedTransitionOptimizer; |
|
75 friend class JIT; |
|
76 friend class JSCell; |
|
77 friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); |
|
78 |
|
79 public: |
|
80 explicit JSObject(NonNullPassRefPtr<Structure>); |
|
81 |
|
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(JSValue prototype); |
|
91 bool setPrototypeWithCycleCheck(JSValue prototype); |
|
92 |
|
93 void setStructure(NonNullPassRefPtr<Structure>); |
|
94 Structure* inheritorID(); |
|
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*) const; |
|
141 |
|
142 virtual JSObject* toThisObject(ExecState*) const; |
|
143 virtual JSObject* unwrappedObject(); |
|
144 |
|
145 bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; |
|
146 |
|
147 // This get function only looks at the property map. |
|
148 JSValue getDirect(const Identifier& propertyName) const |
|
149 { |
|
150 size_t offset = m_structure->get(propertyName); |
|
151 return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); |
|
152 } |
|
153 |
|
154 JSValue* getDirectLocation(const Identifier& propertyName) |
|
155 { |
|
156 size_t offset = m_structure->get(propertyName); |
|
157 return offset != WTF::notFound ? locationForOffset(offset) : 0; |
|
158 } |
|
159 |
|
160 JSValue* getDirectLocation(const Identifier& propertyName, unsigned& attributes) |
|
161 { |
|
162 JSCell* specificFunction; |
|
163 size_t offset = m_structure->get(propertyName, attributes, specificFunction); |
|
164 return offset != WTF::notFound ? locationForOffset(offset) : 0; |
|
165 } |
|
166 |
|
167 size_t offsetForLocation(JSValue* location) const |
|
168 { |
|
169 return location - reinterpret_cast<const JSValue*>(propertyStorage()); |
|
170 } |
|
171 |
|
172 void transitionTo(Structure*); |
|
173 |
|
174 void removeDirect(const Identifier& propertyName); |
|
175 bool hasCustomProperties() { return !m_structure->isEmpty(); } |
|
176 bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } |
|
177 |
|
178 void putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); |
|
179 void putDirect(const Identifier& propertyName, JSValue value, unsigned attr = 0); |
|
180 void putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot&); |
|
181 |
|
182 void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr = 0); |
|
183 void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); |
|
184 void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); |
|
185 void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0); |
|
186 |
|
187 void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); |
|
188 void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); |
|
189 void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); |
|
190 void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0); |
|
191 |
|
192 // Fast access to known property offsets. |
|
193 JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); } |
|
194 void putDirectOffset(size_t offset, JSValue value) { propertyStorage()[offset] = JSValue::encode(value); } |
|
195 |
|
196 void fillGetterPropertySlot(PropertySlot&, JSValue* location); |
|
197 |
|
198 virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); |
|
199 virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); |
|
200 virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); |
|
201 virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName); |
|
202 virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); |
|
203 |
|
204 virtual bool isGlobalObject() const { return false; } |
|
205 virtual bool isVariableObject() const { return false; } |
|
206 virtual bool isActivationObject() const { return false; } |
|
207 virtual bool isNotAnObjectErrorStub() const { return false; } |
|
208 |
|
209 virtual ComplType exceptionType() const { return Throw; } |
|
210 |
|
211 void allocatePropertyStorage(size_t oldSize, size_t newSize); |
|
212 void allocatePropertyStorageInline(size_t oldSize, size_t newSize); |
|
213 bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } |
|
214 |
|
215 static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; |
|
216 static const unsigned nonInlineBaseStorageCapacity = 16; |
|
217 |
|
218 static PassRefPtr<Structure> createStructure(JSValue prototype) |
|
219 { |
|
220 return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); |
|
221 } |
|
222 |
|
223 void flattenDictionaryObject() |
|
224 { |
|
225 m_structure->flattenDictionaryStructure(this); |
|
226 } |
|
227 |
|
228 void putAnonymousValue(unsigned index, JSValue value) |
|
229 { |
|
230 ASSERT(index < m_structure->anonymousSlotCount()); |
|
231 *locationForOffset(index) = value; |
|
232 } |
|
233 JSValue getAnonymousValue(unsigned index) const |
|
234 { |
|
235 ASSERT(index < m_structure->anonymousSlotCount()); |
|
236 return *locationForOffset(index); |
|
237 } |
|
238 |
|
239 protected: |
|
240 static const unsigned StructureFlags = 0; |
|
241 |
|
242 private: |
|
243 // Nobody should ever ask any of these questions on something already known to be a JSObject. |
|
244 using JSCell::isAPIValueWrapper; |
|
245 using JSCell::isGetterSetter; |
|
246 using JSCell::toObject; |
|
247 void getObject(); |
|
248 void getString(ExecState* exec); |
|
249 void isObject(); |
|
250 void isString(); |
|
251 #if USE(JSVALUE32) |
|
252 void isNumber(); |
|
253 #endif |
|
254 |
|
255 ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } |
|
256 PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } |
|
257 |
|
258 const JSValue* locationForOffset(size_t offset) const |
|
259 { |
|
260 return reinterpret_cast<const JSValue*>(&propertyStorage()[offset]); |
|
261 } |
|
262 |
|
263 JSValue* locationForOffset(size_t offset) |
|
264 { |
|
265 return reinterpret_cast<JSValue*>(&propertyStorage()[offset]); |
|
266 } |
|
267 |
|
268 void putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); |
|
269 void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); |
|
270 void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); |
|
271 |
|
272 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); |
|
273 |
|
274 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; |
|
275 Structure* createInheritorID(); |
|
276 |
|
277 union { |
|
278 PropertyStorage m_externalStorage; |
|
279 EncodedJSValue m_inlineStorage[inlineStorageCapacity]; |
|
280 }; |
|
281 |
|
282 RefPtr<Structure> m_inheritorID; |
|
283 }; |
|
284 |
|
285 inline JSObject* asObject(JSCell* cell) |
|
286 { |
|
287 ASSERT(cell->isObject()); |
|
288 return static_cast<JSObject*>(cell); |
|
289 } |
|
290 |
|
291 inline JSObject* asObject(JSValue value) |
|
292 { |
|
293 return asObject(value.asCell()); |
|
294 } |
|
295 |
|
296 inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure) |
|
297 : JSCell(structure.releaseRef()) // ~JSObject balances this ref() |
|
298 { |
|
299 ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); |
|
300 ASSERT(m_structure->isEmpty()); |
|
301 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); |
|
302 #if USE(JSVALUE64) || USE(JSVALUE32_64) |
|
303 ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); |
|
304 #endif |
|
305 } |
|
306 |
|
307 inline JSObject::~JSObject() |
|
308 { |
|
309 ASSERT(m_structure); |
|
310 if (!isUsingInlineStorage()) |
|
311 delete [] m_externalStorage; |
|
312 m_structure->deref(); |
|
313 } |
|
314 |
|
315 inline JSValue JSObject::prototype() const |
|
316 { |
|
317 return m_structure->storedPrototype(); |
|
318 } |
|
319 |
|
320 inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype) |
|
321 { |
|
322 JSValue nextPrototypeValue = prototype; |
|
323 while (nextPrototypeValue && nextPrototypeValue.isObject()) { |
|
324 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); |
|
325 if (nextPrototype == this) |
|
326 return false; |
|
327 nextPrototypeValue = nextPrototype->prototype(); |
|
328 } |
|
329 setPrototype(prototype); |
|
330 return true; |
|
331 } |
|
332 |
|
333 inline void JSObject::setPrototype(JSValue prototype) |
|
334 { |
|
335 ASSERT(prototype); |
|
336 RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype); |
|
337 setStructure(newStructure.release()); |
|
338 } |
|
339 |
|
340 inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure) |
|
341 { |
|
342 m_structure->deref(); |
|
343 m_structure = structure.releaseRef(); // ~JSObject balances this ref() |
|
344 } |
|
345 |
|
346 inline Structure* JSObject::inheritorID() |
|
347 { |
|
348 if (m_inheritorID) |
|
349 return m_inheritorID.get(); |
|
350 return createInheritorID(); |
|
351 } |
|
352 |
|
353 inline bool Structure::isUsingInlineStorage() const |
|
354 { |
|
355 return (propertyStorageCapacity() == JSObject::inlineStorageCapacity); |
|
356 } |
|
357 |
|
358 inline bool JSCell::inherits(const ClassInfo* info) const |
|
359 { |
|
360 for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { |
|
361 if (ci == info) |
|
362 return true; |
|
363 } |
|
364 return false; |
|
365 } |
|
366 |
|
367 // this method is here to be after the inline declaration of JSCell::inherits |
|
368 inline bool JSValue::inherits(const ClassInfo* classInfo) const |
|
369 { |
|
370 return isCell() && asCell()->inherits(classInfo); |
|
371 } |
|
372 |
|
373 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
374 { |
|
375 if (JSValue* location = getDirectLocation(propertyName)) { |
|
376 if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) |
|
377 fillGetterPropertySlot(slot, location); |
|
378 else |
|
379 slot.setValueSlot(this, location, offsetForLocation(location)); |
|
380 return true; |
|
381 } |
|
382 |
|
383 // non-standard Netscape extension |
|
384 if (propertyName == exec->propertyNames().underscoreProto) { |
|
385 slot.setValue(prototype()); |
|
386 return true; |
|
387 } |
|
388 |
|
389 return false; |
|
390 } |
|
391 |
|
392 // It may seem crazy to inline a function this large, especially a virtual function, |
|
393 // but it makes a big difference to property lookup that derived classes can inline their |
|
394 // base class call to this. |
|
395 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
396 { |
|
397 return inlineGetOwnPropertySlot(exec, propertyName, slot); |
|
398 } |
|
399 |
|
400 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
401 { |
|
402 if (!structure()->typeInfo().overridesGetOwnPropertySlot()) |
|
403 return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); |
|
404 return getOwnPropertySlot(exec, propertyName, slot); |
|
405 } |
|
406 |
|
407 // It may seem crazy to inline a function this large but it makes a big difference |
|
408 // since this is function very hot in variable lookup |
|
409 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
410 { |
|
411 JSObject* object = this; |
|
412 while (true) { |
|
413 if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) |
|
414 return true; |
|
415 JSValue prototype = object->prototype(); |
|
416 if (!prototype.isObject()) |
|
417 return false; |
|
418 object = asObject(prototype); |
|
419 } |
|
420 } |
|
421 |
|
422 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) |
|
423 { |
|
424 JSObject* object = this; |
|
425 while (true) { |
|
426 if (object->getOwnPropertySlot(exec, propertyName, slot)) |
|
427 return true; |
|
428 JSValue prototype = object->prototype(); |
|
429 if (!prototype.isObject()) |
|
430 return false; |
|
431 object = asObject(prototype); |
|
432 } |
|
433 } |
|
434 |
|
435 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const |
|
436 { |
|
437 PropertySlot slot(this); |
|
438 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) |
|
439 return slot.getValue(exec, propertyName); |
|
440 |
|
441 return jsUndefined(); |
|
442 } |
|
443 |
|
444 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const |
|
445 { |
|
446 PropertySlot slot(this); |
|
447 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) |
|
448 return slot.getValue(exec, propertyName); |
|
449 |
|
450 return jsUndefined(); |
|
451 } |
|
452 |
|
453 inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) |
|
454 { |
|
455 ASSERT(value); |
|
456 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
|
457 |
|
458 if (m_structure->isDictionary()) { |
|
459 unsigned currentAttributes; |
|
460 JSCell* currentSpecificFunction; |
|
461 size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); |
|
462 if (offset != WTF::notFound) { |
|
463 // If there is currently a specific function, and there now either isn't, |
|
464 // or the new value is different, then despecify. |
|
465 if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) |
|
466 m_structure->despecifyDictionaryFunction(propertyName); |
|
467 if (checkReadOnly && currentAttributes & ReadOnly) |
|
468 return; |
|
469 putDirectOffset(offset, value); |
|
470 // At this point, the objects structure only has a specific value set if previously there |
|
471 // had been one set, and if the new value being specified is the same (otherwise we would |
|
472 // have despecified, above). So, if currentSpecificFunction is not set, or if the new |
|
473 // value is different (or there is no new value), then the slot now has no value - and |
|
474 // as such it is cachable. |
|
475 // If there was previously a value, and the new value is the same, then we cannot cache. |
|
476 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) |
|
477 slot.setExistingProperty(this, offset); |
|
478 return; |
|
479 } |
|
480 |
|
481 size_t currentCapacity = m_structure->propertyStorageCapacity(); |
|
482 offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); |
|
483 if (currentCapacity != m_structure->propertyStorageCapacity()) |
|
484 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); |
|
485 |
|
486 ASSERT(offset < m_structure->propertyStorageCapacity()); |
|
487 putDirectOffset(offset, value); |
|
488 // See comment on setNewProperty call below. |
|
489 if (!specificFunction) |
|
490 slot.setNewProperty(this, offset); |
|
491 return; |
|
492 } |
|
493 |
|
494 size_t offset; |
|
495 size_t currentCapacity = m_structure->propertyStorageCapacity(); |
|
496 if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) { |
|
497 if (currentCapacity != structure->propertyStorageCapacity()) |
|
498 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); |
|
499 |
|
500 ASSERT(offset < structure->propertyStorageCapacity()); |
|
501 setStructure(structure.release()); |
|
502 putDirectOffset(offset, value); |
|
503 // This is a new property; transitions with specific values are not currently cachable, |
|
504 // so leave the slot in an uncachable state. |
|
505 if (!specificFunction) |
|
506 slot.setNewProperty(this, offset); |
|
507 return; |
|
508 } |
|
509 |
|
510 unsigned currentAttributes; |
|
511 JSCell* currentSpecificFunction; |
|
512 offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); |
|
513 if (offset != WTF::notFound) { |
|
514 if (checkReadOnly && currentAttributes & ReadOnly) |
|
515 return; |
|
516 |
|
517 // There are three possibilities here: |
|
518 // (1) There is an existing specific value set, and we're overwriting with *the same value*. |
|
519 // * Do nothing - no need to despecify, but that means we can't cache (a cached |
|
520 // put could write a different value). Leave the slot in an uncachable state. |
|
521 // (2) There is a specific value currently set, but we're writing a different value. |
|
522 // * First, we have to despecify. Having done so, this is now a regular slot |
|
523 // with no specific value, so go ahead & cache like normal. |
|
524 // (3) Normal case, there is no specific value set. |
|
525 // * Go ahead & cache like normal. |
|
526 if (currentSpecificFunction) { |
|
527 // case (1) Do the put, then return leaving the slot uncachable. |
|
528 if (specificFunction == currentSpecificFunction) { |
|
529 putDirectOffset(offset, value); |
|
530 return; |
|
531 } |
|
532 // case (2) Despecify, fall through to (3). |
|
533 setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); |
|
534 } |
|
535 |
|
536 // case (3) set the slot, do the put, return. |
|
537 slot.setExistingProperty(this, offset); |
|
538 putDirectOffset(offset, value); |
|
539 return; |
|
540 } |
|
541 |
|
542 // If we have a specific function, we may have got to this point if there is |
|
543 // already a transition with the correct property name and attributes, but |
|
544 // specialized to a different function. In this case we just want to give up |
|
545 // and despecialize the transition. |
|
546 // In this case we clear the value of specificFunction which will result |
|
547 // in us adding a non-specific transition, and any subsequent lookup in |
|
548 // Structure::addPropertyTransitionToExistingStructure will just use that. |
|
549 if (specificFunction && m_structure->hasTransition(propertyName, attributes)) |
|
550 specificFunction = 0; |
|
551 |
|
552 RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); |
|
553 |
|
554 if (currentCapacity != structure->propertyStorageCapacity()) |
|
555 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); |
|
556 |
|
557 ASSERT(offset < structure->propertyStorageCapacity()); |
|
558 setStructure(structure.release()); |
|
559 putDirectOffset(offset, value); |
|
560 // This is a new property; transitions with specific values are not currently cachable, |
|
561 // so leave the slot in an uncachable state. |
|
562 if (!specificFunction) |
|
563 slot.setNewProperty(this, offset); |
|
564 } |
|
565 |
|
566 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) |
|
567 { |
|
568 ASSERT(value); |
|
569 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
|
570 |
|
571 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); |
|
572 } |
|
573 |
|
574 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) |
|
575 { |
|
576 PutPropertySlot slot; |
|
577 putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); |
|
578 } |
|
579 |
|
580 inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) |
|
581 { |
|
582 ASSERT(value); |
|
583 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
|
584 |
|
585 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); |
|
586 } |
|
587 |
|
588 inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) |
|
589 { |
|
590 PutPropertySlot slot; |
|
591 putDirectInternal(propertyName, value, attributes, false, slot, 0); |
|
592 } |
|
593 |
|
594 inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
|
595 { |
|
596 putDirectInternal(propertyName, value, 0, false, slot, 0); |
|
597 } |
|
598 |
|
599 inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) |
|
600 { |
|
601 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); |
|
602 } |
|
603 |
|
604 inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr) |
|
605 { |
|
606 PutPropertySlot slot; |
|
607 putDirectInternal(propertyName, value, attr, false, slot, value); |
|
608 } |
|
609 |
|
610 inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes) |
|
611 { |
|
612 size_t currentCapacity = m_structure->propertyStorageCapacity(); |
|
613 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); |
|
614 if (currentCapacity != m_structure->propertyStorageCapacity()) |
|
615 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); |
|
616 putDirectOffset(offset, value); |
|
617 } |
|
618 |
|
619 inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes) |
|
620 { |
|
621 size_t currentCapacity = m_structure->propertyStorageCapacity(); |
|
622 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); |
|
623 if (currentCapacity != m_structure->propertyStorageCapacity()) |
|
624 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); |
|
625 putDirectOffset(offset, value); |
|
626 } |
|
627 |
|
628 inline void JSObject::transitionTo(Structure* newStructure) |
|
629 { |
|
630 if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) |
|
631 allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); |
|
632 setStructure(newStructure); |
|
633 } |
|
634 |
|
635 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const |
|
636 { |
|
637 return defaultValue(exec, preferredType); |
|
638 } |
|
639 |
|
640 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const |
|
641 { |
|
642 PropertySlot slot(asValue()); |
|
643 return get(exec, propertyName, slot); |
|
644 } |
|
645 |
|
646 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const |
|
647 { |
|
648 if (UNLIKELY(!isCell())) { |
|
649 JSObject* prototype = synthesizePrototype(exec); |
|
650 if (propertyName == exec->propertyNames().underscoreProto) |
|
651 return prototype; |
|
652 if (!prototype->getPropertySlot(exec, propertyName, slot)) |
|
653 return jsUndefined(); |
|
654 return slot.getValue(exec, propertyName); |
|
655 } |
|
656 JSCell* cell = asCell(); |
|
657 while (true) { |
|
658 if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) |
|
659 return slot.getValue(exec, propertyName); |
|
660 JSValue prototype = asObject(cell)->prototype(); |
|
661 if (!prototype.isObject()) |
|
662 return jsUndefined(); |
|
663 cell = asObject(prototype); |
|
664 } |
|
665 } |
|
666 |
|
667 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const |
|
668 { |
|
669 PropertySlot slot(asValue()); |
|
670 return get(exec, propertyName, slot); |
|
671 } |
|
672 |
|
673 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const |
|
674 { |
|
675 if (UNLIKELY(!isCell())) { |
|
676 JSObject* prototype = synthesizePrototype(exec); |
|
677 if (!prototype->getPropertySlot(exec, propertyName, slot)) |
|
678 return jsUndefined(); |
|
679 return slot.getValue(exec, propertyName); |
|
680 } |
|
681 JSCell* cell = const_cast<JSCell*>(asCell()); |
|
682 while (true) { |
|
683 if (cell->getOwnPropertySlot(exec, propertyName, slot)) |
|
684 return slot.getValue(exec, propertyName); |
|
685 JSValue prototype = asObject(cell)->prototype(); |
|
686 if (!prototype.isObject()) |
|
687 return jsUndefined(); |
|
688 cell = prototype.asCell(); |
|
689 } |
|
690 } |
|
691 |
|
692 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
|
693 { |
|
694 if (UNLIKELY(!isCell())) { |
|
695 synthesizeObject(exec)->put(exec, propertyName, value, slot); |
|
696 return; |
|
697 } |
|
698 asCell()->put(exec, propertyName, value, slot); |
|
699 } |
|
700 |
|
701 inline void JSValue::putDirect(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
|
702 { |
|
703 ASSERT(isCell() && isObject()); |
|
704 asObject(asCell())->putDirect(propertyName, value, slot); |
|
705 } |
|
706 |
|
707 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) |
|
708 { |
|
709 if (UNLIKELY(!isCell())) { |
|
710 synthesizeObject(exec)->put(exec, propertyName, value); |
|
711 return; |
|
712 } |
|
713 asCell()->put(exec, propertyName, value); |
|
714 } |
|
715 |
|
716 ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize) |
|
717 { |
|
718 ASSERT(newSize > oldSize); |
|
719 |
|
720 // It's important that this function not rely on m_structure, since |
|
721 // we might be in the middle of a transition. |
|
722 bool wasInline = (oldSize == JSObject::inlineStorageCapacity); |
|
723 |
|
724 PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage); |
|
725 PropertyStorage newPropertyStorage = new EncodedJSValue[newSize]; |
|
726 |
|
727 for (unsigned i = 0; i < oldSize; ++i) |
|
728 newPropertyStorage[i] = oldPropertyStorage[i]; |
|
729 |
|
730 if (!wasInline) |
|
731 delete [] oldPropertyStorage; |
|
732 |
|
733 m_externalStorage = newPropertyStorage; |
|
734 } |
|
735 |
|
736 ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) |
|
737 { |
|
738 JSCell::markChildren(markStack); |
|
739 |
|
740 markStack.append(prototype()); |
|
741 |
|
742 PropertyStorage storage = propertyStorage(); |
|
743 size_t storageSize = m_structure->propertyStorageSize(); |
|
744 markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); |
|
745 } |
|
746 |
|
747 // --- JSValue inlines ---------------------------- |
|
748 |
|
749 ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const |
|
750 { |
|
751 return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec); |
|
752 } |
|
753 |
|
754 inline JSString* JSValue::toThisJSString(ExecState* exec) const |
|
755 { |
|
756 return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec)); |
|
757 } |
|
758 |
|
759 } // namespace JSC |
|
760 |
|
761 #endif // JSObject_h |