[turbofan] Don't use the CompareIC in JSGenericLowering.

The CompareICStub produces an untagged raw word value, which has to be
translated to true or false manually in the TurboFan code. But for lazy
bailout after the CompareIC, we immediately go back to fullcodegen or
Ignition with the raw value, to a location where both fullcodegen and
Ignition expect a boolean value, which might crash or in the worst case
(depending on the exact computation inside the CompareIC) could lead to
arbitrary memory access.

Short-term fix is to use the proper runtime functions (unified with the
interpreter now) for comparisons. Next task is to provide optimized
versions of these based on the CodeStubAssembler, which can then be used
via code stubs in TurboFan or directly in handlers in the interpreter.

R=mstarzinger@chromium.org
BUG=v8:4788
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#34335}
This commit is contained in:
bmeurer 2016-02-26 10:40:40 -08:00 committed by Commit bot
parent 81f12a74f0
commit d00da47b61
17 changed files with 132 additions and 277 deletions

View File

@ -675,7 +675,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(lhs, rhs);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -632,7 +632,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(lhs, rhs);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -83,117 +83,22 @@ REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
#undef REPLACE_BINARY_OP_IC_CALL
// These ops are not language mode dependent; we arbitrarily pass Strength::WEAK
// here.
#define REPLACE_COMPARE_IC_CALL(op, token) \
void JSGenericLowering::Lower##op(Node* node) { \
ReplaceWithCompareIC(node, token); \
}
REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ)
REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE)
REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT)
REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT)
REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT)
REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT)
REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE)
REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE)
#undef REPLACE_COMPARE_IC_CALL
#define REPLACE_RUNTIME_CALL(op, fun) \
void JSGenericLowering::Lower##op(Node* node) { \
ReplaceWithRuntimeCall(node, fun); \
}
REPLACE_RUNTIME_CALL(JSEqual, Runtime::kEqual)
REPLACE_RUNTIME_CALL(JSNotEqual, Runtime::kNotEqual)
REPLACE_RUNTIME_CALL(JSStrictEqual, Runtime::kStrictEqual)
REPLACE_RUNTIME_CALL(JSStrictNotEqual, Runtime::kStrictNotEqual)
REPLACE_RUNTIME_CALL(JSLessThan, Runtime::kLessThan)
REPLACE_RUNTIME_CALL(JSGreaterThan, Runtime::kGreaterThan)
REPLACE_RUNTIME_CALL(JSLessThanOrEqual, Runtime::kLessThanOrEqual)
REPLACE_RUNTIME_CALL(JSGreaterThanOrEqual, Runtime::kGreaterThanOrEqual)
REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
#undef REPLACE_RUNTIME
static CallDescriptor::Flags FlagsForNode(Node* node) {
CallDescriptor::Flags result = CallDescriptor::kNoFlags;
if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
result |= CallDescriptor::kNeedsFrameState;
}
return result;
}
void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token) {
Callable callable = CodeFactory::CompareIC(isolate(), token);
// Create a new call node asking a CompareIC for help.
NodeVector inputs(zone());
inputs.reserve(node->InputCount() + 1);
inputs.push_back(jsgraph()->HeapConstant(callable.code()));
inputs.push_back(NodeProperties::GetValueInput(node, 0));
inputs.push_back(NodeProperties::GetValueInput(node, 1));
inputs.push_back(NodeProperties::GetContextInput(node));
// Some comparisons (StrictEqual) don't have an effect, control or frame
// state inputs, so handle those cases here.
if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
inputs.push_back(NodeProperties::GetFrameStateInput(node, 0));
}
Node* effect = (node->op()->EffectInputCount() > 0)
? NodeProperties::GetEffectInput(node)
: graph()->start();
inputs.push_back(effect);
Node* control = (node->op()->ControlInputCount() > 0)
? NodeProperties::GetControlInput(node)
: graph()->start();
inputs.push_back(control);
CallDescriptor* desc_compare = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 0,
CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node),
Operator::kNoProperties, MachineType::IntPtr());
Node* compare =
graph()->NewNode(common()->Call(desc_compare),
static_cast<int>(inputs.size()), &inputs.front());
// Decide how the return value from the above CompareIC can be converted into
// a JavaScript boolean oddball depending on the given token.
Node* false_value = jsgraph()->FalseConstant();
Node* true_value = jsgraph()->TrueConstant();
const Operator* op = nullptr;
switch (token) {
case Token::EQ: // a == 0
case Token::EQ_STRICT:
op = machine()->WordEqual();
break;
case Token::NE: // a != 0 becomes !(a == 0)
case Token::NE_STRICT:
op = machine()->WordEqual();
std::swap(true_value, false_value);
break;
case Token::LT: // a < 0
op = machine()->IntLessThan();
break;
case Token::GT: // a > 0 becomes !(a <= 0)
op = machine()->IntLessThanOrEqual();
std::swap(true_value, false_value);
break;
case Token::LTE: // a <= 0
op = machine()->IntLessThanOrEqual();
break;
case Token::GTE: // a >= 0 becomes !(a < 0)
op = machine()->IntLessThan();
std::swap(true_value, false_value);
break;
default:
UNREACHABLE();
}
Node* booleanize = graph()->NewNode(op, compare, jsgraph()->ZeroConstant());
// Finally patch the original node to select a boolean.
NodeProperties::ReplaceUses(node, node, compare, compare, compare);
node->TrimInputCount(3);
node->ReplaceInput(0, booleanize);
node->ReplaceInput(1, true_value);
node->ReplaceInput(2, false_value);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kTagged));
}
#undef REPLACE_RUNTIME_CALL
void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
CallDescriptor::Flags flags) {
@ -209,11 +114,12 @@ void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
Runtime::FunctionId f,
int nargs_override) {
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
Operator::Properties properties = node->op()->properties();
const Runtime::Function* fun = Runtime::FunctionForId(f);
int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
zone(), f, nargs, properties, CallDescriptor::kNeedsFrameState);
CallDescriptor* desc =
Linkage::GetRuntimeCallDescriptor(zone(), f, nargs, properties, flags);
Node* ref = jsgraph()->ExternalConstant(ExternalReference(f, isolate()));
Node* arity = jsgraph()->Int32Constant(nargs);
node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size));

