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,
|
JS_ASSERT_IF(script->code[offset] != JSOP_TRAP,
|
||||||
which < GetDefCount(script, offset) +
|
which < GetDefCount(script, offset) +
|
||||||
(ExtendedDef(script->code + offset) ? 1 : 0));
|
(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);
|
JS_ASSERT(array);
|
||||||
return array + which;
|
return array + which;
|
||||||
}
|
}
|
||||||
@ -988,6 +988,8 @@ class ScriptAnalysis
|
|||||||
return pushedTypes(pc - script->code, which);
|
return pushedTypes(pc - script->code, which);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; }
|
||||||
|
|
||||||
types::TypeBarrier *typeBarriers(uint32 offset) {
|
types::TypeBarrier *typeBarriers(uint32 offset) {
|
||||||
if (getCode(offset).typeBarriers)
|
if (getCode(offset).typeBarriers)
|
||||||
pruneTypeBarriers(offset);
|
pruneTypeBarriers(offset);
|
||||||
@ -1083,6 +1085,15 @@ class ScriptAnalysis
|
|||||||
}
|
}
|
||||||
LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); }
|
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. */
|
/* Accessors for local variable information. */
|
||||||
|
|
||||||
bool localHasUseBeforeDef(uint32 local) {
|
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));
|
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'. */
|
/* Constraints for determining the 'this' object at sites invoked using 'new'. */
|
||||||
class TypeConstraintNewObject : public TypeConstraint
|
class TypeConstraintNewObject : public TypeConstraint
|
||||||
{
|
{
|
||||||
@ -638,10 +678,11 @@ TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
|
|||||||
class TypeConstraintPropagateThis : public TypeConstraint
|
class TypeConstraintPropagateThis : public TypeConstraint
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
jsbytecode *callpc;
|
||||||
jstype type;
|
jstype type;
|
||||||
|
|
||||||
TypeConstraintPropagateThis(JSScript *script, jstype type)
|
TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, jstype type)
|
||||||
: TypeConstraint("propagatethis", script), type(type)
|
: TypeConstraint("propagatethis", script), callpc(callpc), type(type)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void newType(JSContext *cx, TypeSet *source, jstype type);
|
void newType(JSContext *cx, TypeSet *source, jstype type);
|
||||||
@ -650,22 +691,13 @@ public:
|
|||||||
void
|
void
|
||||||
TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
|
TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
|
||||||
{
|
{
|
||||||
/*
|
/* Don't add constraints when the call will be 'new' (see addCallProperty). */
|
||||||
* If this will definitely be popped by a JSOP_NEW, don't add a constraint
|
jsbytecode *callpc = script->analysis(cx)->getCallPC(pc);
|
||||||
* to modify the 'this' types of callees. The initial 'this' value will be
|
UntrapOpcode untrap(cx, script, callpc);
|
||||||
* outright ignored.
|
if (JSOp(*callpc) == JSOP_NEW)
|
||||||
*/
|
return;
|
||||||
SSAValue calleev = SSAValue::PushedValue(pc - script->code, 0);
|
|
||||||
SSAUseChain *uses = script->analysis(cx)->useChain(calleev);
|
|
||||||
|
|
||||||
if (uses && !uses->next && uses->popped) {
|
add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subset constraint which filters out primitive types. */
|
/* Subset constraint which filters out primitive types. */
|
||||||
@ -974,7 +1006,7 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == TYPE_LAZYARGS) {
|
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)))
|
if (assign || (id != JSID_VOID && id != id_length(cx)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -986,16 +1018,38 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TypeObject *object = GetPropertyObject(cx, script, type);
|
TypeObject *object = GetPropertyObject(cx, script, type);
|
||||||
if (object) {
|
if (object)
|
||||||
PropertyAccess(cx, script, pc, object, assign, target, id);
|
PropertyAccess(cx, script, pc, object, assign, target, id);
|
||||||
|
}
|
||||||
|
|
||||||
if (!object->unknownProperties() &&
|
void
|
||||||
(JSOp(*pc) == JSOP_CALLPROP || JSOp(*pc) == JSOP_CALLELEM)) {
|
TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||||
JS_ASSERT(!assign);
|
{
|
||||||
|
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);
|
TypeSet *types = object->getProperty(cx, id, false);
|
||||||
if (!types)
|
if (!types)
|
||||||
return;
|
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
|
void
|
||||||
TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, jstype type)
|
TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||||
{
|
{
|
||||||
/*
|
if (type == TYPE_UNKNOWN) {
|
||||||
* Ignore callees that are calling natives or where the callee is unknown;
|
/*
|
||||||
* the latter will be marked as monitored by a TypeConstraintCall.
|
* The callee is unknown, make sure the call is monitored so we pick up
|
||||||
*/
|
* possible this/callee correlations. This only comes into play for
|
||||||
if (type == TYPE_UNKNOWN || !TypeIsObject(type))
|
* 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;
|
return;
|
||||||
|
|
||||||
|
/* Ignore calls to natives, these will be handled by TypeConstraintCall. */
|
||||||
TypeObject *object = (TypeObject*) type;
|
TypeObject *object = (TypeObject*) type;
|
||||||
if (object->unknownProperties() || !object->isFunction)
|
if (object->unknownProperties() || !object->isFunction)
|
||||||
return;
|
return;
|
||||||
@ -2279,41 +2342,14 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static inline bool
|
||||||
TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
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
|
* Opcodes which can be monitored and whose result should be marked as
|
||||||
* target of the set/call could be statically unknown, and mark the bytecode
|
* unknown when doing so. :XXX: should use type barriers at calls.
|
||||||
* results as unknown.
|
|
||||||
*/
|
*/
|
||||||
JSOp op = JSOp(*pc);
|
|
||||||
switch (op) {
|
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_INCNAME:
|
||||||
case JSOP_DECNAME:
|
case JSOP_DECNAME:
|
||||||
case JSOP_NAMEINC:
|
case JSOP_NAMEINC:
|
||||||
@ -2335,11 +2371,30 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
|
|||||||
case JSOP_FUNCALL:
|
case JSOP_FUNCALL:
|
||||||
case JSOP_FUNAPPLY:
|
case JSOP_FUNAPPLY:
|
||||||
case JSOP_NEW:
|
case JSOP_NEW:
|
||||||
script->analysis(cx)->addPushedType(cx, offset, 0, TYPE_UNKNOWN);
|
return true;
|
||||||
break;
|
|
||||||
default:
|
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);
|
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);
|
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. */
|
/* Add type constraints for the various opcodes. */
|
||||||
switch (op) {
|
switch (op) {
|
||||||
|
|
||||||
@ -3563,12 +3622,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||||||
jsid id = GetAtomId(cx, script, pc, 0);
|
jsid id = GetAtomId(cx, script, pc, 0);
|
||||||
TypeSet *seen = script->bytecodeTypes(pc);
|
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);
|
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]);
|
seen->addSubset(cx, script, &pushed[0]);
|
||||||
if (op == JSOP_CALLPROP)
|
if (op == JSOP_CALLPROP)
|
||||||
@ -3596,8 +3652,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||||||
case JSOP_CALLELEM: {
|
case JSOP_CALLELEM: {
|
||||||
TypeSet *seen = script->bytecodeTypes(pc);
|
TypeSet *seen = script->bytecodeTypes(pc);
|
||||||
|
|
||||||
/* Ditto the JSOP_CALLPROP case for propagating 'this'. */
|
|
||||||
poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
|
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]);
|
seen->addSubset(cx, script, &pushed[0]);
|
||||||
if (op == JSOP_CALLELEM)
|
if (op == JSOP_CALLELEM)
|
||||||
|
@ -366,6 +366,7 @@ class TypeSet
|
|||||||
TypeSet *target, jsid id);
|
TypeSet *target, jsid id);
|
||||||
void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||||
TypeSet *target, jsid id);
|
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 addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target);
|
||||||
void addCall(JSContext *cx, TypeCallsite *site);
|
void addCall(JSContext *cx, TypeCallsite *site);
|
||||||
void addArith(JSContext *cx, JSScript *script,
|
void addArith(JSContext *cx, JSScript *script,
|
||||||
|
Loading…
Reference in New Issue
Block a user