Backed out changeset e09130fcb013

This commit is contained in:
Robert Sayre 2009-07-28 18:51:35 -04:00
parent 19c9082e33
commit d366588e38
17 changed files with 213 additions and 535 deletions

View File

@ -964,8 +964,6 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_DEFFUN_DBGFC */ 0, /* JSOP_DEFFUN_DBGFC */
0, /* JSOP_DEFLOCALFUN_DBGFC */ 0, /* JSOP_DEFLOCALFUN_DBGFC */
0, /* JSOP_LAMBDA_DBGFC */ 0, /* JSOP_LAMBDA_DBGFC */
0, /* JSOP_SETMETHOD */
0, /* JSOP_INITMETHOD */
}; };
#define JSOP_IS_IMACOP(x) (0 \ #define JSOP_IS_IMACOP(x) (0 \
|| x == JSOP_BITOR \ || x == JSOP_BITOR \

View File

@ -3544,7 +3544,7 @@ JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
jsval *vp) jsval *vp)
{ {
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp)) if (!js_GetMethod(cx, obj, id, false, vp))
return JS_FALSE; return JS_FALSE;
if (objp) if (objp)
*objp = obj; *objp = obj;

View File

@ -792,7 +792,7 @@ array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (prop) { if (prop) {
if (OBJ_IS_NATIVE(obj2)) { if (OBJ_IS_NATIVE(obj2)) {
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp)) if (!js_NativeGet(cx, obj, obj2, sprop, vp))
return JS_FALSE; return JS_FALSE;
} }
OBJ_DROP_PROPERTY(cx, obj2, prop); OBJ_DROP_PROPERTY(cx, obj2, prop);

View File

@ -337,9 +337,9 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, IN
jsval FASTCALL jsval FASTCALL
js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
{ {
JS_ASSERT(!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)); JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
jsval v; jsval v;
if (!sprop->get(cx, obj, &v)) if (!js_GetSprop(cx, sprop, obj, &v))
return JSVAL_ERROR_COOKIE; return JSVAL_ERROR_COOKIE;
return v; return v;
} }

View File

