Join lambdas assigned or initialized as methods to the compiler-created function object if we can, with a read barrier to clone on method value extractions other than call expressions (471214, r=jorendorff).

This commit is contained in:
Brendan Eich 2009-09-03 14:41:19 -07:00
parent 9357e5da9b
commit dad15e97d4
24 changed files with 913 additions and 384 deletions

View File

@ -914,6 +914,8 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_DEFLOCALFUN_DBGFC */ 0, /* JSOP_DEFLOCALFUN_DBGFC */
0, /* JSOP_LAMBDA_DBGFC */ 0, /* JSOP_LAMBDA_DBGFC */
3, /* JSOP_CONCATN */ 3, /* JSOP_CONCATN */
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

@ -3534,7 +3534,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, false, vp)) if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp))
return JS_FALSE; return JS_FALSE;
if (objp) if (objp)
*objp = obj; *objp = obj;
@ -5088,8 +5088,8 @@ JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc,
JSAutoTempValueRooter tvr(cx); JSAutoTempValueRooter tvr(cx);
JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); JSAtom *atom = js_Atomize(cx, name, strlen(name), 0);
JSBool ok = atom && JSBool ok = atom &&
JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), NULL, js_GetMethod(cx, obj, ATOM_TO_JSID(atom),
tvr.addr()) && JSGET_NO_METHOD_BARRIER, tvr.addr()) &&
js_InternalCall(cx, obj, tvr.value(), argc, argv, rval); js_InternalCall(cx, obj, tvr.value(), argc, argv, rval);
LAST_FRAME_CHECKS(cx, ok); LAST_FRAME_CHECKS(cx, ok);
return ok; return ok;

View File

@ -787,7 +787,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, vp)) if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp))
return JS_FALSE; return JS_FALSE;
} }
obj2->dropProperty(cx, prop); obj2->dropProperty(cx, prop);

View File

@ -6522,7 +6522,17 @@ 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 &&
!(pn2->pn_right->pn_funbox->tcflags
& (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME))
#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

@ -248,8 +248,8 @@ struct JSTreeContext { /* tree context for semantic checks */
parameter name */ parameter name */
#define TCF_FUN_HEAVYWEIGHT 0x100 /* function needs Call object per call */ #define TCF_FUN_HEAVYWEIGHT 0x100 /* function needs Call object per call */
#define TCF_FUN_IS_GENERATOR 0x200 /* parsed yield statement in function */ #define TCF_FUN_IS_GENERATOR 0x200 /* parsed yield statement in function */
#define TCF_FUN_IS_FUNARG 0x400 /* function escapes as an argument, return #define TCF_FUN_USES_OWN_NAME 0x400 /* named function expression that uses its
value, or via the heap */ own name */
#define TCF_HAS_FUNCTION_STMT 0x800 /* block contains a function statement */ #define TCF_HAS_FUNCTION_STMT 0x800 /* block contains a function statement */
#define TCF_GENEXP_LAMBDA 0x1000 /* flag lambda from generator expression */ #define TCF_GENEXP_LAMBDA 0x1000 /* flag lambda from generator expression */
#define TCF_COMPILE_N_GO 0x2000 /* compiler-and-go mode of script, can #define TCF_COMPILE_N_GO 0x2000 /* compiler-and-go mode of script, can
@ -267,7 +267,7 @@ struct JSTreeContext { /* tree context for semantic checks */
TCF_FUN_PARAM_ARGUMENTS | \ TCF_FUN_PARAM_ARGUMENTS | \
TCF_FUN_HEAVYWEIGHT | \ TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_IS_GENERATOR | \ TCF_FUN_IS_GENERATOR | \
TCF_FUN_IS_FUNARG | \ TCF_FUN_USES_OWN_NAME | \
TCF_HAS_SHARPS) TCF_HAS_SHARPS)
/* /*

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=79: * vim: set ts=8 sw=4 et tw=99:
* *
* ***** 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
@ -186,41 +186,57 @@ 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;
v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); if (sprop->isMethod()) {
if (VALUE_IS_FUNCTION(cx, v)) {
/* /*
* Great, we have a function-valued prototype property where * A compiler-created function object, AKA a method, already
* the getter is JS_PropertyStub. The type id in pobj's scope * memoized in the property tree.
* 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.
*/ */
if (!scope->branded()) { JS_ASSERT(scope->hasMethodBarrier());
PCMETER(cache->brandfills++); v = sprop->methodValue();
#ifdef DEBUG_notme JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
fprintf(stderr, JS_ASSERT(v == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
"branding %p (%s) for funobj %p (%s), shape %lu\n",
pobj, pobj->getClass()->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(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 JSScope::BRANDED
* flag. Once this flag is set, any property assignment
* that changes the value from or to a different function
* 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, pobj->getClass()->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. */
@ -237,30 +253,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. It allows to optimize * always adds the same property. This allows us to optimize
* periodic execution of object initializers or explicit * periodic execution of object initializers or other explicit
* initialization sequences like * initialization sequences such as
* *
* 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
* bigger that the cost of an extra mismatch per loop due to * greater than the cost of an extra mismatch per loop owing 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 JSOP_SETPROP fills the cache with * On the first iteration of such a for loop, JSOP_SETPROP
* the shape of newly created object, not the shape after * fills the cache with the shape of the newly created object
* obj.x is assigned. That mismatches obj's shape on the * obj, not the shape of obj after obj.x has been assigned.
* second iteration. Note that on third and the following * That mismatches obj's shape on the second iteration. Note
* iterations the cache will be hit since the shape no longer * that on the third and subsequent iterations the cache will
* mutates. * be hit because the shape is no longer updated.
*/ */
JS_ASSERT(scope->owned()); JS_ASSERT(scope->owned());
if (sprop->parent) { if (sprop->parent) {
@ -991,7 +1007,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, false, &tvr.u.value); ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &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)) {
@ -2085,9 +2101,9 @@ js_TraceOpcode(JSContext *cx)
fp->script, cx->tracePrevPc); fp->script, cx->tracePrevPc);
/* /*
* If there aren't that many elements on the stack, then * If there aren't that many elements on the stack, then we have
* we have probably entered a new frame, and printing output * probably entered a new frame, and printing output would just be
* would just be misleading. * misleading.
*/ */
if (ndefs != 0 && if (ndefs != 0 &&
ndefs < regs->sp - fp->slots) { ndefs < regs->sp - fp->slots) {
@ -2533,8 +2549,6 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
} }
if (!ok) if (!ok)
return false; return false;
if (!prop)
return true;
if (cx->runtime->gcNumber != sample || if (cx->runtime->gcNumber != sample ||
PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) { PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) {
pobj->dropProperty(cx, prop); pobj->dropProperty(cx, prop);
@ -2546,18 +2560,26 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
JSScopeProperty *sprop = (JSScopeProperty *) prop; JSScopeProperty *sprop = (JSScopeProperty *) prop;
if (PCVAL_IS_SLOT(entry->vword)) { if (PCVAL_IS_SLOT(entry->vword)) {
JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot); JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot);
JS_ASSERT(!sprop->isMethod());
} else if (PCVAL_IS_SPROP(entry->vword)) { } else if (PCVAL_IS_SPROP(entry->vword)) {
JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop); JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop);
JS_ASSERT_IF(sprop->isMethod(),
sprop->methodValue() == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
} else { } else {
jsval v; jsval v;
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(sprop)); JS_ASSERT(SPROP_HAS_STUB_GETTER_OR_IS_METHOD(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));
JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v)); JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v));
if (sprop->isMethod()) {
JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
JS_ASSERT(sprop->methodValue() == v);
}
} }
pobj->dropProperty(cx, prop); pobj->dropProperty(cx, prop);
@ -2590,9 +2612,11 @@ 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. * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
*/ */
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);
@ -2653,14 +2677,6 @@ 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,

View File

