[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_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) \

View File

@ -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());
}

View File

@ -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) {

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.
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 - - -

View File

@ -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 v2 of nonNumberValues) {
assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr,
TypeError);
assertStrongThrowBehaviour("(" + 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 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);
@ -143,8 +252,8 @@ for (let func of strong_funcs) {
%OptimizeFunctionOnNextCall(func);
let c = func(2, "foo");
assertUnreachable();
} catch(e) {
assertTrue(e instanceof TypeError);
} catch (e) {
assertInstanceof(e, TypeError);
assertUnoptimized(func);
assertThrows(function(){func(2, "foo");}, TypeError);
assertDoesNotThrow(function(){func(2, 3);});

View File

@ -456,14 +456,16 @@ 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))));
}
}
}
@ -473,12 +475,14 @@ 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,14 +496,16 @@ 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))));
}
}
}
@ -509,12 +515,14 @@ 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,15 +537,17 @@ 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))));
}
}
}
@ -548,12 +558,14 @@ 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))));
}
}