mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
[INFER] Monitor CALLPROP calls whose callee is not statically known, bug 660204.
This commit is contained in:
parent
e1be20257a
commit
debff956bd
12
js/src/jit-test/tests/basic/bug660204.js
Normal file
12
js/src/jit-test/tests/basic/bug660204.js
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
eval("try { name(); } catch(e) {}");
|
||||
function Employee ( name, dept ) {
|
||||
this.name=name || ""
|
||||
this.dept
|
||||
}
|
||||
function WorkerBee ( name, dept, projs ) {
|
||||
this.base=Employee
|
||||
this.base( name, print("WHAT"))
|
||||
}
|
||||
new WorkerBee;
|
||||
WorkerBee();
|
@ -980,7 +980,7 @@ class ScriptAnalysis
|
||||
JS_ASSERT_IF(script->code[offset] != JSOP_TRAP,
|
||||
which < GetDefCount(script, offset) +
|
||||
(ExtendedDef(script->code + offset) ? 1 : 0));
|
||||
types::TypeSet *array = (types::TypeSet *) (~0x1 & (size_t) getCode(offset).pushedTypes);
|
||||
types::TypeSet *array = getCode(offset).pushedTypes;
|
||||
JS_ASSERT(array);
|
||||
return array + which;
|
||||
}
|
||||
@ -988,6 +988,8 @@ class ScriptAnalysis
|
||||
return pushedTypes(pc - script->code, which);
|
||||
}
|
||||
|
||||
bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; }
|
||||
|
||||
types::TypeBarrier *typeBarriers(uint32 offset) {
|
||||
if (getCode(offset).typeBarriers)
|
||||
pruneTypeBarriers(offset);
|
||||
@ -1083,6 +1085,15 @@ class ScriptAnalysis
|
||||
}
|
||||
LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); }
|
||||
|
||||
/* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */
|
||||
jsbytecode *getCallPC(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(js_CodeSpec[*pc].format & JOF_CALLOP);
|
||||
SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 1));
|
||||
JS_ASSERT(uses && !uses->next && uses->popped);
|
||||
return script->code + uses->offset;
|
||||
}
|
||||
|
||||
/* Accessors for local variable information. */
|
||||
|
||||
bool localHasUseBeforeDef(uint32 local) {
|
||||
|
@ -543,6 +543,46 @@ TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, true));
|
||||
}
|
||||
|
||||
/*
|
||||
* Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM.
|
||||
* These are derived from the types on the properties themselves, rather than
|
||||
* those pushed in the 'this' slot at the call site, which allows us to retain
|
||||
* correlations between the type of the 'this' object and the associated
|
||||
* callee scripts at polymorphic call sites.
|
||||
*/
|
||||
class TypeConstraintCallProp : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
jsbytecode *callpc;
|
||||
|
||||
/* Property being accessed. */
|
||||
jsid id;
|
||||
|
||||
TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
|
||||
: TypeConstraint("callprop", script), callpc(callpc), id(id)
|
||||
{
|
||||
JS_ASSERT(script && callpc);
|
||||
}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, jstype type);
|
||||
};
|
||||
|
||||
void
|
||||
TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
|
||||
{
|
||||
/*
|
||||
* For calls which will go through JSOP_NEW, don't add any constraints to
|
||||
* modify the 'this' types of callees. The initial 'this' value will be
|
||||
* outright ignored.
|
||||
*/
|
||||
jsbytecode *callpc = script->analysis(cx)->getCallPC(pc);
|
||||
UntrapOpcode untrap(cx, script, callpc);
|
||||
if (JSOp(*callpc) == JSOP_NEW)
|
||||
return;
|
||||
|
||||
add(cx, ArenaNew<TypeConstraintCallProp>(cx->compartment->pool, script, callpc, id));
|
||||
}
|
||||
|
||||
/* Constraints for determining the 'this' object at sites invoked using 'new'. */
|
||||
class TypeConstraintNewObject : public TypeConstraint
|
||||
{
|
||||
@ -638,10 +678,11 @@ TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
|
||||
class TypeConstraintPropagateThis : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
jsbytecode *callpc;
|
||||
jstype type;
|
||||
|
||||
TypeConstraintPropagateThis(JSScript *script, jstype type)
|
||||
: TypeConstraint("propagatethis", script), type(type)
|
||||
TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, jstype type)
|
||||
: TypeConstraint("propagatethis", script), callpc(callpc), type(type)
|
||||
{}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, jstype type);
|
||||
@ -650,22 +691,13 @@ public:
|
||||
void
|
||||
TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
|
||||
{
|
||||
/*
|
||||
* If this will definitely be popped by a JSOP_NEW, don't add a constraint
|
||||
* to modify the 'this' types of callees. The initial 'this' value will be
|
||||
* outright ignored.
|
||||
*/
|
||||
SSAValue calleev = SSAValue::PushedValue(pc - script->code, 0);
|
||||
SSAUseChain *uses = script->analysis(cx)->useChain(calleev);
|
||||
/* Don't add constraints when the call will be 'new' (see addCallProperty). */
|
||||
jsbytecode *callpc = script->analysis(cx)->getCallPC(pc);
|
||||
UntrapOpcode untrap(cx, script, callpc);
|
||||
if (JSOp(*callpc) == JSOP_NEW)
|
||||
return;
|
||||
|
||||
if (uses && !uses->next && uses->popped) {
|
||||
jsbytecode *callpc = script->code + uses->offset;
|
||||
UntrapOpcode untrap(cx, script, callpc);
|
||||
if (JSOp(*callpc) == JSOP_NEW)
|
||||
return;
|
||||
}
|
||||
|
||||
add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, type));
|
||||
add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
|
||||
}
|
||||
|
||||
/* Subset constraint which filters out primitive types. */
|
||||
@ -974,7 +1006,7 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
}
|
||||
|
||||
if (type == TYPE_LAZYARGS) {
|
||||
/* Catch cases which will be accounted for by the followEscapingArguments analysis. */
|
||||
/* Ignore cases which will be accounted for by the followEscapingArguments analysis. */
|
||||
if (assign || (id != JSID_VOID && id != id_length(cx)))
|
||||
return;
|
||||
|
||||
@ -986,16 +1018,38 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
}
|
||||
|
||||
TypeObject *object = GetPropertyObject(cx, script, type);
|
||||
if (object) {
|
||||
if (object)
|
||||
PropertyAccess(cx, script, pc, object, assign, target, id);
|
||||
}
|
||||
|
||||
if (!object->unknownProperties() &&
|
||||
(JSOp(*pc) == JSOP_CALLPROP || JSOp(*pc) == JSOP_CALLELEM)) {
|
||||
JS_ASSERT(!assign);
|
||||
void
|
||||
TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
UntrapOpcode untrap(cx, script, callpc);
|
||||
|
||||
/*
|
||||
* For CALLPROP and CALLELEM, we need to update not just the pushed types
|
||||
* but also the 'this' types of possible callees. If we can't figure out
|
||||
* that set of callees, monitor the call to make sure discovered callees
|
||||
* get their 'this' types updated.
|
||||
*/
|
||||
|
||||
if (type == TYPE_UNKNOWN || (!TypeIsObject(type) && !script->global)) {
|
||||
cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
|
||||
return;
|
||||
}
|
||||
|
||||
TypeObject *object = GetPropertyObject(cx, script, type);
|
||||
if (object) {
|
||||
if (object->unknownProperties()) {
|
||||
cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
|
||||
} else {
|
||||
TypeSet *types = object->getProperty(cx, id, false);
|
||||
if (!types)
|
||||
return;
|
||||
types->addPropagateThis(cx, script, pc, type);
|
||||
/* Bypass addPropagateThis, we already have the callpc. */
|
||||
types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
|
||||
script, callpc, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1175,13 +1229,22 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
void
|
||||
TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
/*
|
||||
* Ignore callees that are calling natives or where the callee is unknown;
|
||||
* the latter will be marked as monitored by a TypeConstraintCall.
|
||||
*/
|
||||
if (type == TYPE_UNKNOWN || !TypeIsObject(type))
|
||||
if (type == TYPE_UNKNOWN) {
|
||||
/*
|
||||
* The callee is unknown, make sure the call is monitored so we pick up
|
||||
* possible this/callee correlations. This only comes into play for
|
||||
* CALLPROP and CALLELEM, for other calls we are past the type barrier
|
||||
* already and a TypeConstraintCall will also monitor the call.
|
||||
*/
|
||||
cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore calls to primitives, these will go through a stub. */
|
||||
if (!TypeIsObject(type))
|
||||
return;
|
||||
|
||||
/* Ignore calls to natives, these will be handled by TypeConstraintCall. */
|
||||
TypeObject *object = (TypeObject*) type;
|
||||
if (object->unknownProperties() || !object->isFunction)
|
||||
return;
|
||||
@ -2279,41 +2342,14 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
||||
static inline bool
|
||||
MonitorResultUnknown(JSOp op)
|
||||
{
|
||||
if (script->analysis(cx)->getCode(offset).monitoredTypes)
|
||||
return;
|
||||
|
||||
jsbytecode *pc = script->code + offset;
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
|
||||
/*
|
||||
* Make sure monitoring is limited to property sets and calls where the
|
||||
* target of the set/call could be statically unknown, and mark the bytecode
|
||||
* results as unknown.
|
||||
* Opcodes which can be monitored and whose result should be marked as
|
||||
* unknown when doing so. :XXX: should use type barriers at calls.
|
||||
*/
|
||||
JSOp op = JSOp(*pc);
|
||||
switch (op) {
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_SETGNAME:
|
||||
case JSOP_SETXMLNAME:
|
||||
case JSOP_SETCONST:
|
||||
case JSOP_SETELEM:
|
||||
case JSOP_SETHOLE:
|
||||
case JSOP_SETPROP:
|
||||
case JSOP_SETMETHOD:
|
||||
case JSOP_INITPROP:
|
||||
case JSOP_INITMETHOD:
|
||||
case JSOP_FORPROP:
|
||||
case JSOP_FORNAME:
|
||||
case JSOP_FORGNAME:
|
||||
case JSOP_ENUMELEM:
|
||||
case JSOP_ENUMCONSTELEM:
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFFUN_FC:
|
||||
case JSOP_ARRAYPUSH:
|
||||
break;
|
||||
case JSOP_INCNAME:
|
||||
case JSOP_DECNAME:
|
||||
case JSOP_NAMEINC:
|
||||
@ -2335,11 +2371,30 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
||||
case JSOP_FUNCALL:
|
||||
case JSOP_FUNAPPLY:
|
||||
case JSOP_NEW:
|
||||
script->analysis(cx)->addPushedType(cx, offset, 0, TYPE_UNKNOWN);
|
||||
break;
|
||||
return true;
|
||||
default:
|
||||
TypeFailure(cx, "Monitoring unknown bytecode at #%u:%05u", script->id(), offset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
||||
{
|
||||
ScriptAnalysis *analysis = script->analysis(cx);
|
||||
|
||||
if (analysis->getCode(offset).monitoredTypes)
|
||||
return;
|
||||
|
||||
jsbytecode *pc = script->code + offset;
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
|
||||
/*
|
||||
* We may end up monitoring opcodes before even analyzing them, as we can
|
||||
* peek forward in CALLPROP and CALLELEM ops. Don't add the unknown result
|
||||
* yet in this case, we will do so when analyzing the opcode.
|
||||
*/
|
||||
if (MonitorResultUnknown(JSOp(*pc)) && analysis->hasPushedTypes(pc))
|
||||
analysis->addPushedType(cx, offset, 0, TYPE_UNKNOWN);
|
||||
|
||||
InferSpew(ISpewOps, "addMonitorNeeded: #%u:%05u", script->id(), offset);
|
||||
|
||||
@ -3195,6 +3250,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
InferSpew(ISpewOps, "typeSet: T%p pushed%u #%u:%05u", &pushed[i], i, script->id(), offset);
|
||||
}
|
||||
|
||||
/* Add unknown result for opcodes which were monitored before being analyzed. */
|
||||
if (code.monitoredTypes && MonitorResultUnknown(op))
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
|
||||
/* Add type constraints for the various opcodes. */
|
||||
switch (op) {
|
||||
|
||||
@ -3563,12 +3622,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
jsid id = GetAtomId(cx, script, pc, 0);
|
||||
TypeSet *seen = script->bytecodeTypes(pc);
|
||||
|
||||
/*
|
||||
* For JSOP_CALLPROP, this will inspect the pc and add PropagateThis
|
||||
* constraints. Different types for the receiver may be correlated with
|
||||
* different callee scripts, and we want to retain such correlations.
|
||||
*/
|
||||
poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
|
||||
if (op == JSOP_CALLPROP)
|
||||
poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
|
||||
|
||||
seen->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLPROP)
|
||||
@ -3596,8 +3652,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
case JSOP_CALLELEM: {
|
||||
TypeSet *seen = script->bytecodeTypes(pc);
|
||||
|
||||
/* Ditto the JSOP_CALLPROP case for propagating 'this'. */
|
||||
poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
|
||||
if (op == JSOP_CALLELEM)
|
||||
poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
|
||||
|
||||
seen->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLELEM)
|
||||
|
@ -366,6 +366,7 @@ class TypeSet
|
||||
TypeSet *target, jsid id);
|
||||
void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id);
|
||||
void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
|
||||
void addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target);
|
||||
void addCall(JSContext *cx, TypeCallsite *site);
|
||||
void addArith(JSContext *cx, JSScript *script,
|
||||
|
Loading…
Reference in New Issue
Block a user