mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
[INFER] Infer arithmetic on objects as producing ints, bug 642412.
This commit is contained in:
parent
236d9c0de4
commit
6b4af544cb
@ -1128,55 +1128,53 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
void
|
||||
TypeConstraintArith::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
/*
|
||||
* We only model a subset of the arithmetic behavior that is actually
|
||||
* possible. The following need to be watched for at runtime:
|
||||
*
|
||||
* 1. Operations producing a double where no operand was a double.
|
||||
* 2. Operations producing a string where no operand was a string (addition only).
|
||||
* 3. Operations producing a value other than int/double/string.
|
||||
*/
|
||||
if (other) {
|
||||
/*
|
||||
* Addition operation, consider these cases:
|
||||
* {int,bool} x {int,bool} -> int
|
||||
* float x {int,bool,float} -> float
|
||||
* double x {int,bool,double} -> double
|
||||
* string x any -> string
|
||||
*/
|
||||
switch (type) {
|
||||
case TYPE_UNDEFINED:
|
||||
case TYPE_NULL:
|
||||
case TYPE_INT32:
|
||||
case TYPE_BOOLEAN:
|
||||
/* Note: need to account for overflows from, e.g. int + void */
|
||||
if (other->typeFlags & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
|
||||
TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN))
|
||||
target->addType(cx, TYPE_INT32);
|
||||
if (other->typeFlags & TYPE_FLAG_DOUBLE)
|
||||
target->addType(cx, TYPE_DOUBLE);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
if (other->typeFlags & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
|
||||
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN))
|
||||
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN) ||
|
||||
other->objectCount != 0) {
|
||||
target->addType(cx, TYPE_DOUBLE);
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
target->addType(cx, TYPE_STRING);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Don't try to model arithmetic on objects, this can invoke valueOf,
|
||||
* operate on XML objects, etc.
|
||||
*/
|
||||
case TYPE_UNKNOWN:
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
default:
|
||||
if (other->typeFlags & (TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
|
||||
TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN) ||
|
||||
other->objectCount != 0) {
|
||||
target->addType(cx, TYPE_INT32);
|
||||
}
|
||||
if (other->typeFlags & TYPE_FLAG_DOUBLE)
|
||||
target->addType(cx, TYPE_DOUBLE);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Note: same issues with undefined as addition. */
|
||||
switch (type) {
|
||||
case TYPE_UNDEFINED:
|
||||
case TYPE_NULL:
|
||||
case TYPE_INT32:
|
||||
case TYPE_BOOLEAN:
|
||||
target->addType(cx, TYPE_INT32);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
target->addType(cx, TYPE_DOUBLE);
|
||||
break;
|
||||
default:
|
||||
case TYPE_UNKNOWN:
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
default:
|
||||
target->addType(cx, TYPE_INT32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -555,6 +555,12 @@ JSScript::typeMonitorUndefined(JSContext *cx, const jsbytecode *pc)
|
||||
return typeMonitorResult(cx, pc, js::types::TYPE_UNDEFINED);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::typeMonitorString(JSContext *cx, const jsbytecode *pc)
|
||||
{
|
||||
return typeMonitorResult(cx, pc, js::types::TYPE_STRING);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::typeMonitorUnknown(JSContext *cx, const jsbytecode *pc)
|
||||
{
|
||||
|
@ -3755,12 +3755,15 @@ BEGIN_CASE(JSOP_ADD)
|
||||
goto error;
|
||||
regs.sp--;
|
||||
regs.sp[-1] = rval;
|
||||
if (!script->typeMonitorUnknown(cx, regs.pc))
|
||||
goto error;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (lval.isObject())
|
||||
bool lIsObject, rIsObject;
|
||||
if ((lIsObject = lval.isObject()))
|
||||
DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
|
||||
if (rval.isObject())
|
||||
if ((rIsObject = rval.isObject()))
|
||||
DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
|
||||
bool lIsString, rIsString;
|
||||
if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
|
||||
@ -3784,6 +3787,8 @@ BEGIN_CASE(JSOP_ADD)
|
||||
JSString *str = js_ConcatStrings(cx, lstr, rstr);
|
||||
if (!str)
|
||||
goto error;
|
||||
if ((lIsObject || rIsObject) && !script->typeMonitorString(cx, regs.pc))
|
||||
goto error;
|
||||
regs.sp--;
|
||||
regs.sp[-1].setString(str);
|
||||
} else {
|
||||
@ -3793,7 +3798,7 @@ BEGIN_CASE(JSOP_ADD)
|
||||
l += r;
|
||||
regs.sp--;
|
||||
if (!regs.sp[-1].setNumber(l) &&
|
||||
!(lval.isDouble() || rval.isDouble()) &&
|
||||
(lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble())) &&
|
||||
!script->typeMonitorOverflow(cx, regs.pc)) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -395,7 +395,9 @@ js_math_max(JSContext *cx, uintN argc, Value *vp)
|
||||
return cx->markTypeCallerOverflow();
|
||||
}
|
||||
argv = vp + 2;
|
||||
bool expectDouble = false;
|
||||
for (i = 0; i < argc; i++) {
|
||||
expectDouble |= argv[i].isDouble();
|
||||
if (!ValueToNumber(cx, argv[i], &x))
|
||||
return JS_FALSE;
|
||||
if (JSDOUBLE_IS_NaN(x)) {
|
||||
@ -409,7 +411,8 @@ js_math_max(JSContext *cx, uintN argc, Value *vp)
|
||||
z = (x > z) ? x : z;
|
||||
}
|
||||
}
|
||||
vp->setNumber(z);
|
||||
if (!vp->setNumber(z) && !expectDouble)
|
||||
return cx->markTypeCallerOverflow();
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -425,7 +428,9 @@ js_math_min(JSContext *cx, uintN argc, Value *vp)
|
||||
return cx->markTypeCallerOverflow();
|
||||
}
|
||||
argv = vp + 2;
|
||||
bool expectDouble = false;
|
||||
for (i = 0; i < argc; i++) {
|
||||
expectDouble |= argv[i].isDouble();
|
||||
if (!ValueToNumber(cx, argv[i], &x))
|
||||
return JS_FALSE;
|
||||
if (JSDOUBLE_IS_NaN(x)) {
|
||||
@ -439,7 +444,8 @@ js_math_min(JSContext *cx, uintN argc, Value *vp)
|
||||
z = (x < z) ? x : z;
|
||||
}
|
||||
}
|
||||
vp->setNumber(z);
|
||||
if (!vp->setNumber(z) && !expectDouble)
|
||||
return cx->markTypeCallerOverflow();
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -480,6 +486,7 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
vp->setDouble(js_NaN);
|
||||
return cx->markTypeCallerOverflow();
|
||||
}
|
||||
bool expectDouble = vp[2].isDouble() || vp[3].isDouble();
|
||||
if (!ValueToNumber(cx, vp[2], &x))
|
||||
return JS_FALSE;
|
||||
if (!ValueToNumber(cx, vp[3], &y))
|
||||
@ -491,11 +498,11 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
if (JSDOUBLE_IS_FINITE(x) && x != 0.0) {
|
||||
if (y == 0.5) {
|
||||
vp->setNumber(sqrt(x));
|
||||
return JS_TRUE;
|
||||
return expectDouble || cx->markTypeCallerOverflow();
|
||||
}
|
||||
if (y == -0.5) {
|
||||
vp->setNumber(1.0/sqrt(x));
|
||||
return JS_TRUE;
|
||||
return expectDouble || cx->markTypeCallerOverflow();
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -504,7 +511,7 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
*/
|
||||
if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) {
|
||||
vp->setDouble(js_NaN);
|
||||
return vp[3].isDouble() || cx->markTypeCallerOverflow();
|
||||
return expectDouble || cx->markTypeCallerOverflow();
|
||||
}
|
||||
/* pow(x, +-0) is always 1, even for x = NaN. */
|
||||
if (y == 0) {
|
||||
@ -517,10 +524,8 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
else
|
||||
z = pow(x, y);
|
||||
|
||||
vp->setNumber(z);
|
||||
if (vp->isDouble() && !(vp[2].isDouble() || vp[3].isDouble()) && !cx->markTypeCallerOverflow())
|
||||
return false;
|
||||
|
||||
if (!vp->setNumber(z) && !expectDouble)
|
||||
return cx->markTypeCallerOverflow();
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
@ -522,6 +522,7 @@ struct JSScript {
|
||||
inline bool typeMonitorResult(JSContext *cx, const jsbytecode *pc, const js::Value &val);
|
||||
inline bool typeMonitorUndefined(JSContext *cx, const jsbytecode *pc);
|
||||
inline bool typeMonitorOverflow(JSContext *cx, const jsbytecode *pc);
|
||||
inline bool typeMonitorString(JSContext *cx, const jsbytecode *pc);
|
||||
inline bool typeMonitorUnknown(JSContext *cx, const jsbytecode *pc);
|
||||
|
||||
/* Add a type for a variable in this script. */
|
||||
|
@ -1102,13 +1102,16 @@ stubs::Add(VMFrame &f)
|
||||
THROW();
|
||||
regs.sp--;
|
||||
regs.sp[-1] = rval;
|
||||
if (!f.script()->typeMonitorUnknown(cx, regs.pc))
|
||||
THROW();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* These can convert lval/rval to strings. */
|
||||
if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2))
|
||||
bool lIsObject, rIsObject;
|
||||
if ((lIsObject = lval.isObject()) && !DefaultValue(f, JSTYPE_VOID, lval, -2))
|
||||
THROW();
|
||||
if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1))
|
||||
if ((rIsObject = rval.isObject()) && !DefaultValue(f, JSTYPE_VOID, rval, -1))
|
||||
THROW();
|
||||
if ((lIsString = lval.isString()) || (rIsString = rval.isString())) {
|
||||
if (lIsString) {
|
||||
@ -1127,6 +1130,8 @@ stubs::Add(VMFrame &f)
|
||||
THROW();
|
||||
regs.sp[-1].setString(rstr);
|
||||
}
|
||||
if ((lIsObject || rIsObject) && !f.script()->typeMonitorString(cx, regs.pc))
|
||||
THROW();
|
||||
goto string_concat;
|
||||
|
||||
} else {
|
||||
@ -1134,7 +1139,9 @@ stubs::Add(VMFrame &f)
|
||||
if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
|
||||
THROW();
|
||||
l += r;
|
||||
if (!regs.sp[-2].setNumber(l) && !MonitorArithmeticOverflow(f, regs.sp[-2]))
|
||||
if (!regs.sp[-2].setNumber(l) &&
|
||||
(lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble())) &&
|
||||
!MonitorArithmeticOverflow(f, regs.sp[-2]))
|
||||
THROW();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user