View File

@ -36,7 +36,6 @@ class JSGenericLowering final : public Reducer {
#undef DECLARE_LOWER
// Helpers to replace existing nodes with a generic call.
void ReplaceWithCompareIC(Node* node, Token::Value token);
void ReplaceWithStubCall(Node* node, Callable c, CallDescriptor::Flags flags);
void ReplaceWithRuntimeCall(Node* node, Runtime::FunctionId f, int args = -1);

View File

@ -1447,7 +1447,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(edx);
__ Push(eax);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -1144,7 +1144,7 @@ void Interpreter::DoNewWide(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register equals the accumulator.
void Interpreter::DoTestEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kEquals, assembler);
DoBinaryOp(Runtime::kEqual, assembler);
}
@ -1152,7 +1152,7 @@ void Interpreter::DoTestEqual(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is not equal to the accumulator.
void Interpreter::DoTestNotEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kNotEquals, assembler);
DoBinaryOp(Runtime::kNotEqual, assembler);
}
@ -1160,7 +1160,7 @@ void Interpreter::DoTestNotEqual(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is strictly equal to the accumulator.
void Interpreter::DoTestEqualStrict(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kStrictEquals, assembler);
DoBinaryOp(Runtime::kStrictEqual, assembler);
}
@ -1169,7 +1169,7 @@ void Interpreter::DoTestEqualStrict(InterpreterAssembler* assembler) {
// Test if the value in the <src> register is not strictly equal to the
// accumulator.
void Interpreter::DoTestNotEqualStrict(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kStrictNotEquals, assembler);
DoBinaryOp(Runtime::kStrictNotEqual, assembler);
}
@ -1177,7 +1177,7 @@ void Interpreter::DoTestNotEqualStrict(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is less than the accumulator.
void Interpreter::DoTestLessThan(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kInterpreterLessThan, assembler);
DoBinaryOp(Runtime::kLessThan, assembler);
}
@ -1185,7 +1185,7 @@ void Interpreter::DoTestLessThan(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is greater than the accumulator.
void Interpreter::DoTestGreaterThan(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kInterpreterGreaterThan, assembler);
DoBinaryOp(Runtime::kGreaterThan, assembler);
}
@ -1194,7 +1194,7 @@ void Interpreter::DoTestGreaterThan(InterpreterAssembler* assembler) {
// Test if the value in the <src> register is less than or equal to the
// accumulator.
void Interpreter::DoTestLessThanOrEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kInterpreterLessThanOrEqual, assembler);
DoBinaryOp(Runtime::kLessThanOrEqual, assembler);
}
@ -1203,7 +1203,7 @@ void Interpreter::DoTestLessThanOrEqual(InterpreterAssembler* assembler) {
// Test if the value in the <src> register is greater than or equal to the
// accumulator.
void Interpreter::DoTestGreaterThanOrEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kInterpreterGreaterThanOrEqual, assembler);
DoBinaryOp(Runtime::kGreaterThanOrEqual, assembler);
}