@ -6529,15 +6529,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
ale = cg->atomList.add(cg->compiler, pn3->pn_atom); ale = cg->atomList.add(cg->compiler, pn3->pn_atom);
if (!ale) if (!ale)
return JS_FALSE; return JS_FALSE;
EMIT_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
JSOp initOp = (PN_OP(pn2->pn_right) == JSOP_LAMBDA
#if JS_HAS_GETTER_SETTER
&& op != JSOP_GETTER && op != JSOP_SETTER
#endif
)
? JSOP_INITMETHOD
: JSOP_INITPROP;
EMIT_INDEX_OP(initOp, ALE_INDEX(ale));
} }
} }

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99: * vim: set ts=8 sw=4 et tw=79:
* *
* ***** BEGIN LICENSE BLOCK ***** * ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -184,61 +184,46 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
* is a plain old method? It's a function-valued property with stub * is a plain old method? It's a function-valued property with stub
* getter, so get of a function is idempotent. * getter, so get of a function is idempotent.
*/ */
if (cs->format & JOF_CALLOP) { if ((cs->format & JOF_CALLOP) &&
SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) {
jsval v; jsval v;
if (sprop->isMethod()) { v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
if (VALUE_IS_FUNCTION(cx, v)) {
/* /*
* A compiler-created function object, AKA a method, already * Great, we have a function-valued prototype property where
* memoized in the property tree. * the getter is JS_PropertyStub. The type id in pobj's scope
* does not evolve with changes to property values, however.
*
* So here, on first cache fill for this method, we brand the
* scope with a new shape and set the SCOPE_BRANDED flag. Once
* this scope flag is set, any write to a function-valued plain
* old property in pobj will result in shape being regenerated.
*/ */
JS_ASSERT(scope->hasMethodBarrier()); if (!scope->branded()) {
v = sprop->methodValue(); PCMETER(cache->brandfills++);
JS_ASSERT(VALUE_IS_FUNCTION(cx, v)); #ifdef DEBUG_notme
fprintf(stderr,
"branding %p (%s) for funobj %p (%s), shape %lu\n",
pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
JSVAL_TO_OBJECT(v),
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
OBJ_SHAPE(obj));
#endif
scope->brandingShapeChange(cx, sprop->slot, v);
if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */
return JS_NO_PROP_CACHE_FILL;
scope->setBranded();
}
vword = JSVAL_OBJECT_TO_PCVAL(v); vword = JSVAL_OBJECT_TO_PCVAL(v);
break; break;
} }
if (SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) {
v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
if (VALUE_IS_FUNCTION(cx, v)) {
/*
* Great, we have a function-valued prototype property
* where the getter is JS_PropertyStub. The type id in
* pobj's scope does not evolve with changes to property
* values, however.
*
* So here, on first cache fill for this method, we brand
* the scope with a new shape and set the SCOPE_BRANDED
* flag. Once this scope flag is set, any write that adds
* or deletes a function-valued plain old property in
* scope->object will result in shape being regenerated.
*/
if (!scope->branded()) {
PCMETER(cache->brandfills++);
#ifdef DEBUG_notme
fprintf(stderr,
"branding %p (%s) for funobj %p (%s), shape %lu\n",
pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
JSVAL_TO_OBJECT(v),
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
OBJ_SHAPE(obj));
#endif
scope->brandingShapeChange(cx, sprop->slot, v);
if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */
return JS_NO_PROP_CACHE_FILL;
scope->setBranded();
}
vword = JSVAL_OBJECT_TO_PCVAL(v);
break;
}
}
} }
/* If getting a value via a stub getter, we can cache the slot. */ /* If getting a value via a stub getter, we can cache the slot. */
if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) && if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) { SPROP_HAS_VALID_SLOT(sprop, scope)) {
/* Great, let's cache sprop's slot and use it on cache hit. */ /* Great, let's cache sprop's slot and use it on cache hit. */
vword = SLOT_TO_PCVAL(sprop->slot); vword = SLOT_TO_PCVAL(sprop->slot);
@ -250,30 +235,30 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
scope->shape == sprop->shape) { scope->shape == sprop->shape) {
/* /*
* Our caller added a new property. We also know that a setter * Our caller added a new property. We also know that a setter
* that js_NativeSet could have run has not mutated the scope, * that js_NativeSet could have run has not mutated the scope
* so the added property is still the last one added, and the * so the added property is still the last one added and the
* scope is not branded. * scope is not branded.
* *
* We want to cache under scope's shape before the property * We want to cache under scope's shape before the property
* addition to bias for the case when the mutator opcode * addition to bias for the case when the mutator opcode
* always adds the same property. This allows us to optimize * always adds the same property. It allows to optimize
* periodic execution of object initializers or other explicit * periodic execution of object initializers or explicit
* initialization sequences such as * initialization sequences like
* *
* obj = {}; obj.x = 1; obj.y = 2; * obj = {}; obj.x = 1; obj.y = 2;
* *
* We assume that on average the win from this optimization is * We assume that on average the win from this optimization is
* greater than the cost of an extra mismatch per loop owing to * bigger that the cost of an extra mismatch per loop due to
* the bias for the following case: * the bias for the following case:
* *
* obj = {}; ... for (...) { ... obj.x = ... } * obj = {}; ... for (...) { ... obj.x = ... }
* *
* On the first iteration of such a for loop, JSOP_SETPROP * On the first iteration JSOP_SETPROP fills the cache with
* fills the cache with the shape of the newly created object * the shape of newly created object, not the shape after
* obj, not the shape of obj after obj.x has been assigned. * obj.x is assigned. That mismatches obj's shape on the
* That mismatches obj's shape on the second iteration. Note * second iteration. Note that on third and the following
* that on the third and subsequent iterations the cache will * iterations the cache will be hit since the shape no longer
* be hit because the shape is no longer updated. * mutates.
*/ */
JS_ASSERT(scope->owned()); JS_ASSERT(scope->owned());
if (sprop->parent) { if (sprop->parent) {
@ -1004,7 +989,7 @@ js_OnUnknownMethod(JSContext *cx, jsval *vp)
MUST_FLOW_THROUGH("out"); MUST_FLOW_THROUGH("out");
id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
ok = js_GetMethod(cx, obj, id, 0, &tvr.u.value); ok = js_GetMethod(cx, obj, id, false, &tvr.u.value);
if (!ok) if (!ok)
goto out; goto out;
if (JSVAL_IS_PRIMITIVE(tvr.u.value)) { if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
@ -2147,9 +2132,9 @@ js_TraceOpcode(JSContext *cx)
fp->script, cx->tracePrevPc); fp->script, cx->tracePrevPc);
/* /*
* If there aren't that many elements on the stack, then we have * If there aren't that many elements on the stack, then
* probably entered a new frame, and printing output would just be * we have probably entered a new frame, and printing output
* misleading. * would just be misleading.
*/ */
if (ndefs != 0 && if (ndefs != 0 &&
ndefs < regs->sp - fp->slots) { ndefs < regs->sp - fp->slots) {
@ -2615,7 +2600,7 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));
JS_ASSERT(entry->vword != PCVAL_NULL); JS_ASSERT(entry->vword != PCVAL_NULL);
JS_ASSERT(OBJ_SCOPE(pobj)->branded()); JS_ASSERT(OBJ_SCOPE(pobj)->branded());
JS_ASSERT(SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)); JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop));
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
JS_ASSERT(VALUE_IS_FUNCTION(cx, v)); JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
@ -2652,11 +2637,9 @@ JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
/* /*
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
* remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}. * remain distinct for the decompiler.
*/ */
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH);
JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH);
/* See TRY_BRANCH_AFTER_COND. */ /* See TRY_BRANCH_AFTER_COND. */
JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
@ -2717,6 +2700,14 @@ js_Interpret(JSContext *cx)
#endif #endif
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
#ifdef __GNUC__
# define JS_EXTENSION __extension__
# define JS_EXTENSION_(s) __extension__ ({ s; })
#else
# define JS_EXTENSION
# define JS_EXTENSION_(s) s
#endif
# ifdef DEBUG # ifdef DEBUG
/* /*
* We call this macro from BEGIN_CASE in threaded interpreters, * We call this macro from BEGIN_CASE in threaded interpreters,
@ -3619,7 +3610,7 @@ js_Interpret(JSContext *cx)
goto error; \ goto error; \
JS_END_MACRO JS_END_MACRO
#define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp) \ #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
if (SPROP_HAS_STUB_GETTER(sprop)) { \ if (SPROP_HAS_STUB_GETTER(sprop)) { \
/* Fast path for Object instance properties. */ \ /* Fast path for Object instance properties. */ \
@ -3629,7 +3620,7 @@ js_Interpret(JSContext *cx)
? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
: JSVAL_VOID; \ : JSVAL_VOID; \
} else { \ } else { \
if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \ if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
goto error; \ goto error; \
} \ } \
JS_END_MACRO JS_END_MACRO
@ -4490,7 +4481,7 @@ js_Interpret(JSContext *cx)
} else { } else {
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
sprop = PCVAL_TO_SPROP(entry->vword); sprop = PCVAL_TO_SPROP(entry->vword);
NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval); NATIVE_GET(cx, obj, obj2, sprop, &rval);
} }
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
break; break;
@ -4504,9 +4495,7 @@ js_Interpret(JSContext *cx)
} }
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
if (entry if (entry
? !js_GetPropertyHelper(cx, obj, id, ? !js_GetPropertyHelper(cx, obj, id, true, &rval)
JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
&rval)
: !OBJ_GET_PROPERTY(cx, obj, id, &rval)) { : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {
goto error; goto error;
} }
@ -4583,7 +4572,7 @@ js_Interpret(JSContext *cx)
} else { } else {
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
sprop = PCVAL_TO_SPROP(entry->vword); sprop = PCVAL_TO_SPROP(entry->vword);
NATIVE_GET(cx, obj, obj2, sprop, 0, &rval); NATIVE_GET(cx, obj, obj2, sprop, &rval);
} }
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
STORE_OPND(-1, rval); STORE_OPND(-1, rval);
@ -4602,13 +4591,13 @@ js_Interpret(JSContext *cx)
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
PUSH(JSVAL_NULL); PUSH(JSVAL_NULL);
if (!JSVAL_IS_PRIMITIVE(lval)) { if (!JSVAL_IS_PRIMITIVE(lval)) {
if (!js_GetMethod(cx, obj, id, entry ? JSGET_CACHE_RESULT : 0, &rval)) if (!js_GetMethod(cx, obj, id, !!entry, &rval))
goto error; goto error;
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
STORE_OPND(-2, rval); STORE_OPND(-2, rval);
} else { } else {
JS_ASSERT(obj->map->ops->getProperty == js_GetProperty); JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
if (!js_GetPropertyHelper(cx, obj, id, JSGET_CACHE_RESULT, &rval)) if (!js_GetPropertyHelper(cx, obj, id, true, &rval))
goto error; goto error;
STORE_OPND(-1, lval); STORE_OPND(-1, lval);
STORE_OPND(-2, rval); STORE_OPND(-2, rval);
@ -4639,12 +4628,9 @@ js_Interpret(JSContext *cx)
BEGIN_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETMETHOD)
do_setprop:
rval = FETCH_OPND(-1); rval = FETCH_OPND(-1);
JS_ASSERT_IF(op == JSOP_SETMETHOD, VALUE_IS_FUNCTION(cx, rval));
lval = FETCH_OPND(-2); lval = FETCH_OPND(-2);
JS_ASSERT_IF(op == JSOP_SETNAME, !JSVAL_IS_PRIMITIVE(lval)); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP);
VALUE_TO_OBJECT(cx, -2, lval, obj); VALUE_TO_OBJECT(cx, -2, lval, obj);
do { do {
@ -4798,21 +4784,9 @@ js_Interpret(JSContext *cx)
sprop = sprop2; sprop = sprop2;
} else { } else {
scope->extend(cx, sprop); scope->extend(cx, sprop);
jsuint index;
if (js_IdIsIndex(sprop->id, &index))
scope->setIndexedProperties();
if (sprop->isMethod())
scope->setMethodBarrier();
} }
/* LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
* No LOCKED_OBJ_WRITE_BARRIER because here we
* are adding a new property, not updating an
* existing slot's value that might contain a
* method of a branded scope.
*/
TRACE_2(SetPropHit, entry, sprop); TRACE_2(SetPropHit, entry, sprop);
LOCKED_OBJ_SET_SLOT(obj, slot, rval); LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
@ -4856,10 +4830,7 @@ js_Interpret(JSContext *cx)
LOAD_ATOM(0); LOAD_ATOM(0);
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
if (entry) { if (entry) {
uintN defineHow = (op == JSOP_SETMETHOD) if (!js_SetPropertyHelper(cx, obj, id, true, &rval))
? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
: JSDNP_CACHE_RESULT;
if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval))
goto error; goto error;
} else { } else {
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
@ -4916,7 +4887,7 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_GETELEM) END_CASE(JSOP_GETELEM)
BEGIN_CASE(JSOP_CALLELEM) BEGIN_CASE(JSOP_CALLELEM)
ELEMENT_OP(-1, js_GetMethod(cx, obj, id, 0, &rval)); ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval));
#if JS_HAS_NO_SUCH_METHOD #if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
regs.sp[-2] = regs.sp[-1]; regs.sp[-2] = regs.sp[-1];
@ -5370,7 +5341,7 @@ js_Interpret(JSContext *cx)
} else { } else {
sprop = (JSScopeProperty *)prop; sprop = (JSScopeProperty *)prop;
do_native_get: do_native_get:
NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval); NATIVE_GET(cx, obj, obj2, sprop, &rval);
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *) sprop); OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *) sprop);
} }
@ -5951,7 +5922,7 @@ js_Interpret(JSContext *cx)
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if ((sprop->attrs & JSPROP_PERMANENT) && if ((sprop->attrs & JSPROP_PERMANENT) &&
SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_STUB_SETTER(sprop)) { SPROP_HAS_STUB_SETTER(sprop)) {
/* /*
* Fast globals use frame variables to map the global * Fast globals use frame variables to map the global
@ -6245,64 +6216,23 @@ js_Interpret(JSContext *cx)
obj = FUN_OBJECT(fun); obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) { if (FUN_NULL_CLOSURE(fun)) {
parent = fp->scopeChain; obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
if (!obj)
if (OBJ_GET_PARENT(cx, obj) == parent) { goto error;
JSScope *scope;
lval = FETCH_OPND(-1);
op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]);
/*
* Optimize ({method: function () { ... }, ...}) and
* this.method = function () { ... }; bytecode sequences.
*
* Note that we jump to the entry points for JSOP_SETPROP
* and JSOP_INITPROP without calling the trace recorder,
* because the record hooks for those ops are essentially
* no-ops (this can't change given the predictive shape
* guarding the recorder must do).
*/
if (op == JSOP_SETMETHOD) {
#ifdef DEBUG
op2 = JSOp(regs.pc[JSOP_LAMBDA_LENGTH + JSOP_SETMETHOD_LENGTH]);
JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
#endif
if (JSVAL_IS_OBJECT(lval) &&
(obj2 = JSVAL_TO_OBJECT(lval)) &&
OBJ_GET_CLASS(cx, obj2) == &js_ObjectClass) {
scope = OBJ_SCOPE(obj2);
if (scope->object == obj2) {
PUSH_OPND(OBJECT_TO_JSVAL(obj));
regs.pc += JSOP_LAMBDA_LENGTH;
goto do_setprop;
}
}
} else if (op == JSOP_INITMETHOD) {
JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
obj2 = JSVAL_TO_OBJECT(lval);
scope = OBJ_SCOPE(obj2);
/*
* JSOP_NEWINIT gave the new object it created (obj2
* here) its own scope.
*/
JS_ASSERT(scope->object == obj2);
PUSH_OPND(OBJECT_TO_JSVAL(obj));
regs.pc += JSOP_LAMBDA_LENGTH;
goto do_initprop;
}
}
} else { } else {
parent = js_GetScopeChain(cx, fp); parent = js_GetScopeChain(cx, fp);
if (!parent) if (!parent)
goto error; goto error;
}
obj = js_CloneFunctionObject(cx, fun, parent); /*
if (!obj) * FIXME: bug 471214, Cloning here even when the compiler saw
goto error; * the right parent is wasteful but we don't fully support
* joined function objects, yet.
*/
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj)
goto error;
}
PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(OBJECT_TO_JSVAL(obj));
END_CASE(JSOP_LAMBDA) END_CASE(JSOP_LAMBDA)
@ -6449,26 +6379,11 @@ js_Interpret(JSContext *cx)
BEGIN_CASE(JSOP_NEWINIT) BEGIN_CASE(JSOP_NEWINIT)
i = GET_INT8(regs.pc); i = GET_INT8(regs.pc);
JS_ASSERT(i == JSProto_Array || i == JSProto_Object); JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
if (i == JSProto_Array) { obj = (i == JSProto_Array)
obj = js_NewArrayObject(cx, 0, NULL); ? js_NewArrayObject(cx, 0, NULL)
if (!obj) : js_NewObject(cx, &js_ObjectClass, NULL, NULL);
goto error; if (!obj)
} else { goto error;
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
if (!obj)
goto error;
if (regs.pc[JSOP_NEWINIT_LENGTH] != JSOP_ENDINIT) {
JS_LOCK_OBJ(cx, obj);
JSScope *scope = js_GetMutableScope(cx, obj);
if (!scope) {
JS_UNLOCK_OBJ(cx, obj);
goto error;
}
JS_UNLOCK_SCOPE(cx, scope);
}
}
PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(OBJECT_TO_JSVAL(obj));
fp->sharpDepth++; fp->sharpDepth++;
CHECK_INTERRUPT_HANDLER(); CHECK_INTERRUPT_HANDLER();
@ -6486,8 +6401,6 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_ENDINIT) END_CASE(JSOP_ENDINIT)
BEGIN_CASE(JSOP_INITPROP) BEGIN_CASE(JSOP_INITPROP)
BEGIN_CASE(JSOP_INITMETHOD)
do_initprop:
/* Load the property's initial value into rval. */ /* Load the property's initial value into rval. */
JS_ASSERT(regs.sp - StackBase(fp) >= 2); JS_ASSERT(regs.sp - StackBase(fp) >= 2);
rval = FETCH_OPND(-1); rval = FETCH_OPND(-1);
@ -6508,7 +6421,6 @@ js_Interpret(JSContext *cx)
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj); scope = OBJ_SCOPE(obj);
JS_ASSERT(scope->object == obj);
JS_ASSERT(!scope->sealed()); JS_ASSERT(!scope->sealed());
kshape = scope->shape; kshape = scope->shape;
cache = &JS_PROPERTY_CACHE(cx); cache = &JS_PROPERTY_CACHE(cx);
@ -6538,6 +6450,14 @@ js_Interpret(JSContext *cx)
if (!SPROP_HAS_STUB_SETTER(sprop)) if (!SPROP_HAS_STUB_SETTER(sprop))
goto do_initprop_miss; goto do_initprop_miss;
if (!scope->owned()) {
scope = js_GetMutableScope(cx, obj);
if (!scope) {
JS_UNLOCK_OBJ(cx, obj);
goto error;
}
}
/* /*
* Detect a repeated property name and force a miss to * Detect a repeated property name and force a miss to
* share the strict warning code and cope with complexity * share the strict warning code and cope with complexity
@ -6588,11 +6508,7 @@ js_Interpret(JSContext *cx)
scope->lastProp = sprop; scope->lastProp = sprop;
} }
/* LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
* No LOCKED_OBJ_WRITE_BARRIER because here we are adding a
* new property, not updating an existing slot's value that
* might contain a method of a branded scope.
*/
TRACE_2(SetPropHit, entry, sprop); TRACE_2(SetPropHit, entry, sprop);
LOCKED_OBJ_SET_SLOT(obj, slot, rval); LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
@ -6613,16 +6529,12 @@ js_Interpret(JSContext *cx)
goto error; goto error;
} }
uintN defineHow = (op == JSOP_INITMETHOD)
? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
: JSDNP_CACHE_RESULT;
if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
? js_SetPropertyHelper(cx, obj, id, defineHow, &rval) ? js_SetPropertyHelper(cx, obj, id, true, &rval)
: js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, NULL, JSPROP_ENUMERATE, 0, 0, NULL,
defineHow))) { JSDNP_CACHE_RESULT)))
goto error; goto error;
}
} while (0); } while (0);
/* Common tail for property cache hit and miss cases. */ /* Common tail for property cache hit and miss cases. */

View File

@ -376,7 +376,7 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
*vp = OBJECT_TO_JSVAL(iterobj); *vp = OBJECT_TO_JSVAL(iterobj);
} else { } else {
atom = cx->runtime->atomState.iteratorAtom; atom = cx->runtime->atomState.iteratorAtom;
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, vp)) if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), false, vp))
goto bad; goto bad;
if (JSVAL_IS_VOID(*vp)) { if (JSVAL_IS_VOID(*vp)) {
default_iter: default_iter:

View File

@ -418,7 +418,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
JSScopeProperty *sprop = (JSScopeProperty *) prop; JSScopeProperty *sprop = (JSScopeProperty *) prop;
val = JSVAL_NULL; val = JSVAL_NULL;
if (attrs & JSPROP_GETTER) if (attrs & JSPROP_GETTER)
val = sprop->getterValue(); val = js_CastAsObjectJSVal(sprop->getter);
if (attrs & JSPROP_SETTER) { if (attrs & JSPROP_SETTER) {
if (val != JSVAL_NULL) { if (val != JSVAL_NULL) {
/* Mark the getter, then set val to setter. */ /* Mark the getter, then set val to setter. */
@ -426,7 +426,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
NULL) NULL)
!= NULL); != NULL);
} }
val = sprop->setterValue(); val = js_CastAsObjectJSVal(sprop->setter);
} }
} else { } else {
ok = OBJ_GET_PROPERTY(cx, obj, id, &val); ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
@ -782,7 +782,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
JSScopeProperty *sprop = (JSScopeProperty *) prop; JSScopeProperty *sprop = (JSScopeProperty *) prop;
if (attrs & JSPROP_GETTER) { if (attrs & JSPROP_GETTER) {
val[valcnt] = sprop->getterValue(); val[valcnt] = js_CastAsObjectJSVal(sprop->getter);
gsopold[valcnt] = gsopold[valcnt] =
ATOM_TO_STRING(cx->runtime->atomState.getterAtom); ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
gsop[valcnt] = gsop[valcnt] =
@ -791,7 +791,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
valcnt++; valcnt++;
} }
if (attrs & JSPROP_SETTER) { if (attrs & JSPROP_SETTER) {
val[valcnt] = sprop->setterValue(); val[valcnt] = js_CastAsObjectJSVal(sprop->setter);
gsopold[valcnt] = gsopold[valcnt] =
ATOM_TO_STRING(cx->runtime->atomState.setterAtom); ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
gsop[valcnt] = gsop[valcnt] =
@ -1905,7 +1905,7 @@ obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
if (OBJ_IS_NATIVE(pobj)) { if (OBJ_IS_NATIVE(pobj)) {
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if (sprop->attrs & JSPROP_GETTER) if (sprop->attrs & JSPROP_GETTER)
*vp = sprop->getterValue(); *vp = js_CastAsObjectJSVal(sprop->getter);
} }
OBJ_DROP_PROPERTY(cx, pobj, prop); OBJ_DROP_PROPERTY(cx, pobj, prop);
} }
@ -1930,7 +1930,7 @@ obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
if (OBJ_IS_NATIVE(pobj)) { if (OBJ_IS_NATIVE(pobj)) {
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if (sprop->attrs & JSPROP_SETTER) if (sprop->attrs & JSPROP_SETTER)
*vp = sprop->setterValue(); *vp = js_CastAsObjectJSVal(sprop->setter);
} }
OBJ_DROP_PROPERTY(cx, pobj, prop); OBJ_DROP_PROPERTY(cx, pobj, prop);
} }
@ -3557,8 +3557,6 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
JSScope *scope; JSScope *scope;
JSScopeProperty *sprop; JSScopeProperty *sprop;
JS_ASSERT(!(flags & SPROP_IS_METHOD));
/* /*
* Purge the property cache of now-shadowed id in obj's scope chain. Do * Purge the property cache of now-shadowed id in obj's scope chain. Do
* this optimistically (assuming no failure below) before locking obj, so * this optimistically (assuming no failure below) before locking obj, so
@ -3640,7 +3638,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSScopeProperty *sprop; JSScopeProperty *sprop;
JSBool added; JSBool added;
JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0); JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE)) == 0);
js_LeaveTraceIfGlobalObject(cx, obj); js_LeaveTraceIfGlobalObject(cx, obj);
/* Convert string indices to integers if appropriate. */ /* Convert string indices to integers if appropriate. */
@ -3712,12 +3710,10 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
/* Use the object's class getter and setter by default. */ /* Use the object's class getter and setter by default. */
clasp = LOCKED_OBJ_GET_CLASS(obj); clasp = LOCKED_OBJ_GET_CLASS(obj);
if (!(defineHow & JSDNP_SET_METHOD)) { if (!getter)
if (!getter) getter = clasp->getProperty;
getter = clasp->getProperty; if (!setter)
if (!setter) setter = clasp->setProperty;
setter = clasp->setProperty;
}
/* Get obj's own scope if it has one, or create a new one for obj. */ /* Get obj's own scope if it has one, or create a new one for obj. */
scope = js_GetMutableScope(cx, obj); scope = js_GetMutableScope(cx, obj);
@ -3729,16 +3725,6 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
/* Add a new property, or replace an existing one of the same id. */ /* Add a new property, or replace an existing one of the same id. */
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
attrs |= JSPROP_SHARED; attrs |= JSPROP_SHARED;
if (defineHow & JSDNP_SET_METHOD) {
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_ObjectClass);
JS_ASSERT(VALUE_IS_FUNCTION(cx, value));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JS_ASSERT(!getter && !setter);
flags |= SPROP_IS_METHOD;
getter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(value));
}
sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs, sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs,
flags, shortid); flags, shortid);
if (!sprop) if (!sprop)
@ -3747,12 +3733,8 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
} }
/* Store value before calling addProperty, in case the latter GC's. */ /* Store value before calling addProperty, in case the latter GC's. */
if (SPROP_HAS_VALID_SLOT(sprop, scope)) { if (SPROP_HAS_VALID_SLOT(sprop, scope))
if (added) LOCKED_OBJ_WRITE_SLOT(cx, obj, sprop->slot, value);
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
else
LOCKED_OBJ_WRITE_BARRIER(cx, obj, sprop->slot, value);
}
/* XXXbe called with lock held */ /* XXXbe called with lock held */
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
@ -4156,7 +4138,7 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
JSBool JSBool
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
JSScopeProperty *sprop, uintN getHow, jsval *vp) JSScopeProperty *sprop, jsval *vp)
{ {
js_LeaveTraceIfGlobalObject(cx, pobj); js_LeaveTraceIfGlobalObject(cx, pobj);
@ -4175,20 +4157,17 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
? LOCKED_OBJ_GET_SLOT(pobj, slot) ? LOCKED_OBJ_GET_SLOT(pobj, slot)
: JSVAL_VOID; : JSVAL_VOID;
if (SPROP_HAS_STUB_GETTER(sprop)) if (SPROP_HAS_STUB_GETTER(sprop))
return true; return JS_TRUE;
if (JS_UNLIKELY(sprop->isMethod()) && !(getHow & JSGET_METHOD_BARRIER))
return true;
sample = cx->runtime->propertyRemovals; sample = cx->runtime->propertyRemovals;
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
JS_PUSH_TEMP_ROOT_OBJECT(cx, pobj, &tvr2); JS_PUSH_TEMP_ROOT_OBJECT(cx, pobj, &tvr2);
ok = sprop->get(cx, obj, vp); ok = js_GetSprop(cx, sprop, obj, vp);
JS_POP_TEMP_ROOT(cx, &tvr2); JS_POP_TEMP_ROOT(cx, &tvr2);
JS_POP_TEMP_ROOT(cx, &tvr); JS_POP_TEMP_ROOT(cx, &tvr);
if (!ok) if (!ok)
return false; return JS_FALSE;
JS_LOCK_SCOPE(cx, scope); JS_LOCK_SCOPE(cx, scope);
if (SLOT_IN_SCOPE(slot, scope) && if (SLOT_IN_SCOPE(slot, scope) &&
@ -4197,7 +4176,7 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
} }
return true; return JS_TRUE;
} }
JSBool JSBool
@ -4241,7 +4220,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
sample = cx->runtime->propertyRemovals; sample = cx->runtime->propertyRemovals;
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
ok = sprop->set(cx, obj, vp); ok = js_SetSprop(cx, sprop, obj, vp);
JS_POP_TEMP_ROOT(cx, &tvr); JS_POP_TEMP_ROOT(cx, &tvr);
if (!ok) if (!ok)
return JS_FALSE; return JS_FALSE;
@ -4258,7 +4237,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
} }
JSBool JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow, js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp) jsval *vp)
{ {
JSObject *aobj, *obj2; JSObject *aobj, *obj2;
@ -4266,8 +4245,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
JSProperty *prop; JSProperty *prop;
JSScopeProperty *sprop; JSScopeProperty *sprop;
JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx)); JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
/* Convert string indices to integers if appropriate. */ /* Convert string indices to integers if appropriate. */
id = js_CheckForStringIndex(id); id = js_CheckForStringIndex(id);
@ -4282,7 +4260,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
return JS_FALSE; return JS_FALSE;
PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++); PCMETER(cacheResult && JS_PROPERTY_CACHE(cx).nofills++);
/* /*
* Give a strict warning if foo.bar is evaluated by a script for an * Give a strict warning if foo.bar is evaluated by a script for an
@ -4343,12 +4321,12 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if (getHow & JSGET_CACHE_RESULT) { if (cacheResult) {
JS_ASSERT_NOT_ON_TRACE(cx); JS_ASSERT_NOT_ON_TRACE(cx);
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false); js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false);
} }
if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp)) if (!js_NativeGet(cx, obj, obj2, sprop, vp))
return JS_FALSE; return JS_FALSE;
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
@ -4358,19 +4336,20 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
JSBool JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{ {
return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp); return js_GetPropertyHelper(cx, obj, id, false, vp);
} }
JSBool JSBool
js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp) js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp)
{ {
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
if (obj->map->ops == &js_ObjectOps || if (obj->map->ops == &js_ObjectOps ||
obj->map->ops->getProperty == js_GetProperty) { obj->map->ops->getProperty == js_GetProperty) {
return js_GetPropertyHelper(cx, obj, id, getHow, vp); return js_GetPropertyHelper(cx, obj, id, cacheResult, vp);
} }
JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, OBJ_IS_DENSE_ARRAY(cx, obj)); JS_ASSERT_IF(cacheResult, OBJ_IS_DENSE_ARRAY(cx, obj));
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
if (OBJECT_IS_XML(cx, obj)) if (OBJECT_IS_XML(cx, obj))
return js_GetXMLMethod(cx, obj, id, vp); return js_GetXMLMethod(cx, obj, id, vp);
@ -4401,11 +4380,10 @@ js_CheckUndeclaredVarAssignment(JSContext *cx)
/* /*
* Note: all non-error exits in this function must notify the tracer using * Note: all non-error exits in this function must notify the tracer using
* SetPropHit when called from the interpreter, which is detected by testing * SetPropHit when called from the interpreter loop (cacheResult is true).
* (defineHow & JSDNP_CACHE_RESULT).
*/ */
JSBool JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp) jsval *vp)
{ {
int protoIndex; int protoIndex;
@ -4419,8 +4397,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
JSPropertyOp getter, setter; JSPropertyOp getter, setter;
bool added; bool added;
JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD)) == 0); if (cacheResult)
if (defineHow & JSDNP_CACHE_RESULT)
JS_ASSERT_NOT_ON_TRACE(cx); JS_ASSERT_NOT_ON_TRACE(cx);
/* Convert string indices to integers if appropriate. */ /* Convert string indices to integers if appropriate. */
@ -4493,8 +4470,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
if (attrs & JSPROP_READONLY) { if (attrs & JSPROP_READONLY) {
if (!JS_HAS_STRICT_OPTION(cx)) { if (!JS_HAS_STRICT_OPTION(cx)) {
/* Just return true per ECMA if not in strict mode. */ /* Just return true per ECMA if not in strict mode. */
PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++); PCMETER(cacheResult && JS_PROPERTY_CACHE(cx).rofills++);
if (defineHow & JSDNP_CACHE_RESULT) if (cacheResult)
TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
return JS_TRUE; return JS_TRUE;
error: // TRACE_2 jumps here in case of error. error: // TRACE_2 jumps here in case of error.
@ -4507,10 +4484,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
goto read_only_error; goto read_only_error;
} }
if (pobj == obj) { if (pobj != obj) {
if (!(defineHow & JSDNP_SET_METHOD) != !sprop->isMethod())
sprop = NULL;
} else {
/* /*
* We found id in a prototype object: prepare to share or shadow. * We found id in a prototype object: prepare to share or shadow.
* *
@ -4522,7 +4496,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
/* Don't clone a shared prototype property. */ /* Don't clone a shared prototype property. */
if (attrs & JSPROP_SHARED) { if (attrs & JSPROP_SHARED) {
if (defineHow & JSDNP_CACHE_RESULT) { if (cacheResult) {
JSPropCacheEntry *entry; JSPropCacheEntry *entry;
entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false); entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false);
TRACE_2(SetPropHit, entry, sprop); TRACE_2(SetPropHit, entry, sprop);
@ -4533,7 +4507,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
return JS_TRUE; return JS_TRUE;
} }
return sprop->set(cx, obj, vp); return js_SetSprop(cx, sprop, obj, vp);
} }
/* Restore attrs to the ECMA default for new properties. */ /* Restore attrs to the ECMA default for new properties. */
@ -4580,23 +4554,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
JS_UNLOCK_OBJ(cx, obj); JS_UNLOCK_OBJ(cx, obj);
return JS_FALSE; return JS_FALSE;
} }
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
attrs |= JSPROP_SHARED; attrs |= JSPROP_SHARED;
/*
* 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) &&
LOCKED_OBJ_GET_CLASS(obj) == &js_ObjectClass) {
JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
flags |= SPROP_IS_METHOD;
getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(*vp);
setter = NULL;
}
sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs, sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs,
flags, shortid); flags, shortid);
if (!sprop) { if (!sprop) {
@ -4620,7 +4579,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
added = true; added = true;
} }
if (defineHow & JSDNP_CACHE_RESULT) { if (cacheResult) {
JSPropCacheEntry *entry; JSPropCacheEntry *entry;
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added);
TRACE_2(SetPropHit, entry, sprop); TRACE_2(SetPropHit, entry, sprop);
@ -4799,7 +4758,7 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
} }
if (sprop && if (sprop &&
SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) { SPROP_HAS_VALID_SLOT(sprop, scope)) {
jsval fval = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); jsval fval = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
@ -5589,7 +5548,7 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
older = JS_SetErrorReporter(cx, NULL); older = JS_SetErrorReporter(cx, NULL);
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
fval = JSVAL_VOID; fval = JSVAL_VOID;
ok = js_GetMethod(cx, obj, id, 0, &fval); ok = js_GetMethod(cx, obj, id, false, &fval);
if (!ok) if (!ok)
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
JS_SetErrorReporter(cx, older); JS_SetErrorReporter(cx, older);

View File

@ -664,9 +664,6 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
*/ */
const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */
const uintN JSDNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */ const uintN JSDNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */
const uintN JSDNP_SET_METHOD = 4; /* js_{DefineNativeProperty,SetPropertyHelper}
must pass the SPROP_IS_METHOD flag on to
js_AddScopeProperty */
extern JSBool extern JSBool
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
@ -715,17 +712,6 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id);
extern JSObject * extern JSObject *
js_FindVariableScope(JSContext *cx, JSFunction **funp); js_FindVariableScope(JSContext *cx, JSFunction **funp);
/*
* JSGET_CACHE_RESULT is the analogue of JSDNP_CACHE_RESULT for js_GetMethod.
*
* JSGET_METHOD_BARRIER enables a read barrier that preserves standard function
* object semantics (by default we assume our caller won't leak a joined callee
* to script, where it would create hazardous mutable object sharing as well as
* observable identity according to == and ===.
*/
const uintN JSGET_CACHE_RESULT = 1; // call from a caching interpreter opcode
const uintN JSGET_METHOD_BARRIER = 2; // caller may leak shared function object
/* /*
* NB: js_NativeGet and js_NativeSet are called with the scope containing sprop * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop
* (pobj's scope for Get, obj's for Set) locked, and on successful return, that * (pobj's scope for Get, obj's for Set) locked, and on successful return, that
@ -734,20 +720,21 @@ const uintN JSGET_METHOD_BARRIER = 2; // caller may leak shared function object
*/ */
extern JSBool extern JSBool
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
JSScopeProperty *sprop, uintN getHow, jsval *vp); JSScopeProperty *sprop, jsval *vp);
extern JSBool extern JSBool
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp);
extern JSBool extern JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow, js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp); jsval *vp);
extern JSBool extern JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
extern JSBool extern JSBool
js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp); js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp);
/* /*
* Check whether it is OK to assign an undeclared property of the global * Check whether it is OK to assign an undeclared property of the global
@ -757,7 +744,7 @@ extern JS_FRIEND_API(JSBool)
js_CheckUndeclaredVarAssignment(JSContext *cx); js_CheckUndeclaredVarAssignment(JSContext *cx);
extern JSBool extern JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
jsval *vp); jsval *vp);
extern JSBool extern JSBool

View File

@ -3823,7 +3823,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
goto do_getprop; goto do_getprop;
case JSOP_SETPROP: case JSOP_SETPROP:
case JSOP_SETMETHOD:
LOAD_ATOM(0); LOAD_ATOM(0);
GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
rval = POP_STR(); rval = POP_STR();
@ -4485,7 +4484,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break; break;
case JSOP_INITPROP: case JSOP_INITPROP:
case JSOP_INITMETHOD:
LOAD_ATOM(0); LOAD_ATOM(0);
xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
(jschar) (jschar)

View File

@ -582,16 +582,10 @@ OPDEF(JSOP_OBJTOP, 227,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_LOOP, 228, "loop", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_LOOP, 228, "loop", NULL, 1, 0, 0, 0, JOF_BYTE)
/* /*
* Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops. * Debugger versions of JSOP_{GET,CALL}UPVAR.
*/ */
OPDEF(JSOP_GETUPVAR_DBG, 229,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) OPDEF(JSOP_GETUPVAR_DBG, 229,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DEFFUN_DBGFC, 231,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) OPDEF(JSOP_DEFFUN_DBGFC, 231,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING) OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING)
OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT) OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT)
/*
* Joined function object as method optimization support.
*/
OPDEF(JSOP_SETMETHOD, 234,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITMETHOD, 235,"initprop", NULL, 3, 1, 0, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)

