Bug 739808: Remove method cloning optimization and method barrier, r=luke

--HG--
extra : rebase_source : d383c3704c3efd08f9fbf1324433a94fc4a8dbb8
This commit is contained in:
David Mandelin 2012-03-23 17:59:56 -07:00
parent 15a66966a1
commit 5287c89131
44 changed files with 127 additions and 1154 deletions

View File

@ -4565,43 +4565,6 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return PopStatementBCE(cx, bce);
}
static bool
SetMethodFunction(JSContext *cx, FunctionBox *funbox, JSAtom *atom)
{
RootedVarObject parent(cx);
parent = funbox->function()->getParent();
/*
* Replace a boxed function with a new one with a method atom. Methods
* require a function with the extended size finalize kind, which normal
* functions don't have. We don't eagerly allocate functions with the
* expanded size for boxed functions, as most functions are not methods.
*/
JSFunction *fun = js_NewFunction(cx, NULL, NULL,
funbox->function()->nargs,
funbox->function()->flags,
parent,
funbox->function()->atom,
JSFunction::ExtendedFinalizeKind);
if (!fun)
return false;
JSScript *script = funbox->function()->script();
if (script) {
fun->setScript(script);
if (!script->typeSetFunction(cx, fun))
return false;
}
JS_ASSERT(funbox->function()->joinable());
fun->setJoinable();
fun->setMethodAtom(atom);
funbox->object = fun;
return true;
}
static bool
EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
{
@ -5371,23 +5334,6 @@ EmitStatement(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
#endif
if (op != JSOP_NOP) {
/*
* Specialize JSOP_SETPROP to JSOP_SETMETHOD to defer or
* avoid null closure cloning. Do this only for assignment
* statements that are not completion values wanted by a
* script evaluator, to ensure that the joined function
* can't escape directly.
*/
if (!wantval &&
pn2->isKind(PNK_ASSIGN) &&
pn2->pn_left->isOp(JSOP_SETPROP) &&
pn2->pn_right->isOp(JSOP_LAMBDA) &&
pn2->pn_right->pn_funbox->joinable())
{
if (!SetMethodFunction(cx, pn2->pn_right->pn_funbox, pn2->pn_left->pn_atom))
return false;
pn2->pn_left->setOp(JSOP_SETMETHOD);
}
if (!EmitTree(cx, bce, pn2))
return false;
if (Emit1(cx, bce, op) < 0)
@ -5894,28 +5840,13 @@ EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!bce->makeAtomIndex(pn3->pn_atom, &index))
return false;
/* Check whether we can optimize to JSOP_INITMETHOD. */
ParseNode *init = pn2->pn_right;
bool lambda = init->isOp(JSOP_LAMBDA);
if (lambda)
++methodInits;
if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) {
/*
* Disable NEWOBJECT on initializers that set __proto__, which has
* a non-standard setter on objects.
*/
if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
obj = NULL;
op = JSOP_INITMETHOD;
if (!SetMethodFunction(cx, init->pn_funbox, pn3->pn_atom))
return JS_FALSE;
pn2->setOp(op);
} else {
/*
* Disable NEWOBJECT on initializers that set __proto__, which has
* a non-standard setter on objects.
*/
if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
obj = NULL;
op = JSOP_INITPROP;
if (lambda)
++slowMethodInits;
}
op = JSOP_INITPROP;
if (obj) {
JS_ASSERT(!obj->inDictionaryMode());

View File

@ -108,15 +108,6 @@ ParseNode::clear()
pn_parens = false;
}
bool
FunctionBox::joinable() const
{
return function()->isNullClosure() &&
(tcflags & (TCF_FUN_USES_ARGUMENTS |
TCF_FUN_USES_OWN_NAME |
TCF_COMPILE_N_GO)) == TCF_COMPILE_N_GO;
}
bool
FunctionBox::inAnyDynamicScope() const
{

View File

@ -1554,8 +1554,6 @@ struct FunctionBox : public ObjectBox
JSFunction *function() const { return (JSFunction *) object; }
bool joinable() const;
/*
* True if this function is inside the scope of a with-statement, an E4X
* filter-expression, or a function that uses direct eval.

View File

@ -1069,8 +1069,6 @@ LeaveFunction(ParseNode *fn, TreeContext *funtc, PropertyName *funName = NULL,
dn->setOp(JSOP_CALLEE);
dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
dn->pn_dflags |= PND_BOUND;
funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
foundCallee = 1;
continue;
}
@ -2049,7 +2047,7 @@ DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name)
globalObj != holder ||
shape->configurable() ||
!shape->hasSlot() ||
!shape->hasDefaultGetterOrIsMethod() ||
!shape->hasDefaultGetter() ||
!shape->hasDefaultSetter()) {
return true;
}

View File

@ -217,9 +217,6 @@ SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
FlagHeavyweights(lexdep, funbox, tcflags);
}
}
if (funbox->joinable())
fun->setJoinable();
}
}

View File

@ -1,51 +0,0 @@
var shapes = {};
function stringify(a) {
assertEq(shapes[shapeOf(a)], undefined);
shapes[shapeOf(a)] = 1;
var b = "";
for (var c in a) {
b += c + ":";
if (typeof a[c] == "function")
b += "function,";
else
b += a[c] + ",";
}
return b;
}
function test1() {
return stringify({a: 0, b: 1, a: function() {} });
}
for (var i = 0; i < 3; i++)
assertEq(test1(), "a:function,b:1,");
// This does not cause the object to go to dictionary mode, unlike the above.
function test2() {
return stringify({a: 0, b: 1, a: 2, b: 3});
}
assertEq(test2(), "a:2,b:3,");
function test3() {
return stringify({
aa:0,ab:1,ac:2,ad:3,ae:4,af:5,ag:6,ah:7,ai:8,aj:9,
ba:0,bb:1,bc:2,bd:3,be:4,bf:5,bg:6,bh:7,bi:8,bj:9,
ca:0,cb:1,cc:2,cd:3,ce:4,cf:5,cg:6,ch:7,ci:8,cj:9,
da:0,db:1,dc:2,dd:3,de:4,df:5,dg:6,dh:7,di:8,dj:9,
ea:0,eb:1,ec:2,ed:3,ee:4,ef:5,eg:6,eh:7,ei:8,ej:9,
fa:0,fb:1,fc:2,fd:3,fe:4,ff:5,fg:6,fh:7,fi:8,fj:9,
ga:0,gb:1,gc:2,gd:3,ge:4,gf:5,gg:6,gh:7,gi:8,gj:9,
ha:0,hb:1,hc:2,hd:3,he:4,hf:5,hg:6,hh:7,hi:8,hj:9,
ia:0,ib:1,ic:2,id:3,ie:4,if:5,ig:6,ih:7,ii:8,ij:9,
ja:0,jb:1,jc:2,jd:3,je:4,jf:5,jg:6,jh:7,ji:8,jj:9,
ka:0,kb:1,kc:2,kd:3,ke:4,kf:5,kg:6,kh:7,ki:8,kj:9,
la:0,lb:1,lc:2,ld:3,le:4,lf:5,lg:6,lh:7,li:8,lj:9,
ma:0,mb:1,mc:2,md:3,me:4,mf:5,mg:6,mh:7,mi:8,mj:9,
na:0,nb:1,nc:2,nd:3,ne:4,nf:5,ng:6,nh:7,ni:8,nj:9,
oa:0,ob:1,oc:2,od:3,oe:4,of:5,og:6,oh:7,oi:8,oj:9,
pa:0,pb:1,pc:2,pd:3,pe:4,pf:5,pg:6,ph:7,pi:8,pj:9,
});
}
for (var i = 0; i < 10; i++)
assertEq(test3(), "aa:0,ab:1,ac:2,ad:3,ae:4,af:5,ag:6,ah:7,ai:8,aj:9,ba:0,bb:1,bc:2,bd:3,be:4,bf:5,bg:6,bh:7,bi:8,bj:9,ca:0,cb:1,cc:2,cd:3,ce:4,cf:5,cg:6,ch:7,ci:8,cj:9,da:0,db:1,dc:2,dd:3,de:4,df:5,dg:6,dh:7,di:8,dj:9,ea:0,eb:1,ec:2,ed:3,ee:4,ef:5,eg:6,eh:7,ei:8,ej:9,fa:0,fb:1,fc:2,fd:3,fe:4,ff:5,fg:6,fh:7,fi:8,fj:9,ga:0,gb:1,gc:2,gd:3,ge:4,gf:5,gg:6,gh:7,gi:8,gj:9,ha:0,hb:1,hc:2,hd:3,he:4,hf:5,hg:6,hh:7,hi:8,hj:9,ia:0,ib:1,ic:2,id:3,ie:4,if:5,ig:6,ih:7,ii:8,ij:9,ja:0,jb:1,jc:2,jd:3,je:4,jf:5,jg:6,jh:7,ji:8,jj:9,ka:0,kb:1,kc:2,kd:3,ke:4,kf:5,kg:6,kh:7,ki:8,kj:9,la:0,lb:1,lc:2,ld:3,le:4,lf:5,lg:6,lh:7,li:8,lj:9,ma:0,mb:1,mc:2,md:3,me:4,mf:5,mg:6,mh:7,mi:8,mj:9,na:0,nb:1,nc:2,nd:3,ne:4,nf:5,ng:6,nh:7,ni:8,nj:9,oa:0,ob:1,oc:2,od:3,oe:4,of:5,og:6,oh:7,oi:8,oj:9,pa:0,pb:1,pc:2,pd:3,pe:4,pf:5,pg:6,ph:7,pi:8,pj:9,");

View File

@ -617,11 +617,9 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_NEWARRAY:
case JSOP_NEWOBJECT:
case JSOP_ENDINIT:
case JSOP_INITMETHOD:
case JSOP_INITPROP:
case JSOP_INITELEM:
case JSOP_SETPROP:
case JSOP_SETMETHOD:
case JSOP_IN:
case JSOP_INSTANCEOF:
case JSOP_LINENO:
@ -1522,7 +1520,6 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
break;
case JSOP_INITPROP:
case JSOP_INITMETHOD:
stack[stackDepth - 1].v = code->poppedValues[1];
break;

View File

@ -28,7 +28,6 @@ BEGIN_TEST(testLookup_bug522590)
JSObject *funobj = JSVAL_TO_OBJECT(r);
CHECK(funobj->isFunction());
CHECK(!js::IsInternalFunctionObject(funobj));
CHECK(funobj->toFunction()->isClonedMethod());
return true;
}

View File

@ -3455,11 +3455,6 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id,
if (obj2->isNative()) {
Shape *shape = (Shape *) prop;
if (shape->isMethod()) {
vp->setObject(*obj2->nativeGetMethod(shape));
return !!obj2->methodReadBarrier(cx, *shape, vp);
}
/* Peek at the native property's slot value, without doing a Get. */
if (shape->hasSlot()) {
*vp = obj2->nativeGetSlot(shape->slot());
@ -3855,19 +3850,12 @@ GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
if (obj2->isNative()) {
Shape *shape = (Shape *) prop;
desc->attrs = shape->attributes();
if (shape->isMethod()) {
desc->getter = JS_PropertyStub;
desc->setter = JS_StrictPropertyStub;
desc->value.setObject(*obj2->nativeGetMethod(shape));
} else {
desc->getter = shape->getter();
desc->setter = shape->setter();
if (shape->hasSlot())
desc->value = obj2->nativeGetSlot(shape->slot());
else
desc->value.setUndefined();
}
desc->getter = shape->getter();
desc->setter = shape->setter();
if (shape->hasSlot())
desc->value = obj2->nativeGetSlot(shape->slot());
else
desc->value.setUndefined();
} else {
if (obj2->isProxy()) {
JSAutoResolveFlags rf(cx, flags);
@ -3968,8 +3956,9 @@ SetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, unsigned attrs,
*foundp = false;
return true;
}
Shape *shape = (Shape *) prop;
JSBool ok = obj->isNative()
? js_SetNativeAttributes(cx, obj, (Shape *) prop, attrs)
? obj->changePropertyAttributes(cx, shape, attrs)
: obj->setGenericAttributes(cx, id, &attrs);
if (ok)
*foundp = true;
@ -4072,7 +4061,7 @@ JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *
AssertNoGC(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id);
if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp))
if (!js_GetMethod(cx, obj, id, 0, vp))
return JS_FALSE;
if (objp)
*objp = obj;
@ -5366,7 +5355,7 @@ JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, unsigned arg
Value v;
JSAtom *atom = js_Atomize(cx, name, strlen(name));
return atom &&
js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, &v) &&
js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &v) &&
Invoke(cx, ObjectOrNullValue(obj), v, argc, argv, rval);
}

View File

@ -644,17 +644,6 @@ JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
return Valueify(fp)->maybeCalleev().toObjectOrNull();
}
JS_PUBLIC_API(JSBool)
JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
{
Value v;
if (!Valueify(fp)->getValidCalleeObject(cx, &v))
return false;
*vp = v.isObject() ? v : JSVAL_VOID;
return true;
}
JS_PUBLIC_API(JSBool)
JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
{

View File

@ -292,39 +292,10 @@ JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval);
* Return fp's callee function object (fp->callee) if it has one. Note that
* this API cannot fail. A null return means "no callee": fp is a global or
* eval-from-global frame, not a call frame.
*
* This API began life as an infallible getter, but now it can return either:
*
* 1. An optimized closure that was compiled assuming the function could not
* escape and be called from sites the compiler could not see.
*
* 2. A "joined function object", an optimization whereby SpiderMonkey avoids
* creating fresh function objects for every evaluation of a function
* expression that is used only once by a consumer that either promises to
* clone later when asked for the value or that cannot leak the value.
*
* Because Mozilla's Gecko embedding of SpiderMonkey (and no doubt other
* embeddings) calls this API in potentially performance-sensitive ways (e.g.
* in nsContentUtils::GetDocumentFromCaller), we are leaving this API alone. It
* may now return an unwrapped non-escaping optimized closure, or a joined
* function object. Such optimized objects may work well if called from the
* correct context, never mutated or compared for identity, etc.
*
* However, if you really need to get the same callee object that JS code would
* see, which means undoing the optimizations, where an undo attempt can fail,
* then use JS_GetValidFrameCalleeObject.
*/
extern JS_PUBLIC_API(JSObject *)
JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp);
/**
* Return fp's callee function object after running the deferred closure
* cloning "method read barrier". This API can fail! If the frame has no
* callee, this API returns true with JSVAL_IS_VOID(*vp).
*/
extern JS_PUBLIC_API(JSBool)
JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp);
/************************************************************************/
extern JS_PUBLIC_API(const char *)

View File

