"src/runtime/runtime-generator.cc",
"src/runtime/runtime-i18n.cc",
"src/runtime/runtime-internal.cc",
+ "src/runtime/runtime-interpreter.cc",
"src/runtime/runtime-json.cc",
"src/runtime/runtime-literals.cc",
"src/runtime/runtime-liveedit.cc",
}
-void BytecodeGraphBuilder::VisitTestLessThanEqual(
+void BytecodeGraphBuilder::VisitTestLessThanOrEqual(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
-void BytecodeGraphBuilder::VisitTestGreaterThanEqual(
+void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
case Bytecode::kTestEqualStrict:
case Bytecode::kTestNotEqualStrict:
case Bytecode::kTestLessThan:
- case Bytecode::kTestLessThanEqual:
+ case Bytecode::kTestLessThanOrEqual:
case Bytecode::kTestGreaterThan:
- case Bytecode::kTestGreaterThanEqual:
+ case Bytecode::kTestGreaterThanOrEqual:
case Bytecode::kTestInstanceOf:
case Bytecode::kTestIn:
break;
case Token::Value::GT:
return Bytecode::kTestGreaterThan;
case Token::Value::LTE:
- return Bytecode::kTestLessThanEqual;
+ return Bytecode::kTestLessThanOrEqual;
case Token::Value::GTE:
- return Bytecode::kTestGreaterThanEqual;
+ return Bytecode::kTestGreaterThanOrEqual;
case Token::Value::INSTANCEOF:
return Bytecode::kTestInstanceOf;
case Token::Value::IN:
V(TestNotEqualStrict, OperandType::kReg) \
V(TestLessThan, OperandType::kReg) \
V(TestGreaterThan, OperandType::kReg) \
- V(TestLessThanEqual, OperandType::kReg) \
- V(TestGreaterThanEqual, OperandType::kReg) \
+ V(TestLessThanOrEqual, OperandType::kReg) \
+ V(TestGreaterThanOrEqual, OperandType::kReg) \
V(TestInstanceOf, OperandType::kReg) \
V(TestIn, OperandType::kReg) \
\
}
-void Interpreter::DoCompareOp(Token::Value op,
- compiler::InterpreterAssembler* assembler) {
- // TODO(oth): placeholder until compare path fixed.
- // The accumulator should be set to true on success (or false otherwise)
- // by the comparisons so it can be used for conditional jumps.
- DoLdaTrue(assembler);
-}
-
-
// Add <src>
//
// Add register <src> to accumulator.
//
// Test if the value in the <src> register equals the accumulator.
void Interpreter::DoTestEqual(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::EQ, assembler);
+ DoBinaryOp(Runtime::kInterpreterEquals, assembler);
}
//
// Test if the value in the <src> register is not equal to the accumulator.
void Interpreter::DoTestNotEqual(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::NE, assembler);
+ DoBinaryOp(Runtime::kInterpreterNotEquals, assembler);
}
//
// Test if the value in the <src> register is strictly equal to the accumulator.
void Interpreter::DoTestEqualStrict(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::EQ_STRICT, assembler);
+ DoBinaryOp(Runtime::kInterpreterStrictEquals, assembler);
}
// accumulator.
void Interpreter::DoTestNotEqualStrict(
compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::NE_STRICT, assembler);
+ DoBinaryOp(Runtime::kInterpreterStrictNotEquals, assembler);
}
//
// Test if the value in the <src> register is less than the accumulator.
void Interpreter::DoTestLessThan(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::LT, assembler);
+ DoBinaryOp(Runtime::kInterpreterLessThan, assembler);
}
//
// Test if the value in the <src> register is greater than the accumulator.
void Interpreter::DoTestGreaterThan(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::GT, assembler);
+ DoBinaryOp(Runtime::kInterpreterGreaterThan, assembler);
}
-// TestLessThanEqual <src>
+// TestLessThanOrEqual <src>
//
// Test if the value in the <src> register is less than or equal to the
// accumulator.
-void Interpreter::DoTestLessThanEqual(
+void Interpreter::DoTestLessThanOrEqual(
compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::LTE, assembler);
+ DoBinaryOp(Runtime::kInterpreterLessThanOrEqual, assembler);
}
-// TestGreaterThanEqual <src>
+// TestGreaterThanOrEqual <src>
//
// Test if the value in the <src> register is greater than or equal to the
// accumulator.
-void Interpreter::DoTestGreaterThanEqual(
+void Interpreter::DoTestGreaterThanOrEqual(
compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::GTE, assembler);
+ DoBinaryOp(Runtime::kInterpreterGreaterThanOrEqual, assembler);
}
// TestIn <src>
//
-// Test if the value in the <src> register is in the collection referenced
-// by the accumulator.
+// Test if the object referenced by the register operand is a property of the
+// object referenced by the accumulator.
void Interpreter::DoTestIn(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::IN, assembler);
+ DoBinaryOp(Runtime::kHasProperty, assembler);
}
// Test if the object referenced by the <src> register is an an instance of type
// referenced by the accumulator.
void Interpreter::DoTestInstanceOf(compiler::InterpreterAssembler* assembler) {
- DoCompareOp(Token::Value::INSTANCEOF, assembler);
+ DoBinaryOp(Runtime::kInstanceOf, assembler);
}
enum class ComparisonResult {
kLessThan, // x < y
kEqual, // x = y
- kGreaterThan, // x > x
+ kGreaterThan, // x > y
kUndefined // at least one of x or y was undefined or NaN
};
--- /dev/null
+// Copyright 2015 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.
+
+#include "src/runtime/runtime-utils.h"
+
+#include "src/arguments.h"
+#include "src/isolate-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterEquals) {
+ 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::Equals(x, y);
+ if (result.IsJust()) {
+ return isolate->heap()->ToBoolean(result.FromJust());
+ } else {
+ return isolate->heap()->exception();
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterNotEquals) {
+ 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::Equals(x, y);
+ if (result.IsJust()) {
+ return isolate->heap()->ToBoolean(!result.FromJust());
+ } else {
+ return isolate->heap()->exception();
+ }
+}
+
+
+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_InterpreterStrictEquals) {
+ SealHandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ CONVERT_ARG_CHECKED(Object, x, 0);
+ CONVERT_ARG_CHECKED(Object, y, 1);
+ return isolate->heap()->ToBoolean(x->StrictEquals(y));
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
+ SealHandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ CONVERT_ARG_CHECKED(Object, x, 0);
+ CONVERT_ARG_CHECKED(Object, y, 1);
+ return isolate->heap()->ToBoolean(!x->StrictEquals(y));
+}
+
+
+RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
+ SealHandleScope scope(isolate);
+ DCHECK_EQ(1, args.length());
+ CONVERT_ARG_CHECKED(Object, x, 0);
+ return isolate->heap()->ToBoolean(x->BooleanValue());
+}
+
+
+} // namespace internal
+} // namespace v8
F(ForInStep, 1, 1)
+#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
+ F(InterpreterEquals, 2, 1) \
+ F(InterpreterNotEquals, 2, 1) \
+ F(InterpreterStrictEquals, 2, 1) \
+ F(InterpreterStrictNotEquals, 2, 1) \
+ F(InterpreterLessThan, 2, 1) \
+ F(InterpreterGreaterThan, 2, 1) \
+ F(InterpreterLessThanOrEqual, 2, 1) \
+ F(InterpreterGreaterThanOrEqual, 2, 1) \
+ F(InterpreterToBoolean, 1, 1)
+
+
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
F(FunctionGetName, 1, 1) \
F(FunctionSetName, 2, 1) \
FOR_EACH_INTRINSIC_DATE(F) \
FOR_EACH_INTRINSIC_DEBUG(F) \
FOR_EACH_INTRINSIC_FORIN(F) \
+ FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_FUNCTION(F) \
FOR_EACH_INTRINSIC_FUTEX(F) \
FOR_EACH_INTRINSIC_GENERATOR(F) \
public:
const char* kFunctionName = "f";
- const int kLastParamIndex =
+ static const int kLastParamIndex =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
BytecodeGeneratorHelper() {
{"name"}}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
- helper.MakeBytecode(snippets[i].code_snippet, "f");
+ helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
{"name"}}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
- helper.MakeBytecode(snippets[i].code_snippet, "f");
+ helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
{"func"}}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
- helper.MakeBytecode(snippets[i].code_snippet, "f");
+ helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
- bytecode_array->Print();
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
Handle<Object> unused = helper.factory()->undefined_value();
ExpectedSnippet<Handle<Object>> snippets[] = {
- {"function f() { if (0) { return 1; } else { return -1; } }",
+ {"function f() { if (0) { return 1; } else { return -1; } } f()",
0,
1,
14,
B(Return)}, //
0,
{unused, unused, unused, unused}},
- {"function f() { if ('lucky') { return 1; } else { return -1; } }",
+ {"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
0,
1,
15,
B(LdaUndefined), //
B(Return)}, //
1,
- {helper.factory()->NewStringFromStaticChars("lucky"), unused,
- unused, unused}},
- {"function f() { if (false) { return 1; } else { return -1; } }",
+ {helper.factory()->NewStringFromStaticChars("lucky"), unused, unused,
+ unused}},
+ {"function f() { if (false) { return 1; } else { return -1; } } f();",
0,
1,
13,
B(Return)}, //
0,
{unused, unused, unused, unused}},
- {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }",
+ {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }"
+ "f(99);",
kPointerSize,
2,
19,
- {B(Ldar), R(-5), //
- B(Star), R(0), //
- B(LdaZero), //
- B(TestLessThanEqual), R(0), //
- B(JumpIfFalse), U8(7), //
- B(LdaConstant), U8(0), //
- B(Return), //
- B(Jump), U8(5), // TODO(oth): Unreachable jump after return
- B(LdaConstant), U8(1), //
- B(Return), //
- B(LdaUndefined), //
- B(Return)}, //
+ {B(Ldar), R(-5), //
+ B(Star), R(0), //
+ B(LdaZero), //
+ B(TestLessThanOrEqual), R(0), //
+ B(JumpIfFalse), U8(7), //
+ B(LdaConstant), U8(0), //
+ B(Return), //
+ B(Jump), U8(5), // TODO(oth): Unreachable jump after return
+ B(LdaConstant), U8(1), //
+ B(Return), //
+ B(LdaUndefined), //
+ B(Return)}, //
2,
{helper.factory()->NewNumberFromInt(200),
helper.factory()->NewNumberFromInt(-200), unused, unused}},
- {"function f(a, b) { if (a in b) { return 200; } }",
+ {"function f(a, b) { if (a in b) { return 200; } }"
+ "f('prop', { prop: 'yes'});",
kPointerSize,
3,
17,
B(Return)}, //
1,
{helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
- {"function f(a, b) { if (a instanceof b) { return 200; } }",
- kPointerSize,
- 3,
- 17,
- {B(Ldar), R(-6), //
- B(Star), R(0), //
- B(Ldar), R(-5), //
- B(TestInstanceOf), R(0), //
- B(JumpIfFalse), U8(7), //
- B(LdaConstant), U8(0), //
- B(Return), //
- B(Jump), U8(2), // TODO(oth): Unreachable jump after return
- B(LdaUndefined), //
- B(Return)}, //
- 1,
- {helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
{"function f(z) { var a = 0; var b = 0; if (a === 0.01) { "
#define X "b = a; a = b; "
X X X X X X X X X X X X X X X X X X X X X X X X
#undef X
- " return 200; } else { return -200; } }",
+ " return 200; } else { return -200; } } f(0.001)",
3 * kPointerSize,
2,
218,
{helper.factory()->NewHeapNumber(0.01),
helper.factory()->NewNumberFromInt(200),
helper.factory()->NewNumberFromInt(199),
- helper.factory()->NewNumberFromInt(-200)}}};
+ helper.factory()->NewNumberFromInt(-200)}},
+ {"function f(a, b) {\n"
+ " if (a == b) { return 1; }\n"
+ " if (a === b) { return 1; }\n"
+ " if (a < b) { return 1; }\n"
+ " if (a > b) { return 1; }\n"
+ " if (a <= b) { return 1; }\n"
+ " if (a >= b) { return 1; }\n"
+ " if (a in b) { return 1; }\n"
+ " if (a instanceof b) { return 1; }\n"
+ " /* if (a != b) { return 1; } */" // TODO(oth) Ast visitor yields
+ " /* if (a !== b) { return 1; } */" // UNARY NOT, rather than !=/!==.
+ " return 0;\n"
+ "} f(1, 1);",
+ kPointerSize,
+ 3,
+ 122,
+ {
+#define IF_CONDITION_RETURN(condition) \
+ B(Ldar), R(-6), \
+ B(Star), R(0), \
+ B(Ldar), R(-5), \
+ B(condition), R(0), \
+ B(JumpIfFalse), U8(7), \
+ B(LdaSmi8), U8(1), \
+ B(Return), \
+ B(Jump), U8(2),
+ IF_CONDITION_RETURN(TestEqual) //
+ IF_CONDITION_RETURN(TestEqualStrict) //
+ IF_CONDITION_RETURN(TestLessThan) //
+ IF_CONDITION_RETURN(TestGreaterThan) //
+ IF_CONDITION_RETURN(TestLessThanOrEqual) //
+ IF_CONDITION_RETURN(TestGreaterThanOrEqual) //
+ IF_CONDITION_RETURN(TestIn) //
+ IF_CONDITION_RETURN(TestInstanceOf) //
+#undef IF_CONDITION_RETURN
+ B(LdaZero), //
+ B(Return)}, //
+ 0,
+ {unused, unused, unused, unused}},
+ };
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
- helper.MakeBytecodeForFunction(snippets[i].code_snippet);
+ helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
using v8::internal::BytecodeArray;
using v8::internal::Handle;
+using v8::internal::LanguageMode;
using v8::internal::Object;
using v8::internal::Runtime;
using v8::internal::Smi;
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_value)->value(), 7);
}
+
+
+static const Token::Value kComparisonTypes[] = {
+ Token::Value::EQ, Token::Value::NE, Token::Value::EQ_STRICT,
+ Token::Value::NE_STRICT, Token::Value::LTE, Token::Value::LTE,
+ Token::Value::GT, Token::Value::GTE};
+
+
+template <typename T>
+bool CompareC(Token::Value op, T lhs, T rhs, bool types_differed = false) {
+ switch (op) {
+ case Token::Value::EQ:
+ return lhs == rhs;
+ case Token::Value::NE:
+ return lhs != rhs;
+ case Token::Value::EQ_STRICT:
+ return (lhs == rhs) && !types_differed;
+ case Token::Value::NE_STRICT:
+ return (lhs != rhs) || types_differed;
+ case Token::Value::LT:
+ return lhs < rhs;
+ case Token::Value::LTE:
+ return lhs <= rhs;
+ case Token::Value::GT:
+ return lhs > rhs;
+ case Token::Value::GTE:
+ return lhs >= rhs;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+
+TEST(InterpreterSmiComparisons) {
+ // NB Constants cover 31-bit space.
+ int inputs[] = {v8::internal::kMinInt / 2,
+ v8::internal::kMinInt / 4,
+ -108733832,
+ -999,
+ -42,
+ -2,
+ -1,
+ 0,
+ +1,
+ +2,
+ 42,
+ 12345678,
+ v8::internal::kMaxInt / 4,
+ v8::internal::kMaxInt / 2};
+
+ for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+ Token::Value comparison = kComparisonTypes[c];
+ for (size_t i = 0; i < arraysize(inputs); i++) {
+ for (size_t j = 0; j < arraysize(inputs); j++) {
+ HandleAndZoneScope handles;
+ BytecodeArrayBuilder builder(handles.main_isolate(),
+ handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ builder.LoadLiteral(Smi::FromInt(inputs[i]))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(Smi::FromInt(inputs[j]))
+ .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+ .Return();
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(),
+ CompareC(comparison, inputs[i], inputs[j]));
+ }
+ }
+ }
+}
+
+
+TEST(InterpreterHeapNumberComparisons) {
+ double inputs[] = {std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ -0.001,
+ 0.01,
+ 0.1000001,
+ 1e99,
+ -1e-99};
+ for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+ Token::Value comparison = kComparisonTypes[c];
+ for (size_t i = 0; i < arraysize(inputs); i++) {
+ for (size_t j = 0; j < arraysize(inputs); j++) {
+ HandleAndZoneScope handles;
+ i::Factory* factory = handles.main_isolate()->factory();
+ BytecodeArrayBuilder builder(handles.main_isolate(),
+ handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ builder.LoadLiteral(factory->NewHeapNumber(inputs[i]))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(factory->NewHeapNumber(inputs[j]))
+ .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+ .Return();
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(),
+ CompareC(comparison, inputs[i], inputs[j]));
+ }
+ }
+ }
+}
+
+
+TEST(InterpreterStringComparisons) {
+ std::string inputs[] = {"A", "abc", "z", "", "Foo!", "Foo"};
+
+ for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+ Token::Value comparison = kComparisonTypes[c];
+ for (size_t i = 0; i < arraysize(inputs); i++) {
+ for (size_t j = 0; j < arraysize(inputs); j++) {
+ const char* lhs = inputs[i].c_str();
+ const char* rhs = inputs[j].c_str();
+ HandleAndZoneScope handles;
+ i::Factory* factory = handles.main_isolate()->factory();
+ BytecodeArrayBuilder builder(handles.main_isolate(),
+ handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(factory->NewStringFromAsciiChecked(rhs))
+ .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+ .Return();
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(),
+ CompareC(comparison, inputs[i], inputs[j]));
+ }
+ }
+ }
+}
+
+
+TEST(InterpreterMixedComparisons) {
+ // This test compares a HeapNumber with a String. The latter is
+ // convertible to a HeapNumber so comparison will be between numeric
+ // values except for the strict comparisons where no conversion is
+ // performed.
+ const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e5", "2.01"};
+
+ i::UnicodeCache unicode_cache;
+
+ for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
+ Token::Value comparison = kComparisonTypes[c];
+ for (size_t i = 0; i < arraysize(inputs); i++) {
+ for (size_t j = 0; j < arraysize(inputs); j++) {
+ for (int pass = 0; pass < 2; pass++) {
+ const char* lhs_cstr = inputs[i];
+ const char* rhs_cstr = inputs[j];
+ double lhs = StringToDouble(&unicode_cache, lhs_cstr,
+ i::ConversionFlags::NO_FLAGS);
+ double rhs = StringToDouble(&unicode_cache, rhs_cstr,
+ i::ConversionFlags::NO_FLAGS);
+ HandleAndZoneScope handles;
+ i::Factory* factory = handles.main_isolate()->factory();
+ BytecodeArrayBuilder builder(handles.main_isolate(),
+ handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ if (pass == 0) {
+ // Comparison with HeapNumber on the lhs and String on the rhs
+ builder.LoadLiteral(factory->NewNumber(lhs))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(factory->NewStringFromAsciiChecked(rhs_cstr))
+ .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+ .Return();
+ } else {
+ // Comparison with HeapNumber on the rhs and String on the lhs
+ builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs_cstr))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(factory->NewNumber(rhs))
+ .CompareOperation(comparison, r0, LanguageMode::SLOPPY)
+ .Return();
+ }
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(),
+ CompareC(comparison, lhs, rhs, true));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(InterpreterInstanceOf) {
+ HandleAndZoneScope handles;
+ i::Factory* factory = handles.main_isolate()->factory();
+ Handle<i::String> name = factory->NewStringFromAsciiChecked("cons");
+ Handle<i::JSFunction> func = factory->NewFunction(name);
+ Handle<i::JSObject> instance = factory->NewJSObject(func);
+ Handle<i::Object> other = factory->NewNumber(3.3333);
+ Handle<i::Object> cases[] = {Handle<i::Object>::cast(instance), other};
+ for (size_t i = 0; i < arraysize(cases); i++) {
+ bool expected_value = (i == 0);
+ BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ builder.LoadLiteral(cases[i]);
+ builder.StoreAccumulatorInRegister(r0)
+ .LoadLiteral(func)
+ .CompareOperation(Token::Value::INSTANCEOF, r0, LanguageMode::SLOPPY)
+ .Return();
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(), expected_value);
+ }
+}
+
+
+TEST(InterpreterTestIn) {
+ HandleAndZoneScope handles;
+ i::Factory* factory = handles.main_isolate()->factory();
+ // Allocate an array
+ Handle<i::JSArray> array =
+ factory->NewJSArray(i::ElementsKind::FAST_SMI_ELEMENTS);
+ // Check for these properties on the array object
+ const char* properties[] = {"length", "fuzzle", "x", "0"};
+ for (size_t i = 0; i < arraysize(properties); i++) {
+ bool expected_value = (i == 0);
+ BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
+ Register r0(0);
+ builder.set_locals_count(1);
+ builder.set_parameter_count(0);
+ builder.LoadLiteral(factory->NewStringFromAsciiChecked(properties[i]))
+ .StoreAccumulatorInRegister(r0)
+ .LoadLiteral(Handle<Object>::cast(array))
+ .CompareOperation(Token::Value::IN, r0, LanguageMode::SLOPPY)
+ .Return();
+
+ Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
+ InterpreterTester tester(handles.main_isolate(), bytecode_array);
+ auto callable = tester.GetCallable<>();
+ Handle<Object> return_value = callable().ToHandleChecked();
+ CHECK(return_value->IsBoolean());
+ CHECK_EQ(return_value->BooleanValue(), expected_value);
+ }
+}
-// Copyright 2014 the V8 project authors. All rights reserved.
+// Copyright 2015 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.
--- /dev/null
+// Copyright 2015 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.
+
+#include "src/v8.h"
+
+#include "src/factory.h"
+#include "src/heap/heap.h"
+#include "src/heap/heap-inl.h"
+#include "src/runtime/runtime.h"
+#include "test/unittests/test-utils.h"
+
+namespace v8 {
+namespace internal {
+namespace interpreter {
+
+class RuntimeInterpreterTest : public TestWithIsolateAndZone {
+ public:
+ typedef Object* (*RuntimeMethod)(int, Object**, Isolate*);
+
+ RuntimeInterpreterTest() {}
+ ~RuntimeInterpreterTest() override {}
+
+ 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);
+};
+
+
+bool RuntimeInterpreterTest::TestOperatorWithObjects(RuntimeMethod method,
+ Handle<Object> lhs,
+ Handle<Object> rhs,
+ bool expected) {
+ Object* args_object[] = {*rhs, *lhs};
+ Handle<Object> result =
+ handle(method(2, &args_object[1], isolate()), isolate());
+ CHECK(result->IsTrue() || result->IsFalse());
+ return result->IsTrue() == expected;
+}
+
+
+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(Equals, ==, lhs, rhs);
+ INTEGER_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+ INTEGER_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+ INTEGER_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+ 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(Equals, ==, lhs, rhs);
+ DOUBLE_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+ DOUBLE_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+ DOUBLE_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+ 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(Equals, ==, lhs, rhs);
+ STRING_OPERATOR_CHECK(NotEquals, !=, lhs, rhs);
+ STRING_OPERATOR_CHECK(StrictEquals, ==, lhs, rhs);
+ STRING_OPERATOR_CHECK(StrictNotEquals, !=, lhs, rhs);
+ 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[] = {
+ std::make_pair(isolate()->factory()->NewNumberFromInt(0), false),
+ std::make_pair(isolate()->factory()->NewNumberFromInt(1), true),
+ std::make_pair(isolate()->factory()->NewNumberFromInt(100), true),
+ std::make_pair(isolate()->factory()->NewNumberFromInt(-1), true),
+ std::make_pair(isolate()->factory()->NewNumber(7.7), true),
+ std::make_pair(isolate()->factory()->NewNumber(0.00001), true),
+ std::make_pair(isolate()->factory()->NewNumber(quiet_nan), false),
+ std::make_pair(isolate()->factory()->NewHeapNumber(0.0), false),
+ std::make_pair(isolate()->factory()->undefined_value(), false),
+ std::make_pair(isolate()->factory()->null_value(), false),
+ std::make_pair(isolate()->factory()->true_value(), true),
+ std::make_pair(isolate()->factory()->false_value(), false),
+ std::make_pair(isolate()->factory()->NewStringFromStaticChars(""), false),
+ std::make_pair(isolate()->factory()->NewStringFromStaticChars("_"), true),
+ };
+
+ for (size_t i = 0; i < arraysize(cases); i++) {
+ auto& value_expected_tuple = cases[i];
+ Object* args_object[] = {*value_expected_tuple.first};
+ Handle<Object> result = handle(
+ Runtime_InterpreterToBoolean(1, &args_object[0], isolate()), isolate());
+ CHECK(result->IsBoolean());
+ CHECK_EQ(result->IsTrue(), value_expected_tuple.second);
+ }
+}
+
+} // Namespace interpreter
+} // namespace internal
+} // namespace v8
'heap/heap-unittest.cc',
'heap/scavenge-job-unittest.cc',
'run-all-unittests.cc',
+ 'runtime/runtime-interpreter-unittest.cc',
'test-utils.h',
'test-utils.cc',
],
'../../src/runtime/runtime-generator.cc',
'../../src/runtime/runtime-i18n.cc',
'../../src/runtime/runtime-internal.cc',
+ '../../src/runtime/runtime-interpreter.cc',
'../../src/runtime/runtime-json.cc',
'../../src/runtime/runtime-literals.cc',
'../../src/runtime/runtime-liveedit.cc',