View File

@ -722,7 +722,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(lhs, rhs);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -719,7 +719,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(lhs, rhs);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -1353,6 +1353,10 @@ int HeapNumber::get_sign() {
bool Simd128Value::Equals(Simd128Value* that) {
// TODO(bmeurer): This doesn't match the SIMD.js specification, but it seems
// to be consistent with what the CompareICStub does, and what is tested in
// the current SIMD.js testsuite.
if (this == that) return true;
#define SIMD128_VALUE(TYPE, Type, type, lane_count, lane_type) \
if (this->Is##Type()) { \
if (!that->Is##Type()) return false; \

View File

@ -16,62 +16,6 @@
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_InterpreterLessThan) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::LessThan(x, y);
if (result.IsJust()) {
return isolate->heap()->ToBoolean(result.FromJust());
} else {
return isolate->heap()->exception();
}
}
RUNTIME_FUNCTION(Runtime_InterpreterGreaterThan) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::GreaterThan(x, y);
if (result.IsJust()) {
return isolate->heap()->ToBoolean(result.FromJust());
} else {
return isolate->heap()->exception();
}
}
RUNTIME_FUNCTION(Runtime_InterpreterLessThanOrEqual) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::LessThanOrEqual(x, y);
if (result.IsJust()) {
return isolate->heap()->ToBoolean(result.FromJust());
} else {
return isolate->heap()->exception();
}
}
RUNTIME_FUNCTION(Runtime_InterpreterGreaterThanOrEqual) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::GreaterThanOrEqual(x, y);
if (result.IsJust()) {
return isolate->heap()->ToBoolean(result.FromJust());
} else {
return isolate->heap()->exception();
}
}
RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());

View File

@ -140,7 +140,7 @@ RUNTIME_FUNCTION(Runtime_BitwiseXor) {
return *result;
}
RUNTIME_FUNCTION(Runtime_Equals) {
RUNTIME_FUNCTION(Runtime_Equal) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
@ -150,7 +150,7 @@ RUNTIME_FUNCTION(Runtime_Equals) {
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_NotEquals) {
RUNTIME_FUNCTION(Runtime_NotEqual) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
@ -160,7 +160,7 @@ RUNTIME_FUNCTION(Runtime_NotEquals) {
return isolate->heap()->ToBoolean(!result.FromJust());
}
RUNTIME_FUNCTION(Runtime_StrictEquals) {
RUNTIME_FUNCTION(Runtime_StrictEqual) {
SealHandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
@ -168,7 +168,7 @@ RUNTIME_FUNCTION(Runtime_StrictEquals) {
return isolate->heap()->ToBoolean(x->StrictEquals(y));
}
RUNTIME_FUNCTION(Runtime_StrictNotEquals) {
RUNTIME_FUNCTION(Runtime_StrictNotEqual) {
SealHandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
@ -176,5 +176,45 @@ RUNTIME_FUNCTION(Runtime_StrictNotEquals) {
return isolate->heap()->ToBoolean(!x->StrictEquals(y));
}
RUNTIME_FUNCTION(Runtime_LessThan) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::LessThan(x, y);
if (!result.IsJust()) return isolate->heap()->exception();
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_GreaterThan) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::GreaterThan(x, y);
if (!result.IsJust()) return isolate->heap()->exception();
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_LessThanOrEqual) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::LessThanOrEqual(x, y);
if (!result.IsJust()) return isolate->heap()->exception();
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_GreaterThanOrEqual) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, y, 1);
Maybe<bool> result = Object::GreaterThanOrEqual(x, y);
if (!result.IsJust()) return isolate->heap()->exception();
return isolate->heap()->ToBoolean(result.FromJust());
}
} // namespace internal
} // namespace v8