View File

@ -5508,18 +5508,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn->pn_type = TOK_SEMI; pn->pn_type = TOK_SEMI;
pn->pn_pos = pn2->pn_pos; pn->pn_pos = pn2->pn_pos;
pn->pn_kid = pn2; pn->pn_kid = pn2;
/*
* Specialize JSOP_SETPROP into JSOP_SETMETHOD to defer or avoid null
* closure cloning. Do this here rather than in AssignExpr as only now
* do we know that the uncloned (unjoined in ES3 terms) function object
* result of the assignment expression can't escape.
*/
if (PN_TYPE(pn2) == TOK_ASSIGN && PN_OP(pn2) == JSOP_NOP &&
PN_OP(pn2->pn_left) == JSOP_SETPROP &&
PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
pn2->pn_left->pn_op = JSOP_SETMETHOD;
}
break; break;
} }

View File

@ -444,13 +444,12 @@ js_HashScopeProperty(JSDHashTable *table, const void *key)
/* Accumulate from least to most random so the low bits are most random. */ /* Accumulate from least to most random so the low bits are most random. */
hash = 0; hash = 0;
JS_ASSERT_IF(sprop->isMethod(), !sprop->setter);
gsop = sprop->getter; gsop = sprop->getter;
if (gsop) if (gsop)
hash = JS_ROTATE_LEFT32(hash, 4) ^ jsword(gsop); hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsword)gsop;
gsop = sprop->setter; gsop = sprop->setter;
if (gsop) if (gsop)
hash = JS_ROTATE_LEFT32(hash, 4) ^ jsword(gsop); hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsword)gsop;
hash = JS_ROTATE_LEFT32(hash, 4) hash = JS_ROTATE_LEFT32(hash, 4)
^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED);
@ -1055,9 +1054,6 @@ JSScope::add(JSContext *cx, jsid id,
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this)); JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this));
CHECK_ANCESTOR_LINE(this, true); CHECK_ANCESTOR_LINE(this, true);
JS_ASSERT_IF(attrs & JSPROP_GETTER, getter);
JS_ASSERT_IF(attrs & JSPROP_SETTER, setter);
/* /*
* You can't add properties to a sealed scope. But note well that you can * You can't add properties to a sealed scope. But note well that you can
* change property attributes in a sealed scope, even though that replaces * change property attributes in a sealed scope, even though that replaces
@ -1073,17 +1069,10 @@ JSScope::add(JSContext *cx, jsid id,
* Normalize stub getter and setter values for faster is-stub testing in * Normalize stub getter and setter values for faster is-stub testing in
* the SPROP_CALL_[GS]ETTER macros. * the SPROP_CALL_[GS]ETTER macros.
*/ */
if (getter == JS_PropertyStub)
getter = NULL;
if (setter == JS_PropertyStub) if (setter == JS_PropertyStub)
setter = NULL; setter = NULL;
if (flags & SPROP_IS_METHOD) {
/* Here, getter is the method, a function object reference. */
JS_ASSERT(getter);
JS_ASSERT(!setter);
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
} else {
if (getter == JS_PropertyStub)
getter = NULL;
}
/* /*
* Search for id in order to claim its entry, allocating a property tree * Search for id in order to claim its entry, allocating a property tree
@ -1365,9 +1354,6 @@ JSScope::add(JSContext *cx, jsid id,
if (js_IdIsIndex(sprop->id, &index)) if (js_IdIsIndex(sprop->id, &index))
setIndexedProperties(); setIndexedProperties();
if (sprop->isMethod())
setMethodBarrier();
METER(adds); METER(adds);
return sprop; return sprop;
@ -1669,11 +1655,11 @@ JSScopeProperty::trace(JSTracer *trc)
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
if (attrs & JSPROP_GETTER) { if (attrs & JSPROP_GETTER) {
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0); JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
JS_CallTracer(trc, getterObject(), JSTRACE_OBJECT); JS_CallTracer(trc, js_CastAsObject(getter), JSTRACE_OBJECT);
} }
if (attrs & JSPROP_SETTER) { if (attrs & JSPROP_SETTER) {
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1); JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
JS_CallTracer(trc, setterObject(), JSTRACE_OBJECT); JS_CallTracer(trc, js_CastAsObject(setter), JSTRACE_OBJECT);
} }
} }
#endif /* JS_HAS_GETTER_SETTER */ #endif /* JS_HAS_GETTER_SETTER */

