Bug 620141 - eval cache should key based on calling script and pc, not calling function. r=jorendorff.

This commit is contained in:
Philipp Matthias Schäfer 2013-03-27 14:16:42 -05:00
parent e4f225bcbe
commit b418bd08f8
7 changed files with 69 additions and 41 deletions

View File

@ -50,15 +50,16 @@ IsEvalCacheCandidate(RawScript script)
EvalCacheHashPolicy::hash(const EvalCacheLookup &l)
{
return AddToHash(HashString(l.str->chars(), l.str->length()),
l.caller.get(),
l.staticLevel,
l.callerScript.get(),
l.version,
l.compartment);
l.pc);
}
/* static */ bool
EvalCacheHashPolicy::match(RawScript script, const EvalCacheLookup &l)
EvalCacheHashPolicy::match(const EvalCacheEntry &cacheEntry, const EvalCacheLookup &l)
{
JSScript *script = cacheEntry.script;
JS_ASSERT(IsEvalCacheCandidate(script));
// Get the source string passed for safekeeping in the atom map
@ -66,10 +67,9 @@ EvalCacheHashPolicy::match(RawScript script, const EvalCacheLookup &l)
JSAtom *keyStr = script->atoms[0];
return EqualStrings(keyStr, l.str) &&
script->getCallerFunction() == l.caller &&
script->staticLevel == l.staticLevel &&
cacheEntry.callerScript == l.callerScript &&
script->getVersion() == l.version &&
script->compartment() == l.compartment;
cacheEntry.pc == l.pc;
}
// There are two things we want to do with each script executed in EvalKernel:
@ -100,23 +100,23 @@ class EvalScriptGuard
CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
script_->isActiveEval = false;
script_->isCachedEval = true;
EvalCacheEntry cacheEntry = {script_, lookup_.callerScript, lookup_.pc};
lookup_.str = lookupStr_;
if (lookup_.str && IsEvalCacheCandidate(script_))
cx_->runtime->evalCache.relookupOrAdd(p_, lookup_, script_);
cx_->runtime->evalCache.relookupOrAdd(p_, lookup_, cacheEntry);
}
}
void lookupInEvalCache(JSLinearString *str, JSFunction *caller, unsigned staticLevel)
void lookupInEvalCache(JSLinearString *str, JSScript *callerScript, jsbytecode *pc)
{
lookupStr_ = str;
lookup_.str = str;
lookup_.caller = caller;
lookup_.staticLevel = staticLevel;
lookup_.callerScript = callerScript;
lookup_.version = cx_->findVersion();
lookup_.compartment = cx_->compartment;
lookup_.pc = pc;
p_ = cx_->runtime->evalCache.lookupForAdd(lookup_);
if (p_) {
script_ = *p_;
script_ = p_->script;
cx_->runtime->evalCache.remove(p_);
CallNewScriptHook(cx_, script_, NullPtr());
script_->isCachedEval = false;
@ -205,9 +205,10 @@ enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIR
// On success, store the completion value in call.rval and return true.
static bool
EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFramePtr caller,
HandleObject scopeobj)
HandleObject scopeobj, jsbytecode *pc)
{
JS_ASSERT((evalType == INDIRECT_EVAL) == !caller);
JS_ASSERT((evalType == INDIRECT_EVAL) == !pc);
JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal());
AssertInnerizedScopeChain(cx, *scopeobj);
@ -273,7 +274,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
EvalScriptGuard esg(cx);
if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
esg.lookupInEvalCache(stableStr, caller.fun(), staticLevel);
esg.lookupInEvalCache(stableStr, callerScript, pc);
if (!esg.foundScript()) {
unsigned lineno;
@ -306,7 +307,7 @@ bool
js::DirectEvalFromIon(JSContext *cx,
HandleObject scopeobj, HandleScript callerScript,
HandleValue thisValue, HandleString str,
MutableHandleValue vp)
jsbytecode *pc, MutableHandleValue vp)
{
AssertInnerizedScopeChain(cx, *scopeobj);
@ -336,7 +337,7 @@ js::DirectEvalFromIon(JSContext *cx,
// Ion will not perform cross compartment direct eval calls.
JSPrincipals *principals = cx->compartment->principals;
esg.lookupInEvalCache(stableStr, callerScript->function(), staticLevel);
esg.lookupInEvalCache(stableStr, callerScript, pc);
if (!esg.foundScript()) {
unsigned lineno;
@ -399,23 +400,26 @@ js::IndirectEval(JSContext *cx, unsigned argc, Value *vp)
return false;
Rooted<GlobalObject*> global(cx, &args.callee().global());
return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global);
return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global, NULL);
}
bool
js::DirectEval(JSContext *cx, const CallArgs &args)
{
// Direct eval can assume it was called from an interpreted frame.
StackFrame *caller = cx->fp();
JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
JS_ASSERT_IF(caller->isFunctionFrame(),
caller->compartment() == caller->callee().compartment());
// Direct eval can assume it was called from an interpreted or baseline frame.
ScriptFrameIter iter(cx);
AbstractFramePtr caller = iter.abstractFramePtr();
JS_ASSERT(IsBuiltinEvalForScope(caller.scopeChain(), args.calleev()));
JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL);
JS_ASSERT_IF(caller.isFunctionFrame(),
caller.compartment() == caller.callee().compartment());
if (!WarnOnTooManyArgs(cx, args))
return false;
return EvalKernel(cx, args, DIRECT_EVAL, caller, caller->scopeChain());
RootedObject scopeChain(cx, caller.scopeChain());
return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc());
}
bool