View File

@ -206,10 +206,6 @@ namespace internal {
F(ForInStep, 1, 1)
#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
F(InterpreterLessThan, 2, 1) \
F(InterpreterGreaterThan, 2, 1) \
F(InterpreterLessThanOrEqual, 2, 1) \
F(InterpreterGreaterThanOrEqual, 2, 1) \
F(InterpreterToBoolean, 1, 1) \
F(InterpreterLogicalNot, 1, 1) \
F(InterpreterNewClosure, 2, 1) \
@ -484,10 +480,14 @@ namespace internal {
F(BitwiseAnd, 2, 1) \
F(BitwiseOr, 2, 1) \
F(BitwiseXor, 2, 1) \
F(Equals, 2, 1) \
F(NotEquals, 2, 1) \
F(StrictEquals, 2, 1) \
F(StrictNotEquals, 2, 1)
F(Equal, 2, 1) \
F(NotEqual, 2, 1) \
F(StrictEqual, 2, 1) \
F(StrictNotEqual, 2, 1) \
F(LessThan, 2, 1) \
F(GreaterThan, 2, 1) \
F(LessThanOrEqual, 2, 1) \
F(GreaterThanOrEqual, 2, 1)
#define FOR_EACH_INTRINSIC_PROXY(F) \
F(IsJSProxy, 1, 1) \

View File

@ -1325,7 +1325,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rdx);
__ Push(rax);
__ CallRuntime(strict() ? Runtime::kStrictEquals : Runtime::kEquals);
__ CallRuntime(strict() ? Runtime::kStrictEqual : Runtime::kEqual);
}
// Turn true into 0 and false into some non-zero value.
STATIC_ASSERT(EQUAL == 0);

View File

@ -865,7 +865,6 @@
# TODO(rmcilroy,4680): Test assert failures.
'array-literal-feedback': [FAIL],
'debug-liveedit-2': [FAIL],
'compiler/deopt-tonumber-compare': [FAIL],
'es6/mirror-collections': [FAIL],
'es6/regress/regress-468661': [FAIL],
'regress/regress-2618': [FAIL],

View File

@ -0,0 +1,25 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
var f = (function() {
"use asm";
function foo(x) {
return x == 0;
}
return foo;
})();
function deopt(f) {
return {
toString : function() {
%DeoptimizeFunction(f);
return "2";
}
};
}
%OptimizeFunctionOnNextCall(f);
assertFalse(f(deopt(f)));

View File

@ -0,0 +1,25 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
var f = (function() {
"use asm";
function foo(x) {
return x < x;
}
return foo;
})();
function deopt(f) {
return {
toString : function() {
%DeoptimizeFunction(f);
return "2";
}
};
}
%OptimizeFunctionOnNextCall(f);
assertFalse(f(deopt(f)));

View File

