V(STRICT_EQUALS, 1) \
V(COMPARE, 2) \
V(ADD, 1) \
+ V(ADD_STRONG, 1) \
V(SUB, 1) \
V(SUB_STRONG, 1) \
V(MUL, 1) \
V(TO_STRING, 0) \
V(TO_NAME, 0) \
V(STRING_ADD_LEFT, 1) \
+ V(STRING_ADD_LEFT_STRONG, 1) \
V(STRING_ADD_RIGHT, 1) \
+ V(STRING_ADD_RIGHT_STRONG, 1) \
V(APPLY_PREPARE, 1) \
V(REFLECT_APPLY_PREPARE, 1) \
V(REFLECT_CONSTRUCT_PREPARE, 2) \
state.op(), left, right,
Type::String(zone()), right_type,
result_type, state.fixed_right_arg(),
- allocation_mode));
+ allocation_mode, state.language_mode()));
}
if_leftisstring.Else();
{
Push(BuildBinaryOperation(
state.op(), left, right,
left_type, right_type, result_type,
- state.fixed_right_arg(), allocation_mode));
+ state.fixed_right_arg(), allocation_mode,
+ state.language_mode()));
}
if_leftisstring.End();
result = Pop();
state.op(), left, right,
left_type, Type::String(zone()),
result_type, state.fixed_right_arg(),
- allocation_mode));
+ allocation_mode, state.language_mode()));
}
if_rightisstring.Else();
{
Push(BuildBinaryOperation(
state.op(), left, right,
left_type, right_type, result_type,
- state.fixed_right_arg(), allocation_mode));
+ state.fixed_right_arg(), allocation_mode,
+ state.language_mode()));
}
if_rightisstring.End();
result = Pop();
result = BuildBinaryOperation(
state.op(), left, right,
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
return BuildBinaryOperation(state.op(), left, right,
left_type, right_type, result_type,
- state.fixed_right_arg(), allocation_mode);
+ state.fixed_right_arg(), allocation_mode,
+ state.language_mode());
}
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 BothInputsAre(Type* t) {
// JSAdd(x:number, y:number) => NumberAdd(x, y)
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))
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state);
// a) The inserted ToString operation screws up valueOf vs. toString order.
// b) Deoptimization at ToString doesn't have corresponding bailout id.
// 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) => StringAdd(x, ToString(y))
// JSAdd(x, y:string) => StringAdd(ToString(x), y)
Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
const Operator* numberOp) {
JSBinopReduction r(this, node);
- if (is_strong(OpParameter<LanguageMode>(node))) {
+ if (r.IsStrong()) {
if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(numberOp, Type::Number());
}
Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) {
JSBinopReduction r(this, node);
- if (is_strong(OpParameter<LanguageMode>(node))) {
+ if (r.IsStrong()) {
if (r.BothInputsAre(Type::Number())) {
r.ConvertInputsToUI32(kSigned, kSigned);
return r.ChangeToPureOperator(intOp, Type::Integral32());
Signedness left_signedness,
const Operator* shift_op) {
JSBinopReduction r(this, node);
- Type* reduce_type = is_strong(
- OpParameter<LanguageMode>(node)) ? Type::Number() :
- Type::Primitive();
+ Type* reduce_type = r.IsStrong() ? Type::Number() : Type::Primitive();
if (r.BothInputsAre(reduce_type)) {
r.ConvertInputsForShift(left_signedness);
return r.ChangeToPureOperator(shift_op, Type::Integral32());
}
// 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()));
left = BuildNumberToString(left, left_type);
} else if (!left_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);
return AddUncasted<HInvokeFunction>(function, 2);
}
// 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()));
right = BuildNumberToString(right, right_type);
} else if (!right_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);
return AddUncasted<HInvokeFunction>(function, 2);
}
Type* result_type,
Maybe<int> fixed_right_arg,
HAllocationMode allocation_mode,
- LanguageMode language_mode = SLOPPY);
+ LanguageMode language_mode);
HLoadNamedField* AddLoadFixedArrayLength(HValue *object,
HValue *dependency = NULL);
if (is_strong(language_mode)) {
switch (op) {
default: UNREACHABLE();
- case Token::ADD: return Builtins::ADD;
+ case Token::ADD: return Builtins::ADD_STRONG;
case Token::SUB: return Builtins::SUB_STRONG;
case Token::MUL: return Builtins::MUL_STRONG;
case Token::DIV: return Builtins::DIV_STRONG;
}
+// 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.
function STRING_ADD_LEFT(y) {
if (!IS_STRING(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.
function STRING_ADD_RIGHT(y) {
var x = this;
}
+// 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.
function SUB(y) {
var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
}
-// ECMA-262, section 11.6.2, page 50.
+// Strong mode SUB throws if an implicit conversion would be performed
function SUB_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSub(this, y);
}
-// ECMA-262, section 11.5.1, page 48.
+// Strong mode MUL throws if an implicit conversion would be performed
function MUL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMul(this, y);
}
-// ECMA-262, section 11.5.2, page 49.
+// Strong mode DIV throws if an implicit conversion would be performed
function DIV_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberDiv(this, y);
}
-// ECMA-262, section 11.5.3, page 49.
+// Strong mode MOD throws if an implicit conversion would be performed
function MOD_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMod(this, 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) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberOr(this, 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) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberAnd(this, 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) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberXor(this, y);
}
-//ECMA-262, section 11.7.1, page 51.
+// Strong mode SHL throws if an implicit conversion would be performed
function SHL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShl(this, y);
}
-//ECMA-262, section 11.7.2, page 51.
+// Strong mode SAR throws if an implicit conversion would be performed
function SAR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSar(this, y);
}
-//ECMA-262, section 11.7.3, page 52.
+// Strong mode SHR throws if an implicit conversion would be performed
function SHR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShr(this, y);
"use strict";
// TODO(conradw): Implement other strong operators
-let strongBinops = [
+let strongNumberBinops = [
"-",
"*",
"/",
">>>",
];
+let strongStringOrNumberBinops = [
+ "+"
+];
+
+let strongBinops = strongNumberBinops.concat(strongStringOrNumberBinops);
+
let strongUnops = [
"~",
"+",
"-"
];
-let nonNumberValues = [
+let nonStringOrNumberValues = [
"{}",
- "'foo'",
+ "false",
"(function(){})",
"[]",
- "'0'",
- "'NaN'",
"(class Foo {})"
];
+let stringValues = [
+ "''",
+ "' '",
+ "'foo'",
+ "'f\\u006F\\u006F'",
+ "'0'",
+ "'NaN'"
+];
+
+let nonNumberValues = nonStringOrNumberValues.concat(stringValues);
+
let numberValues = [
"0",
"(-0)",
"(-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) {
"use strong";
return x - y;
return x >>> y;
}
+function typed_add_strong(x, y) {
+ "use strong";
+ return (+x) + (+y);
+}
+
function typed_sub_strong(x, y) {
"use strong";
return (+x) - (+y);
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];
+let strongNumberFuncs = [add_num_strong, sub_strong, mul_strong, div_strong,
+ mod_strong, or_strong, and_strong, xor_strong,
+ shl_strong, shr_strong, sar_strong, typed_add_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];
+
+let strongStringOrNumberFuncs = [add_strong];
+
+let strongFuncs = strongNumberFuncs.concat(strongStringOrNumberFuncs);
function inline_sub_strong(x, y) {
"use strong";
assertThrows("'use strong'; let v = " + expr + ";", TypeError);
}
-for (let op of strongBinops) {
- for (let v1 of numberValues) {
+function checkArgumentCombinations(op, leftList, rightList, willThrow) {
+ for (let v1 of leftList) {
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 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 v2 of rightList) {
+ let compoundAssignment = "'use strong'; let foo = " + v2 + "; " +
+ assignExpr;
+ if(willThrow) {
+ assertThrows(compoundAssignment, TypeError);
+ assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
+ } else {
+ assertDoesNotThrow(compoundAssignment);
+ assertStrongNonThrowBehaviour("(" + 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 value of numberValues) {
assertStrongNonThrowBehaviour("(" + op + value + ")");
}
}
-for (let func of strongFuncs) {
- let a = func(4, 5);
- let b = func(4, 5);
- assertTrue(a === b);
+for (let func of strongNumberFuncs) {
+ // Check IC None*None->None throws
+ assertThrows(function(){func(2, "foo");}, TypeError);
+ %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);
- let c = func(4, 5);
- assertOptimized(func);
- assertTrue(b === c);
+ assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
- let d = func(4, 5);
- assertTrue(c === d);
+ func("foo", "bar");
+ 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);
- %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);