@ -97,107 +97,6 @@ using namespace js;
using namespace js::gc;
using namespace js::types;
bool
StackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
{
if (!isFunctionFrame()) {
vp->setNull();
return true;
}
JSFunction *fun = this->callee().toFunction();
vp->setObject(*fun);
/*
* Check for an escape attempt by a joined function object, which must go
* through the frame's |this| object's method read barrier for the method
* atom by which it was uniquely associated with a property.
*/
const Value &thisv = functionThis();
if (thisv.isObject() && fun->methodAtom() && !fun->isClonedMethod()) {
JSObject *thisp = &thisv.toObject();
JSObject *first_barriered_thisp = NULL;
do {
/*
* While a non-native object is responsible for handling its
* entire prototype chain, notable non-natives including dense
* and typed arrays have native prototypes, so keep going.
*/
if (!thisp->isNative())
continue;
const Shape *shape = thisp->nativeLookup(cx, ATOM_TO_JSID(fun->methodAtom()));
if (shape) {
/*
* Two cases follow: the method barrier was not crossed
* yet, so we cross it here; the method barrier *was*
* crossed but after the call, in which case we fetch
* and validate the cloned (unjoined) funobj from the
* method property's slot.
*
* In either case we must allow for the method property
* to have been replaced, or its value overwritten.
*/
if (shape->isMethod() && thisp->nativeGetMethod(shape) == fun) {
if (!thisp->methodReadBarrier(cx, *shape, vp))
return false;
overwriteCallee(vp->toObject());
return true;
}
if (shape->hasSlot()) {
Value v = thisp->getSlot(shape->slot());
JSFunction *clone;
if (IsFunctionObject(v, &clone) &&
clone->isInterpreted() &&
clone->script() == fun->script() &&
clone->methodObj() == thisp) {
/*
* N.B. If the method barrier was on a function
* with singleton type, then while crossing the
* method barrier CloneFunctionObject will have
* ignored the attempt to clone the function.
*/
JS_ASSERT_IF(!clone->hasSingletonType(), clone != fun);
*vp = v;
overwriteCallee(*clone);
return true;
}
}
}
if (!first_barriered_thisp)
first_barriered_thisp = thisp;
} while ((thisp = thisp->getProto()) != NULL);
if (!first_barriered_thisp)
return true;
/*
* At this point, we couldn't find an already-existing clone (or
* force to exist a fresh clone) created via thisp's method read
* barrier, so we must clone fun and store it in fp's callee to
* avoid re-cloning upon repeated foo.caller access.
*
* This must mean the code in js_DeleteGeneric could not find this
* stack frame on the stack when the method was deleted. We've lost
* track of the method, so we associate it with the first barriered
* object found starting from thisp on the prototype chain.
*/
JSFunction *newfunobj = CloneFunctionObject(cx, fun);
if (!newfunobj)
return false;
newfunobj->setMethodObj(*first_barriered_thisp);
overwriteCallee(*newfunobj);
vp->setObject(*newfunobj);
return true;
}
return true;
}
static JSBool
fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
@ -227,10 +126,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
for (; fp; fp = fp->prev()) {
if (!fp->isFunctionFrame() || fp->isEvalFrame())
continue;
Value callee;
if (!fp->getValidCalleeObject(cx, &callee))
return false;
if (&callee.toObject() == fun)
if (fp->callee().toFunction() == fun)
break;
}
if (!fp)
@ -277,14 +173,13 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
while (frame && frame->isDummyFrame())
frame = frame->prev();
if (frame && !frame->getValidCalleeObject(cx, vp))
return false;
if (!vp->isObject()) {
if (!frame || !frame->isFunctionFrame()) {
JS_ASSERT(vp->isNull());
return true;
}
vp->setObject(frame->callee());
/* Censor the caller if it is from another compartment. */
JSObject &caller = vp->toObject();
if (caller.compartment() != cx->compartment) {
@ -1263,7 +1158,6 @@ LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
JS_ASSERT(!shape->configurable());
JS_ASSERT(shape->isDataDescriptor());
JS_ASSERT(shape->hasSlot());
JS_ASSERT(!shape->isMethod());
return shape;
}

View File

@ -67,10 +67,6 @@
* move to u.i.script->flags. For now we use function flag bits to minimize
* pointer-chasing.
*/
#define JSFUN_JOINABLE 0x0001 /* function is null closure that does not
appear to call itself via its own name
or arguments.callee */
#define JSFUN_PROTOTYPE 0x0800 /* function is Function.prototype for some
global object */
@ -129,14 +125,6 @@ struct JSFunction : public JSObject
#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~uintptr_t(1)))
#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & uintptr_t(1))) != 0)
bool mightEscape() const {
return isInterpreted() && isNullClosure();
}
bool joinable() const {
return flags & JSFUN_JOINABLE;
}
/*
* For an interpreted function, accessors for the initial scope object of
* activations (stack frames) of the function.
@ -147,8 +135,6 @@ struct JSFunction : public JSObject
static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
inline void setJoinable();
js::HeapPtrScript &script() const {
JS_ASSERT(isInterpreted());
return *(js::HeapPtrScript *)&u.i.script_;
@ -218,33 +204,10 @@ struct JSFunction : public JSObject
public:
/* Accessors for data stored in extended functions. */
inline void initializeExtended();
inline void setExtendedSlot(size_t which, const js::Value &val);
inline const js::Value &getExtendedSlot(size_t which) const;
/* Slot holding associated method property, needed for foo.caller handling. */
static const uint32_t METHOD_PROPERTY_SLOT = 0;
/* For cloned methods, slot holding the object this was cloned as a property from. */
static const uint32_t METHOD_OBJECT_SLOT = 1;
/* Whether this is a function cloned from a method. */
inline bool isClonedMethod() const;
/* For a cloned method, pointer to the object the method was cloned for. */
inline JSObject *methodObj() const;
inline void setMethodObj(JSObject& obj);
/*
* Method name imputed from property uniquely assigned to or initialized,
* where the function does not need to be cloned to carry a scope chain.
* This is set on both the original and cloned function.
*/
inline JSAtom *methodAtom() const;
inline void setMethodAtom(JSAtom *atom);
private:
/*
* These member functions are inherited from JSObject, but should never be applied to

View File

@ -84,48 +84,6 @@ JSFunction::initializeExtended()
toExtended()->extendedSlots[1].init(js::UndefinedValue());
}
inline void
JSFunction::setJoinable()
{
JS_ASSERT(isInterpreted());
flags |= JSFUN_JOINABLE;
}
inline bool
JSFunction::isClonedMethod() const
{
return joinable() && isExtended() && getExtendedSlot(METHOD_OBJECT_SLOT).isObject();
}
inline JSAtom *
JSFunction::methodAtom() const
{
return (joinable() && isExtended() && getExtendedSlot(METHOD_PROPERTY_SLOT).isString())
? (JSAtom *) getExtendedSlot(METHOD_PROPERTY_SLOT).toString()
: NULL;
}
inline void
JSFunction::setMethodAtom(JSAtom *atom)
{
JS_ASSERT(joinable());
setExtendedSlot(METHOD_PROPERTY_SLOT, js::StringValue(atom));
}
inline JSObject *
JSFunction::methodObj() const
{
JS_ASSERT(joinable());
return isClonedMethod() ? &getExtendedSlot(METHOD_OBJECT_SLOT).toObject() : NULL;
}
inline void
JSFunction::setMethodObj(JSObject& obj)
{
JS_ASSERT(joinable());
setExtendedSlot(METHOD_OBJECT_SLOT, js::ObjectValue(obj));
}
inline void
JSFunction::setExtendedSlot(size_t which, const js::Value &val)
{
@ -288,10 +246,10 @@ inline JSFunction *
CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent)
{
/*
* For attempts to clone functions at a function definition opcode or from
* a method barrier, don't perform the clone if the function has singleton
* type. This was called pessimistically, and we need to preserve the
* type's property that if it is singleton there is only a single object
* For attempts to clone functions at a function definition opcode,
* don't perform the clone if the function has singleton type. This
* was called pessimistically, and we need to preserve the type's
* property that if it is singleton there is only a single object
* with its type in existence.
*/
if (fun->hasSingletonType()) {

View File

@ -800,7 +800,7 @@ static inline const Shape *
GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
{
const Shape *shape = obj->nativeLookup(cx, id);
if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot())
if (shape && shape->hasDefaultGetter() && shape->hasSlot())
return shape;
return NULL;
}
@ -2739,7 +2739,7 @@ UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *sh
if (shape->hasGetterValue() || shape->hasSetterValue()) {
types->setOwnProperty(cx, true);
types->addType(cx, Type::UnknownType());
} else if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
} else if (shape->hasDefaultGetter() && shape->hasSlot()) {
const Value &value = obj->nativeGetSlot(shape->slot());
/*
@ -3667,8 +3667,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
pushed[0].addType(cx, Type::LazyArgsType());
break;
case JSOP_SETPROP:
case JSOP_SETMETHOD: {
case JSOP_SETPROP: {
jsid id = GetAtomId(cx, script, pc, 0);
poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
@ -3875,8 +3874,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
state.hasHole = true;
break;
case JSOP_INITPROP:
case JSOP_INITMETHOD: {
case JSOP_INITPROP: {
const SSAValue &objv = poppedValue(pc, 1);
jsbytecode *initpc = script->code + objv.pushedOffset();
TypeObject *initializer = GetInitializerType(cx, script, initpc);

View File

@ -369,7 +369,7 @@ js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp)
{
jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
AutoValueRooter tvr(cx);
if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr()))
if (!js_GetMethod(cx, obj, id, 0, tvr.addr()))
return false;
TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc);
@ -1215,11 +1215,9 @@ JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
/*
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
* remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
* remain distinct for the decompiler.
*/
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH);
JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH);
/* See TRY_BRANCH_AFTER_COND. */
JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
@ -1714,6 +1712,8 @@ ADD_EMPTY_CASE(JSOP_UNUSED25)
ADD_EMPTY_CASE(JSOP_UNUSED26)
ADD_EMPTY_CASE(JSOP_UNUSED27)
ADD_EMPTY_CASE(JSOP_UNUSED28)
ADD_EMPTY_CASE(JSOP_UNUSED29)
ADD_EMPTY_CASE(JSOP_UNUSED30)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
#if JS_HAS_XML_SUPPORT
@ -2585,7 +2585,6 @@ END_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_SETGNAME)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETMETHOD)
{
const Value &rval = regs.sp[-1];
const Value &lval = regs.sp[-2];
@ -3159,73 +3158,6 @@ BEGIN_CASE(JSOP_LAMBDA)
JSObject *parent;
if (fun->isNullClosure()) {
parent = &regs.fp()->scopeChain();
if (fun->joinable()) {
jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH;
JSOp op2 = JSOp(*pc2);
/*
* Optimize var obj = {method: function () { ... }, ...},
* this.method = function () { ... }; and other significant
* single-use-of-null-closure bytecode sequences.
*/
if (op2 == JSOP_INITMETHOD) {
#ifdef DEBUG
const Value &lref = regs.sp[-1];
JS_ASSERT(lref.isObject());
JSObject *obj2 = &lref.toObject();
JS_ASSERT(obj2->isObject());
#endif
JS_ASSERT(fun->methodAtom() ==
script->getAtom(GET_UINT32_INDEX(regs.pc + JSOP_LAMBDA_LENGTH)));
break;
}
if (op2 == JSOP_SETMETHOD) {
#ifdef DEBUG
op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]);
JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
#endif
const Value &lref = regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
JS_ASSERT(fun->methodAtom() ==
script->getAtom(GET_UINT32_INDEX(regs.pc + JSOP_LAMBDA_LENGTH)));
break;
}
} else if (op2 == JSOP_CALL) {
/*
* Array.prototype.sort and String.prototype.replace are
* optimized as if they are special form. We know that they
* won't leak the joined function object in obj, therefore
* we don't need to clone that compiler-created function
* object for identity/mutation reasons.
*/
int iargc = GET_ARGC(pc2);
/*
* Note that we have not yet pushed obj as the final argument,
* so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
* is the callee for this JSOP_CALL.
*/
const Value &cref = regs.sp[1 - (iargc + 2)];
JSFunction *fun;
if (IsFunctionObject(cref, &fun)) {
if (Native native = fun->maybeNative()) {
if ((iargc == 1 && native == array_sort) ||
(iargc == 2 && native == str_replace)) {
break;
}
}
}
} else if (op2 == JSOP_NULL) {
pc2 += JSOP_NULL_LENGTH;
op2 = JSOp(*pc2);
if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0)
break;
}
}
} else {
parent = GetScopeChain(cx, regs.fp());
if (!parent)
@ -3405,7 +3337,6 @@ BEGIN_CASE(JSOP_ENDINIT)
END_CASE(JSOP_ENDINIT)
BEGIN_CASE(JSOP_INITPROP)
BEGIN_CASE(JSOP_INITMETHOD)
{
/* Load the property's initial value into rval. */
JS_ASSERT(regs.sp - regs.fp()->base() >= 2);
@ -3419,11 +3350,10 @@ BEGIN_CASE(JSOP_INITMETHOD)
LOAD_ATOM(0, atom);
jsid id = ATOM_TO_JSID(atom);
unsigned defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0;
if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
? !js_SetPropertyHelper(cx, obj, id, 0, &rval, script->strictModeCode)
: !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow)) {
JSPROP_ENUMERATE, 0, 0, 0)) {
goto error;
}