View File

@ -31,7 +31,7 @@ extern bool
DirectEvalFromIon(JSContext *cx,
HandleObject scopeObj, HandleScript callerScript,
HandleValue thisValue, HandleString str,
MutableHandleValue vp);
jsbytecode * pc, MutableHandleValue vp);
// True iff 'v' is the built-in eval function for the global object that
// corresponds to 'scopeChain'.

View File

@ -1711,7 +1711,7 @@ CodeGenerator::visitFilterArguments(LFilterArguments *lir)
}
typedef bool (*DirectEvalFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleString,
MutableHandleValue);
jsbytecode *, MutableHandleValue);
static const VMFunction DirectEvalInfo = FunctionInfo<DirectEvalFn>(DirectEvalFromIon);
bool
@ -1720,6 +1720,7 @@ CodeGenerator::visitCallDirectEval(LCallDirectEval *lir)
Register scopeChain = ToRegister(lir->getScopeChain());
Register string = ToRegister(lir->getString());
pushArg(ImmWord(lir->mir()->pc()));
pushArg(string);
pushArg(ToValue(lir, LCallDirectEval::ThisValueInput));
pushArg(ImmGCPtr(gen->info().script()));

View File

@ -4478,7 +4478,7 @@ IonBuilder::jsop_eval(uint32_t argc)
MInstruction *filterArguments = MFilterArguments::New(string);
current->add(filterArguments);
MInstruction *ins = MCallDirectEval::New(scopeChain, string, thisValue);
MInstruction *ins = MCallDirectEval::New(scopeChain, string, thisValue, pc);
current->add(ins);
current->push(ins);

View File

@ -1495,7 +1495,9 @@ class MCallDirectEval
public MixPolicy<ObjectPolicy<0>, MixPolicy<StringPolicy<1>, BoxPolicy<2> > >
{
protected:
MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue)
MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
jsbytecode *pc)
: pc_(pc)
{
setOperand(0, scopeChain);
setOperand(1, string);
@ -1507,8 +1509,9 @@ class MCallDirectEval
INSTRUCTION_HEADER(CallDirectEval)
static MCallDirectEval *
New(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue) {
return new MCallDirectEval(scopeChain, string, thisValue);
New(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
jsbytecode *pc) {
return new MCallDirectEval(scopeChain, string, thisValue, pc);
}
MDefinition *getScopeChain() const {
@ -1521,9 +1524,16 @@ class MCallDirectEval
return getOperand(2);
}
jsbytecode *pc() const {
return pc_;
}
TypePolicy *typePolicy() {
return this;
}
private:
jsbytecode *pc_;
};
class MBinaryInstruction : public MAryInstruction<2>

View File

@ -274,6 +274,7 @@ template <> struct OutParamToDataType<MutableHandleValue> { static const DataTyp
#define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3)
#define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4)
#define FOR_EACH_ARGS_5(Macro, Sep, Last) FOR_EACH_ARGS_4(Macro, Sep, Sep) Macro(5) Last(5)
#define FOR_EACH_ARGS_6(Macro, Sep, Last) FOR_EACH_ARGS_5(Macro, Sep, Sep) Macro(6) Last(6)
#define COMPUTE_INDEX(NbArg) NbArg
#define COMPUTE_OUTPARAM_RESULT(NbArg) OutParamToDataType<A ## NbArg>::result
@ -370,6 +371,12 @@ template <class R, class A1, class A2, class A3, class A4, class A5>
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_5)
};
template <class R, class A1, class A2, class A3, class A4, class A5, class A6>
struct FunctionInfo<R (*)(JSContext *, A1, A2, A3, A4, A5, A6)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2, A3, A4, A5, A6);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_6)
};
#undef FUNCTION_INFO_STRUCT_BODY
#undef FOR_EACH_ARGS_5

View File

@ -226,14 +226,20 @@ class SourceDataCache
void purge();
};
struct EvalCacheEntry
{
JSScript *script;
JSScript *callerScript;
jsbytecode *pc;
};
struct EvalCacheLookup
{
EvalCacheLookup(JSContext *cx) : str(cx), caller(cx) {}
EvalCacheLookup(JSContext *cx) : str(cx), callerScript(cx) {}
RootedLinearString str;
RootedFunction caller;
unsigned staticLevel;
RootedScript callerScript;
JSVersion version;
JSCompartment *compartment;
jsbytecode *pc;
};
struct EvalCacheHashPolicy
@ -241,10 +247,10 @@ struct EvalCacheHashPolicy
typedef EvalCacheLookup Lookup;
static HashNumber hash(const Lookup &l);
static bool match(RawScript script, const EvalCacheLookup &l);
static bool match(const EvalCacheEntry &entry, const EvalCacheLookup &l);
};
typedef HashSet<RawScript, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
class NativeIterCache
{