@ -260,6 +260,10 @@ struct JSPropCacheEntry {
bool adding() const { bool adding() const {
return PCVCAP_TAG(vcap) == 0 && kshape != PCVCAP_SHAPE(vcap); return PCVCAP_TAG(vcap) == 0 && kshape != PCVCAP_SHAPE(vcap);
} }
bool directHit() const {
return PCVCAP_TAG(vcap) == 0 && kshape == PCVCAP_SHAPE(vcap);
}
}; };
/* /*

View File

@ -374,7 +374,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), false, vp)) if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
goto bad; goto bad;
if (JSVAL_IS_VOID(*vp)) { if (JSVAL_IS_VOID(*vp)) {
default_iter: default_iter:

View File

@ -52,7 +52,6 @@
#include "jscntxt.h" #include "jscntxt.h"
#include "jsdtoa.h" #include "jsdtoa.h"
#include "jsgc.h" #include "jsgc.h"
#include "jsfun.h" /* for VALUE_IS_FUNCTION from LOCKED_OBJ_WRITE_SLOT */
#include "jslock.h" #include "jslock.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsstr.h" #include "jsstr.h"
@ -831,7 +830,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
if (CX_THREAD_IS_RUNNING_GC(cx) || if (CX_THREAD_IS_RUNNING_GC(cx) ||
scope->sealed() || scope->sealed() ||
(title->ownercx && ClaimTitle(title, cx))) { (title->ownercx && ClaimTitle(title, cx))) {
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, v); LOCKED_OBJ_SET_SLOT(obj, slot, v);
return; return;
} }
@ -841,7 +840,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
JS_ASSERT(CURRENT_THREAD_IS_ME(me)); JS_ASSERT(CURRENT_THREAD_IS_ME(me));
if (NativeCompareAndSwap(&tl->owner, 0, me)) { if (NativeCompareAndSwap(&tl->owner, 0, me)) {
if (scope == OBJ_SCOPE(obj)) { if (scope == OBJ_SCOPE(obj)) {
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, v); LOCKED_OBJ_SET_SLOT(obj, slot, v);
if (!NativeCompareAndSwap(&tl->owner, me, 0)) { if (!NativeCompareAndSwap(&tl->owner, me, 0)) {
/* Assert that scope locks never revert to flyweight. */ /* Assert that scope locks never revert to flyweight. */
JS_ASSERT(title->ownercx != cx); JS_ASSERT(title->ownercx != cx);
@ -853,15 +852,14 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
} }
if (!NativeCompareAndSwap(&tl->owner, me, 0)) if (!NativeCompareAndSwap(&tl->owner, me, 0))
js_Dequeue(tl); js_Dequeue(tl);
} } else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {
else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { LOCKED_OBJ_SET_SLOT(obj, slot, v);
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, v);
return; return;
} }
#endif #endif
js_LockObj(cx, obj); js_LockObj(cx, obj);
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, v); LOCKED_OBJ_SET_SLOT(obj, slot, v);
/* /*
* Same drill as above, in js_GetSlotThreadSafe. * Same drill as above, in js_GetSlotThreadSafe.

View File

@ -414,7 +414,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 = js_CastAsObjectJSVal(sprop->getter); val = sprop->getterValue();
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. */
@ -422,7 +422,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
NULL) NULL)
!= NULL); != NULL);
} }
val = js_CastAsObjectJSVal(sprop->setter); val = sprop->setterValue();
} }
} else { } else {
ok = obj->getProperty(cx, id, &val); ok = obj->getProperty(cx, id, &val);
@ -778,7 +778,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] = js_CastAsObjectJSVal(sprop->getter); val[valcnt] = sprop->getterValue();
gsopold[valcnt] = gsopold[valcnt] =
ATOM_TO_STRING(cx->runtime->atomState.getterAtom); ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
gsop[valcnt] = gsop[valcnt] =
@ -787,7 +787,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
valcnt++; valcnt++;
} }
if (attrs & JSPROP_SETTER) { if (attrs & JSPROP_SETTER) {
val[valcnt] = js_CastAsObjectJSVal(sprop->setter); val[valcnt] = sprop->setterValue();
gsopold[valcnt] = gsopold[valcnt] =
ATOM_TO_STRING(cx->runtime->atomState.setterAtom); ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
gsop[valcnt] = gsop[valcnt] =
@ -1897,7 +1897,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 = js_CastAsObjectJSVal(sprop->getter); *vp = sprop->getterValue();
} }
pobj->dropProperty(cx, prop); pobj->dropProperty(cx, prop);
} }
@ -1922,7 +1922,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 = js_CastAsObjectJSVal(sprop->setter); *vp = sprop->setterValue();
} }
pobj->dropProperty(cx, prop); pobj->dropProperty(cx, prop);
} }
@ -3547,6 +3547,8 @@ 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
@ -3598,23 +3600,23 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
* nominal initial value of a slot-full property, while GC safety wants that * nominal initial value of a slot-full property, while GC safety wants that
* value to be stored before the call-out through the hook. Optimize to do * value to be stored before the call-out through the hook. Optimize to do
* both while saving cycles for classes that stub their addProperty hook. * both while saving cycles for classes that stub their addProperty hook.
*
* As in js_SetProtoOrParent (see above), we maintain the "any Array prototype
* has indexed properties hazard" flag by conservatively setting it.
*/ */
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ static inline bool
JS_BEGIN_MACRO \ AddPropertyHelper(JSContext *cx, JSClass *clasp, JSObject *obj, JSScope *scope,
if ((clasp)->addProperty != JS_PropertyStub) { \ JSScopeProperty *sprop, jsval *vp)
jsval nominal_ = *(vp); \ {
if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ if (clasp->addProperty != JS_PropertyStub) {
cleanup; \ jsval nominal = *vp;
} \
if (*(vp) != nominal_) { \ if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp))
if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ return false;
LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *(vp)); \ if (*vp != nominal) {
} \ if (SPROP_HAS_VALID_SLOT(sprop, scope))
} \ LOCKED_OBJ_SET_SLOT(obj, sprop->slot, *vp);
JS_END_MACRO }
}
return true;
}
JSBool JSBool
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
@ -3627,7 +3629,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)) == 0); JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
js_LeaveTraceIfGlobalObject(cx, obj); js_LeaveTraceIfGlobalObject(cx, obj);
/* Convert string indices to integers if appropriate. */ /* Convert string indices to integers if appropriate. */
@ -3699,10 +3701,12 @@ 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 = obj->getClass(); clasp = obj->getClass();
if (!getter) if (!(defineHow & JSDNP_SET_METHOD)) {
getter = clasp->getProperty; if (!getter)
if (!setter) getter = clasp->getProperty;
setter = clasp->setProperty; if (!setter)
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);
@ -3714,6 +3718,20 @@ 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(clasp == &js_ObjectClass);
JS_ASSERT(VALUE_IS_FUNCTION(cx, value));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JS_ASSERT(!getter && !setter);
JSObject *funobj = JSVAL_TO_OBJECT(value);
if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
flags |= SPROP_IS_METHOD;
getter = js_CastAsPropertyOp(funobj);
}
}
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)
@ -3723,12 +3741,13 @@ 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))
LOCKED_OBJ_WRITE_SLOT(cx, obj, sprop->slot, value); LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
/* XXXbe called with lock held */ /* XXXbe called with lock held */
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &value)) {
scope->remove(cx, id); scope->remove(cx, id);
goto error); goto error;
}
if (defineHow & JSDNP_CACHE_RESULT) { if (defineHow & JSDNP_CACHE_RESULT) {
JS_ASSERT_NOT_ON_TRACE(cx); JS_ASSERT_NOT_ON_TRACE(cx);
@ -4108,7 +4127,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, jsval *vp) JSScopeProperty *sprop, uintN getHow, jsval *vp)
{ {
js_LeaveTraceIfGlobalObject(cx, pobj); js_LeaveTraceIfGlobalObject(cx, pobj);
@ -4127,30 +4146,41 @@ 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 JS_TRUE; return true;
if (JS_UNLIKELY(sprop->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
JS_ASSERT(sprop->methodValue() == *vp);
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 = js_GetSprop(cx, sprop, obj, vp); ok = sprop->get(cx, obj, pobj, 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 JS_FALSE; return false;
JS_LOCK_SCOPE(cx, scope); JS_LOCK_SCOPE(cx, scope);
if (SLOT_IN_SCOPE(slot, scope) && if (SLOT_IN_SCOPE(slot, scope) &&
(JS_LIKELY(cx->runtime->propertyRemovals == sample) || (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
scope->has(sprop))) { scope->has(sprop))) {
LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); jsval v = *vp;
if (!scope->methodWriteBarrier(cx, sprop, v)) {
JS_UNLOCK_SCOPE(cx, scope);
return false;
}
LOCKED_OBJ_SET_SLOT(pobj, slot, v);
} }
return JS_TRUE; return true;
} }
JSBool JSBool
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added,
jsval *vp)
{ {
js_LeaveTraceIfGlobalObject(cx, obj); js_LeaveTraceIfGlobalObject(cx, obj);
@ -4169,8 +4199,14 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
OBJ_CHECK_SLOT(obj, slot); OBJ_CHECK_SLOT(obj, slot);
/* If sprop has a stub setter, keep scope locked and just store *vp. */ /* If sprop has a stub setter, keep scope locked and just store *vp. */
if (SPROP_HAS_STUB_SETTER(sprop)) if (SPROP_HAS_STUB_SETTER(sprop)) {
goto set_slot; if (!added && !scope->methodWriteBarrier(cx, sprop, *vp)) {
JS_UNLOCK_SCOPE(cx, scope);
return false;
}
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
return true;
}
} else { } else {
/* /*
* Allow API consumers to create shared properties with stub setters. * Allow API consumers to create shared properties with stub setters.
@ -4183,31 +4219,35 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
*/ */
if (!(sprop->attrs & JSPROP_GETTER) && SPROP_HAS_STUB_SETTER(sprop)) { if (!(sprop->attrs & JSPROP_GETTER) && SPROP_HAS_STUB_SETTER(sprop)) {
JS_ASSERT(!(sprop->attrs & JSPROP_SETTER)); JS_ASSERT(!(sprop->attrs & JSPROP_SETTER));
return JS_TRUE; 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);
ok = js_SetSprop(cx, sprop, obj, vp); ok = sprop->set(cx, obj, vp);
JS_POP_TEMP_ROOT(cx, &tvr); JS_POP_TEMP_ROOT(cx, &tvr);
if (!ok) if (!ok)
return JS_FALSE; return false;
JS_LOCK_SCOPE(cx, scope); JS_LOCK_SCOPE(cx, scope);
if (SLOT_IN_SCOPE(slot, scope) && if (SLOT_IN_SCOPE(slot, scope) &&
(JS_LIKELY(cx->runtime->propertyRemovals == sample) || (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
scope->has(sprop))) { scope->has(sprop))) {
set_slot: jsval v = *vp;
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, *vp); if (!added && !scope->methodWriteBarrier(cx, sprop, v)) {
JS_UNLOCK_SCOPE(cx, scope);
return false;
}
LOCKED_OBJ_SET_SLOT(obj, slot, v);
} }
return JS_TRUE; return true;
} }
JSBool JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
jsval *vp) jsval *vp)
{ {
JSObject *aobj, *obj2; JSObject *aobj, *obj2;
@ -4215,7 +4255,8 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
JSProperty *prop; JSProperty *prop;
JSScopeProperty *sprop; JSScopeProperty *sprop;
JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !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);
@ -4230,7 +4271,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
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(cacheResult && JS_PROPERTY_CACHE(cx).nofills++); PCMETER(getHow & JSGET_CACHE_RESULT && 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
@ -4292,12 +4333,12 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
sprop = (JSScopeProperty *) prop; sprop = (JSScopeProperty *) prop;
if (cacheResult) { if (getHow & JSGET_CACHE_RESULT) {
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, vp)) if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp))
return JS_FALSE; return JS_FALSE;
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
@ -4307,20 +4348,19 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
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, false, vp); return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp);
} }
JSBool JSBool
js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp)
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, cacheResult, vp); return js_GetPropertyHelper(cx, obj, id, getHow, vp);
} }
JS_ASSERT_IF(cacheResult, OBJ_IS_DENSE_ARRAY(cx, obj)); JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, 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);
@ -4351,10 +4391,11 @@ 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 loop (cacheResult is true). * SetPropHit when called from the interpreter, which is detected by testing
* (defineHow & JSDNP_CACHE_RESULT).
*/ */
JSBool JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
jsval *vp) jsval *vp)
{ {
int protoIndex; int protoIndex;
@ -4368,7 +4409,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
JSPropertyOp getter, setter; JSPropertyOp getter, setter;
bool added; bool added;
if (cacheResult) JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD)) == 0);
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. */
@ -4441,8 +4483,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
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(cacheResult && JS_PROPERTY_CACHE(cx).rofills++); PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
if (cacheResult) if (defineHow & JSDNP_CACHE_RESULT)
TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
return JS_TRUE; return JS_TRUE;
#ifdef JS_TRACER #ifdef JS_TRACER
@ -4469,7 +4511,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
/* Don't clone a shared prototype property. */ /* Don't clone a shared prototype property. */
if (attrs & JSPROP_SHARED) { if (attrs & JSPROP_SHARED) {
if (cacheResult) { if (defineHow & JSDNP_CACHE_RESULT) {
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);
@ -4480,7 +4522,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
return JS_TRUE; return JS_TRUE;
} }
return js_SetSprop(cx, sprop, obj, vp); return sprop->set(cx, obj, vp);
} }
/* Restore attrs to the ECMA default for new properties. */ /* Restore attrs to the ECMA default for new properties. */
@ -4527,8 +4569,26 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
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) &&
obj->getClass() == &js_ObjectClass) {
JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JSObject *funobj = JSVAL_TO_OBJECT(*vp);
if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
flags |= SPROP_IS_METHOD;
getter = js_CastAsPropertyOp(funobj);
}
}
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) {
@ -4545,20 +4605,21 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
/* XXXbe called with obj locked */ /* XXXbe called with obj locked */
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, vp)) {
scope->remove(cx, id); scope->remove(cx, id);
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
return JS_FALSE); return JS_FALSE;
}
added = true; added = true;
} }
if (cacheResult) { if (defineHow & JSDNP_CACHE_RESULT) {
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);
} }
if (!js_NativeSet(cx, obj, sprop, vp)) if (!js_NativeSet(cx, obj, sprop, added, vp))
return NULL; return NULL;
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
@ -5493,7 +5554,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, false, &fval); ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval);
if (!ok) if (!ok)
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
JS_SetErrorReporter(cx, older); JS_SetErrorReporter(cx, older);