View File

@ -244,17 +244,13 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp
if (!obj)
return false;
unsigned flags = (op == JSOP_CALLPROP)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
if (!name) {
AssertValidPropertyCacheHit(cx, obj, obj2, entry);
if (!NativeGet(cx, obj, obj2, entry->prop, flags, vp))
if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_CACHE_RESULT, vp))
return false;
return true;
}
@ -265,7 +261,7 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp
if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp))
return false;
} else {
if (!GetPropertyHelper(cx, obj, id, flags, vp))
if (!GetPropertyHelper(cx, obj, id, JSGET_CACHE_RESULT, vp))
return false;
}
@ -289,7 +285,6 @@ SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Val
if (!obj)
return false;
JS_ASSERT_IF(*pc == JSOP_SETMETHOD, IsFunctionObject(rval));
JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject());
JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global());
@ -322,7 +317,7 @@ SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Val
}
#endif
if (shape->hasDefaultSetter() && shape->hasSlot() && !shape->isMethod()) {
if (shape->hasDefaultSetter() && shape->hasSlot()) {
/* Fast path for, e.g., plain Object instance properties. */
obj->nativeSetSlotWithType(cx, shape, rval);
} else {
@ -344,13 +339,9 @@ SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Val
jsid id = ATOM_TO_JSID(name);
if (JS_LIKELY(!obj->getOps()->setProperty)) {
unsigned defineHow;
if (op == JSOP_SETMETHOD)
defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
else if (op == JSOP_SETNAME)
defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
else
defineHow = DNP_CACHE_RESULT;
unsigned defineHow = (op == JSOP_SETNAME)
? DNP_CACHE_RESULT | DNP_UNQUALIFIED
: DNP_CACHE_RESULT;
if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict))
return false;
} else {
@ -384,7 +375,7 @@ NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
if (!name) {
AssertValidPropertyCacheHit(cx, obj, obj2, entry);
if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp))
if (!NativeGet(cx, obj, obj2, entry->prop, 0, vp))
return false;
return true;
}
@ -416,7 +407,7 @@ NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
JSObject *normalized = obj;
if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
normalized = &normalized->asWith().object();
if (!NativeGet(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, vp))
if (!NativeGet(cx, normalized, obj2, shape, 0, vp))
return false;
}

View File

@ -459,7 +459,7 @@ GetCustomIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
/* Check whether we have a valid __iterator__ method. */
JSAtom *atom = cx->runtime->atomState.iteratorAtom;
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, vp))
return false;
/* If there is no custom __iterator__ method, we are done here. */
@ -1242,7 +1242,7 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
} else {
/* Call the iterator object's .next method. */
jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
if (!js_GetMethod(cx, iterobj, id, 0, rval))
return false;
if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
/* Check for StopIteration. */

View File

@ -1963,7 +1963,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v))
if (!js_NativeGet(cx, obj, obj2, shape, 0, &v))
return JS_FALSE;
}
@ -2092,14 +2092,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD
changed |= JSPROP_ENUMERATE;
attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
if (shape->isMethod()) {
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
getter = JS_PropertyStub;
setter = JS_StrictPropertyStub;
} else {
getter = shape->getter();
setter = shape->setter();
}
getter = shape->getter();
setter = shape->setter();
} else if (desc.isDataDescriptor()) {
unsigned unchanged = 0;
if (!desc.hasConfigurable)
@ -2126,8 +2120,6 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD
if (!CheckAccess(cx, obj2, id, JSACC_WATCH, &dummy, &attrs))
return JS_FALSE;
JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
/* 8.12.9 step 12. */
unsigned changed = 0;
if (desc.hasConfigurable)
@ -2143,7 +2135,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD
if (desc.hasGet) {
getter = desc.getter();
} else {
getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue()))
getter = (shape->hasDefaultGetter() && !shape->hasGetterValue())
? JS_PropertyStub
: shape->getter();
}
@ -4399,8 +4391,6 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
unsigned attrs, unsigned flags, int shortid)
{
JS_ASSERT(!(flags & Shape::METHOD));
/* Convert string indices to integers if appropriate. */
id = js_CheckForStringIndex(id);
@ -4415,26 +4405,6 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
}
Shape *
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
Shape *shape, unsigned attrs, unsigned mask,
PropertyOp getter, StrictPropertyOp setter)
{
/*
* Check for freezing an object with shape-memoized methods here, on a
* shape-by-shape basis.
*/
if ((attrs & JSPROP_READONLY) && shape->isMethod()) {
Value v = ObjectValue(*obj->nativeGetMethod(shape));
shape = obj->methodReadBarrier(cx, *shape, &v);
if (!shape)
return NULL;
}
return obj->changeProperty(cx, shape, attrs, mask, getter, setter);
}
JSBool
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
@ -4482,15 +4452,12 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
unsigned flags, int shortid, unsigned defineHow /* = 0 */)
{
JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
DNP_SET_METHOD | DNP_SKIP_TYPE)) == 0);
DNP_SKIP_TYPE)) == 0);
RootObject objRoot(cx, &obj);
RootId idRoot(cx, &id);
/*
* Make a local copy of value, in case a method barrier needs to update the
* value to define, and just so addProperty can mutate its inout parameter.
*/
/* Make a local copy of value so addProperty can mutate its inout parameter. */
RootedVarValue value(cx);
value = value_;
@ -4549,15 +4516,12 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
/* Use the object's class getter and setter by default. */
Class *clasp = obj->getClass();
if (!(defineHow & DNP_SET_METHOD)) {
if (!getter && !(attrs & JSPROP_GETTER))
getter = clasp->getProperty;
if (!setter && !(attrs & JSPROP_SETTER))
setter = clasp->setProperty;
}
if (!getter && !(attrs & JSPROP_GETTER))
getter = clasp->getProperty;
if (!setter && !(attrs & JSPROP_SETTER))
setter = clasp->setProperty;
if (((defineHow & DNP_SET_METHOD) || getter == JS_PropertyStub) &&
!(defineHow & DNP_SKIP_TYPE)) {
if ((getter == JS_PropertyStub) && !(defineHow & DNP_SKIP_TYPE)) {
/*
* Type information for normal native properties should reflect the
* initial value of the property.
@ -4568,32 +4532,6 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
}
if (!shape) {
/* Add a new property, or replace an existing one of the same id. */
if (defineHow & DNP_SET_METHOD) {
JS_ASSERT(clasp == &ObjectClass);
JS_ASSERT(IsFunctionObject(value));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JS_ASSERT(!getter && !setter);
JSObject *funobj = &value.raw().toObject();
if (!funobj->toFunction()->isClonedMethod())
flags |= Shape::METHOD;
}
if (const Shape *existingShape = obj->nativeLookup(cx, id)) {
if (existingShape->isMethod() &&
ObjectValue(*obj->nativeGetMethod(existingShape)) == value)
{
/*
* Redefining an existing shape-memoized method object without
* changing the property's value, perhaps to change attributes.
* Clone now via the method read barrier.
*/
if (!obj->methodReadBarrier(cx, *existingShape, value.address()))
return NULL;
}
}
shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
attrs, flags, shortid);
if (!shape)
@ -4970,7 +4908,7 @@ js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *p
if (shape->hasSlot()) {
*vp = pobj->nativeGetSlot(shape->slot());
JS_ASSERT(!vp->isMagic());
JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(),
JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(),
js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp));
} else {
vp->setUndefined();
@ -4978,9 +4916,6 @@ js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *p
if (shape->hasDefaultGetter())
return true;
if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER))
return true;
jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc);
if (script && script->hasAnalysis()) {
@ -4993,11 +4928,8 @@ js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *p
return false;
/* Update slotful shapes according to the value produced by the getter. */
if (shape->hasSlot() && pobj->nativeContains(cx, *shape)) {
/* Method shapes were removed by methodReadBarrier under shape->get(). */
JS_ASSERT(!shape->isMethod());
if (shape->hasSlot() && pobj->nativeContains(cx, *shape))
pobj->nativeSetSlot(shape->slot(), *vp);
}
return true;
}
@ -5021,10 +4953,6 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool
/* If shape has a stub setter, just store *vp. */
if (shape->hasDefaultSetter()) {
if (!added) {
if (shape->isMethod() && !obj->methodShapeChange(cx, *shape))
return false;
}
obj->nativeSetSlot(slot, *vp);
return true;
}
@ -5162,7 +5090,7 @@ JSBool
js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
{
/* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp);
}
JSBool
@ -5173,7 +5101,7 @@ js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index,
return false;
/* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp);
}
JSBool
@ -5261,25 +5189,10 @@ bool
JSObject::callMethod(JSContext *cx, jsid id, unsigned argc, Value *argv, Value *vp)
{
Value fval;
return js_GetMethod(cx, this, id, JSGET_NO_METHOD_BARRIER, &fval) &&
return js_GetMethod(cx, this, id, 0, &fval) &&
Invoke(cx, ObjectValue(*this), fval, argc, argv, vp);
}
static bool
CloneFunctionForSetMethod(JSContext *cx, Value *vp)
{
JSFunction *fun = vp->toObject().toFunction();
/* Clone the fun unless it already has been. */
if (!fun->isClonedMethod()) {
fun = CloneFunctionObject(cx, fun);
if (!fun)
return false;
vp->setObject(*fun);
}
return true;
}
JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
Value *vp, JSBool strict)
@ -5294,7 +5207,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
StrictPropertyOp setter;
bool added;
JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_SET_METHOD | DNP_UNQUALIFIED)) == 0);
JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0);
/* Convert string indices to integers if appropriate. */
id = js_CheckForStringIndex(id);
@ -5304,9 +5217,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
WatchpointMap *wpmap = cx->compartment->watchpointMap;
if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
return false;
/* A watchpoint handler may set *vp to a non-function value. */
defineHow &= ~DNP_SET_METHOD;
}
if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
@ -5381,12 +5291,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
* We found id in a prototype object: prepare to share or shadow.
*/
if (!shape->shadowable()) {
if (defineHow & DNP_SET_METHOD) {
JS_ASSERT(!shape->isMethod());
if (!CloneFunctionForSetMethod(cx, vp))
return false;
}
if (defineHow & DNP_CACHE_RESULT)
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, pobj, shape);
@ -5412,7 +5316,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
* about to create in obj.
*/
if (!shape->hasSlot()) {
defineHow &= ~DNP_SET_METHOD;
if (shape->hasShortID()) {
flags = Shape::HAS_SHORTID;
shortid = shape->shortid();
@ -5431,26 +5334,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
*/
shape = NULL;
}
if (shape && (defineHow & DNP_SET_METHOD)) {
/*
* JSOP_SETMETHOD is assigning to an existing own property. If it
* is an identical method property, do nothing. Otherwise downgrade
* to ordinary assignment. Either way, do not fill the property
* cache, as the interpreter has no fast path for these unusual
* cases.
*/
if (shape->isMethod()) {
if (obj->nativeGetMethod(shape) == &vp->toObject())
return true;
shape = obj->methodShapeChange(cx, *shape);
if (!shape)
return false;
}
if (!CloneFunctionForSetMethod(cx, vp))
return false;
return js_NativeSet(cx, obj, shape, false, strict, vp);
}
}
added = false;
@ -5471,19 +5354,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
if (!js_PurgeScopeChain(cx, obj, id))
return JS_FALSE;
/*
* Check for Object class here to avoid defining a method on a class
* with magic resolve, addProperty, getProperty, etc. hooks.
*/
if ((defineHow & DNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
JS_ASSERT(IsFunctionObject(*vp));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JSObject *funobj = &vp->toObject();
if (!funobj->toFunction()->isClonedMethod())
flags |= Shape::METHOD;
}
shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
attrs, flags, shortid);
if (!shape)
@ -5557,14 +5427,6 @@ js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *
return true;
}
JSBool
js_SetNativeAttributes(JSContext *cx, JSObject *obj, Shape *shape, unsigned attrs)
{
JS_ASSERT(obj->isNative());
return !!js_ChangeNativePropertyAttrs(cx, obj, shape, attrs, 0,
shape->getter(), shape->setter());
}
JSBool
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
{
@ -5574,7 +5436,7 @@ js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
if (!prop)
return true;
return obj->isNative()
? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
? obj->changePropertyAttributes(cx, (Shape *) prop, *attrsp)
: obj->setGenericAttributes(cx, id, attrsp);
}
@ -5587,7 +5449,7 @@ js_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *
if (!prop)
return true;
return obj->isNative()
? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
? obj->changePropertyAttributes(cx, (Shape *) prop, *attrsp)
: obj->setElementAttributes(cx, index, attrsp);
}
@ -5624,35 +5486,6 @@ js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool stri
if (shape->hasSlot()) {
const Value &v = obj->nativeGetSlot(shape->slot());
GCPoke(cx->runtime, v);
/*
* Delete is rare enough that we can take the hit of checking for an
* active cloned method function object that must be homed to a callee
* slot on the active stack frame before this delete completes, in case
* someone saved the clone and checks it against foo.caller for a foo
* called from the active method.
*
* We do not check suspended frames. They can't be reached via caller,
* so the only way they could have the method's joined function object
* as callee is through an API abusage. We break any such edge case.
*/
JSFunction *fun;
if (IsFunctionObject(v, &fun) && fun->isClonedMethod()) {
for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
if (fp->isFunctionFrame() &&
fp->fun()->script() == fun->script() &&
fp->thisValue().isObject())
{
JSObject *tmp = &fp->thisValue().toObject();
do {
if (tmp == obj) {
fp->overwriteCallee(*fun);
break;
}
} while ((tmp = tmp->getProto()) != NULL);
}
}
}
}
if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, shape->getUserId(), rval))
@ -5691,7 +5524,7 @@ HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT(id == js_CheckForStringIndex(id));
if (const Shape *shape = obj->nativeLookup(cx, id)) {
if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
if (shape->hasDefaultGetter() && shape->hasSlot()) {
*vp = obj->nativeGetSlot(shape->slot());
return true;
}
@ -5711,7 +5544,7 @@ HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
static bool
MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, vp))
if (!js_GetMethod(cx, obj, id, 0, vp))
return false;
if (!js_IsCallable(*vp)) {
*vp = ObjectValue(*obj);
@ -6221,7 +6054,6 @@ DumpProperty(JSObject *obj, const Shape &shape)
if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
if (shape.isMethod()) fprintf(stderr, "method ");
if (shape.hasGetterValue())
fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());

