source: webkit/trunk/JavaScriptCore/runtime/JSObject.h@ 55379

Last change on this file since 55379 was 55379, checked in by [email protected], 15 years ago

Bug 35537 - put_by_id does will incorrectly cache writes where a specific value exists,

where at the point of caching the same value is being written.

Reviewed by Oliver Hunt.

JavaScriptCore:

When performing a put_by_id that is replacing a property already present on the object,
there are three interesting cases regarding the state of the specific value:

(1) No specific value set - nothing to do, leave the structure in it's current state,

can cache.

(2) A specific value was set, the new put is not of a specified value (i.e. function),

or is of a different specific value - in these cases we need to perform a despecifying
transition to clear the specific value in the structure, but having done so this is a
normal property so as such we can again cache normally.

(3) A specific value was set, and we are overwriting with the same value - in these cases

leave the structure unchanged, but since a specific value is set we cannot cache this
put (we would need the JIT to dynamically check the value being written matched).

Unfortunately, the current behaviour does not match this. the checks for a specific value
being present & the value matching are combined in such a way that in case (2), above we
will unnecessarily prevent the transition being cached, but in case (3) we will incorrectly
fail to prevent caching.

The bug exposes itself if multiple puts of the same specific value are performed to a
property, and erroneously the put is allowed to be cached by the JIT. Method checks may be
generated caching calls of this structure. Subsequent puts performed from JIT code may
write different values without triggering a despecify transition, and as such cached method
checks will continue to pass, despite the value having changed.

  • runtime/JSObject.h:

(JSC::JSObject::putDirectInternal):

LayoutTests:

Add test case.

  • fast/js/method-check-expected.txt:
  • fast/js/script-tests/method-check.js:

(addOne):
(addOneHundred):
(totalizer.makeCall):

  • Property svn:eol-style set to native