View File

@ -350,41 +350,6 @@ STOBJ_GET_CLASS(const JSObject* obj)
#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ #define LOCKED_OBJ_SET_SLOT(obj,slot,value) \
(OBJ_CHECK_SLOT(obj, slot), STOBJ_SET_SLOT(obj, slot, value)) (OBJ_CHECK_SLOT(obj, slot), STOBJ_SET_SLOT(obj, slot, value))
/*
* NB: Don't call LOCKED_OBJ_SET_SLOT or STOBJ_SET_SLOT for a write to a slot
* that may contain a function reference already, or where the new value is a
* function ref, and the object's scope may be branded with a property cache
* structural type capability that distinguishes versions of the object with
* and without the function property. Instead use LOCKED_OBJ_WRITE_SLOT or a
* fast inline equivalent (JSOP_SETNAME/JSOP_SETPROP cases in jsinterp.cpp).
*/
#define LOCKED_OBJ_WRITE_SLOT(cx,obj,slot,newval) \
JS_BEGIN_MACRO \
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, newval); \
LOCKED_OBJ_SET_SLOT(obj, slot, newval); \
JS_END_MACRO
/*
* Write barrier macro monitoring property update for slot in obj from its old
* value to newval.
*
* NB: obj must be locked, and remains locked after the calls to this macro.
*/
#define LOCKED_OBJ_WRITE_BARRIER(cx,obj,slot,newval) \
JS_BEGIN_MACRO \
JSScope *scope_ = OBJ_SCOPE(obj); \
JS_ASSERT(scope_->object == obj); \
if (scope_->branded()) { \
jsval oldval_ = LOCKED_OBJ_GET_SLOT(obj, slot); \
if (oldval_ != (newval) && \
(VALUE_IS_FUNCTION(cx, oldval_) || \
VALUE_IS_FUNCTION(cx, newval))) { \
scope_->methodShapeChange(cx, slot, newval); \
} \
} \
GC_POKE(cx, oldval); \
JS_END_MACRO
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
/* Thread-safe functions and wrapper macros for accessing slots in obj. */ /* Thread-safe functions and wrapper macros for accessing slots in obj. */
@ -398,7 +363,7 @@ STOBJ_GET_CLASS(const JSObject* obj)
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
OBJ_CHECK_SLOT(obj, slot); \ OBJ_CHECK_SLOT(obj, slot); \
if (OBJ_SCOPE(obj)->title.ownercx == cx) \ if (OBJ_SCOPE(obj)->title.ownercx == cx) \
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, value); \ LOCKED_OBJ_SET_SLOT(obj, slot, value); \
else \ else \
js_SetSlotThreadSafe(cx, obj, slot, value); \ js_SetSlotThreadSafe(cx, obj, slot, value); \
JS_END_MACRO JS_END_MACRO
@ -424,7 +389,7 @@ STOBJ_GET_CLASS(const JSObject* obj)
#else /* !JS_THREADSAFE */ #else /* !JS_THREADSAFE */
#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) #define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot)
#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_WRITE_SLOT(cx,obj,slot,value) #define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value)
#endif /* !JS_THREADSAFE */ #endif /* !JS_THREADSAFE */
@ -724,6 +689,9 @@ 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 */
/* /*
* On error, return false. On success, if propp is non-null, return true with * On error, return false. On success, if propp is non-null, return true with
@ -801,6 +769,23 @@ 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 (the default, hence 0 but provided for documentation)
* 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 ===.
*
* JSGET_NO_METHOD_BARRIER avoids the performance overhead of the method read
* barrier, which is not needed when invoking a lambda that otherwise does not
* leak its callee reference (via arguments.callee or its name).
*/
const uintN JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode
const uintN JSGET_METHOD_BARRIER = 0; // get can leak joined function object
const uintN JSGET_NO_METHOD_BARRIER = 2; // call to joined function can't leak
/* /*
* 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
@ -809,21 +794,21 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp);
*/ */
extern JSBool extern JSBool
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
JSScopeProperty *sprop, jsval *vp); JSScopeProperty *sprop, uintN getHow, jsval *vp);
extern JSBool extern JSBool
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added,
jsval *vp);
extern JSBool extern JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
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, JSBool cacheResult, js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp);
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
@ -833,7 +818,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, JSBool cacheResult, js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
jsval *vp); jsval *vp);
extern JSBool extern JSBool

View File

@ -3856,6 +3856,7 @@ 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();
@ -4517,6 +4518,7 @@ 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

@ -246,8 +246,8 @@ OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16
/* Object and array literal support. */ /* Object and array literal support. */
OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 2, 0, 1, 19, JOF_INT8) OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 2, 0, 1, 19, JOF_INT8)
OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE)
OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16)
@ -582,7 +582,7 @@ 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. * Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops.
*/ */
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)
@ -595,3 +595,9 @@ OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_
* immediate. See record_JSOP_CONCATN for recording behavior. * immediate. See record_JSOP_CONCATN for recording behavior.
*/ */
OPDEF(JSOP_CONCATN, 234,"concatn", NULL, 3, -1, 1, 0, JOF_UINT16|JOF_TMPSLOT2) OPDEF(JSOP_CONCATN, 234,"concatn", NULL, 3, -1, 1, 0, JOF_UINT16|JOF_TMPSLOT2)
/*
* Joined function object as method optimization support.
*/
OPDEF(JSOP_SETMETHOD, 235,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITMETHOD, 236,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)

View File