View File

@ -509,19 +509,8 @@ struct JSObject : public js::ObjectImpl
public:
inline bool nativeEmpty() const;
js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
bool shadowingShapeChange(JSContext *cx, const js::Shape &shape);
/*
* Read barrier to clone a joined function object stored as a method.
* Defined in jsobjinlines.h, but not declared inline per standard style in
* order to avoid gcc warnings.
*/
js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp);
/* Whether method shapes can be added to this object. */
inline bool canHaveMethodBarrier() const;
/* Whether there may be indexed properties on this object. */
inline bool isIndexed() const;
@ -573,8 +562,6 @@ struct JSObject : public js::ObjectImpl
void rollbackProperties(JSContext *cx, uint32_t slotSpan);
inline JSFunction *nativeGetMethod(const js::Shape *shape) const;
inline void nativeSetSlot(unsigned slot, const js::Value &value);
inline void nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value);
@ -940,6 +927,8 @@ struct JSObject : public js::ObjectImpl
js::Shape *changeProperty(JSContext *cx, js::Shape *shape, unsigned attrs, unsigned mask,
JSPropertyOp getter, JSStrictPropertyOp setter);
inline bool changePropertyAttributes(JSContext *cx, js::Shape *shape, unsigned attrs);
/* Remove the property named by id from this object. */
bool removeProperty(JSContext *cx, jsid id);
@ -1351,16 +1340,6 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot,
unsigned attrs, unsigned flags, int shortid);
/*
* Change shape to have the given attrs, getter, and setter in scope, morphing
* it into a potentially new js::Shape. Return a pointer to the changed
* or identical property.
*/
extern js::Shape *
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
js::Shape *shape, unsigned attrs, unsigned mask,
JSPropertyOp getter, JSStrictPropertyOp setter);
extern JSBool
js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
const js::Value &descriptor, JSBool *bp);
@ -1372,13 +1351,10 @@ namespace js {
*/
const unsigned DNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */
const unsigned DNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */
const unsigned DNP_SET_METHOD = 4; /* DefineNativeProperty,js_SetPropertyHelper
must pass the js::Shape::METHOD
flag on to JSObject::{add,put}Property */
const unsigned DNP_UNQUALIFIED = 8; /* Unqualified property set. Only used in
const unsigned DNP_UNQUALIFIED = 4; /* Unqualified property set. Only used in
the defineHow argument of
js_SetPropertyHelper. */
const unsigned DNP_SKIP_TYPE = 0x10; /* Don't update type information */
const unsigned DNP_SKIP_TYPE = 8; /* Don't update type information */
/*
* Return successfully added or changed shape or NULL on error.
@ -1460,22 +1436,8 @@ FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name);
extern JSObject *
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 unsigned JSGET_METHOD_BARRIER = 0; // get can leak joined function object
const unsigned JSGET_NO_METHOD_BARRIER = 1; // call to joined function can't leak
const unsigned JSGET_CACHE_RESULT = 2; // from a caching interpreter opcode
/* JSGET_CACHE_RESULT is the analogue of DNP_CACHE_RESULT for js_GetMethod. */
const unsigned JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode
/*
* NB: js_NativeGet and js_NativeSet are called with the scope containing shape
@ -1526,14 +1488,6 @@ GetMethod(JSContext *cx, JSObject *obj, PropertyName *name, unsigned getHow, Val
} /* namespace js */
/*
* Change attributes for the given native property. The caller must ensure
* that obj is locked and this function always unlocks obj on return.
*/
extern JSBool
js_SetNativeAttributes(JSContext *cx, JSObject *obj, js::Shape *shape,
unsigned attrs);
namespace js {
/*

View File

@ -170,6 +170,12 @@ JSObject::setSpecialAttributes(JSContext *cx, js::SpecialId sid, unsigned *attrs
return setGenericAttributes(cx, SPECIALID_TO_JSID(sid), attrsp);
}
inline bool
JSObject::changePropertyAttributes(JSContext *cx, js::Shape *shape, unsigned attrs)
{
return !!changeProperty(cx, shape, attrs, 0, shape->getter(), shape->setter());
}
inline JSBool
JSObject::getGeneric(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp)
{
@ -265,50 +271,6 @@ JSObject::enclosingScope()
return isScope() ? &asScope().enclosingScope() : getParent();
}
/*
* Property read barrier for deferred cloning of compiler-created function
* objects optimized as typically non-escaping, ad-hoc methods in obj.
*/
inline js::Shape *
JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
{
JS_ASSERT(nativeContains(cx, shape));
JS_ASSERT(shape.isMethod());
JS_ASSERT(shape.hasSlot());
JS_ASSERT(shape.hasDefaultSetter());
JS_ASSERT(!isGlobal()); /* i.e. we are not changing the global shape */
JSFunction *fun = vp->toObject().toFunction();
JS_ASSERT(!fun->isClonedMethod());
JS_ASSERT(fun->isNullClosure());
fun = js::CloneFunctionObject(cx, fun);
if (!fun)
return NULL;
fun->setMethodObj(*this);
/*
* Replace the method property with an ordinary data property. This is
* equivalent to this->setProperty(cx, shape.id, vp) except that any
* watchpoint on the property is not triggered.
*/
uint32_t slot = shape.slot();
js::Shape *newshape = methodShapeChange(cx, shape);
if (!newshape)
return NULL;
JS_ASSERT(!newshape->isMethod());
JS_ASSERT(newshape->slot() == slot);
vp->setObject(*fun);
nativeSetSlot(slot, *vp);
return newshape;
}
inline bool
JSObject::canHaveMethodBarrier() const
{
return isObject() || isFunction() || isPrimitive() || isDate();
}
inline bool
JSObject::isFixedSlot(size_t slot)
{
@ -964,22 +926,6 @@ JSObject::principals(JSContext *cx)
return cx->compartment ? cx->compartment->principals : NULL;
}
inline JSFunction *
JSObject::nativeGetMethod(const js::Shape *shape) const
{
/*
* For method shapes, this object must have an uncloned function object in
* the shape's slot.
*/
JS_ASSERT(shape->isMethod());
#ifdef DEBUG
JSObject *obj = &nativeGetSlot(shape->slot()).toObject();
JS_ASSERT(obj->isFunction() && !obj->toFunction()->isClonedMethod());
#endif
return static_cast<JSFunction *>(&nativeGetSlot(shape->slot()).toObject());
}
inline void
JSObject::nativeSetSlot(unsigned slot, const js::Value &value)
{

View File

@ -312,7 +312,7 @@ PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, Value *vp, Stringi
if (vp->isObject()) {
Value toJSON;
jsid id = ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom);
if (!js_GetMethod(cx, &vp->toObject(), id, JSGET_NO_METHOD_BARRIER, &toJSON))
if (!js_GetMethod(cx, &vp->toObject(), id, 0, &toJSON))
return false;
if (js_IsCallable(toJSON)) {

View File

@ -4515,7 +4515,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
break;
case JSOP_SETPROP:
case JSOP_SETMETHOD:
{
LOAD_ATOM(0);
GET_QUOTE_AND_FMT("[%s] %s= ", ".%s %s= ", xval);
@ -5120,7 +5119,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
break;
case JSOP_INITPROP:
case JSOP_INITMETHOD:
LOAD_ATOM(0);
xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\''));
if (!xval)

View File

@ -569,7 +569,7 @@ class OpcodeCounts
* Access ops include all name, element and property reads, as well as
* SETELEM and SETPROP (for ElementCounts/PropertyCounts alignment).
*/
if (op == JSOP_SETELEM || op == JSOP_SETPROP || op == JSOP_SETMETHOD)
if (op == JSOP_SETELEM || op == JSOP_SETPROP)
return true;
int format = js_CodeSpec[op].format;
return !!(format & (JOF_NAME | JOF_GNAME | JOF_ELEM | JOF_PROP))

View File

@ -532,15 +532,10 @@ OPDEF(JSOP_LENGTH, 217, "length", NULL, 5, 1, 1, 18, JOF_ATOM|J
OPDEF(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED17, 219,"unused17", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED24, 220,"unused18", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED25, 221,"unused19", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Joined function object as method optimization support.
*/
OPDEF(JSOP_SETMETHOD, 222,"setmethod", NULL, 5, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITMETHOD, 223,"initmethod", NULL, 5, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_UNUSED24, 220,"unused24", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED25, 221,"unused25", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED29, 222,"unused29", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED30, 223,"unused30", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED16, 224,"unused16", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Pop the stack, convert to a jsid (int or string), and push back. */

View File

@ -297,7 +297,6 @@ Shape::dump(JSContext *cx, FILE *fp) const
fputs("(", fp);
#define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
DUMP_FLAG(HAS_SHORTID, has_shortid);
DUMP_FLAG(METHOD, method);
DUMP_FLAG(IN_DICTIONARY, in_dictionary);
#undef DUMP_FLAG
fputs(") ", fp);

View File

@ -498,16 +498,9 @@ NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
JS_ASSERT(!(attrs & JSPROP_SETTER));
setter = NULL;
}
if (flags & Shape::METHOD) {
JS_ASSERT_IF(getter, getter == JS_PropertyStub);
JS_ASSERT(!setter);
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
if (getter == JS_PropertyStub) {
JS_ASSERT(!(attrs & JSPROP_GETTER));
getter = NULL;
} else {
if (getter == JS_PropertyStub) {
JS_ASSERT(!(attrs & JSPROP_GETTER));
getter = NULL;
}
}
return true;
@ -808,9 +801,6 @@ JSObject::changeProperty(JSContext *cx, Shape *shape, unsigned attrs, unsigned m
JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
!(attrs & JSPROP_SHARED));
/* Don't allow method properties to be changed to have a getter or setter. */
JS_ASSERT_IF(shape->isMethod(), !getter && !setter);
types::MarkTypePropertyConfigured(cx, this, shape->propid());
if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType());
@ -1041,44 +1031,6 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n
return newShape;
}
Shape *
JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
{
JS_ASSERT(shape.isMethod());
if (!inDictionaryMode() && !toDictionaryMode(cx))
return NULL;
Shape *spare = js_NewGCShape(cx);
if (!spare)
return NULL;
new (spare) Shape(shape.base()->unowned(), 0);
#ifdef DEBUG
JS_ASSERT(canHaveMethodBarrier());
JS_ASSERT(!shape.setter());
JS_ASSERT(!shape.hasShortID());
#endif
/*
* Clear Shape::METHOD from flags as we are despecializing from a
* method memoized in the property tree to a plain old function-valued
* property.
*/
Shape *result =
putProperty(cx, shape.propid(), NULL, NULL, shape.slot(),
shape.attrs,
shape.getFlags() & ~Shape::METHOD,
0);
if (!result)
return NULL;
if (result != lastProperty())
JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
return result;
}
bool
JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
{

View File

@ -649,43 +649,15 @@ struct Shape : public js::gc::Cell
/* Public bits stored in shape->flags. */
enum {
HAS_SHORTID = 0x40,
METHOD = 0x80,
PUBLIC_FLAGS = HAS_SHORTID | METHOD
PUBLIC_FLAGS = HAS_SHORTID
};
bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; }
unsigned getFlags() const { return flags & PUBLIC_FLAGS; }
bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
/*
* A shape 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 in objects without
* cloning.
*
* 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. When storing a function to a property, we use method
* shapes to speculate that these effects will never be observed: the
* property will only be used in calls, and f.callee will not be used
* to get a handle on the object.
*
* If either a non-call use or callee access occurs, then the function is
* cloned and the object is reshaped with a non-method property.
*
* Note that method shapes do not imply the object has a particular
* uncloned function, just that the object has *some* uncloned function
* in the shape's slot.
*/
bool isMethod() const {
JS_ASSERT_IF(flags & METHOD, !base()->rawGetter);
return (flags & METHOD) != 0;
}
PropertyOp getter() const { return base()->rawGetter; }
bool hasDefaultGetterOrIsMethod() const { return !base()->rawGetter; }
bool hasDefaultGetter() const { return !base()->rawGetter && !isMethod(); }
bool hasDefaultGetter() const { return !base()->rawGetter; }
PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }

View File

@ -282,16 +282,10 @@ Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js:
JS_ASSERT(!hasDefaultGetter());
if (hasGetterValue()) {
JS_ASSERT(!isMethod());
js::Value fval = getterValue();
return js::InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
}
if (isMethod()) {
vp->setObject(*pobj->nativeGetMethod(this));
return pobj->methodReadBarrier(cx, *this, vp);
}
/*
* |with (it) color;| ends up here, as do XML filter-expressions.
* Avoid exposing the With object to native getters.

View File

@ -3312,7 +3312,7 @@ js_ValueToSource(JSContext *cx, const Value &v)
Value rval = NullValue();
Value fval;
jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
if (!js_GetMethod(cx, &v.toObject(), id, JSGET_NO_METHOD_BARRIER, &fval))
if (!js_GetMethod(cx, &v.toObject(), id, 0, &fval))
return NULL;
if (js_IsCallable(fval)) {
if (!Invoke(cx, v, fval, 0, NULL, &rval))

View File

@ -149,26 +149,8 @@ WatchpointMap::triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *v
old.setUndefined();
if (obj->isNative()) {
if (const Shape *shape = obj->nativeLookup(cx, id)) {
if (shape->hasSlot()) {
if (shape->isMethod()) {
/*
* The existing watched property is a method. Trip
* the method read barrier in order to avoid
* passing an uncloned function object to the
* handler.
*/
old = UndefinedValue();
Value method = ObjectValue(*obj->nativeGetMethod(shape));
if (!obj->methodReadBarrier(cx, *shape, &method))
return false;
shape = obj->nativeLookup(cx, id);
JS_ASSERT(shape->isDataDescriptor());
JS_ASSERT(!shape->isMethod());
old = method;
} else {
old = obj->nativeGetSlot(shape->slot());
}
}
if (shape->hasSlot())
old = obj->nativeGetSlot(shape->slot());
}
}