View File

@ -1,5 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99: * vim: set ts=8 sw=4 et tw=78:
* *
* ***** BEGIN LICENSE BLOCK ***** * ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -286,13 +286,12 @@ struct JSScope {
BRANDED = 0x0004, BRANDED = 0x0004,
INDEXED_PROPERTIES = 0x0008, INDEXED_PROPERTIES = 0x0008,
OWN_SHAPE = 0x0010, OWN_SHAPE = 0x0010,
METHOD_BARRIER = 0x0020,
/* /*
* This flag toggles with each shape-regenerating GC cycle. * This flag toggles with each shape-regenerating GC cycle.
* See JSRuntime::gcRegenShapesScopeFlag. * See JSRuntime::gcRegenShapesScopeFlag.
*/ */
SHAPE_REGEN = 0x0040 SHAPE_REGEN = 0x0020
}; };
bool hadMiddleDelete() { return flags & MIDDLE_DELETE; } bool hadMiddleDelete() { return flags & MIDDLE_DELETE; }
@ -323,42 +322,6 @@ struct JSScope {
bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; } bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; }
/*
* A scope has a method barrier when some compiler-created "null closure"
* function objects (functions that do not use lexical bindings above their
* scope, only free variable names) that have a correct JSSLOT_PARENT value
* thanks to the COMPILE_N_GO optimization are stored as newly added direct
* property values.
*
* The de-facto standard JS language requires each evaluation of such a
* closure to result in a unique (according to === and observable effects)
* function object. ES3 tried to allow implementations to "join" such
* objects to a single compiler-created object, but this makes an overt
* mutation hazard, also an "identity hazard" against interoperation among
* implementations that join and do not join.
*
* To stay compatible with the de-facto standard, we store the compiler-
* created function object as the method value, set the METHOD_BARRIER
* flag, and brand the scope with a predictable shape that reflects its
* method values, which are cached and traced without being loaded, based
* on shape-qualified cache hit logic and equivalent trace guards. See
* BRANDED above.
*
* This means scope->hasMethodBarrier() => scope->branded(), but of course
* not the other way around.
*
* Then when reading from a scope for which scope->hasMethodBarrier() is
* true, we count on the scope's qualified/guarded shape being unique and
* add a read barrier that clones the compiler-created function object on
* demand, reshaping the scope.
*
* This read barrier is bypassed when evaluating the callee sub-expression
* of a call expression (see the JOF_CALLOP opcodes in jsopcode.tbl), since
* such ops do not present an identity or mutation hazard.
*/
bool hasMethodBarrier() { return flags & METHOD_BARRIER; }
void setMethodBarrier() { flags |= METHOD_BARRIER | BRANDED; }
bool owned() { return object != NULL; } bool owned() { return object != NULL; }
}; };
@ -400,8 +363,7 @@ js_CastAsPropertyOp(JSObject *object)
struct JSScopeProperty { struct JSScopeProperty {
jsid id; /* int-tagged jsval/untagged JSAtom* */ jsid id; /* int-tagged jsval/untagged JSAtom* */
JSPropertyOp getter; /* getter and setter hooks or objects */ JSPropertyOp getter; /* getter and setter hooks or objects */
JSPropertyOp setter; /* getter is JSObject* and setter is 0 JSPropertyOp setter;
if sprop->isMethod() */
uint32 slot; /* abstract index in object slots */ uint32 slot; /* abstract index in object slots */
uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ uint8 attrs; /* attributes, see jsapi.h JSPROP_* */
uint8 flags; /* flags, see below for defines */ uint8 flags; /* flags, see below for defines */
@ -411,53 +373,6 @@ struct JSScopeProperty {
to many-kids data structure */ to many-kids data structure */
uint32 shape; /* property cache shape identifier */ uint32 shape; /* property cache shape identifier */
/* Bits stored in sprop->flags. */
#define SPROP_MARK 0x01
#define SPROP_IS_ALIAS 0x02
#define SPROP_HAS_SHORTID 0x04
#define SPROP_FLAG_SHAPE_REGEN 0x08
#define SPROP_IS_METHOD 0x10
bool isMethod() const {
return flags & SPROP_IS_METHOD;
}
JSObject *method() const {
JS_ASSERT(isMethod());
return js_CastAsObject(getter);
}
jsval methodValue() const {
JS_ASSERT(isMethod());
return js_CastAsObjectJSVal(getter);
}
bool hasGetter() const {
return attrs & JSPROP_GETTER;
}
JSObject *getterObject() const {
JS_ASSERT(hasGetter());
return js_CastAsObject(getter);
}
jsval getterValue() const {
JS_ASSERT(hasGetter());
return js_CastAsObjectJSVal(getter);
}
bool hasSetter() const {
return attrs & JSPROP_SETTER;
}
JSObject *setterObject() const {
JS_ASSERT(hasSetter());
return js_CastAsObject(setter);
}
jsval setterValue() const {
JS_ASSERT(hasSetter());
return js_CastAsObjectJSVal(setter);
}
bool methodBarrier(JSContext *cx, JSObject *obj, jsval *vp);
bool get(JSContext* cx, JSObject* obj, jsval* vp);
bool set(JSContext* cx, JSObject* obj, jsval* vp);
void trace(JSTracer *trc); void trace(JSTracer *trc);
}; };
@ -493,6 +408,12 @@ JSScope::has(JSScopeProperty *sprop)
return lookup(sprop->id) == sprop; return lookup(sprop->id) == sprop;
} }
/* Bits stored in sprop->flags. */
#define SPROP_MARK 0x01
#define SPROP_IS_ALIAS 0x02
#define SPROP_HAS_SHORTID 0x04
#define SPROP_FLAG_SHAPE_REGEN 0x08
/* /*
* If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather
* than id when calling sprop's getter or setter. * than id when calling sprop's getter or setter.
@ -509,9 +430,6 @@ JSScope::has(JSScopeProperty *sprop)
#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) #define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter)
#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) #define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter)
#define SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) \
(SPROP_HAS_STUB_GETTER(sprop) || (sprop)->isMethod())
#ifndef JS_THREADSAFE #ifndef JS_THREADSAFE
# define js_GenerateShape(cx, gcLocked) js_GenerateShape (cx) # define js_GenerateShape(cx, gcLocked) js_GenerateShape (cx)
#endif #endif
@ -633,47 +551,15 @@ JSScope::trace(JSTracer *trc)
} }
/* static JS_INLINE bool
* Read barrier for deferred cloning of compiler-created function objects js_GetSprop(JSContext* cx, JSScopeProperty* sprop, JSObject* obj, jsval* vp)
* optimized as typically non-escaping, ad-hoc methods in obj.
*/
JS_ALWAYS_INLINE bool
JSScopeProperty::methodBarrier(JSContext *cx, JSObject *obj, jsval *vp)
{ {
JSScope *scope = OBJ_SCOPE(obj); JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
#ifdef JS_THREADSAFE
JS_ASSERT(scope->title.ownercx == cx);
#endif
if (scope->hasMethodBarrier()) { if (sprop->attrs & JSPROP_GETTER) {
JSObject *funobj = JSVAL_TO_OBJECT(*vp); jsval fval = js_CastAsObjectJSVal(sprop->getter);
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); return js_InternalGetOrSet(cx, obj, sprop->id, fval, JSACC_READ,
0, 0, vp);
if (FUN_OBJECT(fun) == funobj && FUN_INTERPRETED(fun)) {
funobj = js_CloneFunctionObject(cx, fun, OBJ_GET_PARENT(cx, funobj));
if (!funobj)
return false;
*vp = OBJECT_TO_JSVAL(funobj);
return js_SetPropertyHelper(cx, obj, id, 0, vp);
}
}
return true;
}
JS_ALWAYS_INLINE bool
JSScopeProperty::get(JSContext* cx, JSObject* obj, jsval* vp)
{
JS_ASSERT(!SPROP_HAS_STUB_GETTER(this));
if (attrs & JSPROP_GETTER) {
JS_ASSERT(!isMethod());
jsval fval = getterValue();
return js_InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp);
}
if (isMethod()) {
*vp = methodValue();
return methodBarrier(cx, obj, vp);
} }
/* /*
@ -684,28 +570,30 @@ JSScopeProperty::get(JSContext* cx, JSObject* obj, jsval* vp)
*/ */
if (STOBJ_GET_CLASS(obj) == &js_WithClass) if (STOBJ_GET_CLASS(obj) == &js_WithClass)
obj = obj->map->ops->thisObject(cx, obj); obj = obj->map->ops->thisObject(cx, obj);
return getter(cx, obj, SPROP_USERID(this), vp); return sprop->getter(cx, obj, SPROP_USERID(sprop), vp);
} }
JS_ALWAYS_INLINE bool static JS_INLINE bool
JSScopeProperty::set(JSContext* cx, JSObject* obj, jsval* vp) js_SetSprop(JSContext* cx, JSScopeProperty* sprop, JSObject* obj, jsval* vp)
{ {
JS_ASSERT_IF(SPROP_HAS_STUB_SETTER(this), attrs & JSPROP_GETTER); JS_ASSERT(!(SPROP_HAS_STUB_SETTER(sprop) &&
!(sprop->attrs & JSPROP_GETTER)));
if (attrs & JSPROP_SETTER) { if (sprop->attrs & JSPROP_SETTER) {
jsval fval = setterValue(); jsval fval = js_CastAsObjectJSVal(sprop->setter);
return js_InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp); return js_InternalGetOrSet(cx, obj, (sprop)->id, fval, JSACC_WRITE,
1, vp, vp);
} }
if (attrs & JSPROP_GETTER) { if (sprop->attrs & JSPROP_GETTER) {
js_ReportGetterOnlyAssignment(cx); js_ReportGetterOnlyAssignment(cx);
return JS_FALSE; return JS_FALSE;
} }
/* See the comment in JSScopeProperty::get as to why we can check for With. */ /* See the comment in js_GetSprop as to why we can check for 'with'. */
if (STOBJ_GET_CLASS(obj) == &js_WithClass) if (STOBJ_GET_CLASS(obj) == &js_WithClass)
obj = obj->map->ops->thisObject(cx, obj); obj = obj->map->ops->thisObject(cx, obj);
return setter(cx, obj, SPROP_USERID(this), vp); return sprop->setter(cx, obj, SPROP_USERID(sprop), vp);
} }
/* Macro for common expression to test for shared permanent attributes. */ /* Macro for common expression to test for shared permanent attributes. */