@ -590,7 +590,7 @@
goto error; \ goto error; \
JS_END_MACRO JS_END_MACRO
#define NATIVE_GET(cx,obj,pobj,sprop,vp) \ #define NATIVE_GET(cx,obj,pobj,sprop,getHow,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. */ \
@ -600,7 +600,7 @@
? 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, vp)) \ if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \
goto error; \ goto error; \
} \ } \
JS_END_MACRO JS_END_MACRO
@ -609,11 +609,12 @@
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
TRACE_2(SetPropHit, entry, sprop); \ TRACE_2(SetPropHit, entry, sprop); \
if (SPROP_HAS_STUB_SETTER(sprop) && \ if (SPROP_HAS_STUB_SETTER(sprop) && \
(sprop)->slot != SPROP_INVALID_SLOT) { \ (sprop)->slot != SPROP_INVALID_SLOT && \
/* Fast path for, e.g., Object instance properties. */ \ !OBJ_SCOPE(obj)->branded()) { \
LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *vp); \ /* Fast path for, e.g., plain Object instance properties. */ \
LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \
} else { \ } else { \
if (!js_NativeSet(cx, obj, sprop, vp)) \ if (!js_NativeSet(cx, obj, sprop, false, vp)) \
goto error; \ goto error; \
} \ } \
JS_END_MACRO JS_END_MACRO
@ -1487,6 +1488,11 @@
JSObject *aobj; JSObject *aobj;
JSPropCacheEntry *entry; JSPropCacheEntry *entry;
/*
* We do not impose the method read barrier if in an imacro,
* assuming any property gets it does (e.g., for 'toString'
* from JSOP_NEW) will not be leaked to the calling script.
*/
aobj = js_GetProtoIfDenseArray(cx, obj); aobj = js_GetProtoIfDenseArray(cx, obj);
if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
@ -1501,7 +1507,9 @@
} 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, &rval); NATIVE_GET(cx, obj, obj2, sprop,
fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
&rval);
} }
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
break; break;
@ -1515,7 +1523,11 @@
} }
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
if (entry if (entry
? !js_GetPropertyHelper(cx, obj, id, true, &rval) ? !js_GetPropertyHelper(cx, obj, id,
fp->imacpc
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
&rval)
: !obj->getProperty(cx, id, &rval)) { : !obj->getProperty(cx, id, &rval)) {
goto error; goto error;
} }
@ -1592,7 +1604,7 @@
} 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, &rval); NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval);
} }
JS_UNLOCK_OBJ(cx, obj2); JS_UNLOCK_OBJ(cx, obj2);
STORE_OPND(-1, rval); STORE_OPND(-1, rval);
@ -1611,14 +1623,22 @@
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, &rval)) if (!js_GetMethod(cx, obj, id,
entry
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_NO_METHOD_BARRIER,
&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, true, &rval)) if (!js_GetPropertyHelper(cx, obj, id,
JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
&rval)) {
goto error; goto error;
}
STORE_OPND(-1, lval); STORE_OPND(-1, lval);
STORE_OPND(-2, rval); STORE_OPND(-2, rval);
} }
@ -1648,9 +1668,11 @@
BEGIN_CASE(JSOP_SETNAME) BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETMETHOD)
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(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP); JS_ASSERT_IF(op == JSOP_SETNAME, !JSVAL_IS_PRIMITIVE(lval));
VALUE_TO_OBJECT(cx, -2, lval, obj); VALUE_TO_OBJECT(cx, -2, lval, obj);
do { do {
@ -1806,7 +1828,12 @@
scope->extend(cx, sprop); scope->extend(cx, sprop);
} }
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval); /*
* No method change check here 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);
@ -1850,7 +1877,10 @@
LOAD_ATOM(0); LOAD_ATOM(0);
id = ATOM_TO_JSID(atom); id = ATOM_TO_JSID(atom);
if (entry) { if (entry) {
if (!js_SetPropertyHelper(cx, obj, id, true, &rval)) uintN defineHow = (op == JSOP_SETMETHOD)
? 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->setProperty(cx, id, &rval)) if (!obj->setProperty(cx, id, &rval))
@ -1907,7 +1937,7 @@
END_CASE(JSOP_GETELEM) END_CASE(JSOP_GETELEM)
BEGIN_CASE(JSOP_CALLELEM) BEGIN_CASE(JSOP_CALLELEM)
ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval)); ELEMENT_OP(-1, js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &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];
@ -2310,7 +2340,7 @@
} else { } else {
sprop = (JSScopeProperty *)prop; sprop = (JSScopeProperty *)prop;
do_native_get: do_native_get:
NATIVE_GET(cx, obj, obj2, sprop, &rval); NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval);
obj2->dropProperty(cx, (JSProperty *) sprop); obj2->dropProperty(cx, (JSProperty *) sprop);
} }
@ -2840,8 +2870,13 @@
} else { } else {
slot = JSVAL_TO_INT(lval); slot = JSVAL_TO_INT(lval);
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, rval); JSScope *scope = OBJ_SCOPE(obj);
JS_UNLOCK_OBJ(cx, obj); if (!scope->methodWriteBarrier(cx, slot, rval)) {
JS_UNLOCK_SCOPE(cx, scope);
goto error;
}
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_SCOPE(cx, scope);
} }
END_SET_CASE(JSOP_SETGVAR) END_SET_CASE(JSOP_SETGVAR)
@ -2892,7 +2927,7 @@
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(sprop) && SPROP_HAS_STUB_GETTER_OR_IS_METHOD(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
@ -2967,8 +3002,7 @@
/* /*
* Protect obj from any GC hiding below JSObject::setProperty or * Protect obj from any GC hiding below JSObject::setProperty or
* JSObject::defineProperty. All paths from here must flow through * JSObject::defineProperty. All paths from here must flow through
* the "Restore fp->scopeChain" code below the * the fp->scopeChain code below the parent->defineProperty call.
* parent->defineProperty call.
*/ */
MUST_FLOW_THROUGH("restore_scope"); MUST_FLOW_THROUGH("restore_scope");
fp->scopeChain = obj; fp->scopeChain = obj;
@ -2988,7 +3022,7 @@
* and setters do not need a slot, their value is stored elsewhere * and setters do not need a slot, their value is stored elsewhere
* in the property itself, not in obj slots. * in the property itself, not in obj slots.
*/ */
setter = getter = JS_PropertyStub; getter = setter = JS_PropertyStub;
flags = JSFUN_GSFLAG2ATTR(fun->flags); flags = JSFUN_GSFLAG2ATTR(fun->flags);
if (flags) { if (flags) {
/* Function cannot be both getter a setter. */ /* Function cannot be both getter a setter. */
@ -3102,10 +3136,10 @@
ok = parent->defineProperty(cx, id, rval, ok = parent->defineProperty(cx, id, rval,
(flags & JSPROP_GETTER) (flags & JSPROP_GETTER)
? JS_EXTENSION (JSPropertyOp) obj ? js_CastAsPropertyOp(obj)
: JS_PropertyStub, : JS_PropertyStub,
(flags & JSPROP_SETTER) (flags & JSPROP_SETTER)
? JS_EXTENSION (JSPropertyOp) obj ? js_CastAsPropertyOp(obj)
: JS_PropertyStub, : JS_PropertyStub,
attrs); attrs);
} }
@ -3185,24 +3219,55 @@
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
obj = FUN_OBJECT(fun); obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) { /* do-while(0) so we can break instead of using a goto. */
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); do {
if (!obj) if (FUN_NULL_CLOSURE(fun)) {
goto error; parent = fp->scopeChain;
} else {
parent = js_GetScopeChain(cx, fp); if (OBJ_GET_PARENT(cx, obj) == parent) {
if (!parent) op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]);
goto error;
/*
* 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
lval = FETCH_OPND(-1);
if (JSVAL_IS_OBJECT(lval) &&
(obj2 = JSVAL_TO_OBJECT(lval)) &&
OBJ_GET_CLASS(cx, obj2) == &js_ObjectClass) {
break;
}
} else if (op == JSOP_INITMETHOD) {
lval = FETCH_OPND(-1);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
obj2 = JSVAL_TO_OBJECT(lval);
JS_ASSERT(OBJ_GET_CLASS(cx, obj2) == &js_ObjectClass);
JS_ASSERT(OBJ_SCOPE(obj2)->object == obj2);
break;
}
}
} else {
parent = js_GetScopeChain(cx, fp);
if (!parent)
goto error;
}
/*
* FIXME: bug 471214, Cloning here even when the compiler saw
* the right parent is wasteful but we don't fully support
* joined function objects, yet.
*/
obj = js_CloneFunctionObject(cx, fun, parent); obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj) if (!obj)
goto error; goto error;
} } while (0);
PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(OBJECT_TO_JSVAL(obj));
END_CASE(JSOP_LAMBDA) END_CASE(JSOP_LAMBDA)
@ -3306,12 +3371,12 @@
goto error; goto error;
if (op == JSOP_GETTER) { if (op == JSOP_GETTER) {
getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); getter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
setter = JS_PropertyStub; setter = JS_PropertyStub;
attrs = JSPROP_GETTER; attrs = JSPROP_GETTER;
} else { } else {
getter = JS_PropertyStub; getter = JS_PropertyStub;
setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); setter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
attrs = JSPROP_SETTER; attrs = JSPROP_SETTER;
} }
attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
@ -3324,8 +3389,10 @@
goto error; goto error;
regs.sp += i; regs.sp += i;
if (js_CodeSpec[op2].ndefs) if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
STORE_OPND(-1, rval); STORE_OPND(-1, rval);
}
len = js_CodeSpec[op2].length; len = js_CodeSpec[op2].length;
DO_NEXT_OP(len); DO_NEXT_OP(len);
#endif /* JS_HAS_GETTER_SETTER */ #endif /* JS_HAS_GETTER_SETTER */
@ -3347,11 +3414,26 @@
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);
obj = (i == JSProto_Array) if (i == JSProto_Array) {
? js_NewArrayObject(cx, 0, NULL) obj = js_NewArrayObject(cx, 0, NULL);
: js_NewObject(cx, &js_ObjectClass, NULL, NULL); if (!obj)
if (!obj) goto error;
goto error; } else {
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();
@ -3369,6 +3451,7 @@
END_CASE(JSOP_ENDINIT) END_CASE(JSOP_ENDINIT)
BEGIN_CASE(JSOP_INITPROP) BEGIN_CASE(JSOP_INITPROP)
BEGIN_CASE(JSOP_INITMETHOD)
/* 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);
@ -3388,6 +3471,10 @@
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj); scope = OBJ_SCOPE(obj);
// FIXME: bug 513291 -- uncomment this assertion and remove the
// (!scope->owned()) => js_GetMutableScope code further
// below.
// 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);
@ -3469,13 +3556,26 @@
JS_ASSERT(sprop2 == sprop); JS_ASSERT(sprop2 == sprop);
} else { } else {
JS_ASSERT(scope->owned()); JS_ASSERT(scope->owned());
/* Inline-specialized version of JSScope::extend. */
js_LeaveTraceIfGlobalObject(cx, obj); js_LeaveTraceIfGlobalObject(cx, obj);
scope->shape = sprop->shape; scope->shape = sprop->shape;
++scope->entryCount; ++scope->entryCount;
scope->lastProp = sprop; scope->lastProp = 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 method change check here 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);
@ -3496,12 +3596,16 @@
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, true, &rval) ? js_SetPropertyHelper(cx, obj, id, defineHow, &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,
JSDNP_CACHE_RESULT))) defineHow))) {
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

@ -2332,20 +2332,10 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
/* /*
* If this named function expression uses its own name other * If this named function expression uses its own name other
* than to call itself, flag this function as using arguments, * than to call itself, flag this function specially.
* as if it had used arguments.callee instead of its own name.
*
* This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
* we are out of tcflags bits at the moment. If it deoptimizes
* code unfairly (see JSCompiler::setFunctionKinds, where this
* flag is interpreted in its broader sense, not only to mean
* "this function might leak arguments.callee"), we can perhaps
* try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
* use that more precisely, both here and for unnamed function
* expressions.
*/ */
if (dn->isFunArg()) if (dn->isFunArg())
fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS; fn->pn_funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
foundCallee = 1; foundCallee = 1;
continue; continue;
} }
@ -5479,6 +5469,20 @@ 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_right->pn_funbox->tcflags
& (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME))) {
pn2->pn_left->pn_op = JSOP_SETMETHOD;
}
break; break;
} }
@ -6745,7 +6749,7 @@ CheckForImmediatelyAppliedLambda(JSParseNode *pn)
JSFunctionBox *funbox = pn->pn_funbox; JSFunctionBox *funbox = pn->pn_funbox;
JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA); JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
if (!(funbox->tcflags & TCF_FUN_USES_ARGUMENTS)) if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
pn->pn_dflags &= ~PND_FUNARG; pn->pn_dflags &= ~PND_FUNARG;
} }
return pn; return pn;