View File

@ -1715,8 +1715,7 @@ mjit::Compiler::finishThisUp()
jitPics[i].shapeRegHasBaseShape = true;
jitPics[i].pc = pics[i].pc;
if (pics[i].kind == ic::PICInfo::SET ||
pics[i].kind == ic::PICInfo::SETMETHOD) {
if (pics[i].kind == ic::PICInfo::SET) {
jitPics[i].u.vr = pics[i].vr;
} else if (pics[i].kind != ic::PICInfo::NAME) {
if (pics[i].hasTypeCheck) {
@ -2900,11 +2899,6 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_ENDINIT)
END_CASE(JSOP_ENDINIT)
BEGIN_CASE(JSOP_INITMETHOD)
jsop_initmethod();
frame.pop();
END_CASE(JSOP_INITMETHOD)
BEGIN_CASE(JSOP_INITPROP)
jsop_initprop();
frame.pop();
@ -2971,7 +2965,6 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETMETHOD)
{
jsbytecode *next = &PC[JSOP_SETNAME_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
@ -3078,29 +3071,6 @@ mjit::Compiler::generateMethod()
JSObjStubFun stub = stubs::Lambda;
uint32_t uses = 0;
jsbytecode *pc2 = NULL;
if (fun->joinable()) {
pc2 = PC + JSOP_LAMBDA_LENGTH;
JSOp next = JSOp(*pc2);
if (next == JSOP_INITMETHOD) {
stub = stubs::LambdaJoinableForInit;
} else if (next == JSOP_SETMETHOD) {
stub = stubs::LambdaJoinableForSet;
uses = 1;
} else if (next == JSOP_CALL) {
int iargc = GET_ARGC(pc2);
if (iargc == 1 || iargc == 2) {
stub = stubs::LambdaJoinableForCall;
uses = frame.frameSlots();
}
} else if (next == JSOP_NULL) {
pc2 += JSOP_NULL_LENGTH;
if (JSOp(*pc2) == JSOP_CALL && GET_ARGC(pc2) == 0)
stub = stubs::LambdaJoinableForNull;
}
}
prepareStubCall(Uses(uses));
masm.move(ImmPtr(fun), Registers::ArgReg1);
@ -3249,7 +3219,7 @@ mjit::Compiler::generateMethod()
/* Update information about the result type of access operations. */
if (OpcodeCounts::accessOp(op) &&
op != JSOP_SETPROP && op != JSOP_SETMETHOD && op != JSOP_SETELEM) {
op != JSOP_SETPROP && op != JSOP_SETELEM) {
FrameEntry *fe = (GetDefCount(script, lastPC - script->code) == 1)
? frame.peek(-1)
: frame.peek(-2);
@ -5267,7 +5237,7 @@ mjit::Compiler::testSingletonProperty(JSObject *obj, jsid id)
return false;
if (holder->getSlot(shape->slot()).isUndefined())
return false;
} else if (!shape->isMethod()) {
} else {
return false;
}
@ -5600,10 +5570,7 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
}
#endif
ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
? ic::PICInfo::SETMETHOD
: ic::PICInfo::SET;
PICGenInfo pic(kind, op);
PICGenInfo pic(ic::PICInfo::SET, op);
pic.name = name;
if (monitored(PC)) {
@ -6374,7 +6341,7 @@ mjit::Compiler::jsop_getgname(uint32_t index)
* reallocation of the global object's slots.
*/
const js::Shape *shape = globalObj->nativeLookup(cx, ATOM_TO_JSID(name));
if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
if (shape && shape->hasDefaultGetter() && shape->hasSlot()) {
HeapSlot *value = &globalObj->getSlotRef(shape->slot());
if (!value->isUndefined() &&
!propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
@ -6497,7 +6464,7 @@ mjit::Compiler::jsop_setgname(PropertyName *name, bool popGuaranteed)
if (!types)
return;
const js::Shape *shape = globalObj->nativeLookup(cx, ATOM_TO_JSID(name));
if (shape && !shape->isMethod() && shape->hasDefaultSetter() &&
if (shape && shape->hasDefaultSetter() &&
shape->writable() && shape->hasSlot() &&
!types->isOwnProperty(cx, globalObj->getType(cx), true)) {
watchGlobalReallocation();

View File

@ -262,7 +262,7 @@ class Compiler : public BaseCompiler
return getPropLabels_;
}
ic::SetPropLabels &setPropLabels() {
JS_ASSERT(kind == ic::PICInfo::SET || kind == ic::PICInfo::SETMETHOD);
JS_ASSERT(kind == ic::PICInfo::SET);
return setPropLabels_;
}
ic::BindNameLabels &bindNameLabels() {

View File

@ -2658,22 +2658,6 @@ mjit::Compiler::jsop_pos()
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_initmethod()
{
#ifdef DEBUG
FrameEntry *obj = frame.peek(-2);
#endif
JSAtom *atom = script->getAtom(GET_UINT32_INDEX(PC));
/* Initializers with INITMETHOD are not fast yet. */
JS_ASSERT(!frame.extra(obj).initObject);
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitMethod, REJOIN_FALLTHROUGH);
}
void
mjit::Compiler::jsop_initprop()
{

View File

@ -1869,8 +1869,7 @@ LoopState::analyzeLoopBody(unsigned frame)
break;
}
case JSOP_SETPROP:
case JSOP_SETMETHOD: {
case JSOP_SETPROP: {
JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));

View File

@ -99,7 +99,7 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
}
if (!shape ||
!shape->hasDefaultGetterOrIsMethod() ||
!shape->hasDefaultGetter() ||
!shape->hasSlot())
{
if (shape)
@ -165,8 +165,7 @@ UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Sh
if (!shape)
return Lookup_Uncacheable;
if (shape->isMethod() ||
!shape->hasDefaultSetter() ||
if (!shape->hasDefaultSetter() ||
!shape->writable() ||
!shape->hasSlot() ||
obj->watched())

View File

@ -350,23 +350,6 @@ class SetPropCompiler : public PICStubCompiler
lastReg = pic.shapeReg;
}
if (pic.kind == ic::PICInfo::SETMETHOD) {
/*
* Guard that the value is equal to the shape's method.
* We already know it is a function, so test the payload.
*/
JS_ASSERT(shape->isMethod());
JSObject *funobj = obj->nativeGetMethod(shape);
if (pic.u.vr.isConstant()) {
JS_ASSERT(funobj == &pic.u.vr.value().toObject());
} else {
Jump mismatchedFunction =
masm.branchPtr(Assembler::NotEqual, pic.u.vr.dataReg(), ImmPtr(funobj));
if (!slowExits.append(mismatchedFunction))
return error();
}
}
if (obj->isFixedSlot(shape->slot())) {
Address address(pic.objReg,
JSObject::getFixedSlotOffset(shape->slot()));
@ -389,7 +372,6 @@ class SetPropCompiler : public PICStubCompiler
/* Write the object's new shape. */
masm.storePtr(ImmPtr(shape), Address(pic.objReg, JSObject::offsetOfShape()));
} else if (shape->hasDefaultSetter()) {
JS_ASSERT(!shape->isMethod());
Address address = masm.objPropAddress(obj, pic.objReg, shape->slot());
masm.storeValue(pic.u.vr, address);
} else {
@ -585,17 +567,6 @@ class SetPropCompiler : public PICStubCompiler
unsigned flags = 0;
PropertyOp getter = clasp->getProperty;
if (pic.kind == ic::PICInfo::SETMETHOD) {
if (!obj->canHaveMethodBarrier())
return disable("can't have method barrier");
JSObject *funobj = &f.regs.sp[-1].toObject();
if (funobj->toFunction()->isClonedMethod())
return disable("mismatched function");
flags |= Shape::METHOD;
}
/*
* Define the property but do not set it yet. For setmethod,
* populate the slot to satisfy the method invariant (in case we
@ -606,8 +577,6 @@ class SetPropCompiler : public PICStubCompiler
SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0);
if (!shape)
return error();
if (flags & Shape::METHOD)
obj->nativeSetSlot(shape->slot(), f.regs.sp[-1]);
if (monitor.recompiled())
return Lookup_Uncacheable;
@ -647,13 +616,8 @@ class SetPropCompiler : public PICStubCompiler
}
const Shape *shape = (const Shape *) prop;
if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
return disable("set method on non-method shape");
if (!shape->writable())
return disable("readonly");
if (shape->isMethod())
return disable("method");
if (shape->hasDefaultSetter()) {
if (!shape->hasSlot())
return disable("invalid slot");
@ -789,27 +753,22 @@ struct GetPropHelper {
LookupStatus testForGet() {
if (!shape->hasDefaultGetter()) {
if (shape->isMethod()) {
if (JSOp(*f.pc()) != JSOP_CALLPROP)
return ic.disable(f, "method valued shape");
} else {
if (shape->hasGetterValue())
return ic.disable(f, "getter value shape");
if (shape->hasSlot() && holder != obj)
return ic.disable(f, "slotful getter hook through prototype");
if (!ic.canCallHook)
return ic.disable(f, "can't call getter hook");
if (f.regs.inlined()) {
/*
* As with native stubs, getter hook stubs can't be
* generated for inline frames. Mark the inner function
* as uninlineable and recompile.
*/
f.script()->uninlineable = true;
MarkTypeObjectFlags(cx, f.script()->function(),
types::OBJECT_FLAG_UNINLINEABLE);
return Lookup_Uncacheable;
}
if (shape->hasGetterValue())
return ic.disable(f, "getter value shape");
if (shape->hasSlot() && holder != obj)
return ic.disable(f, "slotful getter hook through prototype");
if (!ic.canCallHook)
return ic.disable(f, "can't call getter hook");
if (f.regs.inlined()) {
/*
* As with native stubs, getter hook stubs can't be
* generated for inline frames. Mark the inner function
* as uninlineable and recompile.
*/
f.script()->uninlineable = true;
MarkTypeObjectFlags(cx, f.script()->function(),
types::OBJECT_FLAG_UNINLINEABLE);
return Lookup_Uncacheable;
}
} else if (!shape->hasSlot()) {
return ic.disable(f, "no slot");
@ -962,7 +921,7 @@ class GetPropCompiler : public PICStubCompiler
return status;
if (getprop.obj != getprop.holder)
return disable("proto walk on String.prototype");
if (!getprop.shape->hasDefaultGetterOrIsMethod())
if (!getprop.shape->hasDefaultGetter())
return disable("getter hook on String.prototype");
if (hadGC())
return Lookup_Uncacheable;
@ -1239,7 +1198,7 @@ class GetPropCompiler : public PICStubCompiler
pic.secondShapeGuard = 0;
}
if (!shape->hasDefaultGetterOrIsMethod()) {
if (!shape->hasDefaultGetter()) {
generateGetterStub(masm, shape, start, shapeMismatches);
if (setStubShapeOffset)
pic.getPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel);
@ -1327,7 +1286,7 @@ class GetPropCompiler : public PICStubCompiler
return Lookup_Uncacheable;
if (obj == getprop.holder &&
getprop.shape->hasDefaultGetterOrIsMethod() &&
getprop.shape->hasDefaultGetter() &&
!pic.inlinePathPatched) {
return patchInline(getprop.holder, getprop.shape);
}
@ -1675,7 +1634,7 @@ class ScopeNameCompiler : public PICStubCompiler
JSObject *normalized = obj;
if (obj->isWith() && !shape->hasDefaultGetter())
normalized = &obj->asWith().object();
NATIVE_GET(cx, normalized, holder, shape, JSGET_METHOD_BARRIER, vp, return false);
NATIVE_GET(cx, normalized, holder, shape, 0, vp, return false);
return true;
}
};

View File

@ -384,7 +384,6 @@ struct PICInfo : public BasePolyIC {
{
GET, // JSOP_GETPROP
SET, // JSOP_SETPROP, JSOP_SETNAME
SETMETHOD, // JSOP_SETMETHOD
NAME, // JSOP_NAME
BIND, // JSOP_BINDNAME
XNAME // JSOP_GETXPROP
@ -461,7 +460,7 @@ struct PICInfo : public BasePolyIC {
types::TypeSet *rhsTypes;
inline bool isSet() const {
return kind == SET || kind == SETMETHOD;
return kind == SET;
}
inline bool isGet() const {
return kind == GET;

View File

@ -998,69 +998,6 @@ stubs::RegExp(VMFrame &f, JSObject *regex)
f.regs.sp[0].setObject(*obj);
}
JSObject * JS_FASTCALL
stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_UINT32_INDEX(nextpc)));
return fun;
}
JSObject * JS_FASTCALL
stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
const Value &lref = f.regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_UINT32_INDEX(nextpc)));
return fun;
}
return Lambda(f, fun);
}
JSObject * JS_FASTCALL
stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
/*
* Array.prototype.sort and String.prototype.replace are optimized as if
* they are special form. We know that they won't leak the joined function
* object fun, therefore we don't need to clone that compiler-created
* function object for identity/mutation reasons.
*/
int iargc = GET_ARGC(f.regs.pc + JSOP_LAMBDA_LENGTH);
/*
* Note that we have not yet pushed fun as the final argument, so
* regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)], is the callee
* for this JSOP_CALL.
*/
const Value &cref = f.regs.sp[1 - (iargc + 2)];
JSFunction *callee;
if (IsFunctionObject(cref, &callee)) {
Native native = callee->maybeNative();
if (native) {
if (iargc == 1 && native == array_sort)
return fun;
if (iargc == 2 && native == str_replace)
return fun;
}
}
return Lambda(f, fun);
}
JSObject * JS_FASTCALL
stubs::LambdaJoinableForNull(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
return fun;
}
JSObject * JS_FASTCALL
stubs::Lambda(VMFrame &f, JSFunction *fun)
{
@ -1141,11 +1078,10 @@ InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op)
/* Get the immediate property name into id. */
jsid id = ATOM_TO_JSID(name);
unsigned defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0;
if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false)
? !js_SetPropertyHelper(cx, obj, id, 0, &rval, false)
: !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow)) {
JSPROP_ENUMERATE, 0, 0, 0)) {
THROW();
}
}
@ -1156,12 +1092,6 @@ stubs::InitProp(VMFrame &f, PropertyName *name)
InitPropOrMethod(f, name, JSOP_INITPROP);
}
void JS_FASTCALL
stubs::InitMethod(VMFrame &f, PropertyName *name)
{
InitPropOrMethod(f, name, JSOP_INITMETHOD);
}
void JS_FASTCALL
stubs::IterNext(VMFrame &f, int32_t offset)
{

View File

@ -62,7 +62,6 @@ void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL RecompileForInline(VMFrame &f);
void JS_FASTCALL InitElem(VMFrame &f, uint32_t last);
void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name);
void JS_FASTCALL InitMethod(VMFrame &f, PropertyName *name);
void JS_FASTCALL HitStackQuota(VMFrame &f);
void * JS_FASTCALL FixupArity(VMFrame &f, uint32_t argc);
@ -137,10 +136,6 @@ void JS_FASTCALL SetConst(VMFrame &f, PropertyName *name);
template<JSBool strict> void JS_FASTCALL DefFun(VMFrame &f, JSFunction *fun);
void JS_FASTCALL RegExp(VMFrame &f, JSObject *regex);
JSObject * JS_FASTCALL Lambda(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForInit(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForSet(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForCall(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL LambdaJoinableForNull(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL FlatLambda(VMFrame &f, JSFunction *fun);
void JS_FASTCALL Arguments(VMFrame &f);
void JS_FASTCALL EnterBlock(VMFrame &f, JSObject *obj);

View File

@ -2170,8 +2170,7 @@ DumpStack(JSContext *cx, unsigned argc, Value *vp)
Value v;
if (iter.isScript()) {
if (iter.fp()->isNonEvalFunctionFrame()) {
if (!iter.fp()->getValidCalleeObject(cx, &v))
return false;
v = ObjectValue(iter.fp()->callee());
} else if (iter.fp()->isEvalFrame()) {
v = StringValue(evalStr);
} else {
@ -2782,11 +2781,7 @@ CopyProperty(JSContext *cx, JSObject *obj, JSObject *referent, jsid id,
return true;
const Shape *shape = (Shape *) prop;
if (shape->isMethod()) {
shape = referent->methodReadBarrier(cx, *shape, &desc.value);
if (!shape)
return false;
} else if (shape->hasSlot()) {
if (shape->hasSlot()) {
desc.value = referent->nativeGetSlot(shape->slot());
} else {
desc.value.setUndefined();

View File

@ -925,7 +925,7 @@ CallMethodIfPresent(JSContext *cx, JSObject *obj, const char *name, int argc, Va
JSAtom *atom = js_Atomize(cx, name, strlen(name));
Value fval;
return atom &&
js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, &fval) &&
js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &fval) &&
(!js_IsCallable(fval) ||
Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
}

View File

@ -189,13 +189,6 @@ StackFrame::initFixupFrame(StackFrame *prev, StackFrame::Flags flags, void *ncod
u.nactual = nactual;
}
inline void
StackFrame::overwriteCallee(JSObject &newCallee)
{
JS_ASSERT(callee().toFunction()->script() == newCallee.toFunction()->script());
mutableCalleev().setObject(newCallee);
}
inline Value &
StackFrame::canonicalActualArg(unsigned i) const
{

View File

@ -818,13 +818,6 @@ class StackFrame
return calleev;
}
/*
* Beware! Ad hoc changes can corrupt the stack layout; the callee should
* only be changed to something that is equivalent to the current callee in
* terms of numFormalArgs etc. Prefer overwriteCallee since it checks.
*/
inline void overwriteCallee(JSObject &newCallee);
Value &mutableCalleev() const {
JS_ASSERT(isFunctionFrame());
if (isEvalFrame())
@ -832,13 +825,6 @@ class StackFrame
return formalArgs()[-2];
}
/*
* Compute the callee function for this stack frame, cloning if needed to
* implement the method read barrier. If this is not a function frame,
* set *vp to null.
*/
bool getValidCalleeObject(JSContext *cx, Value *vp);
CallReceiver callReceiver() const {
return CallReceiverFromArgv(formalArgs());
}