From: conradw Date: Tue, 28 Apr 2015 11:20:13 +0000 (-0700) Subject: [strong] Disallow implicit conversions for bitwise ops, shifts X-Git-Tag: upstream/4.7.83~2936 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6988aec61f071a1caec96abae1cf39353a234455;p=platform%2Fupstream%2Fv8.git [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} --- diff --git a/src/builtins.h b/src/builtins.h index ecf83c5..79d6f61 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -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) \ diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index 8e93d52..c0ed7c4 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -347,8 +347,7 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node, const Operator* numberOp) { JSBinopReduction r(this, node); if (is_strong(OpParameter(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(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(node)) ? Type::Number() : + Type::Primitive(); + if (r.BothInputsAre(reduce_type)) { r.ConvertInputsForShift(left_signedness); return r.ChangeToPureOperator(shift_op, Type::Integral32()); } diff --git a/src/ic/ic.cc b/src/ic/ic.cc index 2100567..7e0fd2b 100644 --- a/src/ic/ic.cc +++ b/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) { diff --git a/src/runtime.js b/src/runtime.js index a4677da..9bb02ae 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -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 - - - diff --git a/test/mjsunit/strong/implicit-conversions.js b/test/mjsunit/strong/implicit-conversions.js index dd22db1..6aa2b1d 100644 --- a/test/mjsunit/strong/implicit-conversions.js +++ b/test/mjsunit/strong/implicit-conversions.js @@ -4,72 +4,165 @@ // Flags: --strong-mode --allow-natives-syntax -'use strict'; +"use strict"; // TODO(conradw): Implement other strong operators -let strong_arith = [ - "-", - "*", - "/", - "%" -] - -let nonnumber_values = [ - "{}", - "'foo'", - "(function(){})", - "[]", - "'0'", - "'NaN'", - "(class Foo {})" -] - -let number_values = [ - "0", - "(-0)", - "1", - "0.79", - "(-0.79)", - "4294967295", - "4294967296", - "(-4294967295)", - "(-4294967296)", - "9999999999999", - "(-9999999999999)", - "1.5e10", - "(-1.5e10)", - "0xFFF", - "(-0xFFF)", - "NaN", - "Infinity", - "(-Infinity)" -] +let strongBinops = [ + "-", + "*", + "/", + "%", + "|", + "&", + "^", + "<<", + ">>", + ">>>", +]; + +let strongUnops = [ + "~", + "+", + "-" +]; + +let nonNumberValues = [ + "{}", + "'foo'", + "(function(){})", + "[]", + "'0'", + "'NaN'", + "(class Foo {})" +]; + +let numberValues = [ + "0", + "(-0)", + "1", + "0.79", + "(-0.79)", + "4294967295", + "4294967296", + "(-4294967295)", + "(-4294967296)", + "9999999999999", + "(-9999999999999)", + "1.5e10", + "(-1.5e10)", + "0xFFF", + "(-0xFFF)", + "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 + ")"; - assertEquals(eval(expr), eval("'use strong';" + expr)); - assertDoesNotThrow("'use strong'; " + expr + ";"); - assertDoesNotThrow("'use strong'; let v = " + expr + ";"); +function assertStrongNonThrowBehaviour(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 left of number_values) { - for (let right of 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 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);}); diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc index 05ddf0b..f4b5a3a 100644 --- a/test/unittests/compiler/js-typed-lowering-unittest.cc +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -456,13 +456,15 @@ TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndConstant) { Node* const effect = graph()->start(); Node* const control = graph()->start(); TRACED_FORRANGE(double, rhs, 0, 31) { - Reduction r = - Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY), - lhs, NumberConstant(rhs), context, effect, - control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Shl(lhs, IsNumberConstant(BitEq(rhs)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = + 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(); - Reduction r = - Reduce(graph()->NewNode(javascript()->ShiftLeft(LanguageMode::SLOPPY), - lhs, rhs, context, effect, control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Shl(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = + 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,13 +496,15 @@ TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndConstant) { Node* const effect = graph()->start(); Node* const control = graph()->start(); TRACED_FORRANGE(double, rhs, 0, 31) { - Reduction r = - Reduce(graph()->NewNode(javascript()-> - ShiftRight(LanguageMode::SLOPPY), lhs, - NumberConstant(rhs), context, effect, control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Sar(lhs, IsNumberConstant(BitEq(rhs)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = + 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(); - Reduction r = Reduce(graph()->NewNode(javascript()-> - ShiftRight(LanguageMode::SLOPPY), - lhs, rhs, context, effect, control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = Reduce(graph()->NewNode(javascript()-> + ShiftRight(language_mode), lhs, rhs, + context, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); + } } @@ -529,14 +537,16 @@ TEST_F(JSTypedLoweringTest, Node* const effect = graph()->start(); Node* const control = graph()->start(); TRACED_FORRANGE(double, rhs, 0, 31) { - Reduction r = - Reduce(graph()->NewNode(javascript()-> - ShiftRightLogical(LanguageMode::SLOPPY), - lhs, NumberConstant(rhs), context, effect, - control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Shr(lhs, IsNumberConstant(BitEq(rhs)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = + Reduce(graph()->NewNode(javascript()-> + 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(); - Reduction r = Reduce(graph()->NewNode(javascript()-> - ShiftRightLogical(LanguageMode::SLOPPY), - lhs, rhs, context, effect, control)); - ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), - IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); + TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { + Reduction r = Reduce(graph()->NewNode(javascript()-> + ShiftRightLogical(language_mode), lhs, + rhs, context, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f)))); + } }