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