View File

@ -262,26 +262,6 @@ JSScope::destroy(JSContext *cx, JSScope *scope)
} }
#ifdef JS_DUMP_PROPTREE_STATS #ifdef JS_DUMP_PROPTREE_STATS
typedef struct JSScopeStats {
jsrefcount searches;
jsrefcount hits;
jsrefcount misses;
jsrefcount hashes;
jsrefcount steps;
jsrefcount stepHits;
jsrefcount stepMisses;
jsrefcount adds;
jsrefcount redundantAdds;
jsrefcount addFailures;
jsrefcount changeFailures;
jsrefcount compresses;
jsrefcount grows;
jsrefcount removes;
jsrefcount removeFrees;
jsrefcount uselessRemoves;
jsrefcount shrinks;
} JSScopeStats;
JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0}; JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0};
# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
@ -444,12 +424,14 @@ 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 || sprop->setter == js_watch_set);
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);
@ -1054,6 +1036,9 @@ 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
@ -1069,10 +1054,17 @@ 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 || setter == js_watch_set);
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
@ -1350,10 +1342,6 @@ JSScope::add(JSContext *cx, jsid id,
(void) createTable(cx, false); (void) createTable(cx, false);
} }
jsuint index;
if (js_IdIsIndex(sprop->id, &index))
setIndexedProperties();
METER(adds); METER(adds);
return sprop; return sprop;
@ -1432,8 +1420,7 @@ JSScope::change(JSContext *cx, JSScopeProperty *sprop,
* Optimize the case where the last property added to this scope is * Optimize the case where the last property added to this scope is
* changed to have a different attrs, getter, or setter. In the last * changed to have a different attrs, getter, or setter. In the last
* property case, we need not fork the property tree. But since we do * property case, we need not fork the property tree. But since we do
* not call JSScope::addProperty, we may need to allocate a new slot * not call JSScope::add, we may need to allocate a new slot directly.
* directly.
*/ */
if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) {
JS_ASSERT(child.slot == SPROP_INVALID_SLOT); JS_ASSERT(child.slot == SPROP_INVALID_SLOT);
@ -1453,8 +1440,8 @@ JSScope::change(JSContext *cx, JSScopeProperty *sprop,
} }
} else { } else {
/* /*
* Let JSScope::addProperty handle this |overwriting| case, including * Let JSScope::add handle this |overwriting| case, including the
* the conservation of sprop->slot (if it's valid). We must not call * conservation of sprop->slot (if it's valid). We must not call
* JSScope::remove here, because it will free a valid sprop->slot and * JSScope::remove here, because it will free a valid sprop->slot and
* JSScope::add won't re-allocate it. * JSScope::add won't re-allocate it.
*/ */
@ -1589,10 +1576,47 @@ JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
generateOwnShape(cx); generateOwnShape(cx);
} }
void bool
JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval)
{
if (sprop->isMethod()) {
#ifdef DEBUG
jsval prev = LOCKED_OBJ_GET_SLOT(object, sprop->slot);
JS_ASSERT(sprop->methodValue() == prev);
JS_ASSERT(hasMethodBarrier());
JS_ASSERT(object->getClass() == &js_ObjectClass);
JS_ASSERT(!sprop->setter || sprop->setter == js_watch_set);
#endif
/*
* Pass null to make a stub getter, but pass along sprop->setter to
* preserve watchpoints. Clear SPROP_IS_METHOD from flags as we are
* despecializing from a method memoized in the property tree to a
* plain old function-valued property.
*/
sprop = add(cx, sprop->id, NULL, sprop->setter, sprop->slot,
sprop->attrs, sprop->flags & ~SPROP_IS_METHOD,
sprop->shortid);
if (!sprop)
return false;
}
generateOwnShape(cx);
return true;
}
bool
JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval) JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
{ {
generateOwnShape(cx); if (!hasMethodBarrier()) {
generateOwnShape(cx);
} else {
for (JSScopeProperty *sprop = lastProp; sprop; sprop = sprop->parent) {
if (sprop->slot == slot && (!hadMiddleDelete() || has(sprop)))
return methodShapeChange(cx, sprop, toval);
}
}
return true;
} }
void void
@ -1656,6 +1680,23 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
JS_snprintf(buf, bufsize, "<object> %s", name); JS_snprintf(buf, bufsize, "<object> %s", name);
} }
} }
static void
PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
{
JSScopeProperty *sprop;
jsid id;
size_t n;
JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
sprop = (JSScopeProperty *)trc->debugPrintArg;
id = sprop->id;
JS_ASSERT(JSID_IS_ATOM(id));
n = js_PutEscapedString(buf, bufsize - 1, ATOM_TO_STRING(JSID_TO_ATOM(id)), 0);
if (n < bufsize - 1)
JS_snprintf(buf + n, bufsize - n, " method");
}
#endif #endif
void void
@ -1669,14 +1710,19 @@ 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, js_CastAsObject(getter), JSTRACE_OBJECT); JS_CallTracer(trc, getterObject(), 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, js_CastAsObject(setter), JSTRACE_OBJECT); JS_CallTracer(trc, setterObject(), JSTRACE_OBJECT);
} }
} }
#endif /* JS_HAS_GETTER_SETTER */ #endif /* JS_HAS_GETTER_SETTER */
if (isMethod()) {
JS_SET_TRACING_DETAILS(trc, PrintPropertyMethod, this, 0);
JS_CallTracer(trc, methodObject(), JSTRACE_OBJECT);
}
} }
#ifdef JS_DUMP_PROPTREE_STATS #ifdef JS_DUMP_PROPTREE_STATS

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=78: * vim: set ts=8 sw=4 et tw=99:
* *
* ***** 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
@ -45,6 +45,7 @@
*/ */
#include "jstypes.h" #include "jstypes.h"
#include "jslock.h" #include "jslock.h"
#include "jsfun.h"
#include "jsobj.h" #include "jsobj.h"
#include "jsprvtd.h" #include "jsprvtd.h"
#include "jspubtd.h" #include "jspubtd.h"
@ -279,15 +280,30 @@ struct JSScope {
void extend(JSContext *cx, JSScopeProperty *sprop); void extend(JSContext *cx, JSScopeProperty *sprop);
/*
* Read barrier to clone a joined function object stored as a method.
* Defined inline further below.
*/
inline bool methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp);
/*
* Write barrier to check for a method value change. Defined inline below
* after methodReadBarrier. Two flavors to handle JSOP_*GVAR, which deals
* in slots not sprops, while not deoptimizing to map slot to sprop unless
* flags show this is necessary. The methodShapeChange overload (directly
* below) parallels this.
*/
inline bool methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v);
inline bool methodWriteBarrier(JSContext *cx, uint32 slot, jsval v);
void trace(JSTracer *trc); void trace(JSTracer *trc);
void brandingShapeChange(JSContext *cx, uint32 slot, jsval v); void brandingShapeChange(JSContext *cx, uint32 slot, jsval v);
void deletingShapeChange(JSContext *cx, JSScopeProperty *sprop); void deletingShapeChange(JSContext *cx, JSScopeProperty *sprop);
void methodShapeChange(JSContext *cx, uint32 slot, jsval toval); bool methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval);
bool methodShapeChange(JSContext *cx, uint32 slot, jsval toval);
void protoShapeChange(JSContext *cx); void protoShapeChange(JSContext *cx);
void replacingShapeChange(JSContext *cx, void replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProperty *newsprop);
JSScopeProperty *sprop,
JSScopeProperty *newsprop);
void sealingShapeChange(JSContext *cx); void sealingShapeChange(JSContext *cx);
void shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop); void shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop);
@ -300,12 +316,13 @@ 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 = 0x0020 SHAPE_REGEN = 0x0040
}; };
bool hadMiddleDelete() { return flags & MIDDLE_DELETE; } bool hadMiddleDelete() { return flags & MIDDLE_DELETE; }
@ -336,6 +353,42 @@ 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; }
}; };
@ -377,7 +430,8 @@ 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; JSPropertyOp setter; /* getter is JSObject* and setter is 0
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 */
@ -387,6 +441,52 @@ 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 *methodObject() const {
JS_ASSERT(isMethod());
return js_CastAsObject(getter);
}
jsval methodValue() const {
JS_ASSERT(isMethod());
return js_CastAsObjectJSVal(getter);
}
bool hasGetterObject() const {
return attrs & JSPROP_GETTER;
}
JSObject *getterObject() const {
JS_ASSERT(hasGetterObject());
return js_CastAsObject(getter);
}
jsval getterValue() const {
JS_ASSERT(hasGetterObject());
return js_CastAsObjectJSVal(getter);
}
bool hasSetterObject() const {
return attrs & JSPROP_SETTER;
}
JSObject *setterObject() const {
JS_ASSERT(hasSetterObject());
return js_CastAsObject(setter);
}
jsval setterValue() const {
JS_ASSERT(hasSetterObject());
return js_CastAsObjectJSVal(setter);
}
bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp);
bool set(JSContext* cx, JSObject* obj, jsval* vp);
void trace(JSTracer *trc); void trace(JSTracer *trc);
}; };
@ -410,24 +510,18 @@ struct JSScopeProperty {
(*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \
| SPROP_HAD_COLLISION(*(spp)))) | SPROP_HAD_COLLISION(*(spp))))
JS_ALWAYS_INLINE JSScopeProperty * inline JSScopeProperty *
JSScope::lookup(jsid id) JSScope::lookup(jsid id)
{ {
return SPROP_FETCH(search(id, false)); return SPROP_FETCH(search(id, false));
} }
JS_ALWAYS_INLINE bool inline bool
JSScope::has(JSScopeProperty *sprop) 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.
@ -444,6 +538,9 @@ 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
@ -452,6 +549,28 @@ extern uint32
js_GenerateShape(JSContext *cx, bool gcLocked); js_GenerateShape(JSContext *cx, bool gcLocked);
#ifdef JS_DUMP_PROPTREE_STATS #ifdef JS_DUMP_PROPTREE_STATS
struct JSScopeStats {
jsrefcount searches;
jsrefcount hits;
jsrefcount misses;
jsrefcount hashes;
jsrefcount steps;
jsrefcount stepHits;
jsrefcount stepMisses;
jsrefcount adds;
jsrefcount redundantAdds;
jsrefcount addFailures;
jsrefcount changeFailures;
jsrefcount compresses;
jsrefcount grows;
jsrefcount removes;
jsrefcount removeFrees;
jsrefcount uselessRemoves;
jsrefcount shrinks;
};
extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
#else #else
# define METER(x) /* nothing */ # define METER(x) /* nothing */
@ -512,9 +631,69 @@ JSScope::extend(JSContext *cx, JSScopeProperty *sprop)
js_LeaveTraceIfGlobalObject(cx, object); js_LeaveTraceIfGlobalObject(cx, object);
shape = (!lastProp || shape == lastProp->shape) shape = (!lastProp || shape == lastProp->shape)
? sprop->shape ? sprop->shape
: js_GenerateShape(cx, JS_FALSE); : js_GenerateShape(cx, false);
++entryCount; ++entryCount;
lastProp = sprop; lastProp = sprop;
jsuint index;
if (js_IdIsIndex(sprop->id, &index))
setIndexedProperties();
if (sprop->isMethod())
setMethodBarrier();
}
/*
* Property read barrier for deferred cloning of compiler-created function
* objects optimized as typically non-escaping, ad-hoc methods in obj.
*
* Called only from JSScopeProperty::get when sprop->isMethod(), or JIT-
* equivalent code. sprop->isMethod() implies that scope->hasMethodBarrier()
* for the scope containing that sprop.
*/
inline bool
JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp)
{
JS_ASSERT(hasMethodBarrier());
JS_ASSERT(has(sprop));
JS_ASSERT(sprop->isMethod());
JS_ASSERT(sprop->methodValue() == *vp);
JS_ASSERT(object->getClass() == &js_ObjectClass);
JSObject *funobj = JSVAL_TO_OBJECT(*vp);
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
JS_ASSERT(FUN_OBJECT(fun) == funobj && FUN_NULL_CLOSURE(fun));
funobj = js_CloneFunctionObject(cx, fun, OBJ_GET_PARENT(cx, funobj));
if (!funobj)
return false;
*vp = OBJECT_TO_JSVAL(funobj);
return js_SetPropertyHelper(cx, object, sprop->id, 0, vp);
}
inline bool
JSScope::methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v)
{
if (branded()) {
jsval prev = LOCKED_OBJ_GET_SLOT(object, sprop->slot);
if (prev != v && VALUE_IS_FUNCTION(cx, prev))
return methodShapeChange(cx, sprop, v);
}
return true;
}
inline bool
JSScope::methodWriteBarrier(JSContext *cx, uint32 slot, jsval v)
{
if (branded()) {
jsval prev = LOCKED_OBJ_GET_SLOT(object, slot);
if (prev != v && VALUE_IS_FUNCTION(cx, prev))
return methodShapeChange(cx, slot, v);
}
return true;
} }
inline void inline void
@ -564,16 +743,23 @@ JSScope::trace(JSTracer *trc)
} }
} }
inline bool
static JS_INLINE bool JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp)
js_GetSprop(JSContext* cx, JSScopeProperty* sprop, JSObject* obj, jsval* vp)
{ {
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop)); JS_ASSERT(!SPROP_HAS_STUB_GETTER(this));
if (sprop->attrs & JSPROP_GETTER) { if (attrs & JSPROP_GETTER) {
jsval fval = js_CastAsObjectJSVal(sprop->getter); JS_ASSERT(!isMethod());
return js_InternalGetOrSet(cx, obj, sprop->id, fval, JSACC_READ, jsval fval = getterValue();
0, 0, vp); return js_InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp);
}
if (isMethod()) {
*vp = methodValue();
JSScope *scope = OBJ_SCOPE(pobj);
JS_ASSERT(scope->object == pobj);
return scope->methodReadBarrier(cx, this, vp);
} }
/* /*
@ -584,30 +770,28 @@ js_GetSprop(JSContext* cx, JSScopeProperty* sprop, 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 sprop->getter(cx, obj, SPROP_USERID(sprop), vp); return getter(cx, obj, SPROP_USERID(this), vp);
} }
static JS_INLINE bool inline bool
js_SetSprop(JSContext* cx, JSScopeProperty* sprop, JSObject* obj, jsval* vp) JSScopeProperty::set(JSContext* cx, JSObject* obj, jsval* vp)
{ {
JS_ASSERT(!(SPROP_HAS_STUB_SETTER(sprop) && JS_ASSERT_IF(SPROP_HAS_STUB_SETTER(this), attrs & JSPROP_GETTER);
!(sprop->attrs & JSPROP_GETTER)));
if (sprop->attrs & JSPROP_SETTER) { if (attrs & JSPROP_SETTER) {
jsval fval = js_CastAsObjectJSVal(sprop->setter); jsval fval = setterValue();
return js_InternalGetOrSet(cx, obj, (sprop)->id, fval, JSACC_WRITE, return js_InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp);
1, vp, vp);
} }
if (sprop->attrs & JSPROP_GETTER) { if (attrs & JSPROP_GETTER) {
js_ReportGetterOnlyAssignment(cx); js_ReportGetterOnlyAssignment(cx);
return JS_FALSE; return false;
} }
/* See the comment in js_GetSprop as to why we can check for 'with'. */ /* See the comment in JSScopeProperty::get 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 sprop->setter(cx, obj, SPROP_USERID(sprop), vp); return setter(cx, obj, SPROP_USERID(this), vp);
} }
/* Macro for common expression to test for shared permanent attributes. */ /* Macro for common expression to test for shared permanent attributes. */

