Use distinct types and scripts for generic inner wrapper functions, bug 638794. r=jandem

This commit is contained in:
Brian Hackett 2012-07-18 06:35:00 -06:00
parent 673d7cb15b
commit 9ade53ec36
3 changed files with 83 additions and 11 deletions

View File

@ -1268,7 +1268,7 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
clone->initializeExtended();
}
if (cx->compartment == fun->compartment()) {
if (cx->compartment == fun->compartment() && !types::UseNewTypeForClone(fun)) {
/*
* We can use the same type as the original function provided that (a)
* its prototype is correct, and (b) its type is not a singleton. The
@ -1279,6 +1279,9 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
if (fun->getProto() == proto && !fun->hasSingletonType())
clone->setType(fun->type());
} else {
if (!clone->setSingletonType(cx))
return NULL;
/*
* Across compartments we have to clone the script for interpreted
* functions. Cross-compartment cloning only happens via JSAPI
@ -1289,22 +1292,24 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
RootedScript script(cx, clone->script());
JS_ASSERT(script);
JS_ASSERT(script->compartment() == fun->compartment());
JS_ASSERT(script->compartment() != cx->compartment);
JS_ASSERT(!script->enclosingStaticScope());
JS_ASSERT_IF(script->compartment() != cx->compartment,
!script->enclosingStaticScope() && !script->compileAndGo);
RootedObject scope(cx, script->enclosingStaticScope());
clone->mutableScript().init(NULL);
JSScript *cscript = CloneScript(cx, NullPtr(), clone, script);
JSScript *cscript = CloneScript(cx, scope, clone, script);
if (!cscript)
return NULL;
clone->setScript(cscript);
cscript->setFunction(clone);
if (!clone->setTypeForScriptedFunction(cx))
return NULL;
GlobalObject *global = script->compileAndGo ? &script->global() : NULL;
js_CallNewScriptHook(cx, clone->script(), clone);
Debugger::onNewScript(cx, clone->script(), NULL);
Debugger::onNewScript(cx, clone->script(), global);
}
}
return clone;

View File

@ -3629,7 +3629,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
res = &pushed[0];
if (res) {
if (script->hasGlobal())
if (script->hasGlobal() && !UseNewTypeForClone(obj->toFunction()))
res->addType(cx, Type::ObjectType(obj));
else
res->addType(cx, Type::UnknownType());
@ -3926,12 +3926,14 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
pushed[0].addType(cx, Type::UnknownType());
break;
case JSOP_CALLEE:
if (script->hasGlobal())
pushed[0].addType(cx, Type::ObjectType(script->function()));
case JSOP_CALLEE: {
JSFunction *fun = script->function();
if (script->hasGlobal() && !UseNewTypeForClone(fun))
pushed[0].addType(cx, Type::ObjectType(fun));
else
pushed[0].addType(cx, Type::UnknownType());
break;
}
default:
/* Display fine-grained debug information first */
@ -5040,6 +5042,11 @@ JSFunction::setTypeForScriptedFunction(JSContext *cx, bool singleton)
if (singleton) {
if (!setSingletonType(cx))
return false;
} else if (UseNewTypeForClone(this)) {
/*
* Leave the default unknown-properties type for the function, it
* should not be used by scripts or appear in type sets.
*/
} else {
RootedFunction self(cx, this);

View File

@ -489,6 +489,66 @@ UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
UseNewType(cx, fp->prev()->script(), fp->prevpc());
}
inline bool
UseNewTypeForClone(JSFunction *fun)
{
if (fun->hasSingletonType() || !fun->isInterpreted())
return false;
/*
* When a function is being used as a wrapper for another function, it
* improves precision greatly to distinguish between different instances of
* the wrapper; otherwise we will conflate much of the information about
* the wrapped functions.
*
* An important example is the Class.create function at the core of the
* Prototype.js library, which looks like:
*
* var Class = {
* create: function() {
* return function() {
* this.initialize.apply(this, arguments);
* }
* }
* };
*
* Each instance of the innermost function will have a different wrapped
* initialize method. We capture this, along with similar cases, by looking
* for short scripts which use both .apply and arguments. For such scripts,
* whenever creating a new instance of the function we both give that
* instance a singleton type and clone the underlying script.
*/
JSScript *script = fun->script();
if (script->length >= 50)
return false;
if (script->hasConsts() ||
script->hasObjects() ||
script->hasRegexps() ||
script->hasClosedArgs() ||
script->hasClosedVars())
{
return false;
}
bool hasArguments = false;
bool hasApply = false;
for (jsbytecode *pc = script->code;
pc != script->code + script->length;
pc += GetBytecodeLength(pc))
{
if (*pc == JSOP_ARGUMENTS)
hasArguments = true;
if (*pc == JSOP_FUNAPPLY)
hasApply = true;
}
return hasArguments && hasApply;
}
/////////////////////////////////////////////////////////////////////
// Script interface functions
/////////////////////////////////////////////////////////////////////