View File

@ -2636,9 +2636,8 @@ TraceRecorder::isValidSlot(JSScope* scope, JSScopeProperty* sprop)
if (sprop->attrs & JSPROP_READONLY) if (sprop->attrs & JSPROP_READONLY)
ABORT_TRACE_RV("writing to a read-only property", false); ABORT_TRACE_RV("writing to a read-only property", false);
} }
/* This check applies even when setflags == 0. */ /* This check applies even when setflags == 0. */
if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)) if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop))
ABORT_TRACE_RV("non-stub getter", false); ABORT_TRACE_RV("non-stub getter", false);
if (!SPROP_HAS_VALID_SLOT(sprop, scope)) if (!SPROP_HAS_VALID_SLOT(sprop, scope))
@ -4101,7 +4100,7 @@ TraceRecorder::hasMethod(JSObject* obj, jsid id)
JSScope* scope = OBJ_SCOPE(pobj); JSScope* scope = OBJ_SCOPE(pobj);
JSScopeProperty* sprop = (JSScopeProperty*) prop; JSScopeProperty* sprop = (JSScopeProperty*) prop;
if (SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && if (SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) { SPROP_HAS_VALID_SLOT(sprop, scope)) {
jsval v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); jsval v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
if (VALUE_IS_FUNCTION(cx, v)) { if (VALUE_IS_FUNCTION(cx, v)) {
@ -10486,33 +10485,32 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
if (setflags && (sprop->attrs & JSPROP_READONLY)) if (setflags && (sprop->attrs & JSPROP_READONLY))
ABORT_TRACE("writing to a readonly property"); ABORT_TRACE("writing to a readonly property");
if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) { if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
if (setflags == 0) { // FIXME 450335: generalize this away from regexp built-in getters.
// FIXME 450335: generalize this away from regexp built-in getters. if (setflags == 0 &&
if (sprop->getter == js_RegExpClass.getProperty && sprop->getter == js_RegExpClass.getProperty &&
sprop->shortid < 0) { sprop->shortid < 0) {
if (sprop->shortid == REGEXP_LAST_INDEX) if (sprop->shortid == REGEXP_LAST_INDEX)
ABORT_TRACE("can't trace RegExp.lastIndex yet"); ABORT_TRACE("can't trace RegExp.lastIndex yet");
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins }; LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
v_ins = lir->insCall(&js_CallGetter_ci, args); v_ins = lir->insCall(&js_CallGetter_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT); guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
/*
/* * BIG FAT WARNING: This snapshot cannot be a BRANCH_EXIT, since
* BIG FAT WARNING: This snapshot cannot be a BRANCH_EXIT, since * the value to the top of the stack is not the value we unbox.
* the value to the top of the stack is not the value we unbox. */
*/ unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN, v_ins,
v_ins, snapshot(MISMATCH_EXIT));
snapshot(MISMATCH_EXIT)); return JSRS_CONTINUE;
return JSRS_CONTINUE; }
} if (setflags == 0 &&
if (sprop->getter == js_StringClass.getProperty && sprop->getter == js_StringClass.getProperty &&
sprop->id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { sprop->id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
if (!guardClass(obj, obj_ins, &js_StringClass, snapshot(MISMATCH_EXIT))) if (!guardClass(obj, obj_ins, &js_StringClass, snapshot(MISMATCH_EXIT)))
ABORT_TRACE("can't trace String.length on non-String objects"); ABORT_TRACE("can't trace String.length on non-String objects");
LIns* str_ins = stobj_get_private(obj_ins, JSVAL_TAGMASK); LIns* str_ins = stobj_get_private(obj_ins, JSVAL_TAGMASK);
v_ins = lir->ins1(LIR_i2f, getStringLength(str_ins)); v_ins = lir->ins1(LIR_i2f, getStringLength(str_ins));
return JSRS_CONTINUE; return JSRS_CONTINUE;
}
} }
ABORT_TRACE("non-stub getter"); ABORT_TRACE("non-stub getter");
} }
@ -11864,9 +11862,7 @@ TraceRecorder::record_JSOP_CALLPROP()
} else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) { } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
if (l == JSVAL_VOID) if (l == JSVAL_VOID)
ABORT_TRACE("callprop on void"); ABORT_TRACE("callprop on void");
guard(false, guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)), MISMATCH_EXIT);
lir->ins2i(LIR_eq, get(&l), JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)),
MISMATCH_EXIT);
i = JSProto_Boolean; i = JSProto_Boolean;
debug_only_stmt(protoname = "Boolean.prototype";) debug_only_stmt(protoname = "Boolean.prototype";)
} else { } else {
@ -12378,18 +12374,6 @@ DBG_STUB(JSOP_DEFFUN_DBGFC)
DBG_STUB(JSOP_DEFLOCALFUN_DBGFC) DBG_STUB(JSOP_DEFLOCALFUN_DBGFC)
DBG_STUB(JSOP_LAMBDA_DBGFC) DBG_STUB(JSOP_LAMBDA_DBGFC)
JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::record_JSOP_SETMETHOD()
{
return record_JSOP_SETPROP();
}
JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::record_JSOP_INITMETHOD()
{
return record_JSOP_INITPROP();
}
#ifdef JS_JIT_SPEW #ifdef JS_JIT_SPEW
/* Prints information about entry typemaps and unstable exits for all peers at a PC */ /* Prints information about entry typemaps and unstable exits for all peers at a PC */
void void

View File

@ -456,14 +456,6 @@ typedef JSUintPtr JSUword;
# define JS_DATA_TO_FUNC_PTR(type, ptr) ((type) (void *) (ptr)) # define JS_DATA_TO_FUNC_PTR(type, ptr) ((type) (void *) (ptr))
#endif #endif
#ifdef __GNUC__
# define JS_EXTENSION __extension__
# define JS_EXTENSION_(s) __extension__ ({ s; })
#else
# define JS_EXTENSION
# define JS_EXTENSION_(s) s
#endif
JS_END_EXTERN_C JS_END_EXTERN_C
#endif /* jstypes_h___ */ #endif /* jstypes_h___ */

View File

@ -204,7 +204,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match * before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file. * the current version, abort deserialization and invalidate the file.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 50) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 49)
/* /*
* Library-private functions. * Library-private functions.