[strong] Disallow implicit conversions for bitwise ops, shifts

See https://codereview.chromium.org/1092353002/

Due to parser rewrites, also implements restrictions for unary ~.

Still to come, implementing restrictions for binary + and comparison.

BUG=v8:3956
LOG=N

Review URL: https://codereview.chromium.org/1102923002

Cr-Commit-Position: refs/heads/master@{#28104}
This commit is contained in:
conradw 2015-04-28 04:20:13 -07:00 committed by Commit bot
parent b3000dda14
commit 6988aec61f
6 changed files with 310 additions and 121 deletions

View File

@ -179,11 +179,17 @@ enum BuiltinExtraArguments {
V(MOD, 1) \ V(MOD, 1) \
V(MOD_STRONG, 1) \ V(MOD_STRONG, 1) \
V(BIT_OR, 1) \ V(BIT_OR, 1) \
V(BIT_OR_STRONG, 1) \
V(BIT_AND, 1) \ V(BIT_AND, 1) \
V(BIT_AND_STRONG, 1) \
V(BIT_XOR, 1) \ V(BIT_XOR, 1) \
V(BIT_XOR_STRONG, 1) \
V(SHL, 1) \ V(SHL, 1) \
V(SHL_STRONG, 1) \
V(SAR, 1) \ V(SAR, 1) \
V(SAR_STRONG, 1) \
V(SHR, 1) \ V(SHR, 1) \
V(SHR_STRONG, 1) \
V(DELETE, 2) \ V(DELETE, 2) \
V(IN, 1) \ V(IN, 1) \
V(INSTANCE_OF, 1) \ V(INSTANCE_OF, 1) \

View File

@ -347,8 +347,7 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
const Operator* numberOp) { const Operator* numberOp) {
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
if (is_strong(OpParameter<LanguageMode>(node))) { if (is_strong(OpParameter<LanguageMode>(node))) {
if (r.left_type()->Is(Type::Number()) && if (r.BothInputsAre(Type::Number())) {
(r.right_type()->Is(Type::Number()))) {
return r.ChangeToPureOperator(numberOp, Type::Number()); return r.ChangeToPureOperator(numberOp, Type::Number());
} }
return NoChange(); return NoChange();
@ -361,6 +360,13 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) { Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) {
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
if (is_strong(OpParameter<LanguageMode>(node))) {
if (r.BothInputsAre(Type::Number())) {
r.ConvertInputsToUI32(kSigned, kSigned);
return r.ChangeToPureOperator(intOp, Type::Integral32());
}
return NoChange();
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state); r.ConvertInputsToNumber(frame_state);
r.ConvertInputsToUI32(kSigned, kSigned); r.ConvertInputsToUI32(kSigned, kSigned);
@ -372,7 +378,10 @@ Reduction JSTypedLowering::ReduceUI32Shift(Node* node,
Signedness left_signedness, Signedness left_signedness,
const Operator* shift_op) { const Operator* shift_op) {
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Primitive())) { Type* reduce_type = is_strong(
OpParameter<LanguageMode>(node)) ? Type::Number() :
Type::Primitive();
if (r.BothInputsAre(reduce_type)) {
r.ConvertInputsForShift(left_signedness); r.ConvertInputsForShift(left_signedness);
return r.ChangeToPureOperator(shift_op, Type::Integral32()); return r.ChangeToPureOperator(shift_op, Type::Integral32());
} }

View File

@ -2826,12 +2826,12 @@ Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op,
case Token::MUL: return Builtins::MUL_STRONG; case Token::MUL: return Builtins::MUL_STRONG;
case Token::DIV: return Builtins::DIV_STRONG; case Token::DIV: return Builtins::DIV_STRONG;
case Token::MOD: return Builtins::MOD_STRONG; case Token::MOD: return Builtins::MOD_STRONG;
case Token::BIT_OR: return Builtins::BIT_OR; case Token::BIT_OR: return Builtins::BIT_OR_STRONG;
case Token::BIT_AND: return Builtins::BIT_AND; case Token::BIT_AND: return Builtins::BIT_AND_STRONG;
case Token::BIT_XOR: return Builtins::BIT_XOR; case Token::BIT_XOR: return Builtins::BIT_XOR_STRONG;
case Token::SAR: return Builtins::SAR; case Token::SAR: return Builtins::SAR_STRONG;
case Token::SHR: return Builtins::SHR; case Token::SHR: return Builtins::SHR_STRONG;
case Token::SHL: return Builtins::SHL; case Token::SHL: return Builtins::SHL_STRONG;
} }
} else { } else {
switch (op) { switch (op) {

View File

@ -272,6 +272,15 @@ function BIT_OR(y) {
} }
//ECMA-262, section 11.10, page 57.
function BIT_OR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberOr(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.10, page 57. // ECMA-262, section 11.10, page 57.
function BIT_AND(y) { function BIT_AND(y) {
var x; var x;
@ -294,6 +303,15 @@ function BIT_AND(y) {
} }
//ECMA-262, section 11.10, page 57.
function BIT_AND_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberAnd(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.10, page 57. // ECMA-262, section 11.10, page 57.
function BIT_XOR(y) { function BIT_XOR(y) {
var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this); var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
@ -302,6 +320,15 @@ function BIT_XOR(y) {
} }
//ECMA-262, section 11.10, page 57.
function BIT_XOR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberXor(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.7.1, page 51. // ECMA-262, section 11.7.1, page 51.
function SHL(y) { function SHL(y) {
var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this); var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
@ -310,6 +337,15 @@ function SHL(y) {
} }
//ECMA-262, section 11.7.1, page 51.
function SHL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShl(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.7.2, page 51. // ECMA-262, section 11.7.2, page 51.
function SAR(y) { function SAR(y) {
var x; var x;
@ -332,6 +368,15 @@ function SAR(y) {
} }
//ECMA-262, section 11.7.2, page 51.
function SAR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSar(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.7.3, page 52. // ECMA-262, section 11.7.3, page 52.
function SHR(y) { function SHR(y) {
var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this); var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
@ -340,6 +385,14 @@ function SHR(y) {
} }
//ECMA-262, section 11.7.3, page 52.
function SHR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShr(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
/* ----------------------------- /* -----------------------------
- - - H e l p e r s - - - - - - H e l p e r s - - -

View File

@ -4,72 +4,165 @@
// Flags: --strong-mode --allow-natives-syntax // Flags: --strong-mode --allow-natives-syntax
'use strict'; "use strict";
// TODO(conradw): Implement other strong operators // TODO(conradw): Implement other strong operators
let strong_arith = [ let strongBinops = [
"-", "-",
"*", "*",
"/", "/",
"%" "%",
] "|",
"&",
"^",
"<<",
">>",
">>>",
];
let nonnumber_values = [ let strongUnops = [
"{}", "~",
"'foo'", "+",
"(function(){})", "-"
"[]", ];
"'0'",
"'NaN'",
"(class Foo {})"
]
let number_values = [ let nonNumberValues = [
"0", "{}",
"(-0)", "'foo'",
"1", "(function(){})",
"0.79", "[]",
"(-0.79)", "'0'",
"4294967295", "'NaN'",
"4294967296", "(class Foo {})"
"(-4294967295)", ];
"(-4294967296)",
"9999999999999", let numberValues = [
"(-9999999999999)", "0",
"1.5e10", "(-0)",
"(-1.5e10)", "1",
"0xFFF", "0.79",
"(-0xFFF)", "(-0.79)",
"NaN", "4294967295",
"Infinity", "4294967296",
"(-Infinity)" "(-4294967295)",
] "(-4294967296)",
"9999999999999",
"(-9999999999999)",
"1.5e10",
"(-1.5e10)",
"0xFFF",
"(-0xFFF)",
"NaN",
"Infinity",
"(-Infinity)"
];
function sub_strong(x, y) { function sub_strong(x, y) {
"use strong"; "use strong";
let v = x - y; return x - y;
return v;
} }
function mul_strong(x, y) { function mul_strong(x, y) {
"use strong"; "use strong";
let v = x * y; return x * y;
return v;
} }
function div_strong(x, y) { function div_strong(x, y) {
"use strong"; "use strong";
let v = x / y; return x / y;
return v;
} }
function mod_strong(x, y) { function mod_strong(x, y) {
"use strong"; "use strong";
let v = x % y; return x % y;
return v;
} }
let strong_funcs = [sub_strong, mul_strong, div_strong, mod_strong]; function or_strong(x, y) {
"use strong";
return x | y;
}
function and_strong(x, y) {
"use strong";
return x & y;
}
function xor_strong(x, y) {
"use strong";
return x ^ y;
}
function shl_strong(x, y) {
"use strong";
return x << y;
}
function shr_strong(x, y) {
"use strong";
return x >> y;
}
function sar_strong(x, y) {
"use strong";
return x >>> y;
}
function typed_sub_strong(x, y) {
"use strong";
return (+x) - (+y);
}
function typed_mul_strong(x, y) {
"use strong";
return (+x) * (+y);
}
function typed_div_strong(x, y) {
"use strong";
return (+x) / (+y);
}
function typed_mod_strong(x, y) {
"use strong";
return (+x) % (+y);
}
function typed_or_strong(x, y) {
"use strong";
return (+x) | (+y);
}
function typed_and_strong(x, y) {
"use strong";
return (+x) & (+y);
}
function typed_xor_strong(x, y) {
"use strong";
return (+x) ^ (+y);
}
function typed_shl_strong(x, y) {
"use strong";
return (+x) << (+y);
}
function typed_shr_strong(x, y) {
"use strong";
return (+x) >> (+y);
}
function typed_sar_strong(x, y) {
"use strong";
return (+x) >>> (+y);
}
let strongFuncs = [sub_strong, mul_strong, div_strong, mod_strong, or_strong,
and_strong, xor_strong, shl_strong, shr_strong, sar_strong,
typed_sub_strong, typed_mul_strong, typed_div_strong,
typed_mod_strong, typed_or_strong, typed_and_strong,
typed_xor_strong, typed_shl_strong, typed_shr_strong,
typed_sar_strong];
function inline_sub_strong(x, y) { function inline_sub_strong(x, y) {
"use strong"; "use strong";
@ -91,36 +184,52 @@ function inline_outer_strong(x, y) {
return inline_sub(x, y); return inline_sub(x, y);
} }
for (let op of strong_arith) { function assertStrongNonThrowBehaviour(expr) {
for (let left of number_values) { assertEquals(eval(expr), eval("'use strong';" + expr));
for (let right of number_values) { assertDoesNotThrow("'use strong'; " + expr + ";");
let expr = "(" + left + op + right + ")"; assertDoesNotThrow("'use strong'; let v = " + expr + ";");
assertEquals(eval(expr), eval("'use strong';" + expr)); }
assertDoesNotThrow("'use strong'; " + expr + ";");
assertDoesNotThrow("'use strong'; let v = " + expr + ";"); function assertStrongThrowBehaviour(expr) {
assertDoesNotThrow("'use strict'; " + expr + ";");
assertDoesNotThrow("'use strict'; let v = " + expr + ";");
assertThrows("'use strong'; " + expr + ";", TypeError);
assertThrows("'use strong'; let v = " + expr + ";", TypeError);
}
for (let op of strongBinops) {
for (let v1 of numberValues) {
let assignExpr = "foo " + op + "= " + v1 + ";";
for (let v2 of numberValues) {
assertDoesNotThrow("'use strong'; let foo = " + v2 + "; " + assignExpr);
assertStrongNonThrowBehaviour("(" + v1 + op + v2 + ")");
}
for (let v2 of nonNumberValues) {
assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr,
TypeError);
assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
} }
} }
for (let left of number_values) { for (let v1 of nonNumberValues) {
for (let right of nonnumber_values) { let assignExpr = "foo " + op + "= " + v1 + ";";
let expr = "(" + left + op + right + ")"; for (let v2 of numberValues.concat(nonNumberValues)) {
assertDoesNotThrow("'use strict'; " + expr + ";"); assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr,
assertDoesNotThrow("'use strict'; let v = " + expr + ";"); TypeError);
assertThrows("'use strong'; " + expr + ";", TypeError); assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
assertThrows("'use strong'; let v = " + expr + ";", TypeError);
}
}
for (let left of nonnumber_values) {
for (let right of number_values.concat(nonnumber_values)) {
let expr = "(" + left + op + right + ")";
assertDoesNotThrow("'use strict'; " + expr + ";");
assertDoesNotThrow("'use strict'; let v = " + expr + ";");
assertThrows("'use strong'; " + expr + ";", TypeError);
assertThrows("'use strong'; let v = " + expr + ";", TypeError);
} }
} }
} }
for (let func of strong_funcs) { for (let op of strongUnops) {
for (let value of numberValues) {
assertStrongNonThrowBehaviour("(" + op + value + ")");
}
for (let value of nonNumberValues) {
assertStrongThrowBehaviour("(" + op + value + ")");
}
}
for (let func of strongFuncs) {
let a = func(4, 5); let a = func(4, 5);
let b = func(4, 5); let b = func(4, 5);
assertTrue(a === b); assertTrue(a === b);
@ -135,7 +244,7 @@ for (let func of strong_funcs) {
%ClearFunctionTypeFeedback(func); %ClearFunctionTypeFeedback(func);
} }
for (let func of strong_funcs) { for (let func of strongFuncs) {
try { try {
let a = func(2, 3); let a = func(2, 3);
let b = func(2, 3); let b = func(2, 3);
@ -143,8 +252,8 @@ for (let func of strong_funcs) {
%OptimizeFunctionOnNextCall(func); %OptimizeFunctionOnNextCall(func);
let c = func(2, "foo"); let c = func(2, "foo");
assertUnreachable(); assertUnreachable();
} catch(e) { } catch (e) {
assertTrue(e instanceof TypeError); assertInstanceof(e, TypeError);
assertUnoptimized(func); assertUnoptimized(func);
assertThrows(function(){func(2, "foo");}, TypeError); assertThrows(function(){func(2, "foo");}, TypeError);
assertDoesNotThrow(function(){func(2, 3);}); assertDoesNotThrow(function(){func(2, 3);});

View File

@ -456,13 +456,15 @@ TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndConstant) {
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) { TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r = TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY), Reduction r =
lhs, NumberConstant(rhs), context, effect, Reduce(graph()->NewNode(javascript()->ShiftLeft(language_mode), lhs,
control)); NumberConstant(rhs), context, effect,
ASSERT_TRUE(r.Changed()); control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Shl(lhs, IsNumberConstant(BitEq(rhs)))); EXPECT_THAT(r.replacement(),
IsWord32Shl(lhs, IsNumberConstant(BitEq(rhs))));
}
} }
} }
@ -473,12 +475,14 @@ TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndUnsigned32) {
Node* const context = UndefinedConstant(); Node* const context = UndefinedConstant();
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
Reduction r = TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY), Reduction r =
lhs, rhs, context, effect, control)); Reduce(graph()->NewNode(javascript()->ShiftLeft(language_mode), lhs,
ASSERT_TRUE(r.Changed()); rhs, context, effect, control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Shl(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); EXPECT_THAT(r.replacement(),
IsWord32Shl(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
} }
@ -492,13 +496,15 @@ TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndConstant) {
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) { TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r = TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Reduce(graph()->NewNode(javascript()-> Reduction r =
ShiftRight(LanguageMode::SLOPPY), lhs, Reduce(graph()->NewNode(javascript()-> ShiftRight(language_mode), lhs,
NumberConstant(rhs), context, effect, control)); NumberConstant(rhs), context, effect,
ASSERT_TRUE(r.Changed()); control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Sar(lhs, IsNumberConstant(BitEq(rhs)))); EXPECT_THAT(r.replacement(),
IsWord32Sar(lhs, IsNumberConstant(BitEq(rhs))));
}
} }
} }
@ -509,12 +515,14 @@ TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndUnsigned32) {
Node* const context = UndefinedConstant(); Node* const context = UndefinedConstant();
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()-> TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
ShiftRight(LanguageMode::SLOPPY), Reduction r = Reduce(graph()->NewNode(javascript()->
lhs, rhs, context, effect, control)); ShiftRight(language_mode), lhs, rhs,
ASSERT_TRUE(r.Changed()); context, effect, control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); EXPECT_THAT(r.replacement(),
IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
} }
@ -529,14 +537,16 @@ TEST_F(JSTypedLoweringTest,
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) { TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r = TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Reduce(graph()->NewNode(javascript()-> Reduction r =
ShiftRightLogical(LanguageMode::SLOPPY), Reduce(graph()->NewNode(javascript()->
lhs, NumberConstant(rhs), context, effect, ShiftRightLogical(language_mode), lhs,
control)); NumberConstant(rhs), context, effect,
ASSERT_TRUE(r.Changed()); control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Shr(lhs, IsNumberConstant(BitEq(rhs)))); EXPECT_THAT(r.replacement(),
IsWord32Shr(lhs, IsNumberConstant(BitEq(rhs))));
}
} }
} }
@ -548,12 +558,14 @@ TEST_F(JSTypedLoweringTest,
Node* const context = UndefinedConstant(); Node* const context = UndefinedConstant();
Node* const effect = graph()->start(); Node* const effect = graph()->start();
Node* const control = graph()->start(); Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()-> TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
ShiftRightLogical(LanguageMode::SLOPPY), Reduction r = Reduce(graph()->NewNode(javascript()->
lhs, rhs, context, effect, control)); ShiftRightLogical(language_mode), lhs,
ASSERT_TRUE(r.Changed()); rhs, context, effect, control));
EXPECT_THAT(r.replacement(), ASSERT_TRUE(r.Changed());
IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); EXPECT_THAT(r.replacement(),
IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
} }