mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 559653 - Record assignment before the interpreter goes; avoid record_SetPropHit. r=brendan.
--HG-- extra : rebase_source : c6698a313d7d00cd0584e1968d96d4b67ab0373c
This commit is contained in:
parent
1de1fd1451
commit
d55c165fa7
@ -548,7 +548,7 @@ js_SweepWatchPoints(JSContext *cx)
|
||||
* NB: FindWatchPoint must be called with rt->debuggerLock acquired.
|
||||
*/
|
||||
static JSWatchPoint *
|
||||
FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
|
||||
FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id)
|
||||
{
|
||||
JSWatchPoint *wp;
|
||||
|
||||
@ -562,7 +562,7 @@ FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
|
||||
}
|
||||
|
||||
JSScopeProperty *
|
||||
js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
|
||||
js_FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id)
|
||||
{
|
||||
JSWatchPoint *wp;
|
||||
JSScopeProperty *sprop;
|
||||
|
@ -111,7 +111,7 @@ extern void
|
||||
js_SweepWatchPoints(JSContext *cx);
|
||||
|
||||
extern JSScopeProperty *
|
||||
js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id);
|
||||
js_FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id);
|
||||
|
||||
/*
|
||||
* NB: callers outside of jsdbgapi.c must pass non-null scope.
|
||||
|
@ -4306,9 +4306,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
PropertyCacheEntry *entry =
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
}
|
||||
if (propp)
|
||||
*propp = (JSProperty *) sprop;
|
||||
@ -4952,11 +4950,6 @@ ReportReadOnly(JSContext* cx, jsid id, uintN flags)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: all non-error exits in this function must notify the tracer using
|
||||
* SetPropHit when called from the interpreter, which is detected by testing
|
||||
* (defineHow & JSDNP_CACHE_RESULT).
|
||||
*/
|
||||
JSBool
|
||||
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
jsval *vp)
|
||||
@ -5035,8 +5028,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
if (sprop->isAccessorDescriptor()) {
|
||||
if (sprop->hasDefaultSetter()) {
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
if (defineHow & JSDNP_CACHE_RESULT)
|
||||
TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
|
||||
return js_ReportGetterOnlyAssignment(cx);
|
||||
}
|
||||
} else {
|
||||
@ -5046,20 +5037,11 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
|
||||
PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
|
||||
}
|
||||
|
||||
/* Warn in strict mode, otherwise do nothing. */
|
||||
if (JS_HAS_STRICT_OPTION(cx))
|
||||
return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
return JS_TRUE;
|
||||
|
||||
#ifdef JS_TRACER
|
||||
error: // TRACE_2 jumps here in case of error.
|
||||
return JS_FALSE;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (scope->sealed() && !sprop->hasSlot()) {
|
||||
@ -5082,9 +5064,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
if (!sprop->hasSlot()) {
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
PropertyCacheEntry *entry =
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
|
||||
}
|
||||
|
||||
if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
|
||||
@ -5142,8 +5122,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
* Check for Object class here to avoid defining a method on a class
|
||||
* with magic resolve, addProperty, getProperty, etc. hooks.
|
||||
*/
|
||||
if ((defineHow & JSDNP_SET_METHOD) &&
|
||||
obj->getClass() == &js_ObjectClass) {
|
||||
if ((defineHow & JSDNP_SET_METHOD) && obj->getClass() == &js_ObjectClass) {
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
|
||||
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
||||
|
||||
@ -5180,8 +5159,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
PropertyCacheEntry *entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
}
|
||||
|
||||
if (!js_NativeSet(cx, obj, sprop, added, vp))
|
||||
|
@ -619,7 +619,6 @@ END_CASE(JSOP_PICK)
|
||||
|
||||
#define NATIVE_SET(cx,obj,sprop,entry,vp) \
|
||||
JS_BEGIN_MACRO \
|
||||
TRACE_2(SetPropHit, entry, sprop); \
|
||||
if (sprop->hasDefaultSetter() && \
|
||||
(sprop)->slot != SPROP_INVALID_SLOT && \
|
||||
!(obj)->scope()->brandedOrHasMethodBarrier()) { \
|
||||
@ -1783,7 +1782,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
* slot's value that might contain a method of a
|
||||
* branded scope.
|
||||
*/
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
obj->lockedSetSlot(slot, rval);
|
||||
|
||||
/*
|
||||
@ -3351,6 +3349,7 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
lval = FETCH_OPND(-2);
|
||||
obj = JSVAL_TO_OBJECT(lval);
|
||||
JS_ASSERT(obj->isNative());
|
||||
JS_ASSERT(obj->getClass() == &js_ObjectClass);
|
||||
JS_ASSERT(!obj->getClass()->reserveSlots);
|
||||
|
||||
JSScope *scope = obj->scope();
|
||||
@ -3405,7 +3404,6 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
* property, not updating an existing slot's value that might
|
||||
* contain a method of a branded scope.
|
||||
*/
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
obj->lockedSetSlot(slot, rval);
|
||||
} else {
|
||||
PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
|
||||
|
@ -763,7 +763,7 @@ JSScope::addProperty(JSContext *cx, jsid id,
|
||||
* SPROP_CALL_[GS]ETTER macros.
|
||||
*/
|
||||
static inline bool
|
||||
NormalizeGetterAndSetter(JSContext *cx, JSScope *scope,
|
||||
NormalizeGetterAndSetter(JSContext *cx, const JSScope *scope,
|
||||
jsid id, uintN attrs, uintN flags,
|
||||
JSPropertyOp &getter,
|
||||
JSPropertyOp &setter)
|
||||
@ -855,6 +855,25 @@ JSScope::addPropertyHelper(JSContext *cx, jsid id,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSScopeProperty *
|
||||
JSScope::prepareForAddProperty(JSContext *cx, jsid id, JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs, uintN flags, intN shortid) const
|
||||
{
|
||||
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
|
||||
|
||||
// Find or create a property tree node labeled by our arguments. Do not use
|
||||
// getChildProperty because it also effectively adds the property to this
|
||||
// JSScope.
|
||||
if (inDictionaryMode()) {
|
||||
JSScopeProperty *sprop = JS_PROPERTY_TREE(cx).newScopeProperty(cx, true);
|
||||
if (sprop)
|
||||
new (sprop) JSScopeProperty(id, getter, setter, slot, attrs, flags, shortid);
|
||||
return sprop;
|
||||
}
|
||||
JSScopeProperty child(id, getter, setter, slot, attrs, flags, shortid);
|
||||
return JS_PROPERTY_TREE(cx).getChild(cx, lastProp, shape, child);
|
||||
}
|
||||
|
||||
JSScopeProperty *
|
||||
JSScope::putProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
|
@ -279,8 +279,8 @@ struct JSScope : public JSObjectMap
|
||||
bool changeTable(JSContext *cx, int change);
|
||||
void reportReadOnlyScope(JSContext *cx);
|
||||
|
||||
void setOwnShape() { flags |= OWN_SHAPE; }
|
||||
void clearOwnShape() { flags &= ~OWN_SHAPE; }
|
||||
void setOwnShape() { flags |= OWN_SHAPE; }
|
||||
void clearOwnShape() { flags &= ~OWN_SHAPE; }
|
||||
void generateOwnShape(JSContext *cx);
|
||||
|
||||
JSScopeProperty **searchTable(jsid id, bool adding);
|
||||
@ -327,6 +327,17 @@ struct JSScope : public JSObjectMap
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid);
|
||||
|
||||
/*
|
||||
* Like addProperty, but just return the new (or cached) JSScopeProperty
|
||||
* without adding it. This is for use with js_AddProperty.
|
||||
*
|
||||
* Note that the return value is not necessarily rooted.
|
||||
*/
|
||||
JSScopeProperty *prepareForAddProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid) const;
|
||||
|
||||
/* Add a data property whose id is not yet in this scope. */
|
||||
JSScopeProperty *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
|
||||
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
||||
@ -400,16 +411,16 @@ struct JSScope : public JSObjectMap
|
||||
GENERIC = 0x0080
|
||||
};
|
||||
|
||||
bool inDictionaryMode() { return flags & DICTIONARY_MODE; }
|
||||
void setDictionaryMode() { flags |= DICTIONARY_MODE; }
|
||||
void clearDictionaryMode() { flags &= ~DICTIONARY_MODE; }
|
||||
bool inDictionaryMode() const { return flags & DICTIONARY_MODE; }
|
||||
void setDictionaryMode() { flags |= DICTIONARY_MODE; }
|
||||
void clearDictionaryMode() { flags &= ~DICTIONARY_MODE; }
|
||||
|
||||
/*
|
||||
* Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
|
||||
* will avoid taking the lock if the object owns its scope and the scope is
|
||||
* sealed.
|
||||
*/
|
||||
bool sealed() { return flags & SEALED; }
|
||||
bool sealed() const { return flags & SEALED; }
|
||||
|
||||
void seal(JSContext *cx) {
|
||||
JS_ASSERT(!isSharedEmpty());
|
||||
@ -423,7 +434,7 @@ struct JSScope : public JSObjectMap
|
||||
* properties without magic getters and setters), and its scope->shape
|
||||
* evolves whenever a function value changes.
|
||||
*/
|
||||
bool branded() { JS_ASSERT(!generic()); return flags & BRANDED; }
|
||||
bool branded() const { JS_ASSERT(!generic()); return flags & BRANDED; }
|
||||
|
||||
bool brand(JSContext *cx, uint32 slot, jsval v) {
|
||||
JS_ASSERT(!branded());
|
||||
@ -434,15 +445,15 @@ struct JSScope : public JSObjectMap
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generic() { return flags & GENERIC; }
|
||||
void setGeneric() { flags |= GENERIC; }
|
||||
bool generic() const { return flags & GENERIC; }
|
||||
void setGeneric() { flags |= GENERIC; }
|
||||
|
||||
bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
|
||||
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
|
||||
bool hadIndexedProperties() const { return flags & INDEXED_PROPERTIES; }
|
||||
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
|
||||
|
||||
bool hasOwnShape() { return flags & OWN_SHAPE; }
|
||||
bool hasOwnShape() const { return flags & OWN_SHAPE; }
|
||||
|
||||
bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; }
|
||||
bool hasRegenFlag(uint8 regenFlag) const { return (flags & SHAPE_REGEN) == regenFlag; }
|
||||
|
||||
/*
|
||||
* A scope has a method barrier when some compiler-created "null closure"
|
||||
@ -484,8 +495,8 @@ struct JSScope : public JSObjectMap
|
||||
* METHOD_BARRIER too, and regenerate this scope's shape if the method's
|
||||
* value is in fact changing.
|
||||
*/
|
||||
bool hasMethodBarrier() { return flags & METHOD_BARRIER; }
|
||||
void setMethodBarrier() { flags |= METHOD_BARRIER; }
|
||||
bool hasMethodBarrier() const { return flags & METHOD_BARRIER; }
|
||||
void setMethodBarrier() { flags |= METHOD_BARRIER; }
|
||||
|
||||
/*
|
||||
* Test whether this scope may be branded due to method calls, which means
|
||||
@ -493,10 +504,9 @@ struct JSScope : public JSObjectMap
|
||||
* test whether this scope has method properties, which require a method
|
||||
* write barrier.
|
||||
*/
|
||||
bool
|
||||
brandedOrHasMethodBarrier() { return flags & (BRANDED | METHOD_BARRIER); }
|
||||
bool brandedOrHasMethodBarrier() const { return flags & (BRANDED | METHOD_BARRIER); }
|
||||
|
||||
bool isSharedEmpty() const { return !object; }
|
||||
bool isSharedEmpty() const { return !object; }
|
||||
|
||||
static bool initRuntimeState(JSContext *cx);
|
||||
static void finishRuntimeState(JSContext *cx);
|
||||
|
@ -2445,6 +2445,7 @@ TraceRecorder::insImmVal(jsval val)
|
||||
inline LIns*
|
||||
TraceRecorder::insImmObj(JSObject* obj)
|
||||
{
|
||||
JS_ASSERT(obj);
|
||||
tree->gcthings.addUnique(OBJECT_TO_JSVAL(obj));
|
||||
return lir->insImmP((void*)obj);
|
||||
}
|
||||
@ -2452,6 +2453,7 @@ TraceRecorder::insImmObj(JSObject* obj)
|
||||
inline LIns*
|
||||
TraceRecorder::insImmFun(JSFunction* fun)
|
||||
{
|
||||
JS_ASSERT(fun);
|
||||
tree->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun)));
|
||||
return lir->insImmP((void*)fun);
|
||||
}
|
||||
@ -2459,6 +2461,7 @@ TraceRecorder::insImmFun(JSFunction* fun)
|
||||
inline LIns*
|
||||
TraceRecorder::insImmStr(JSString* str)
|
||||
{
|
||||
JS_ASSERT(str);
|
||||
tree->gcthings.addUnique(STRING_TO_JSVAL(str));
|
||||
return lir->insImmP((void*)str);
|
||||
}
|
||||
@ -2466,6 +2469,7 @@ TraceRecorder::insImmStr(JSString* str)
|
||||
inline LIns*
|
||||
TraceRecorder::insImmSprop(JSScopeProperty* sprop)
|
||||
{
|
||||
JS_ASSERT(sprop);
|
||||
tree->sprops.addUnique(sprop);
|
||||
return lir->insImmP((void*)sprop);
|
||||
}
|
||||
@ -11245,20 +11249,95 @@ TraceRecorder::record_JSOP_GETPROP()
|
||||
return getProp(stackval(-1));
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETPROP()
|
||||
/*
|
||||
* If possible, lookup obj[id] without calling any resolve hooks or touching
|
||||
* any non-native objects or objects that have been shared across contexts,
|
||||
* store the results in *pobjp and *spropp (NULL if no such property exists),
|
||||
* and return true. This does not take any locks. That is safe because we
|
||||
* avoid shared objects.
|
||||
*
|
||||
* If a safe lookup is not possible, return false; *pobjp and *spropp are
|
||||
* undefined.
|
||||
*/
|
||||
static bool
|
||||
SafeLookup(JSContext *cx, JSObject* obj, jsid id, JSObject** pobjp, JSScopeProperty** spropp)
|
||||
{
|
||||
jsval& l = stackval(-2);
|
||||
if (JSVAL_IS_PRIMITIVE(l))
|
||||
RETURN_STOP_A("primitive this for SETPROP");
|
||||
do {
|
||||
// Avoid non-native lookupProperty hooks.
|
||||
if (obj->map->ops->lookupProperty != js_LookupProperty)
|
||||
return false;
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(l);
|
||||
if (obj->map->ops->setProperty != js_SetProperty)
|
||||
RETURN_STOP_A("non-native JSObjectOps::setProperty");
|
||||
return ARECORD_CONTINUE;
|
||||
// Avoid shared objects.
|
||||
JSScope *scope = obj->scope();
|
||||
if (!CX_OWNS_SCOPE_TITLE(cx, scope))
|
||||
return false;
|
||||
|
||||
if (JSScopeProperty *sprop = scope->lookup(id)) {
|
||||
*pobjp = obj;
|
||||
*spropp = sprop;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Avoid resolve hooks.
|
||||
if (obj->getClass()->resolve != JS_ResolveStub)
|
||||
return false;
|
||||
} while ((obj = obj->getProto()) != NULL);
|
||||
*pobjp = NULL;
|
||||
*spropp = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Emit a specialized, inlined copy of js_NativeSet. */
|
||||
/*
|
||||
* Lookup the property for the SETPROP/SETNAME/SETMETHOD instruction at pc.
|
||||
* Emit guards to ensure that the result at run time is the same.
|
||||
*/
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::lookupForSetPropertyOp(JSObject* obj, LIns* obj_ins, jsid id,
|
||||
bool* safep, JSObject** pobjp, JSScopeProperty** spropp)
|
||||
{
|
||||
// We could consult the property cache here, but the contract for
|
||||
// PropertyCache::testForSet is intricate enough that it's a lot less code
|
||||
// to do a SafeLookup.
|
||||
*safep = SafeLookup(cx, obj, id, pobjp, spropp);
|
||||
if (!*safep)
|
||||
return RECORD_CONTINUE;
|
||||
|
||||
VMSideExit *exit = snapshot(BRANCH_EXIT);
|
||||
if (*spropp) {
|
||||
if (obj != globalObj)
|
||||
CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_kshape", exit));
|
||||
if (obj != *pobjp && *pobjp != globalObj) {
|
||||
CHECK_STATUS(guardShape(INS_CONSTOBJ(*pobjp), *pobjp, (*pobjp)->shape(),
|
||||
"guard_vshape", exit));
|
||||
}
|
||||
} else {
|
||||
for (;;) {
|
||||
if (obj != globalObj)
|
||||
CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_proto_chain", exit));
|
||||
obj = obj->getProto();
|
||||
if (!obj)
|
||||
break;
|
||||
obj_ins = INS_CONSTOBJ(obj);
|
||||
}
|
||||
}
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
static JSBool FASTCALL
|
||||
MethodWriteBarrier(JSContext* cx, JSObject* obj, uint32 slot, jsval v)
|
||||
{
|
||||
AutoValueRooter tvr(cx, v);
|
||||
return obj->scope()->methodWriteBarrier(cx, slot, v);
|
||||
}
|
||||
JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, UINT32, JSVAL,
|
||||
0, ACC_STORE_ANY)
|
||||
|
||||
/*
|
||||
* Emit a specialized, inlined copy of js_NativeSet.
|
||||
*
|
||||
* Note that since this runs before the interpreter opcode, obj->scope() may
|
||||
* not actually have the property sprop yet.
|
||||
*/
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
|
||||
jsval v, LIns* v_ins)
|
||||
@ -11266,9 +11345,13 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
|
||||
JSScope* scope = obj->scope();
|
||||
uint32 slot = sprop->slot;
|
||||
|
||||
// If obj is the global, it must already have sprop. Otherwise we would
|
||||
// have gone through addDataProperty, which would have stopped recording.
|
||||
JS_ASSERT_IF(obj == globalObj, scope->hasProperty(sprop));
|
||||
|
||||
/*
|
||||
* We do not trace assignment to properties that have both a nonstub setter
|
||||
* and a slot, for several reasons.
|
||||
* We do not trace assignment to properties that have both a non-default
|
||||
* setter and a slot, for several reasons.
|
||||
*
|
||||
* First, that would require sampling rt->propertyRemovals before and after
|
||||
* (see js_NativeSet), and even more code to handle the case where the two
|
||||
@ -11283,8 +11366,18 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
|
||||
* setter's return value differed from the record-time type of v, in which
|
||||
* case unboxing would fail and, having called a native setter, we could
|
||||
* not just retry the instruction in the interpreter.
|
||||
*
|
||||
* If obj is branded, we would have a similar problem recovering from a
|
||||
* failed call to MethodWriteBarrier.
|
||||
*/
|
||||
JS_ASSERT(sprop->hasDefaultSetter() || slot == SPROP_INVALID_SLOT);
|
||||
if (!sprop->hasDefaultSetter() && slot != SPROP_INVALID_SLOT)
|
||||
RETURN_STOP("can't trace set of property with setter and slot");
|
||||
|
||||
// These two cases are strict-mode errors and can't be traced.
|
||||
if (sprop->hasGetterValue() && sprop->hasDefaultSetter())
|
||||
RETURN_STOP("can't set a property that has only a getter");
|
||||
if (sprop->isDataDescriptor() && !sprop->writable())
|
||||
RETURN_STOP("can't assign to readonly property");
|
||||
|
||||
// Box the value to be stored, if necessary.
|
||||
LIns* boxed_ins = NULL;
|
||||
@ -11292,14 +11385,36 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
|
||||
boxed_ins = box_jsval(v, v_ins);
|
||||
|
||||
// Call the setter, if any.
|
||||
if (!sprop->hasDefaultSetter())
|
||||
if (!sprop->hasDefaultSetter()) {
|
||||
if (sprop->hasSetterValue())
|
||||
RETURN_STOP("can't trace JavaScript function setter yet");
|
||||
emitNativePropertyOp(scope, sprop, obj_ins, true, boxed_ins);
|
||||
}
|
||||
|
||||
// Store the value, if this property has a slot.
|
||||
if (slot != SPROP_INVALID_SLOT) {
|
||||
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
|
||||
if (scope->brandedOrHasMethodBarrier()) {
|
||||
if (obj == globalObj) {
|
||||
// Because the trace is type-specialized to the global object's
|
||||
// slots, no run-time check is needed. Avoid recording a global
|
||||
// shape change, though.
|
||||
if (VALUE_IS_FUNCTION(cx, obj->getSlot(slot)))
|
||||
RETURN_STOP("can't trace set of function-valued property in branded global");
|
||||
} else {
|
||||
// Setting a function-valued property might need to rebrand the
|
||||
// object. Call the method write barrier. Note that even if the
|
||||
// property is not function-valued now, it might be on trace.
|
||||
enterDeepBailCall();
|
||||
LIns* args[] = { boxed_ins, INS_CONST(slot), obj_ins, cx_ins };
|
||||
LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
|
||||
guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
|
||||
leaveDeepBailCall();
|
||||
}
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
JS_ASSERT(sprop->hasSlot());
|
||||
if (obj == globalObj) {
|
||||
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
|
||||
if (!lazilyImportGlobalSlot(slot))
|
||||
RETURN_STOP("lazy import of global slot failed");
|
||||
set(&obj->getSlotRef(slot), v_ins);
|
||||
@ -11312,89 +11427,65 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
static JSBool FASTCALL
|
||||
MethodWriteBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj)
|
||||
{
|
||||
AutoValueRooter tvr(cx, funobj);
|
||||
|
||||
return obj->scope()->methodWriteBarrier(cx, sprop, tvr.value());
|
||||
}
|
||||
JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
|
||||
0, ACC_STORE_ANY)
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::setProp(jsval &l, PropertyCacheEntry* entry, JSScopeProperty* sprop,
|
||||
jsval &v, LIns*& v_ins)
|
||||
TraceRecorder::addDataProperty(JSObject* obj, LIns* obj_ins,
|
||||
jsid id, JSPropertyOp getter, JSPropertyOp setter,
|
||||
uintN flags, intN shortid, jsval v, LIns* v_ins)
|
||||
{
|
||||
if (entry == JS_NO_PROP_CACHE_FILL)
|
||||
RETURN_STOP("can't trace uncacheable property set");
|
||||
JS_ASSERT_IF(entry->vcapTag() >= 1, !sprop->hasSlot());
|
||||
if (!sprop->hasDefaultSetter() && sprop->slot != SPROP_INVALID_SLOT)
|
||||
RETURN_STOP("can't trace set of property with setter and slot");
|
||||
if (sprop->hasSetterValue())
|
||||
RETURN_STOP("can't trace JavaScript function setter");
|
||||
// If obj is the global, the global shape is about to change. Note also
|
||||
// that since we do not record this case, SETNAME and SETPROP are identical
|
||||
// as far as the tracer is concerned. (js_CheckUndeclaredVarAssignment
|
||||
// distinguishes the two, in the interpreter.)
|
||||
if (obj == globalObj)
|
||||
RETURN_STOP("set new property of global object"); // global shape change
|
||||
|
||||
// These two cases are errors and can't be traced.
|
||||
if (sprop->hasGetterValue())
|
||||
RETURN_STOP("can't assign to property with script getter but no setter");
|
||||
if (!sprop->writable())
|
||||
RETURN_STOP("can't assign to readonly property");
|
||||
// js_AddProperty does not call the addProperty hook.
|
||||
if (obj->getClass()->addProperty != JS_PropertyStub)
|
||||
RETURN_STOP("set new property of object with addProperty hook");
|
||||
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
|
||||
JSObject* obj = JSVAL_TO_OBJECT(l);
|
||||
LIns* obj_ins = get(&l);
|
||||
// See comment in TR::nativeSet about why we do not support setting a
|
||||
// property that has both a setter and a slot.
|
||||
if (setter != JS_PropertyStub)
|
||||
RETURN_STOP("set new property with setter and slot");
|
||||
|
||||
JS_ASSERT_IF(entry->directHit(), obj->scope()->hasProperty(sprop));
|
||||
// Methods can be defined only on plain Objects.
|
||||
jsbytecode op = *cx->fp->regs->pc;
|
||||
if ((op == JSOP_INITMETHOD || op == JSOP_SETMETHOD) && obj->getClass() == &js_ObjectClass) {
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
|
||||
|
||||
// Fast path for CallClass. This is about 20% faster than the general case.
|
||||
v_ins = get(&v);
|
||||
if (obj->getClass() == &js_CallClass)
|
||||
return setCallProp(obj, obj_ins, sprop, v_ins, v);
|
||||
|
||||
// Find obj2. If entry->adding(), the TAG bits are all 0.
|
||||
JSObject* obj2 = obj;
|
||||
for (jsuword i = entry->scopeIndex(); i; i--)
|
||||
obj2 = obj2->getParent();
|
||||
for (jsuword j = entry->protoIndex(); j; j--)
|
||||
obj2 = obj2->getProto();
|
||||
JSScope *scope = obj2->scope();
|
||||
JS_ASSERT_IF(entry->adding(), obj2 == obj);
|
||||
|
||||
// Guard before anything else.
|
||||
PCVal pcval;
|
||||
CHECK_STATUS(guardPropertyCacheHit(obj_ins, obj, obj2, entry, pcval));
|
||||
JS_ASSERT(scope->object == obj2);
|
||||
JS_ASSERT(scope->hasProperty(sprop));
|
||||
JS_ASSERT_IF(obj2 != obj, !sprop->hasSlot());
|
||||
|
||||
/*
|
||||
* Setting a function-valued property might need to rebrand the object, so
|
||||
* we emit a call to the method write barrier. There's no need to guard on
|
||||
* this, because functions have distinct trace-type from other values and
|
||||
* branded-ness is implied by the shape, which we've already guarded on.
|
||||
*/
|
||||
if (scope->brandedOrHasMethodBarrier() && VALUE_IS_FUNCTION(cx, v) && entry->directHit()) {
|
||||
if (obj == globalObj)
|
||||
RETURN_STOP("can't trace function-valued property set in branded global scope");
|
||||
|
||||
enterDeepBailCall();
|
||||
LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
|
||||
LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
|
||||
guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
|
||||
leaveDeepBailCall();
|
||||
JSObject *funobj = JSVAL_TO_OBJECT(v);
|
||||
if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
|
||||
flags |= JSScopeProperty::METHOD;
|
||||
getter = js_CastAsPropertyOp(funobj);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a property to the object if necessary.
|
||||
if (entry->adding()) {
|
||||
JS_ASSERT(sprop->hasSlot());
|
||||
if (obj == globalObj)
|
||||
RETURN_STOP("adding a property to the global object");
|
||||
// Determine what slot the interpreter will assign this property.
|
||||
JSScope *scope;
|
||||
uint32 slot;
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = js_GetMutableScope(cx, obj);
|
||||
if (!scope)
|
||||
RETURN_ERROR("js_GetMutableScope failed");
|
||||
if (!js_AllocSlot(cx, obj, &slot))
|
||||
RETURN_ERROR("js_AllocSlot failed");
|
||||
js_FreeSlot(cx, obj, slot);
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
|
||||
LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
|
||||
LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
|
||||
guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
|
||||
}
|
||||
// Find or create an sprop for the new property but avoid adding it to
|
||||
// obj's scope. The sprop returned by prepareForAddProperty may not be
|
||||
// rooted, but we immediately pass it to INS_CONSTSPROP, which roots it.
|
||||
JSScopeProperty* sprop = obj->scope()->prepareForAddProperty(cx, id, getter, setter, slot,
|
||||
JSPROP_ENUMERATE, flags, shortid);
|
||||
if (!sprop)
|
||||
RETURN_ERROR("JSScope::prepareForAddProperty failed");
|
||||
|
||||
// On trace, call js_AddProperty to do the dirty work.
|
||||
LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
|
||||
LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
|
||||
guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
|
||||
|
||||
// Box the value and store it in the new slot.
|
||||
return nativeSet(obj, obj_ins, sprop, v, v_ins);
|
||||
}
|
||||
|
||||
@ -11545,27 +11636,169 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_SetPropHit(PropertyCacheEntry* entry, JSScopeProperty* sprop)
|
||||
/*
|
||||
* Emit a specialized, inlined copy of js_SetPropertyHelper for the current
|
||||
* instruction.
|
||||
*/
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::setProperty(JSObject* obj, LIns* obj_ins, jsval v, LIns* v_ins)
|
||||
{
|
||||
jsval& r = stackval(-1);
|
||||
jsval& l = stackval(-2);
|
||||
LIns* v_ins;
|
||||
CHECK_STATUS_A(setProp(l, entry, sprop, r, v_ins));
|
||||
JSAtom *atom;
|
||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, cx->fp->regs->pc, 0, atom);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
jsbytecode* pc = cx->fp->regs->pc;
|
||||
switch (*pc) {
|
||||
case JSOP_SETPROP:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_SETMETHOD:
|
||||
if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
|
||||
set(&l, v_ins);
|
||||
break;
|
||||
if (obj->map->ops->setProperty != js_SetProperty)
|
||||
RETURN_STOP("non-native object"); // TODO - fall back on slow path
|
||||
if (obj->scope()->sealed())
|
||||
RETURN_STOP("setting property of sealed object"); // this will throw
|
||||
|
||||
default:;
|
||||
bool safe;
|
||||
JSObject* pobj;
|
||||
JSScopeProperty* sprop;
|
||||
CHECK_STATUS(lookupForSetPropertyOp(obj, obj_ins, id, &safe, &pobj, &sprop));
|
||||
if (!safe)
|
||||
RETURN_STOP("setprop: lookup fail"); // TODO - fall back on slow path
|
||||
|
||||
// Handle Call objects specially. The Call objects we create on trace are
|
||||
// bogus. Calling the setter on such an object wouldn't work.
|
||||
if (obj->getClass() == &js_CallClass)
|
||||
return setCallProp(obj, obj_ins, sprop, v_ins, v);
|
||||
|
||||
// Handle setting a property that is not found on obj or anywhere on its
|
||||
// the prototype chain.
|
||||
if (!sprop) {
|
||||
JSClass *cls = obj->getClass();
|
||||
return addDataProperty(obj, obj_ins, id, cls->getProperty, cls->setProperty, 0, 0,
|
||||
v, v_ins);
|
||||
}
|
||||
|
||||
return ARECORD_CONTINUE;
|
||||
// Check whether we can assign to/over the existing property.
|
||||
if (sprop->isAccessorDescriptor()) {
|
||||
if (sprop->hasDefaultSetter())
|
||||
RETURN_STOP("setting accessor property with no setter");
|
||||
} else if (!sprop->writable()) {
|
||||
RETURN_STOP("setting readonly data property");
|
||||
}
|
||||
|
||||
// Handle setting an existing own property.
|
||||
if (pobj == obj)
|
||||
return nativeSet(obj, obj_ins, sprop, v, v_ins);
|
||||
|
||||
// Handle setting an inherited non-SHARED property.
|
||||
if (sprop->hasSlot()) {
|
||||
JSPropertyOp getter, setter;
|
||||
uintN flags;
|
||||
intN shortid;
|
||||
|
||||
if (sprop->hasShortID()) {
|
||||
getter = sprop->getter();
|
||||
setter = sprop->setter();
|
||||
flags = JSScopeProperty::HAS_SHORTID;
|
||||
shortid = sprop->shortid;
|
||||
} else {
|
||||
JSClass *cls = obj->getClass();
|
||||
getter = cls->getProperty;
|
||||
setter = cls->setProperty;
|
||||
flags = 0;
|
||||
shortid = 0;
|
||||
}
|
||||
return addDataProperty(obj, obj_ins, id, getter, setter, flags, shortid, v, v_ins);
|
||||
}
|
||||
|
||||
// Handle setting an inherited SHARED property.
|
||||
if (pobj->scope()->sealed())
|
||||
RETURN_STOP("setting SHARED property inherited from sealed object");
|
||||
if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
|
||||
return RECORD_CONTINUE; // nothing happens
|
||||
|
||||
// Handle setting an inherited SHARED property with a non-default setter.
|
||||
return nativeSet(obj, obj_ins, sprop, v, v_ins);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record a JSOP_SET{PROP,NAME,METHOD} instruction or a JSOP_INITPROP
|
||||
* instruction initializing __proto__.
|
||||
*/
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::recordSetPropertyOp()
|
||||
{
|
||||
jsval& l = stackval(-2);
|
||||
if (JSVAL_IS_PRIMITIVE(l))
|
||||
RETURN_STOP("primitive operand object");
|
||||
JSObject* obj = JSVAL_TO_OBJECT(l);
|
||||
LIns* obj_ins = get(&l);
|
||||
|
||||
jsval& r = stackval(-1);
|
||||
LIns* r_ins = get(&r);
|
||||
|
||||
CHECK_STATUS(setProperty(obj, obj_ins, r, r_ins));
|
||||
set(&l, r_ins);
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETPROP()
|
||||
{
|
||||
return InjectStatus(recordSetPropertyOp());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETMETHOD()
|
||||
{
|
||||
return InjectStatus(recordSetPropertyOp());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETNAME()
|
||||
{
|
||||
return InjectStatus(recordSetPropertyOp());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::recordInitPropertyOp()
|
||||
{
|
||||
jsval& l = stackval(-2);
|
||||
JSObject* obj = JSVAL_TO_OBJECT(l);
|
||||
LIns* obj_ins = get(&l);
|
||||
JS_ASSERT(obj->getClass() == &js_ObjectClass);
|
||||
|
||||
jsval& v = stackval(-1);
|
||||
LIns* v_ins = get(&v);
|
||||
|
||||
JSAtom *atom;
|
||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, cx->fp->regs->pc, 0, atom);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
// If obj already has this property (because the id appears more than once
|
||||
// in the initializer), just set it.
|
||||
JSScope* scope = obj->scope();
|
||||
if (!CX_OWNS_SCOPE_TITLE(cx, scope))
|
||||
RETURN_STOP("object being initialized is shared among contexts");
|
||||
if (JSScopeProperty* sprop = scope->lookup(id)) {
|
||||
JS_ASSERT(sprop->isDataDescriptor());
|
||||
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
|
||||
JS_ASSERT(sprop->hasDefaultSetter());
|
||||
return nativeSet(obj, obj_ins, sprop, v, v_ins);
|
||||
}
|
||||
|
||||
// Duplicate the interpreter's special treatment of __proto__.
|
||||
if (atom == cx->runtime->atomState.protoAtom)
|
||||
return recordSetPropertyOp();
|
||||
|
||||
// Define a new property.
|
||||
return addDataProperty(obj, obj_ins, id, JS_PropertyStub, JS_PropertyStub, 0, 0, v, v_ins);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITPROP()
|
||||
{
|
||||
return InjectStatus(recordInitPropertyOp());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITMETHOD()
|
||||
{
|
||||
return InjectStatus(recordInitPropertyOp());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK VMSideExit*
|
||||
@ -13510,13 +13743,6 @@ TraceRecorder::record_JSOP_ENDINIT()
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITPROP()
|
||||
{
|
||||
// All the action is in record_SetPropHit.
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITELEM()
|
||||
{
|
||||
@ -13858,13 +14084,6 @@ TraceRecorder::record_JSOP_BINDNAME()
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETNAME()
|
||||
{
|
||||
// record_SetPropHit does all the work.
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_THROW()
|
||||
{
|
||||
@ -15262,18 +15481,6 @@ TraceRecorder::record_JSOP_CONCATN()
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETMETHOD()
|
||||
{
|
||||
return record_JSOP_SETPROP();
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITMETHOD()
|
||||
{
|
||||
return record_JSOP_INITPROP();
|
||||
}
|
||||
|
||||
JSBool FASTCALL
|
||||
js_Unbrand(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -1304,24 +1304,37 @@ class TraceRecorder
|
||||
nanojit::LIns* obj_ins,
|
||||
JSScopeProperty* sprop);
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
JSScopeProperty* sprop,
|
||||
jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, PropertyCacheEntry* entry,
|
||||
JSScopeProperty* sprop,
|
||||
jsval &v, nanojit::LIns*& v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
|
||||
JSScopeProperty *sprop, nanojit::LIns *v_ins,
|
||||
jsval v);
|
||||
JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
|
||||
jsval* idvalp, jsval* rvalp,
|
||||
bool init);
|
||||
jsval* idvalp, jsval* rvalp,
|
||||
bool init);
|
||||
JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
|
||||
nanojit::LIns* index_ins,
|
||||
jsval* rvalp, bool init);
|
||||
nanojit::LIns* index_ins,
|
||||
jsval* rvalp, bool init);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
|
||||
int v_spindex);
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus lookupForSetPropertyOp(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
jsid id, bool* safep,
|
||||
JSObject** pobjp,
|
||||
JSScopeProperty** spropp);
|
||||
JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
JSScopeProperty* sprop,
|
||||
jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus addDataProperty(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uintN flags, intN shortid,
|
||||
jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
|
||||
JSScopeProperty *sprop, nanojit::LIns *v_ins,
|
||||
jsval v);
|
||||
JS_REQUIRES_STACK RecordingStatus setProperty(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus recordSetPropertyOp();
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus recordInitPropertyOp();
|
||||
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* box_jsval(jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK nanojit::LIns* unbox_jsval(jsval v, nanojit::LIns* v_ins, VMSideExit* exit);
|
||||
JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
|
||||
@ -1460,8 +1473,6 @@ public:
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame(uintN& inlineCallCount);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame();
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(PropertyCacheEntry* entry,
|
||||
JSScopeProperty* sprop);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete();
|
||||
void forgetGuardedShapesForObject(JSObject* obj);
|
||||
|
4
js/src/trace-test/tests/basic/testMethodWriteBarrier.js
Normal file
4
js/src/trace-test/tests/basic/testMethodWriteBarrier.js
Normal file
@ -0,0 +1,4 @@
|
||||
var x = {p: 0.1, m: function(){}};
|
||||
x.m(); // the interpreter brands x
|
||||
for (var i = 0; i < 9; i++)
|
||||
x.p = 0.1;
|
17
js/src/trace-test/tests/basic/testMethodWriteBarrier2.js
Normal file
17
js/src/trace-test/tests/basic/testMethodWriteBarrier2.js
Normal file
@ -0,0 +1,17 @@
|
||||
function C() {
|
||||
this.m = function () {}; // JSOP_SETMETHOD
|
||||
}
|
||||
|
||||
var a = [new C, new C, new C, new C, new C, new C, new C, new C, new C];
|
||||
var b = [new C, new C, new C, new C, new C, new C, a[8], new C, new C];
|
||||
|
||||
var thrown = 'none';
|
||||
try {
|
||||
for (var i = 0; i < 9; i++) {
|
||||
a[i].m();
|
||||
b[i].m = 0.7; // MethodWriteBarrier required here
|
||||
}
|
||||
} catch (exc) {
|
||||
thrown = exc.name;
|
||||
}
|
||||
assertEq(thrown, 'TypeError');
|
27
js/src/trace-test/tests/basic/testMethodWriteBarrier3.js
Normal file
27
js/src/trace-test/tests/basic/testMethodWriteBarrier3.js
Normal file
@ -0,0 +1,27 @@
|
||||
function g() {}
|
||||
|
||||
function h() {
|
||||
for (var i = 0; i < 9; i++)
|
||||
x.f = i;
|
||||
}
|
||||
|
||||
function j() {
|
||||
x.f();
|
||||
}
|
||||
|
||||
var x = {f: 0.7, g: g};
|
||||
x.g(); // interpreter brands x
|
||||
h();
|
||||
print(shapeOf(x));
|
||||
x.f = function (){}; // does not change x's shape
|
||||
j();
|
||||
print(shapeOf(x));
|
||||
h(); // should change x's shape
|
||||
|
||||
var thrown = 'none';
|
||||
try {
|
||||
j(); // should throw since x.f === 8
|
||||
} catch (exc) {
|
||||
thrown = exc.name;
|
||||
}
|
||||
assertEq(thrown, 'TypeError');
|
Loading…
Reference in New Issue
Block a user