File size: 28.8 KB
Line 
1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
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
40namespace 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, 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(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
200 void allocatePropertyStorage(size_t oldSize, size_t newSize);
201 void allocatePropertyStorageInline(size_t oldSize, size_t newSize);
202 bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); }
203
204 static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3;
205 static const unsigned nonInlineBaseStorageCapacity = 16;
206
207 static PassRefPtr<Structure> createStructure(JSValue prototype)
208 {
209 return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount);
210 }
211
212 void flattenDictionaryObject()
213 {
214 m_structure->flattenDictionaryStructure(this);
215 }
216
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
231 private:
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
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
261 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
262
263 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
264 Structure* createInheritorID();
265
266 union {
267 PropertyStorage m_externalStorage;
268 EncodedJSValue m_inlineStorage[inlineStorageCapacity];
269 };
270
271 RefPtr<Structure> m_inheritorID;
272 };
273
274inline JSObject* asObject(JSCell* cell)
275{
276 ASSERT(cell->isObject());
277 return static_cast<JSObject*>(cell);
278}
279
280inline JSObject* asObject(JSValue value)
281{
282 return asObject(value.asCell());
283}
284
285inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure)
286 : JSCell(structure.releaseRef()) // ~JSObject balances this ref()
287{
288 ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity);
289 ASSERT(m_structure->isEmpty());
290 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
291#if USE(JSVALUE64) || USE(JSVALUE32_64)
292 ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0);
293#endif
294}
295
296inline JSObject::~JSObject()
297{
298 ASSERT(m_structure);
299 if (!isUsingInlineStorage())
300 delete [] m_externalStorage;
301 m_structure->deref();
302}
303
304inline JSValue JSObject::prototype() const
305{
306 return m_structure->storedPrototype();
307}
308
309inline void JSObject::setPrototype(JSValue prototype)
310{
311 ASSERT(prototype);
312 RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype);
313 setStructure(newStructure.release());
314}
315
316inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure)
317{
318 m_structure->deref();
319 m_structure = structure.releaseRef(); // ~JSObject balances this ref()
320}
321
322inline Structure* JSObject::inheritorID()
323{
324 if (m_inheritorID)
325 return m_inheritorID.get();
326 return createInheritorID();
327}
328
329inline bool Structure::isUsingInlineStorage() const
330{
331 return (propertyStorageCapacity() == JSObject::inlineStorageCapacity);
332}
333
334inline bool JSCell::inherits(const ClassInfo* info) const
335{
336 for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
337 if (ci == info)
338 return true;
339 }
340 return false;
341}
342
343// this method is here to be after the inline declaration of JSCell::inherits
344inline bool JSValue::inherits(const ClassInfo* classInfo) const
345{
346 return isCell() && asCell()->inherits(classInfo);
347}
348
349ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
350{
351 if (JSValue* location = getDirectLocation(propertyName)) {
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
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.
371ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
372{
373 return inlineGetOwnPropertySlot(exec, propertyName, slot);
374}
375
376ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
377{
378 if (!structure()->typeInfo().overridesGetOwnPropertySlot())
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
385ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
386{
387 JSObject* object = this;
388 while (true) {
389 if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
390 return true;
391 JSValue prototype = object->prototype();
392 if (!prototype.isObject())
393 return false;
394 object = asObject(prototype);
395 }
396}
397
398ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
399{
400 JSObject* object = this;
401 while (true) {
402 if (object->getOwnPropertySlot(exec, propertyName, slot))
403 return true;
404 JSValue prototype = object->prototype();
405 if (!prototype.isObject())
406 return false;
407 object = asObject(prototype);
408 }
409}
410
411inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
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
420inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
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
429inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
430{
431 ASSERT(value);
432 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
433
434 if (m_structure->isDictionary()) {
435 unsigned currentAttributes;
436 JSCell* currentSpecificFunction;
437 size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
438 if (offset != WTF::notFound) {
439 // If there is currently a specific function, and there now either isn't,
440 // or the new value is different, then despecify.
441 if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
442 m_structure->despecifyDictionaryFunction(propertyName);
443 if (checkReadOnly && currentAttributes & ReadOnly)
444 return;
445 putDirectOffset(offset, value);
446 // At this point, the objects structure only has a specific value set if previously there
447 // had been one set, and if the new value being specified is the same (otherwise we would
448 // have despecified, above). So, if currentSpecificFunction is not set, or if the new
449 // value is different (or there is no new value), then the slot now has no value - and
450 // as such it is cachable.
451 // If there was previously a value, and the new value is the same, then we cannot cache.
452 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
453 slot.setExistingProperty(this, offset);
454 return;
455 }
456
457 size_t currentCapacity = m_structure->propertyStorageCapacity();
458 offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction);
459 if (currentCapacity != m_structure->propertyStorageCapacity())
460 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
461
462 ASSERT(offset < m_structure->propertyStorageCapacity());
463 putDirectOffset(offset, value);
464 // See comment on setNewProperty call below.
465 if (!specificFunction)
466 slot.setNewProperty(this, offset);
467 return;
468 }
469
470 size_t offset;
471 size_t currentCapacity = m_structure->propertyStorageCapacity();
472 if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) {
473 if (currentCapacity != structure->propertyStorageCapacity())
474 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
475
476 ASSERT(offset < structure->propertyStorageCapacity());
477 setStructure(structure.release());
478 putDirectOffset(offset, value);
479 // This is a new property; transitions with specific values are not currently cachable,
480 // so leave the slot in an uncachable state.
481 if (!specificFunction)
482 slot.setNewProperty(this, offset);
483 return;
484 }
485
486 unsigned currentAttributes;
487 JSCell* currentSpecificFunction;
488 offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
489 if (offset != WTF::notFound) {
490 if (checkReadOnly && currentAttributes & ReadOnly)
491 return;
492
493 // There are three possibilities here:
494 // (1) There is an existing specific value set, and we're overwriting with *the same value*.
495 // * Do nothing – no need to despecify, but that means we can't cache (a cached
496 // put could write a different value). Leave the slot in an uncachable state.
497 // (2) There is a specific value currently set, but we're writing a different value.
498 // * First, we have to despecify. Having done so, this is now a regular slot
499 // with no specific value, so go ahead & cache like normal.
500 // (3) Normal case, there is no specific value set.
501 // * Go ahead & cache like normal.
502 if (currentSpecificFunction) {
503 // case (1) Do the put, then return leaving the slot uncachable.
504 if (specificFunction == currentSpecificFunction) {
505 putDirectOffset(offset, value);
506 return;
507 }
508 // case (2) Despecify, fall through to (3).
509 setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName));
510 }
511
512 // case (3) set the slot, do the put, return.
513 slot.setExistingProperty(this, offset);
514 putDirectOffset(offset, value);
515 return;
516 }
517
518 // If we have a specific function, we may have got to this point if there is
519 // already a transition with the correct property name and attributes, but
520 // specialized to a different function. In this case we just want to give up
521 // and despecialize the transition.
522 // In this case we clear the value of specificFunction which will result
523 // in us adding a non-specific transition, and any subsequent lookup in
524 // Structure::addPropertyTransitionToExistingStructure will just use that.
525 if (specificFunction && m_structure->hasTransition(propertyName, attributes))
526 specificFunction = 0;
527
528 RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset);
529
530 if (currentCapacity != structure->propertyStorageCapacity())
531 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
532
533 ASSERT(offset < structure->propertyStorageCapacity());
534 setStructure(structure.release());
535 putDirectOffset(offset, value);
536 // This is a new property; transitions with specific values are not currently cachable,
537 // so leave the slot in an uncachable state.
538 if (!specificFunction)
539 slot.setNewProperty(this, offset);
540}
541
542inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
543{
544 ASSERT(value);
545 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
546
547 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
548}
549
550inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
551{
552 PutPropertySlot slot;
553 putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value));
554}
555
556inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
557{
558 ASSERT(value);
559 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
560
561 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0);
562}
563
564inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes)
565{
566 PutPropertySlot slot;
567 putDirectInternal(propertyName, value, attributes, false, slot, 0);
568}
569
570inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
571{
572 putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value);
573}
574
575inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr)
576{
577 PutPropertySlot slot;
578 putDirectInternal(propertyName, value, attr, false, slot, value);
579}
580
581inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes)
582{
583 size_t currentCapacity = m_structure->propertyStorageCapacity();
584 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0);
585 if (currentCapacity != m_structure->propertyStorageCapacity())
586 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
587 putDirectOffset(offset, value);
588}
589
590inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes)
591{
592 size_t currentCapacity = m_structure->propertyStorageCapacity();
593 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value);
594 if (currentCapacity != m_structure->propertyStorageCapacity())
595 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
596 putDirectOffset(offset, value);
597}
598
599inline void JSObject::transitionTo(Structure* newStructure)
600{
601 if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
602 allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
603 setStructure(newStructure);
604}
605
606inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
607{
608 return defaultValue(exec, preferredType);
609}
610
611inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
612{
613 PropertySlot slot(asValue());
614 return get(exec, propertyName, slot);
615}
616
617inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
618{
619 if (UNLIKELY(!isCell())) {
620 JSObject* prototype = synthesizePrototype(exec);
621 if (propertyName == exec->propertyNames().underscoreProto)
622 return prototype;
623 if (!prototype->getPropertySlot(exec, propertyName, slot))
624 return jsUndefined();
625 return slot.getValue(exec, propertyName);
626 }
627 JSCell* cell = asCell();
628 while (true) {
629 if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
630 return slot.getValue(exec, propertyName);
631 JSValue prototype = asObject(cell)->prototype();
632 if (!prototype.isObject())
633 return jsUndefined();
634 cell = asObject(prototype);
635 }
636}
637
638inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
639{
640 PropertySlot slot(asValue());
641 return get(exec, propertyName, slot);
642}
643
644inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
645{
646 if (UNLIKELY(!isCell())) {
647 JSObject* prototype = synthesizePrototype(exec);
648 if (!prototype->getPropertySlot(exec, propertyName, slot))
649 return jsUndefined();
650 return slot.getValue(exec, propertyName);
651 }
652 JSCell* cell = const_cast<JSCell*>(asCell());
653 while (true) {
654 if (cell->getOwnPropertySlot(exec, propertyName, slot))
655 return slot.getValue(exec, propertyName);
656 JSValue prototype = asObject(cell)->prototype();
657 if (!prototype.isObject())
658 return jsUndefined();
659 cell = prototype.asCell();
660 }
661}
662
663inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
664{
665 if (UNLIKELY(!isCell())) {
666 synthesizeObject(exec)->put(exec, propertyName, value, slot);
667 return;
668 }
669 asCell()->put(exec, propertyName, value, slot);
670}
671
672inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
673{
674 if (UNLIKELY(!isCell())) {
675 synthesizeObject(exec)->put(exec, propertyName, value);
676 return;
677 }
678 asCell()->put(exec, propertyName, value);
679}
680
681ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize)
682{
683 ASSERT(newSize > oldSize);
684
685 // It's important that this function not rely on m_structure, since
686 // we might be in the middle of a transition.
687 bool wasInline = (oldSize == JSObject::inlineStorageCapacity);
688
689 PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage);
690 PropertyStorage newPropertyStorage = new EncodedJSValue[newSize];
691
692 for (unsigned i = 0; i < oldSize; ++i)
693 newPropertyStorage[i] = oldPropertyStorage[i];
694
695 if (!wasInline)
696 delete [] oldPropertyStorage;
697
698 m_externalStorage = newPropertyStorage;
699}
700
701ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack)
702{
703 JSCell::markChildren(markStack);
704
705 markStack.append(prototype());
706
707 PropertyStorage storage = propertyStorage();
708 size_t storageSize = m_structure->propertyStorageSize();
709 markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize);
710}
711
712} // namespace JSC
713
714#endif // JSObject_h
Note: See TracBrowser for help on using the repository browser.