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