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

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

2010-04-06 Adam Barth <[email protected]>

Reviewed by Eric Seidel.

REGRESSION: Worker termination via JS timeout may cause worker tests like fast/workers/worker-terminate.html fail.
https://bugs.webkit.org/show_bug.cgi?id=36646

Add a new exception type for forcibly terminating a JavaScript stack.
The new exception functions similarly to the
InterruptedExecutionException but is conceptually different because
execution is terminated instead of just interrupted.

  • GNUmakefile.am:
    • Added new Terminator.h file.
  • JavaScriptCore.gypi:
    • Added new Terminator.h file.
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
    • Added new Terminator.h file.
  • JavaScriptCore.xcodeproj/project.pbxproj:
    • Added new Terminator.h file.
  • interpreter/Interpreter.cpp: (JSC::Interpreter::throwException):
    • Fully unwind the stack for TerminatedExecutionException.

(JSC::Interpreter::privateExecute):

  • Check if we've been terminated at the same time we check if we've timed out.
  • jit/JITStubs.cpp: (JSC::DEFINE_STUB_FUNCTION):
    • Check if we've been terminated at the same time we check if we've timed out.
  • runtime/Completion.cpp:
    • Some exceptions define special completion types so that calls can see why we terminated evaluation.

(JSC::evaluate):

  • runtime/Completion.h:
    • Define a new completion type for termination.

(JSC::):

  • runtime/ExceptionHelpers.cpp:
    • Define TerminatedExecutionException and refactor pseudo-RTTI virtual function to be more semantic.

(JSC::InterruptedExecutionError::exceptionType):
(JSC::TerminatedExecutionError::TerminatedExecutionError):
(JSC::TerminatedExecutionError::exceptionType):
(JSC::TerminatedExecutionError::toString):
(JSC::createTerminatedExecutionException):

  • runtime/ExceptionHelpers.h:
    • Entry point for generating a TerminatedExecutionException.
  • runtime/JSGlobalData.cpp: (JSC::JSGlobalData::JSGlobalData):
    • Add a Terminator object that can be used to asynchronously terminate a JavaScript execution stack.
  • runtime/JSGlobalData.h:
  • runtime/JSObject.h: (JSC::JSObject::exceptionType):
    • Define that, by default, thrown objects have a normal exception type.
  • runtime/Terminator.h: Added.
    • Added a new controller object that can be used to terminate execution asynchronously. This object is more or less a glorified bool.

(JSC::Terminator::Terminator):
(JSC::Terminator::termianteSoon):
(JSC::Terminator::shouldTerminate):

2010-04-06 Adam Barth <[email protected]>

Reviewed by Eric Seidel.

REGRESSION: Worker termination via JS timeout may cause worker tests like fast/workers/worker-terminate.html fail.
https://bugs.webkit.org/show_bug.cgi?id=36646

  • fast/workers/resources/worker-run-forever.js: Added.
  • fast/workers/worker-terminate-forever-expected.txt: Added.
  • fast/workers/worker-terminate-forever.html: Added.
    • Test what happens when we terminate an infinitely running worker. The essential point is that we don't spam the console with nonsense about a timeout.
  • platform/mac/Skipped:
    • Rescue worker-terminate.html from the Skipped list now that we've fixed the underlying bug that was causing the flakiness.

2010-04-06 Adam Barth <[email protected]>

Reviewed by Eric Seidel.

REGRESSION: Worker termination via JS timeout may cause worker tests like fast/workers/worker-terminate.html fail.
https://bugs.webkit.org/show_bug.cgi?id=36646

Cause the worker code to swallow termination exceptions because these
need not be reported to the user because they are an implementation
detail of how we terminate worker execution.

Test: fast/workers/worker-terminate-forever.html

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