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