View File

@ -2577,7 +2577,7 @@ GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double*
#ifdef DEBUG #ifdef DEBUG
JSBool rv = JSBool rv =
#endif #endif
js_GetPropertyHelper(cx, call, cv->id, JS_FALSE, &v); js_GetPropertyHelper(cx, call, cv->id, JSGET_METHOD_BARRIER, &v);
JS_ASSERT(rv); JS_ASSERT(rv);
} }
JSTraceType type = getCoercedType(v); JSTraceType type = getCoercedType(v);
@ -2932,8 +2932,10 @@ TraceRecorder::isValidSlot(JSScope* scope, JSScopeProperty* sprop)
} }
/* This check applies even when setflags == 0. */ /* This check applies even when setflags == 0. */
if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
JS_ASSERT(!sprop->isMethod());
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))
ABORT_TRACE_RV("slotless obj property", false); ABORT_TRACE_RV("slotless obj property", false);
@ -3289,7 +3291,7 @@ TraceRecorder::snapshot(ExitType exitType)
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS); JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS);
if (resumeAfter) { if (resumeAfter) {
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW || JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW ||
*pc == JSOP_SETPROP || *pc == JSOP_SETNAME); *pc == JSOP_SETPROP || *pc == JSOP_SETNAME || *pc == JSOP_SETMETHOD);
pc += cs.length; pc += cs.length;
regs->pc = pc; regs->pc = pc;
MUST_FLOW_THROUGH("restore_pc"); MUST_FLOW_THROUGH("restore_pc");
@ -4411,7 +4413,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(sprop) && if (SPROP_HAS_STUB_GETTER_OR_IS_METHOD(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)) {
@ -5727,7 +5729,7 @@ LeaveTree(InterpState& state, VMSideExit* lr)
op == JSOP_GETPROP || op == JSOP_GETTHISPROP || op == JSOP_GETARGPROP || op == JSOP_GETPROP || op == JSOP_GETTHISPROP || op == JSOP_GETARGPROP ||
op == JSOP_GETLOCALPROP || op == JSOP_LENGTH || op == JSOP_GETLOCALPROP || op == JSOP_LENGTH ||
op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM || op == JSOP_CALLELEM ||
op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETMETHOD ||
op == JSOP_SETELEM || op == JSOP_INITELEM || op == JSOP_SETELEM || op == JSOP_INITELEM ||
op == JSOP_INSTANCEOF); op == JSOP_INSTANCEOF);
const JSCodeSpec& cs = js_CodeSpec[op]; const JSCodeSpec& cs = js_CodeSpec[op];
@ -6987,7 +6989,7 @@ TraceRecorder::scopeChainProp(JSObject* obj, jsval*& vp, LIns*& ins, NameResult&
ABORT_TRACE("deep abort from property lookup"); ABORT_TRACE("deep abort from property lookup");
if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass)
return callProp(obj, obj2, prop, ATOM_TO_JSID(atom), vp, ins, nr); return callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr);
obj2->dropProperty(cx, prop); obj2->dropProperty(cx, prop);
ABORT_TRACE("fp->scopeChain is not global or active call object"); ABORT_TRACE("fp->scopeChain is not global or active call object");
@ -6997,12 +6999,13 @@ TraceRecorder::scopeChainProp(JSObject* obj, jsval*& vp, LIns*& ins, NameResult&
* Generate LIR to access a property of a Call object. * Generate LIR to access a property of a Call object.
*/ */
JS_REQUIRES_STACK JSRecordingStatus JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::callProp(JSObject* obj, JSObject* obj2, JSProperty* prop, jsid id, jsval*& vp, TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
LIns*& ins, NameResult& nr) LIns*& ins, NameResult& nr)
{ {
JSScopeProperty *sprop = (JSScopeProperty*) prop; JSScopeProperty *sprop = (JSScopeProperty*) prop;
uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR)); JSOp op = JSOp(*cx->fp->regs->pc);
uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
if (setflags && (sprop->attrs & JSPROP_READONLY)) if (setflags && (sprop->attrs & JSPROP_READONLY))
ABORT_TRACE("writing to a read-only property"); ABORT_TRACE("writing to a read-only property");
@ -7025,7 +7028,7 @@ TraceRecorder::callProp(JSObject* obj, JSObject* obj2, JSProperty* prop, jsid id
} else { } else {
ABORT_TRACE("dynamic property of Call object"); ABORT_TRACE("dynamic property of Call object");
} }
obj2->dropProperty(cx, prop); obj->dropProperty(cx, prop);
if (frameIfInRange(obj)) { if (frameIfInRange(obj)) {
// At this point we are guaranteed to be looking at an active call oject // At this point we are guaranteed to be looking at an active call oject
@ -7035,12 +7038,19 @@ TraceRecorder::callProp(JSObject* obj, JSObject* obj2, JSProperty* prop, jsid id
return JSRS_CONTINUE; return JSRS_CONTINUE;
} }
} else { } else {
// Call objects do not yet have sprop->isMethod() properties, but they
// should. See bug 514046, for which this code is future-proof. Remove
// this comment when that bug is fixed (so, FIXME: 514046).
#ifdef DEBUG #ifdef DEBUG
JSBool rv = JSBool rv =
#endif #endif
js_GetPropertyHelper(cx, obj, sprop->id, JS_FALSE, &nr.v); js_GetPropertyHelper(cx, obj, sprop->id,
(op == JSOP_CALLNAME)
? JSGET_NO_METHOD_BARRIER
: JSGET_METHOD_BARRIER,
&nr.v);
JS_ASSERT(rv); JS_ASSERT(rv);
obj2->dropProperty(cx, prop); obj->dropProperty(cx, prop);
} }
LIns* obj_ins; LIns* obj_ins;
@ -8081,7 +8091,8 @@ JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, jsuword& pcval) TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, jsuword& pcval)
{ {
jsbytecode* pc = cx->fp->regs->pc; jsbytecode* pc = cx->fp->regs->pc;
JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_SETNAME && *pc != JSOP_SETPROP); JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_INITMETHOD &&
*pc != JSOP_SETNAME && *pc != JSOP_SETPROP && *pc != JSOP_SETMETHOD);
// Mimic the interpreter's special case for dense arrays by skipping up one // Mimic the interpreter's special case for dense arrays by skipping up one
// hop along the proto chain when accessing a named (not indexed) property, // hop along the proto chain when accessing a named (not indexed) property,
@ -8297,20 +8308,6 @@ TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins)
return stobj_get_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins); return stobj_get_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins);
} }
JSRecordingStatus
TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop,
LIns*& dslots_ins, LIns*& v_ins)
{
if (!SPROP_HAS_STUB_GETTER(sprop))
return JSRS_STOP;
if (sprop->slot != SPROP_INVALID_SLOT)
v_ins = stobj_get_slot(pobj_ins, sprop->slot, dslots_ins);
else
v_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID));
return JSRS_CONTINUE;
}
JS_REQUIRES_STACK LIns* JS_REQUIRES_STACK LIns*
TraceRecorder::box_jsval(jsval v, LIns* v_ins) TraceRecorder::box_jsval(jsval v, LIns* v_ins)
{ {
@ -9225,7 +9222,7 @@ TraceRecorder::emitNativePropertyOp(JSScope* scope, JSScopeProperty* sprop, LIns
bool setflag, LIns* boxed_ins) bool setflag, LIns* boxed_ins)
{ {
JS_ASSERT(!(sprop->attrs & (setflag ? JSPROP_SETTER : JSPROP_GETTER))); JS_ASSERT(!(sprop->attrs & (setflag ? JSPROP_SETTER : JSPROP_GETTER)));
JS_ASSERT(setflag ? !SPROP_HAS_STUB_SETTER(sprop) : !SPROP_HAS_STUB_GETTER(sprop)); JS_ASSERT(setflag ? !SPROP_HAS_STUB_SETTER(sprop) : !SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop));
enterDeepBailCall(); enterDeepBailCall();
@ -9454,8 +9451,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
switch (argc) { switch (argc) {
case 1: case 1:
if (isNumber(vp[2]) && if (isNumber(vp[2]) &&
(native == js_math_ceil || native == js_math_floor || native == js_math_round)) { (native == js_math_ceil || native == js_math_floor || native == js_math_round)) {
LIns* a = get(&vp[2]); LIns* a = get(&vp[2]);
if (isPromote(a)) { if (isPromote(a)) {
set(&vp[0], a); set(&vp[0], a);
@ -9464,9 +9461,10 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
} }
} }
break; break;
case 2: case 2:
if (isNumber(vp[2]) && isNumber(vp[3]) && if (isNumber(vp[2]) && isNumber(vp[3]) &&
(native == js_math_min || native == js_math_max)) { (native == js_math_min || native == js_math_max)) {
LIns* a = get(&vp[2]); LIns* a = get(&vp[2]);
LIns* b = get(&vp[3]); LIns* b = get(&vp[3]);
if (isPromote(a) && isPromote(b)) { if (isPromote(a) && isPromote(b)) {
@ -9939,6 +9937,16 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
return JSRS_CONTINUE; return JSRS_CONTINUE;
} }
static JSBool FASTCALL
MethodWriteBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj)
{
JSAutoTempValueRooter tvr(cx, funobj);
return OBJ_SCOPE(obj)->methodWriteBarrier(cx, sprop, tvr.value());
}
JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
0, 0)
JS_REQUIRES_STACK JSRecordingStatus JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop, TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop,
jsval &v, LIns*& v_ins) jsval &v, LIns*& v_ins)
@ -9965,21 +9973,26 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->has(sprop)); JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->has(sprop));
// Fast path for CallClass. This is about 20% faster than the general case. // Fast path for CallClass. This is about 20% faster than the general case.
if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) { v_ins = get(&v);
v_ins = get(&v); if (OBJ_GET_CLASS(cx, obj) == &js_CallClass)
return setCallProp(obj, obj_ins, sprop, v_ins, v); return setCallProp(obj, obj_ins, sprop, v_ins, v);
}
/* /*
* Setting a function-valued property might need to rebrand the object; we * Setting a function-valued property might need to rebrand the object, so
* don't trace that case. There's no need to guard on that, though, because * we emit a call to the method write barrier. There's no need to guard on
* separating functions into the trace-time type TT_FUNCTION will save the * this, because functions have distinct trace-type from other values and
* day! * branded-ness is implied by the shape, which we've already guarded on.
*/ */
if (scope->branded() && VALUE_IS_FUNCTION(cx, v)) if (scope->branded() && VALUE_IS_FUNCTION(cx, v) && entry->directHit()) {
ABORT_TRACE("can't trace function-valued property set in branded scope"); if (obj == globalObj)
ABORT_TRACE("can't trace function-valued property set in branded global scope");
// Find obj2. If entry->adding(), the TAG bits are all 0. LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
}
// Find obj2. If entry->adding(), the TAG bits are all 0.
JSObject* obj2 = obj; JSObject* obj2 = obj;
for (jsuword i = PCVCAP_TAG(entry->vcap) >> PCVCAP_PROTOBITS; i; i--) for (jsuword i = PCVCAP_TAG(entry->vcap) >> PCVCAP_PROTOBITS; i; i--)
obj2 = OBJ_GET_PARENT(cx, obj2); obj2 = OBJ_GET_PARENT(cx, obj2);
@ -10008,7 +10021,6 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
guard(false, lir->ins_eq0(ok_ins), OOM_EXIT); guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
} }
v_ins = get(&v);
return nativeSet(obj, obj_ins, sprop, v, v_ins); return nativeSet(obj, obj_ins, sprop, v, v_ins);
} }
@ -10062,8 +10074,16 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop
CHECK_STATUS(setProp(l, entry, sprop, r, v_ins)); CHECK_STATUS(setProp(l, entry, sprop, r, v_ins));
jsbytecode* pc = cx->fp->regs->pc; jsbytecode* pc = cx->fp->regs->pc;
if (*pc != JSOP_INITPROP && pc[JSOP_SETPROP_LENGTH] != JSOP_POP) switch (*pc) {
set(&l, v_ins); case JSOP_SETPROP:
case JSOP_SETNAME:
case JSOP_SETMETHOD:
if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
set(&l, v_ins);
break;
default:;
}
return JSRS_CONTINUE; return JSRS_CONTINUE;
} }
@ -10133,7 +10153,7 @@ GetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, jsval* vp)
jsid id; jsid id;
if (!RootedStringToId(cx, namep, &id) || !obj->getProperty(cx, id, vp)) { if (!RootedStringToId(cx, namep, &id) || !obj->getProperty(cx, id, vp)) {
js_SetBuiltinError(cx); js_SetBuiltinError(cx);
return JS_FALSE; return false;
} }
return cx->interpState->builtinStatus == 0; return cx->interpState->builtinStatus == 0;
} }
@ -10271,8 +10291,9 @@ GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop
obj->dropProperty(cx, prop); obj->dropProperty(cx, prop);
#endif #endif
// js_GetSprop contains a special case for With objects. We can elide it // JSScopeProperty::get contains a special case for With objects. We can
// here because With objects are, we claim, never on the operand stack. // elide it here because With objects are, we claim, never on the operand
// stack while recording.
JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_WithClass); JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_WithClass);
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
@ -10290,7 +10311,7 @@ TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, JSScopeProperty* sprop
{ {
JS_ASSERT(!(sprop->attrs & JSPROP_GETTER)); JS_ASSERT(!(sprop->attrs & JSPROP_GETTER));
JS_ASSERT(sprop->slot == SPROP_INVALID_SLOT); JS_ASSERT(sprop->slot == SPROP_INVALID_SLOT);
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop)); JS_ASSERT(!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop));
// Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp. // Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp.
// FIXME - We should call the getter directly. Using a builtin function for // FIXME - We should call the getter directly. Using a builtin function for
@ -11156,6 +11177,19 @@ TraceRecorder::name(jsval*& vp, LIns*& ins, NameResult& nr)
return JSRS_CONTINUE; return JSRS_CONTINUE;
} }
static JSObject* FASTCALL
MethodReadBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj)
{
JSAutoTempValueRooter tvr(cx, funobj);
if (!OBJ_SCOPE(obj)->methodReadBarrier(cx, sprop, tvr.addr()))
return NULL;
JS_ASSERT(VALUE_IS_FUNCTION(cx, tvr.value()));
return JSVAL_TO_OBJECT(tvr.value());
}
JS_DEFINE_CALLINFO_4(static, OBJECT_FAIL, MethodReadBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
0, 0)
/* /*
* Get a property. The current opcode has JOF_ATOM. * Get a property. The current opcode has JOF_ATOM.
* *
@ -11227,15 +11261,19 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
uint32 setflags = (cs.format & (JOF_INCDEC | JOF_FOR)); uint32 setflags = (cs.format & (JOF_INCDEC | JOF_FOR));
JS_ASSERT(!(cs.format & JOF_SET)); JS_ASSERT(!(cs.format & JOF_SET));
JSScopeProperty* sprop;
uint32 slot; uint32 slot;
bool isMethod;
if (PCVAL_IS_SPROP(pcval)) { if (PCVAL_IS_SPROP(pcval)) {
JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval); sprop = PCVAL_TO_SPROP(pcval);
JS_ASSERT(OBJ_SCOPE(obj2)->has(sprop));
if (setflags && !SPROP_HAS_STUB_SETTER(sprop)) if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
ABORT_TRACE("non-stub setter"); ABORT_TRACE("non-stub setter");
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 (!SPROP_HAS_STUB_GETTER(sprop)) { if (!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)) {
if (slotp) if (slotp)
ABORT_TRACE("can't trace non-stub getter for this opcode"); ABORT_TRACE("can't trace non-stub getter for this opcode");
if (sprop->attrs & JSPROP_GETTER) if (sprop->attrs & JSPROP_GETTER)
@ -11247,13 +11285,17 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)))
ABORT_TRACE("no valid slot"); ABORT_TRACE("no valid slot");
slot = sprop->slot; slot = sprop->slot;
isMethod = sprop->isMethod();
JS_ASSERT_IF(isMethod, OBJ_SCOPE(obj2)->hasMethodBarrier());
} else { } else {
if (!PCVAL_IS_SLOT(pcval)) if (!PCVAL_IS_SLOT(pcval))
ABORT_TRACE("PCE is not a slot"); ABORT_TRACE("PCE is not a slot");
slot = PCVAL_TO_SLOT(pcval); slot = PCVAL_TO_SLOT(pcval);
sprop = NULL;
isMethod = false;
} }
/* We have a slot. */ /* We have a slot. Check whether it is direct or in a prototype. */
if (obj2 != obj) { if (obj2 != obj) {
if (setflags) if (setflags)
ABORT_TRACE("JOF_INCDEC|JOF_FOR opcode hit prototype chain"); ABORT_TRACE("JOF_INCDEC|JOF_FOR opcode hit prototype chain");
@ -11263,10 +11305,10 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
* proto slot loads, updating obj as we go, leaving obj set to obj2 with * proto slot loads, updating obj as we go, leaving obj set to obj2 with
* obj_ins the last proto-load. * obj_ins the last proto-load.
*/ */
while (obj != obj2) { do {
obj_ins = stobj_get_proto(obj_ins); obj_ins = stobj_get_proto(obj_ins);
obj = STOBJ_GET_PROTO(obj); obj = STOBJ_GET_PROTO(obj);
} } while (obj != obj2);
} }
LIns* dslots_ins = NULL; LIns* dslots_ins = NULL;
@ -11274,6 +11316,21 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
stobj_get_slot(obj_ins, slot, dslots_ins), stobj_get_slot(obj_ins, slot, dslots_ins),
snapshot(BRANCH_EXIT)); snapshot(BRANCH_EXIT));
/*
* Joined function object stored as a method must be cloned when extracted
* as a property value other than a callee. Note that shapes cover method
* value as well as other property attributes and order, so this condition
* is trace-invariant.
*
* We do not impose the method read barrier if in an imacro, assuming any
* property gets it does (e.g., for 'toString' from JSOP_NEW) will not be
* leaked to the calling script.
*/
if (isMethod && !cx->fp->imacpc) {
LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
v_ins = lir->insCall(&MethodReadBarrier_ci, args);
}
if (slotp) { if (slotp) {
*slotp = slot; *slotp = slot;
*v_insp = v_ins; *v_insp = v_ins;
@ -11590,7 +11647,7 @@ JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::record_JSOP_NEWINIT() TraceRecorder::record_JSOP_NEWINIT()
{ {
JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc)); JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc));
LIns *proto_ins; LIns* proto_ins;
CHECK_STATUS(getClassPrototype(key, proto_ins)); CHECK_STATUS(getClassPrototype(key, proto_ins));
LIns* args[] = { proto_ins, cx_ins }; LIns* args[] = { proto_ins, cx_ins };
@ -13272,6 +13329,18 @@ 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
/* /*
* Print information about entry typemaps and unstable exits for all peers * Print information about entry typemaps and unstable exits for all peers

View File

@ -762,7 +762,7 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const; JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
JS_REQUIRES_STACK JSRecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins); JS_REQUIRES_STACK JSRecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins);
JS_REQUIRES_STACK JSRecordingStatus scopeChainProp(JSObject* obj, jsval*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK JSRecordingStatus scopeChainProp(JSObject* obj, jsval*& vp, nanojit::LIns*& ins, NameResult& nr);
JS_REQUIRES_STACK JSRecordingStatus callProp(JSObject* obj, JSObject* obj2, JSProperty* sprop, jsid id, jsval*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK JSRecordingStatus callProp(JSObject* obj, JSProperty* sprop, jsid id, jsval*& vp, nanojit::LIns*& ins, NameResult& nr);
JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n); JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i); JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i);
@ -852,10 +852,6 @@ class TraceRecorder : public avmplus::GCObject {
return stobj_get_fslot(obj_ins, JSSLOT_PARENT); return stobj_get_fslot(obj_ins, JSSLOT_PARENT);
} }
JSRecordingStatus native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins,
JSScopeProperty* sprop, nanojit::LIns*& dslots_ins,
nanojit::LIns*& v_ins);
nanojit::LIns* getStringLength(nanojit::LIns* str_ins); nanojit::LIns* getStringLength(nanojit::LIns* str_ins);
JS_REQUIRES_STACK JSRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK JSRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, NameResult& nr);

View File

@ -456,6 +456,14 @@ 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 - 52) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 53)
/* /*
* Library-private functions. * Library-private functions.

View File

@ -0,0 +1,9 @@
for (var i = 0; i < 9; i++)
x = {a: function() {}, b: function() {}};
checkStats({
recorderStarted: 1,
recorderAborted: 0,
traceCompleted: 1,
sideExitIntoInterpreter: 1
});

View File

@ -0,0 +1,12 @@
for (var i = 0; i < 9; i++) {
var x = {f: function() {}};
x.f++;
assertEq(""+x.f, "NaN");
}
checkStats({
recorderStarted: 1,
recorderAborted: 1,
traceCompleted: 0,
sideExitIntoInterpreter: 0
});

View File

@ -0,0 +1,13 @@
function C() {
this.a = function() {};
this.b = function() {};
}
for (var i = 0; i < 9; i++)
new C;
checkStats({
recorderStarted: 1,
recorderAborted: 0,
traceCompleted: 1,
sideExitIntoInterpreter: 1
});