[strong] Disallow implicit conversions for bitwise ops, shifts
authorconradw <conradw@chromium.org>
Tue, 28 Apr 2015 11:20:13 +0000 (04:20 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 28 Apr 2015 11:19:49 +0000 (11:19 +0000)
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}

src/builtins.h
src/compiler/js-typed-lowering.cc
src/ic/ic.cc
src/runtime.js
test/mjsunit/strong/implicit-conversions.js
test/unittests/compiler/js-typed-lowering-unittest.cc

index ecf83c5..79d6f61 100644 (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)                        \
index 8e93d52..c0ed7c4 100644 (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());
   }
index 2100567..7e0fd2b 100644 (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) {
index a4677da..9bb02ae 100644 (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   - - -
index dd22db1..6aa2b1d 100644 (file)
 
 // 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);});
index 05ddf0b..f4b5a3a 100644 (file)
@@ -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))));
+  }
 }