[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:
parent
b3000dda14
commit
6988aec61f
@ -179,11 +179,17 @@ enum BuiltinExtraArguments {
|
||||
V(MOD, 1) \
|
||||
V(MOD_STRONG, 1) \
|
||||
V(BIT_OR, 1) \
|
||||
V(BIT_OR_STRONG, 1) \
|
||||
V(BIT_AND, 1) \
|
||||
V(BIT_AND_STRONG, 1) \
|
||||
V(BIT_XOR, 1) \
|
||||
V(BIT_XOR_STRONG, 1) \
|
||||
V(SHL, 1) \
|
||||
V(SHL_STRONG, 1) \
|
||||
V(SAR, 1) \
|
||||
V(SAR_STRONG, 1) \
|
||||
V(SHR, 1) \
|
||||
V(SHR_STRONG, 1) \
|
||||
V(DELETE, 2) \
|
||||
V(IN, 1) \
|
||||
V(INSTANCE_OF, 1) \
|
||||
|
@ -347,8 +347,7 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
|
||||
const Operator* numberOp) {
|
||||
JSBinopReduction r(this, node);
|
||||
if (is_strong(OpParameter<LanguageMode>(node))) {
|
||||
if (r.left_type()->Is(Type::Number()) &&
|
||||
(r.right_type()->Is(Type::Number()))) {
|
||||
if (r.BothInputsAre(Type::Number())) {
|
||||
return r.ChangeToPureOperator(numberOp, Type::Number());
|
||||
}
|
||||
return NoChange();
|
||||
@ -361,6 +360,13 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
|
||||
|
||||
Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) {
|
||||
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);
|
||||
r.ConvertInputsToNumber(frame_state);
|
||||
r.ConvertInputsToUI32(kSigned, kSigned);
|
||||
@ -372,7 +378,10 @@ Reduction JSTypedLowering::ReduceUI32Shift(Node* node,
|
||||
Signedness left_signedness,
|
||||
const Operator* shift_op) {
|
||||
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);
|
||||
return r.ChangeToPureOperator(shift_op, Type::Integral32());
|
||||
}
|
||||
|
12
src/ic/ic.cc
12
src/ic/ic.cc
@ -2826,12 +2826,12 @@ Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op,
|
||||
case Token::MUL: return Builtins::MUL_STRONG;
|
||||
case Token::DIV: return Builtins::DIV_STRONG;
|
||||
case Token::MOD: return Builtins::MOD_STRONG;
|
||||
case Token::BIT_OR: return Builtins::BIT_OR;
|
||||
case Token::BIT_AND: return Builtins::BIT_AND;
|
||||
case Token::BIT_XOR: return Builtins::BIT_XOR;
|
||||
case Token::SAR: return Builtins::SAR;
|
||||
case Token::SHR: return Builtins::SHR;
|
||||
case Token::SHL: return Builtins::SHL;
|
||||
case Token::BIT_OR: return Builtins::BIT_OR_STRONG;
|
||||
case Token::BIT_AND: return Builtins::BIT_AND_STRONG;
|
||||
case Token::BIT_XOR: return Builtins::BIT_XOR_STRONG;
|
||||
case Token::SAR: return Builtins::SAR_STRONG;
|
||||
case Token::SHR: return Builtins::SHR_STRONG;
|
||||
case Token::SHL: return Builtins::SHL_STRONG;
|
||||
}
|
||||
} else {
|
||||
switch (op) {
|
||||
|
@ -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.
|
||||
function BIT_AND(y) {
|
||||
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.
|
||||
function BIT_XOR(y) {
|
||||
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.
|
||||
function SHL(y) {
|
||||
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.
|
||||
function SAR(y) {
|
||||
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.
|
||||
function SHR(y) {
|
||||
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 - - -
|
||||
|
@ -4,17 +4,29 @@
|
||||
|
||||
// Flags: --strong-mode --allow-natives-syntax
|
||||
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
// TODO(conradw): Implement other strong operators
|
||||
let strong_arith = [
|
||||
let strongBinops = [
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"%"
|
||||
]
|
||||
"%",
|
||||
"|",
|
||||
"&",
|
||||
"^",
|
||||
"<<",
|
||||
">>",
|
||||
">>>",
|
||||
];
|
||||
|
||||
let nonnumber_values = [
|
||||
let strongUnops = [
|
||||
"~",
|
||||
"+",
|
||||
"-"
|
||||
];
|
||||
|
||||
let nonNumberValues = [
|
||||
"{}",
|
||||
"'foo'",
|
||||
"(function(){})",
|
||||
@ -22,9 +34,9 @@ let nonnumber_values = [
|
||||
"'0'",
|
||||
"'NaN'",
|
||||
"(class Foo {})"
|
||||
]
|
||||
];
|
||||
|
||||
let number_values = [
|
||||
let numberValues = [
|
||||
"0",
|
||||
"(-0)",
|
||||
"1",
|
||||
@ -43,33 +55,114 @@ let number_values = [
|
||||
"NaN",
|
||||
"Infinity",
|
||||
"(-Infinity)"
|
||||
]
|
||||
];
|
||||
|
||||
function sub_strong(x, y) {
|
||||
"use strong";
|
||||
let v = x - y;
|
||||
return v;
|
||||
return x - y;
|
||||
}
|
||||
|
||||
function mul_strong(x, y) {
|
||||
"use strong";
|
||||
let v = x * y;
|
||||
return v;
|
||||
return x * y;
|
||||
}
|
||||
|
||||
function div_strong(x, y) {
|
||||
"use strong";
|
||||
let v = x / y;
|
||||
return v;
|
||||
return x / y;
|
||||
}
|
||||
|
||||
function mod_strong(x, y) {
|
||||
"use strong";
|
||||
let v = x % y;
|
||||
return v;
|
||||
return x % y;
|
||||
}
|
||||
|
||||
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) {
|
||||
"use strong";
|
||||
@ -91,36 +184,52 @@ function inline_outer_strong(x, y) {
|
||||
return inline_sub(x, y);
|
||||
}
|
||||
|
||||
for (let op of strong_arith) {
|
||||
for (let left of number_values) {
|
||||
for (let right of number_values) {
|
||||
let expr = "(" + left + op + right + ")";
|
||||
function assertStrongNonThrowBehaviour(expr) {
|
||||
assertEquals(eval(expr), eval("'use strong';" + expr));
|
||||
assertDoesNotThrow("'use strong'; " + expr + ";");
|
||||
assertDoesNotThrow("'use strong'; let v = " + expr + ";");
|
||||
}
|
||||
}
|
||||
for (let left of number_values) {
|
||||
for (let right of nonnumber_values) {
|
||||
let expr = "(" + left + op + right + ")";
|
||||
|
||||
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 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 v2 of nonNumberValues) {
|
||||
assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr,
|
||||
TypeError);
|
||||
assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
|
||||
}
|
||||
}
|
||||
for (let v1 of nonNumberValues) {
|
||||
let assignExpr = "foo " + op + "= " + v1 + ";";
|
||||
for (let v2 of numberValues.concat(nonNumberValues)) {
|
||||
assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr,
|
||||
TypeError);
|
||||
assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 b = func(4, 5);
|
||||
assertTrue(a === b);
|
||||
@ -135,7 +244,7 @@ for (let func of strong_funcs) {
|
||||
%ClearFunctionTypeFeedback(func);
|
||||
}
|
||||
|
||||
for (let func of strong_funcs) {
|
||||
for (let func of strongFuncs) {
|
||||
try {
|
||||
let a = func(2, 3);
|
||||
let b = func(2, 3);
|
||||
@ -144,7 +253,7 @@ for (let func of strong_funcs) {
|
||||
let c = func(2, "foo");
|
||||
assertUnreachable();
|
||||
} catch (e) {
|
||||
assertTrue(e instanceof TypeError);
|
||||
assertInstanceof(e, TypeError);
|
||||
assertUnoptimized(func);
|
||||
assertThrows(function(){func(2, "foo");}, TypeError);
|
||||
assertDoesNotThrow(function(){func(2, 3);});
|
||||
|
@ -456,15 +456,17 @@ TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndConstant) {
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FORRANGE(double, rhs, 0, 31) {
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r =
|
||||
Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY),
|
||||
lhs, NumberConstant(rhs), context, effect,
|
||||
Reduce(graph()->NewNode(javascript()->ShiftLeft(language_mode), lhs,
|
||||
NumberConstant(rhs), context, effect,
|
||||
control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Shl(lhs, IsNumberConstant(BitEq(rhs))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndUnsigned32) {
|
||||
@ -473,13 +475,15 @@ TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndUnsigned32) {
|
||||
Node* const context = UndefinedConstant();
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r =
|
||||
Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY),
|
||||
lhs, rhs, context, effect, control));
|
||||
Reduce(graph()->NewNode(javascript()->ShiftLeft(language_mode), lhs,
|
||||
rhs, context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Shl(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -492,15 +496,17 @@ TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndConstant) {
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FORRANGE(double, rhs, 0, 31) {
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r =
|
||||
Reduce(graph()->NewNode(javascript()->
|
||||
ShiftRight(LanguageMode::SLOPPY), lhs,
|
||||
NumberConstant(rhs), context, effect, control));
|
||||
Reduce(graph()->NewNode(javascript()-> ShiftRight(language_mode), lhs,
|
||||
NumberConstant(rhs), context, effect,
|
||||
control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Sar(lhs, IsNumberConstant(BitEq(rhs))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndUnsigned32) {
|
||||
@ -509,13 +515,15 @@ TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndUnsigned32) {
|
||||
Node* const context = UndefinedConstant();
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r = Reduce(graph()->NewNode(javascript()->
|
||||
ShiftRight(LanguageMode::SLOPPY),
|
||||
lhs, rhs, context, effect, control));
|
||||
ShiftRight(language_mode), lhs, rhs,
|
||||
context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -529,16 +537,18 @@ TEST_F(JSTypedLoweringTest,
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FORRANGE(double, rhs, 0, 31) {
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r =
|
||||
Reduce(graph()->NewNode(javascript()->
|
||||
ShiftRightLogical(LanguageMode::SLOPPY),
|
||||
lhs, NumberConstant(rhs), context, effect,
|
||||
ShiftRightLogical(language_mode), lhs,
|
||||
NumberConstant(rhs), context, effect,
|
||||
control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Shr(lhs, IsNumberConstant(BitEq(rhs))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(JSTypedLoweringTest,
|
||||
@ -548,13 +558,15 @@ TEST_F(JSTypedLoweringTest,
|
||||
Node* const context = UndefinedConstant();
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction r = Reduce(graph()->NewNode(javascript()->
|
||||
ShiftRightLogical(LanguageMode::SLOPPY),
|
||||
lhs, rhs, context, effect, control));
|
||||
ShiftRightLogical(language_mode), lhs,
|
||||
rhs, context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user