@ -23,12 +23,6 @@ class RuntimeInterpreterTest : public TestWithIsolateAndZone {
bool TestOperatorWithObjects(RuntimeMethod method, Handle<Object> lhs,
Handle<Object> rhs, bool expected);
bool TestOperator(RuntimeMethod method, int32_t lhs, int32_t rhs,
bool expected);
bool TestOperator(RuntimeMethod method, double lhs, double rhs,
bool expected);
bool TestOperator(RuntimeMethod method, const char* lhs, const char* rhs,
bool expected);
};
@ -44,87 +38,6 @@ bool RuntimeInterpreterTest::TestOperatorWithObjects(RuntimeMethod method,
}
bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, int32_t lhs,
int32_t rhs, bool expected) {
Handle<Object> x = isolate()->factory()->NewNumberFromInt(lhs);
Handle<Object> y = isolate()->factory()->NewNumberFromInt(rhs);
return TestOperatorWithObjects(method, x, y, expected);
}
bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, double lhs,
double rhs, bool expected) {
Handle<Object> x = isolate()->factory()->NewNumber(lhs);
Handle<Object> y = isolate()->factory()->NewNumber(rhs);
CHECK_EQ(HeapNumber::cast(*x)->value(), lhs);
CHECK_EQ(HeapNumber::cast(*y)->value(), rhs);
return TestOperatorWithObjects(method, x, y, expected);
}
bool RuntimeInterpreterTest::TestOperator(RuntimeMethod method, const char* lhs,
const char* rhs, bool expected) {
Handle<Object> x = isolate()->factory()->NewStringFromAsciiChecked(lhs);
Handle<Object> y = isolate()->factory()->NewStringFromAsciiChecked(rhs);
return TestOperatorWithObjects(method, x, y, expected);
}
TEST_F(RuntimeInterpreterTest, TestOperatorsWithIntegers) {
int32_t inputs[] = {kMinInt, Smi::kMinValue, -17, -1, 0, 1,
991, Smi::kMaxValue, kMaxInt};
TRACED_FOREACH(int, lhs, inputs) {
TRACED_FOREACH(int, rhs, inputs) {
#define INTEGER_OPERATOR_CHECK(r, op, x, y) \
CHECK(TestOperator(Runtime_Interpreter##r, x, y, x op y))
INTEGER_OPERATOR_CHECK(LessThan, <, lhs, rhs);
INTEGER_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
INTEGER_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
INTEGER_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
#undef INTEGER_OPERATOR_CHECK
}
}
}
TEST_F(RuntimeInterpreterTest, TestOperatorsWithDoubles) {
double inputs[] = {std::numeric_limits<double>::min(),
std::numeric_limits<double>::max(),
-0.001,
0.01,
3.14,
-6.02214086e23};
TRACED_FOREACH(double, lhs, inputs) {
TRACED_FOREACH(double, rhs, inputs) {
#define DOUBLE_OPERATOR_CHECK(r, op, x, y) \
CHECK(TestOperator(Runtime_Interpreter##r, x, y, x op y))
DOUBLE_OPERATOR_CHECK(LessThan, <, lhs, rhs);
DOUBLE_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
DOUBLE_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
DOUBLE_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
#undef DOUBLE_OPERATOR_CHECK
}
}
}
TEST_F(RuntimeInterpreterTest, TestOperatorsWithString) {
const char* inputs[] = {"abc", "a", "def", "0"};
TRACED_FOREACH(const char*, lhs, inputs) {
TRACED_FOREACH(const char*, rhs, inputs) {
#define STRING_OPERATOR_CHECK(r, op, x, y) \
CHECK(TestOperator(Runtime_Interpreter##r, x, y, \
std::string(x) op std::string(y)))
STRING_OPERATOR_CHECK(LessThan, <, lhs, rhs);
STRING_OPERATOR_CHECK(GreaterThan, >, lhs, rhs);
STRING_OPERATOR_CHECK(LessThanOrEqual, <=, lhs, rhs);
STRING_OPERATOR_CHECK(GreaterThanOrEqual, >=, lhs, rhs);
#undef STRING_OPERATOR_CHECK
}
}
}
TEST_F(RuntimeInterpreterTest, ToBoolean) {
double quiet_nan = std::numeric_limits<double>::quiet_NaN();
std::pair<Handle<Object>, bool> cases[] = {