Commit dea0d9b5 authored by conradw's avatar conradw Committed by Commit bot

[strong] Disallow implicit conversions for add

Implements the strong mode proposal's restrictions on implicit conversions
for the binary + operator. Test suite is also cleaned up/refactored to allow
easier testing of the comparison operators in the future.

BUG=v8:3956
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#28159}
parent b4007197
...@@ -170,6 +170,7 @@ enum BuiltinExtraArguments { ...@@ -170,6 +170,7 @@ enum BuiltinExtraArguments {
V(STRICT_EQUALS, 1) \ V(STRICT_EQUALS, 1) \
V(COMPARE, 2) \ V(COMPARE, 2) \
V(ADD, 1) \ V(ADD, 1) \
V(ADD_STRONG, 1) \
V(SUB, 1) \ V(SUB, 1) \
V(SUB_STRONG, 1) \ V(SUB_STRONG, 1) \
V(MUL, 1) \ V(MUL, 1) \
...@@ -203,7 +204,9 @@ enum BuiltinExtraArguments { ...@@ -203,7 +204,9 @@ enum BuiltinExtraArguments {
V(TO_STRING, 0) \ V(TO_STRING, 0) \
V(TO_NAME, 0) \ V(TO_NAME, 0) \
V(STRING_ADD_LEFT, 1) \ V(STRING_ADD_LEFT, 1) \
V(STRING_ADD_LEFT_STRONG, 1) \
V(STRING_ADD_RIGHT, 1) \ V(STRING_ADD_RIGHT, 1) \
V(STRING_ADD_RIGHT_STRONG, 1) \
V(APPLY_PREPARE, 1) \ V(APPLY_PREPARE, 1) \
V(REFLECT_APPLY_PREPARE, 1) \ V(REFLECT_APPLY_PREPARE, 1) \
V(REFLECT_CONSTRUCT_PREPARE, 2) \ V(REFLECT_CONSTRUCT_PREPARE, 2) \
......
...@@ -1187,14 +1187,15 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() { ...@@ -1187,14 +1187,15 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
state.op(), left, right, state.op(), left, right,
Type::String(zone()), right_type, Type::String(zone()), right_type,
result_type, state.fixed_right_arg(), result_type, state.fixed_right_arg(),
allocation_mode)); allocation_mode, state.language_mode()));
} }
if_leftisstring.Else(); if_leftisstring.Else();
{ {
Push(BuildBinaryOperation( Push(BuildBinaryOperation(
state.op(), left, right, state.op(), left, right,
left_type, right_type, result_type, left_type, right_type, result_type,
state.fixed_right_arg(), allocation_mode)); state.fixed_right_arg(), allocation_mode,
state.language_mode()));
} }
if_leftisstring.End(); if_leftisstring.End();
result = Pop(); result = Pop();
...@@ -1207,14 +1208,15 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() { ...@@ -1207,14 +1208,15 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
state.op(), left, right, state.op(), left, right,
left_type, Type::String(zone()), left_type, Type::String(zone()),
result_type, state.fixed_right_arg(), result_type, state.fixed_right_arg(),
allocation_mode)); allocation_mode, state.language_mode()));
} }
if_rightisstring.Else(); if_rightisstring.Else();
{ {
Push(BuildBinaryOperation( Push(BuildBinaryOperation(
state.op(), left, right, state.op(), left, right,
left_type, right_type, result_type, left_type, right_type, result_type,
state.fixed_right_arg(), allocation_mode)); state.fixed_right_arg(), allocation_mode,
state.language_mode()));
} }
if_rightisstring.End(); if_rightisstring.End();
result = Pop(); result = Pop();
...@@ -1223,7 +1225,7 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() { ...@@ -1223,7 +1225,7 @@ HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
result = BuildBinaryOperation( result = BuildBinaryOperation(
state.op(), left, right, state.op(), left, right,
left_type, right_type, result_type, left_type, right_type, result_type,
state.fixed_right_arg(), allocation_mode); state.fixed_right_arg(), allocation_mode, state.language_mode());
} }
// If we encounter a generic argument, the number conversion is // If we encounter a generic argument, the number conversion is
...@@ -1257,7 +1259,8 @@ HValue* CodeStubGraphBuilder<BinaryOpWithAllocationSiteStub>::BuildCodeStub() { ...@@ -1257,7 +1259,8 @@ HValue* CodeStubGraphBuilder<BinaryOpWithAllocationSiteStub>::BuildCodeStub() {
return BuildBinaryOperation(state.op(), left, right, return BuildBinaryOperation(state.op(), left, right,
left_type, right_type, result_type, left_type, right_type, result_type,
state.fixed_right_arg(), allocation_mode); state.fixed_right_arg(), allocation_mode,
state.language_mode());
} }
......
...@@ -165,6 +165,8 @@ class JSBinopReduction final { ...@@ -165,6 +165,8 @@ class JSBinopReduction final {
return ChangeToPureOperator(op, false, type); return ChangeToPureOperator(op, false, type);
} }
bool IsStrong() { return is_strong(OpParameter<LanguageMode>(node_)); }
bool OneInputIs(Type* t) { return left_type()->Is(t) || right_type()->Is(t); } bool OneInputIs(Type* t) { return left_type()->Is(t) || right_type()->Is(t); }
bool BothInputsAre(Type* t) { bool BothInputsAre(Type* t) {
...@@ -320,7 +322,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) { ...@@ -320,7 +322,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
// JSAdd(x:number, y:number) => NumberAdd(x, y) // JSAdd(x:number, y:number) => NumberAdd(x, y)
return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number()); return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
} }
if (r.NeitherInputCanBe(Type::StringOrReceiver())) { if (r.NeitherInputCanBe(Type::StringOrReceiver()) && !r.IsStrong()) {
// JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y)) // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state); r.ConvertInputsToNumber(frame_state);
...@@ -331,7 +333,8 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) { ...@@ -331,7 +333,8 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
// a) The inserted ToString operation screws up valueOf vs. toString order. // a) The inserted ToString operation screws up valueOf vs. toString order.
// b) Deoptimization at ToString doesn't have corresponding bailout id. // b) Deoptimization at ToString doesn't have corresponding bailout id.
// c) Our current StringAddStub is actually non-pure and requires context. // c) Our current StringAddStub is actually non-pure and requires context.
if (r.OneInputIs(Type::String())) { if ((r.OneInputIs(Type::String()) && !r.IsStrong()) ||
r.BothInputsAre(Type::String())) {
// JSAdd(x:string, y:string) => StringAdd(x, y) // JSAdd(x:string, y:string) => StringAdd(x, y)
// JSAdd(x:string, y) => StringAdd(x, ToString(y)) // JSAdd(x:string, y) => StringAdd(x, ToString(y))
// JSAdd(x, y:string) => StringAdd(ToString(x), y) // JSAdd(x, y:string) => StringAdd(ToString(x), y)
...@@ -346,7 +349,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) { ...@@ -346,7 +349,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
Reduction JSTypedLowering::ReduceNumberBinop(Node* node, 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 (r.IsStrong()) {
if (r.BothInputsAre(Type::Number())) { if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(numberOp, Type::Number()); return r.ChangeToPureOperator(numberOp, Type::Number());
} }
...@@ -360,7 +363,7 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node, ...@@ -360,7 +363,7 @@ 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.IsStrong()) {
if (r.BothInputsAre(Type::Number())) { if (r.BothInputsAre(Type::Number())) {
r.ConvertInputsToUI32(kSigned, kSigned); r.ConvertInputsToUI32(kSigned, kSigned);
return r.ChangeToPureOperator(intOp, Type::Integral32()); return r.ChangeToPureOperator(intOp, Type::Integral32());
...@@ -378,9 +381,7 @@ Reduction JSTypedLowering::ReduceUI32Shift(Node* node, ...@@ -378,9 +381,7 @@ 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);
Type* reduce_type = is_strong( Type* reduce_type = r.IsStrong() ? Type::Number() : Type::Primitive();
OpParameter<LanguageMode>(node)) ? Type::Number() :
Type::Primitive();
if (r.BothInputsAre(reduce_type)) { 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());
......
...@@ -10611,23 +10611,27 @@ HValue* HGraphBuilder::BuildBinaryOperation( ...@@ -10611,23 +10611,27 @@ HValue* HGraphBuilder::BuildBinaryOperation(
} }
// Convert left argument as necessary. // Convert left argument as necessary.
if (left_type->Is(Type::Number())) { if (left_type->Is(Type::Number()) && !is_strong(language_mode)) {
DCHECK(right_type->Is(Type::String())); DCHECK(right_type->Is(Type::String()));
left = BuildNumberToString(left, left_type); left = BuildNumberToString(left, left_type);
} else if (!left_type->Is(Type::String())) { } else if (!left_type->Is(Type::String())) {
DCHECK(right_type->Is(Type::String())); DCHECK(right_type->Is(Type::String()));
HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_RIGHT); HValue* function = AddLoadJSBuiltin(is_strong(language_mode) ?
Builtins::STRING_ADD_RIGHT_STRONG :
Builtins::STRING_ADD_RIGHT);
Add<HPushArguments>(left, right); Add<HPushArguments>(left, right);
return AddUncasted<HInvokeFunction>(function, 2); return AddUncasted<HInvokeFunction>(function, 2);
} }
// Convert right argument as necessary. // Convert right argument as necessary.
if (right_type->Is(Type::Number())) { if (right_type->Is(Type::Number()) && !is_strong(language_mode)) {
DCHECK(left_type->Is(Type::String())); DCHECK(left_type->Is(Type::String()));
right = BuildNumberToString(right, right_type); right = BuildNumberToString(right, right_type);
} else if (!right_type->Is(Type::String())) { } else if (!right_type->Is(Type::String())) {
DCHECK(left_type->Is(Type::String())); DCHECK(left_type->Is(Type::String()));
HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_LEFT); HValue* function = AddLoadJSBuiltin(is_strong(language_mode) ?
Builtins::STRING_ADD_LEFT_STRONG :
Builtins::STRING_ADD_LEFT);
Add<HPushArguments>(left, right); Add<HPushArguments>(left, right);
return AddUncasted<HInvokeFunction>(function, 2); return AddUncasted<HInvokeFunction>(function, 2);
} }
......
...@@ -1437,7 +1437,7 @@ class HGraphBuilder { ...@@ -1437,7 +1437,7 @@ class HGraphBuilder {
Type* result_type, Type* result_type,
Maybe<int> fixed_right_arg, Maybe<int> fixed_right_arg,
HAllocationMode allocation_mode, HAllocationMode allocation_mode,
LanguageMode language_mode = SLOPPY); LanguageMode language_mode);
HLoadNamedField* AddLoadFixedArrayLength(HValue *object, HLoadNamedField* AddLoadFixedArrayLength(HValue *object,
HValue *dependency = NULL); HValue *dependency = NULL);
......
...@@ -2821,7 +2821,7 @@ Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op, ...@@ -2821,7 +2821,7 @@ Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op,
if (is_strong(language_mode)) { if (is_strong(language_mode)) {
switch (op) { switch (op) {
default: UNREACHABLE(); default: UNREACHABLE();
case Token::ADD: return Builtins::ADD; case Token::ADD: return Builtins::ADD_STRONG;
case Token::SUB: return Builtins::SUB_STRONG; case Token::SUB: return Builtins::SUB_STRONG;
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;
......
...@@ -160,6 +160,15 @@ function ADD(x) { ...@@ -160,6 +160,15 @@ function ADD(x) {
} }
// Strong mode ADD throws if an implicit conversion would be performed
function ADD_STRONG(x) {
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);
throw %MakeTypeError('strong_implicit_cast');
}
// Left operand (this) is already a string. // Left operand (this) is already a string.
function STRING_ADD_LEFT(y) { function STRING_ADD_LEFT(y) {
if (!IS_STRING(y)) { if (!IS_STRING(y)) {
...@@ -175,6 +184,15 @@ function STRING_ADD_LEFT(y) { ...@@ -175,6 +184,15 @@ function STRING_ADD_LEFT(y) {
} }
// Left operand (this) is already a string.
function STRING_ADD_LEFT_STRONG(y) {
if (IS_STRING(y)) {
return %_StringAdd(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// Right operand (y) is already a string. // Right operand (y) is already a string.
function STRING_ADD_RIGHT(y) { function STRING_ADD_RIGHT(y) {
var x = this; var x = this;
...@@ -191,6 +209,15 @@ function STRING_ADD_RIGHT(y) { ...@@ -191,6 +209,15 @@ function STRING_ADD_RIGHT(y) {
} }
// Right operand (y) is already a string.
function STRING_ADD_RIGHT_STRONG(y) {
if (IS_STRING(this)) {
return %_StringAdd(this, y);
}
throw %MakeTypeError('strong_implicit_cast');
}
// ECMA-262, section 11.6.2, page 50. // ECMA-262, section 11.6.2, page 50.
function SUB(y) { function SUB(y) {
var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this); var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
...@@ -199,7 +226,7 @@ function SUB(y) { ...@@ -199,7 +226,7 @@ function SUB(y) {
} }
// ECMA-262, section 11.6.2, page 50. // Strong mode SUB throws if an implicit conversion would be performed
function SUB_STRONG(y) { function SUB_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSub(this, y); return %NumberSub(this, y);
...@@ -216,7 +243,7 @@ function MUL(y) { ...@@ -216,7 +243,7 @@ function MUL(y) {
} }
// ECMA-262, section 11.5.1, page 48. // Strong mode MUL throws if an implicit conversion would be performed
function MUL_STRONG(y) { function MUL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMul(this, y); return %NumberMul(this, y);
...@@ -233,7 +260,7 @@ function DIV(y) { ...@@ -233,7 +260,7 @@ function DIV(y) {
} }
// ECMA-262, section 11.5.2, page 49. // Strong mode DIV throws if an implicit conversion would be performed
function DIV_STRONG(y) { function DIV_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberDiv(this, y); return %NumberDiv(this, y);
...@@ -250,7 +277,7 @@ function MOD(y) { ...@@ -250,7 +277,7 @@ function MOD(y) {
} }
// ECMA-262, section 11.5.3, page 49. // Strong mode MOD throws if an implicit conversion would be performed
function MOD_STRONG(y) { function MOD_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMod(this, y); return %NumberMod(this, y);
...@@ -272,7 +299,7 @@ function BIT_OR(y) { ...@@ -272,7 +299,7 @@ function BIT_OR(y) {
} }
//ECMA-262, section 11.10, page 57. // Strong mode BIT_OR throws if an implicit conversion would be performed
function BIT_OR_STRONG(y) { function BIT_OR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberOr(this, y); return %NumberOr(this, y);
...@@ -303,7 +330,7 @@ function BIT_AND(y) { ...@@ -303,7 +330,7 @@ function BIT_AND(y) {
} }
//ECMA-262, section 11.10, page 57. // Strong mode BIT_AND throws if an implicit conversion would be performed
function BIT_AND_STRONG(y) { function BIT_AND_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberAnd(this, y); return %NumberAnd(this, y);
...@@ -320,7 +347,7 @@ function BIT_XOR(y) { ...@@ -320,7 +347,7 @@ function BIT_XOR(y) {
} }
//ECMA-262, section 11.10, page 57. // Strong mode BIT_XOR throws if an implicit conversion would be performed
function BIT_XOR_STRONG(y) { function BIT_XOR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberXor(this, y); return %NumberXor(this, y);
...@@ -337,7 +364,7 @@ function SHL(y) { ...@@ -337,7 +364,7 @@ function SHL(y) {
} }
//ECMA-262, section 11.7.1, page 51. // Strong mode SHL throws if an implicit conversion would be performed
function SHL_STRONG(y) { function SHL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShl(this, y); return %NumberShl(this, y);
...@@ -368,7 +395,7 @@ function SAR(y) { ...@@ -368,7 +395,7 @@ function SAR(y) {
} }
//ECMA-262, section 11.7.2, page 51. // Strong mode SAR throws if an implicit conversion would be performed
function SAR_STRONG(y) { function SAR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSar(this, y); return %NumberSar(this, y);
...@@ -385,7 +412,7 @@ function SHR(y) { ...@@ -385,7 +412,7 @@ function SHR(y) {
} }
//ECMA-262, section 11.7.3, page 52. // Strong mode SHR throws if an implicit conversion would be performed
function SHR_STRONG(y) { function SHR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) { if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShr(this, y); return %NumberShr(this, y);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"use strict"; "use strict";
// TODO(conradw): Implement other strong operators // TODO(conradw): Implement other strong operators
let strongBinops = [ let strongNumberBinops = [
"-", "-",
"*", "*",
"/", "/",
...@@ -20,22 +20,37 @@ let strongBinops = [ ...@@ -20,22 +20,37 @@ let strongBinops = [
">>>", ">>>",
]; ];
let strongStringOrNumberBinops = [
"+"
];
let strongBinops = strongNumberBinops.concat(strongStringOrNumberBinops);
let strongUnops = [ let strongUnops = [
"~", "~",
"+", "+",
"-" "-"
]; ];
let nonNumberValues = [ let nonStringOrNumberValues = [
"{}", "{}",
"'foo'", "false",
"(function(){})", "(function(){})",
"[]", "[]",
"'0'",
"'NaN'",
"(class Foo {})" "(class Foo {})"
]; ];
let stringValues = [
"''",
"' '",
"'foo'",
"'f\\u006F\\u006F'",
"'0'",
"'NaN'"
];
let nonNumberValues = nonStringOrNumberValues.concat(stringValues);
let numberValues = [ let numberValues = [
"0", "0",
"(-0)", "(-0)",
...@@ -57,6 +72,16 @@ let numberValues = [ ...@@ -57,6 +72,16 @@ let numberValues = [
"(-Infinity)" "(-Infinity)"
]; ];
function add_strong(x, y) {
"use strong";
return x + y;
}
function add_num_strong(x, y) {
"use strong";
return x + y;
}
function sub_strong(x, y) { function sub_strong(x, y) {
"use strong"; "use strong";
return x - y; return x - y;
...@@ -107,6 +132,11 @@ function sar_strong(x, y) { ...@@ -107,6 +132,11 @@ function sar_strong(x, y) {
return x >>> y; return x >>> y;
} }
function typed_add_strong(x, y) {
"use strong";
return (+x) + (+y);
}
function typed_sub_strong(x, y) { function typed_sub_strong(x, y) {
"use strong"; "use strong";
return (+x) - (+y); return (+x) - (+y);
...@@ -157,12 +187,17 @@ function typed_sar_strong(x, y) { ...@@ -157,12 +187,17 @@ function typed_sar_strong(x, y) {
return (+x) >>> (+y); return (+x) >>> (+y);
} }
let strongFuncs = [sub_strong, mul_strong, div_strong, mod_strong, or_strong, let strongNumberFuncs = [add_num_strong, sub_strong, mul_strong, div_strong,
and_strong, xor_strong, shl_strong, shr_strong, sar_strong, mod_strong, or_strong, and_strong, xor_strong,
typed_sub_strong, typed_mul_strong, typed_div_strong, shl_strong, shr_strong, sar_strong, typed_add_strong,
typed_mod_strong, typed_or_strong, typed_and_strong, typed_sub_strong, typed_mul_strong, typed_div_strong,
typed_xor_strong, typed_shl_strong, typed_shr_strong, typed_mod_strong, typed_or_strong, typed_and_strong,
typed_sar_strong]; typed_xor_strong, typed_shl_strong, typed_shr_strong,
typed_sar_strong];
let strongStringOrNumberFuncs = [add_strong];
let strongFuncs = strongNumberFuncs.concat(strongStringOrNumberFuncs);
function inline_sub_strong(x, y) { function inline_sub_strong(x, y) {
"use strong"; "use strong";
...@@ -197,29 +232,40 @@ function assertStrongThrowBehaviour(expr) { ...@@ -197,29 +232,40 @@ function assertStrongThrowBehaviour(expr) {
assertThrows("'use strong'; let v = " + expr + ";", TypeError); assertThrows("'use strong'; let v = " + expr + ";", TypeError);
} }
for (let op of strongBinops) { function checkArgumentCombinations(op, leftList, rightList, willThrow) {
for (let v1 of numberValues) { for (let v1 of leftList) {
let assignExpr = "foo " + op + "= " + v1 + ";"; let assignExpr = "foo " + op + "= " + v1 + ";";
for (let v2 of numberValues) { for (let v2 of rightList) {
assertDoesNotThrow("'use strong'; let foo = " + v2 + "; " + assignExpr); let compoundAssignment = "'use strong'; let foo = " + v2 + "; " +
assertStrongNonThrowBehaviour("(" + v1 + op + v2 + ")"); assignExpr;
} if(willThrow) {
for (let v2 of nonNumberValues) { assertThrows(compoundAssignment, TypeError);
assertThrows("'use strong'; let foo = " + v2 + "; " + assignExpr, assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
TypeError); } else {
assertStrongThrowBehaviour("(" + v1 + op + v2 + ")"); assertDoesNotThrow(compoundAssignment);
} assertStrongNonThrowBehaviour("(" + 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 op of strongBinops) {
checkArgumentCombinations(op, numberValues, numberValues, false);
checkArgumentCombinations(op, numberValues, nonNumberValues, true);
}
for (let op of strongNumberBinops) {
checkArgumentCombinations(op, nonNumberValues,
numberValues.concat(nonNumberValues), true);
}
for (let op of strongStringOrNumberBinops) {
checkArgumentCombinations(op, nonNumberValues,
numberValues.concat(nonStringOrNumberValues), true);
checkArgumentCombinations(op, nonStringOrNumberValues, stringValues, true);
checkArgumentCombinations(op, stringValues, stringValues, false);
}
for (let op of strongUnops) { for (let op of strongUnops) {
for (let value of numberValues) { for (let value of numberValues) {
assertStrongNonThrowBehaviour("(" + op + value + ")"); assertStrongNonThrowBehaviour("(" + op + value + ")");
...@@ -229,35 +275,48 @@ for (let op of strongUnops) { ...@@ -229,35 +275,48 @@ for (let op of strongUnops) {
} }
} }
for (let func of strongFuncs) { for (let func of strongNumberFuncs) {
let a = func(4, 5); // Check IC None*None->None throws
let b = func(4, 5); assertThrows(function(){func(2, "foo");}, TypeError);
assertTrue(a === b); %OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
func(4, 5);
func(4, 5);
// Check IC Smi*Smi->Smi throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
func(NaN, NaN);
func(NaN, NaN);
// Check IC Number*Number->Number throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
}
for (let func of strongStringOrNumberFuncs) {
// Check IC None*None->None throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func); %OptimizeFunctionOnNextCall(func);
let c = func(4, 5); assertThrows(function(){func(2, "foo");}, TypeError);
assertOptimized(func);
assertTrue(b === c);
%DeoptimizeFunction(func); %DeoptimizeFunction(func);
let d = func(4, 5); func("foo", "bar");
assertTrue(c === d); func("foo", "bar");
// Check IC String*String->String throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
func(NaN, NaN);
func(NaN, NaN);
// Check IC Generic*Generic->Generic throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func); %DeoptimizeFunction(func);
%ClearFunctionTypeFeedback(func);
}
for (let func of strongFuncs) {
try {
let a = func(2, 3);
let b = func(2, 3);
assertTrue(a === b);
%OptimizeFunctionOnNextCall(func);
let c = func(2, "foo");
assertUnreachable();
} catch (e) {
assertInstanceof(e, TypeError);
assertUnoptimized(func);
assertThrows(function(){func(2, "foo");}, TypeError);
assertDoesNotThrow(function(){func(2, 3);});
}
} }
assertThrows(function(){inline_outer(1, {})}, TypeError); assertThrows(function(){inline_outer(1, {})}, TypeError);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment