[Interpreter] Unary operators - typeof, void, and logical not.
Implementations and tests for typeof, void, and logical not. Add missing string type to Object::TypeOf. BUG=v8:4280 LOG=NO Review URL: https://codereview.chromium.org/1390483002 Cr-Commit-Position: refs/heads/master@{#31124}
This commit is contained in:
parent
6ff9516b45
commit
565f0d730b
@ -338,6 +338,18 @@ void BytecodeGraphBuilder::VisitMod(
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitLogicalNot(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitTypeOf(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitTestEqual(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -134,6 +134,18 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
|
||||
Output(Bytecode::kLogicalNot);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
|
||||
Output(Bytecode::kTypeOf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
|
||||
Token::Value op, Register reg, LanguageMode language_mode) {
|
||||
if (!is_sloppy(language_mode)) {
|
||||
@ -299,6 +311,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
|
||||
UNREACHABLE();
|
||||
case Bytecode::kLdaTrue:
|
||||
case Bytecode::kLdaFalse:
|
||||
case Bytecode::kLogicalNot:
|
||||
case Bytecode::kTestEqual:
|
||||
case Bytecode::kTestNotEqual:
|
||||
case Bytecode::kTestEqualStrict:
|
||||
|
@ -84,6 +84,10 @@ class BytecodeArrayBuilder {
|
||||
// Operators (register == lhs, accumulator = rhs).
|
||||
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
|
||||
|
||||
// Unary Operators.
|
||||
BytecodeArrayBuilder& LogicalNot();
|
||||
BytecodeArrayBuilder& TypeOf();
|
||||
|
||||
// Tests.
|
||||
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
||||
LanguageMode language_mode);
|
||||
|
@ -619,8 +619,41 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGenerator::VisitVoid(UnaryOperation* expr) {
|
||||
Visit(expr->expression());
|
||||
builder()->LoadUndefined();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
|
||||
Visit(expr->expression());
|
||||
builder()->TypeOf();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGenerator::VisitNot(UnaryOperation* expr) {
|
||||
Visit(expr->expression());
|
||||
builder()->LogicalNot();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
|
||||
UNIMPLEMENTED();
|
||||
switch (expr->op()) {
|
||||
case Token::Value::NOT:
|
||||
VisitNot(expr);
|
||||
break;
|
||||
case Token::Value::TYPEOF:
|
||||
VisitTypeOf(expr);
|
||||
break;
|
||||
case Token::Value::VOID:
|
||||
VisitVoid(expr);
|
||||
break;
|
||||
case Token::Value::BIT_NOT:
|
||||
case Token::Value::DELETE:
|
||||
UNIMPLEMENTED();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +34,11 @@ class BytecodeGenerator : public AstVisitor {
|
||||
void VisitPropertyLoad(Register obj, Property* expr);
|
||||
void VisitVariableLoad(Variable* variable);
|
||||
|
||||
// Dispatched from VisitUnaryOperation.
|
||||
void VisitVoid(UnaryOperation* expr);
|
||||
void VisitTypeOf(UnaryOperation* expr);
|
||||
void VisitNot(UnaryOperation* expr);
|
||||
|
||||
inline BytecodeArrayBuilder* builder() { return &builder_; }
|
||||
inline Scope* scope() const { return scope_; }
|
||||
inline void set_scope(Scope* scope) { scope_ = scope; }
|
||||
|
@ -65,6 +65,10 @@ namespace interpreter {
|
||||
V(Div, OperandType::kReg8) \
|
||||
V(Mod, OperandType::kReg8) \
|
||||
\
|
||||
/* Unary Operators */ \
|
||||
V(LogicalNot, OperandType::kNone) \
|
||||
V(TypeOf, OperandType::kNone) \
|
||||
\
|
||||
/* Call operations. */ \
|
||||
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
|
||||
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
|
||||
|
@ -334,10 +334,34 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) {
|
||||
}
|
||||
|
||||
|
||||
// LogicalNot
|
||||
//
|
||||
// Perform logical-not on the accumulator, first casting the
|
||||
// accumulator to a boolean value if required.
|
||||
void Interpreter::DoLogicalNot(compiler::InterpreterAssembler* assembler) {
|
||||
Node* accumulator = __ GetAccumulator();
|
||||
Node* result = __ CallRuntime(Runtime::kInterpreterLogicalNot, accumulator);
|
||||
__ SetAccumulator(result);
|
||||
__ Dispatch();
|
||||
}
|
||||
|
||||
|
||||
// TypeOf
|
||||
//
|
||||
// Load the accumulator with the string representating type of the
|
||||
// object in the accumulator.
|
||||
void Interpreter::DoTypeOf(compiler::InterpreterAssembler* assembler) {
|
||||
Node* accumulator = __ GetAccumulator();
|
||||
Node* result = __ CallRuntime(Runtime::kInterpreterTypeOf, accumulator);
|
||||
__ SetAccumulator(result);
|
||||
__ Dispatch();
|
||||
}
|
||||
|
||||
|
||||
// Call <callable> <receiver> <arg_count>
|
||||
//
|
||||
// Call a JSfunction or Callable in |callable| with receiver and |arg_count|
|
||||
// arguments in subsequent registers.
|
||||
// Call a JSfunction or Callable in |callable| with the |receiver| and
|
||||
// |arg_count| arguments in subsequent registers.
|
||||
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
|
||||
Node* function_reg = __ BytecodeOperandReg8(0);
|
||||
Node* function = __ LoadRegister(function_reg);
|
||||
@ -352,8 +376,9 @@ void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
|
||||
|
||||
// CallRuntime <function_id> <first_arg> <arg_count>
|
||||
//
|
||||
// Call the runtime function |function_id| with first argument in register
|
||||
// |first_arg| and |arg_count| arguments in subsequent registers.
|
||||
// Call the runtime function |function_id| with the first argument in
|
||||
// register |first_arg| and |arg_count| arguments in subsequent
|
||||
// registers.
|
||||
void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
|
||||
Node* function_id = __ BytecodeOperandIdx16(0);
|
||||
Node* first_arg_reg = __ BytecodeOperandReg8(1);
|
||||
@ -457,15 +482,16 @@ void Interpreter::DoTestInstanceOf(compiler::InterpreterAssembler* assembler) {
|
||||
//
|
||||
// Cast the object referenced by the accumulator to a boolean.
|
||||
void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) {
|
||||
// TODO(oth): The next CL for test operations has interpreter specific
|
||||
// runtime calls. This looks like another candidate.
|
||||
Node* accumulator = __ GetAccumulator();
|
||||
Node* result = __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator);
|
||||
__ SetAccumulator(result);
|
||||
__ Dispatch();
|
||||
}
|
||||
|
||||
|
||||
// Jump <imm8>
|
||||
//
|
||||
// Jump by number of bytes represented by an immediate operand.
|
||||
// Jump by number of bytes represented by the immediate operand |imm8|.
|
||||
void Interpreter::DoJump(compiler::InterpreterAssembler* assembler) {
|
||||
Node* relative_jump = __ BytecodeOperandImm8(0);
|
||||
__ Jump(relative_jump);
|
||||
@ -539,7 +565,7 @@ void Interpreter::DoJumpIfFalseConstant(
|
||||
|
||||
// Return
|
||||
//
|
||||
// Return the value in register 0.
|
||||
// Return the value in the accumulator.
|
||||
void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
|
||||
__ Return();
|
||||
}
|
||||
|
@ -381,6 +381,7 @@ Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
|
||||
return isolate->factory()->undefined_string();
|
||||
}
|
||||
if (object->IsBoolean()) return isolate->factory()->boolean_string();
|
||||
if (object->IsString()) return isolate->factory()->string_string();
|
||||
if (object->IsSymbol()) return isolate->factory()->symbol_string();
|
||||
#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
|
||||
if (object->Is##Type()) return isolate->factory()->type##_string();
|
||||
|
@ -96,7 +96,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterGreaterThanOrEqual) {
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
|
||||
SealHandleScope scope(isolate);
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||
CONVERT_ARG_CHECKED(Object, y, 1);
|
||||
@ -105,7 +105,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
|
||||
SealHandleScope scope(isolate);
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||
CONVERT_ARG_CHECKED(Object, y, 1);
|
||||
@ -114,12 +114,28 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
|
||||
SealHandleScope scope(isolate);
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||
return isolate->heap()->ToBoolean(x->BooleanValue());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InterpreterLogicalNot) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||
return isolate->heap()->ToBoolean(!x->BooleanValue());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InterpreterTypeOf) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
|
||||
return Object::cast(*Object::TypeOf(isolate, x));
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -225,7 +225,9 @@ namespace internal {
|
||||
F(InterpreterGreaterThan, 2, 1) \
|
||||
F(InterpreterLessThanOrEqual, 2, 1) \
|
||||
F(InterpreterGreaterThanOrEqual, 2, 1) \
|
||||
F(InterpreterToBoolean, 1, 1)
|
||||
F(InterpreterToBoolean, 1, 1) \
|
||||
F(InterpreterLogicalNot, 1, 1) \
|
||||
F(InterpreterTypeOf, 1, 1)
|
||||
|
||||
|
||||
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
|
||||
|
@ -1260,6 +1260,124 @@ TEST(BasicLoops) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(UnaryOperators) {
|
||||
InitializedHandleScope handle_scope;
|
||||
BytecodeGeneratorHelper helper;
|
||||
|
||||
ExpectedSnippet<int> snippets[] = {
|
||||
{"var x = 0;"
|
||||
"while (x != 10) {"
|
||||
" x = x + 10;"
|
||||
"}"
|
||||
"return x;",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
29,
|
||||
{
|
||||
B(LdaZero), //
|
||||
B(Star), R(0), //
|
||||
B(Jump), U8(12), //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(10), //
|
||||
B(Add), R(1), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(10), //
|
||||
B(TestEqual), R(1), //
|
||||
B(LogicalNot), //
|
||||
B(JumpIfTrue), U8(-19), //
|
||||
B(Ldar), R(0), //
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
{"var x = false;"
|
||||
"do {"
|
||||
" x = !x;"
|
||||
"} while(x == false);"
|
||||
"return x;",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
20,
|
||||
{
|
||||
B(LdaFalse), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(LogicalNot), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(1), //
|
||||
B(LdaFalse), //
|
||||
B(TestEqual), R(1), //
|
||||
B(JumpIfTrue), U8(-12), //
|
||||
B(Ldar), R(0), //
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
{"var x = 101;"
|
||||
"return void(x * 3);",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
14,
|
||||
{
|
||||
B(LdaSmi8), U8(101), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Mul), R(1), //
|
||||
B(LdaUndefined), //
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
{"var x = 1234;"
|
||||
"var y = void (x * x - 1);"
|
||||
"return y;",
|
||||
4 * kPointerSize,
|
||||
1,
|
||||
24,
|
||||
{
|
||||
B(LdaConstant), U8(0), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(3), //
|
||||
B(Ldar), R(0), //
|
||||
B(Mul), R(3), //
|
||||
B(Star), R(2), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Sub), R(2), //
|
||||
B(LdaUndefined), //
|
||||
B(Star), R(1), //
|
||||
B(Ldar), R(1), //
|
||||
B(Return), //
|
||||
},
|
||||
1,
|
||||
{1234}},
|
||||
{"var x = 13;"
|
||||
"return typeof(x);",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
8,
|
||||
{
|
||||
B(LdaSmi8), U8(13), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(TypeOf), //
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(snippets); i++) {
|
||||
Handle<BytecodeArray> bytecode_array =
|
||||
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
|
||||
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace interpreter
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1291,6 +1291,164 @@ TEST(InterpreterTestIn) {
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterUnaryNot) {
|
||||
HandleAndZoneScope handles;
|
||||
for (size_t i = 1; i < 10; i++) {
|
||||
bool expected_value = ((i & 1) == 1);
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
Register r0(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
builder.EnterBlock();
|
||||
builder.LoadFalse();
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
builder.LogicalNot();
|
||||
}
|
||||
builder.LeaveBlock().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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void LoadAny(BytecodeArrayBuilder* builder,
|
||||
v8::internal::Factory* factory, Handle<Object> obj) {
|
||||
if (obj->IsOddball()) {
|
||||
if (obj->SameValue(*factory->true_value())) {
|
||||
builder->LoadTrue();
|
||||
} else if (obj->SameValue(*factory->false_value())) {
|
||||
builder->LoadFalse();
|
||||
} else if (obj->SameValue(*factory->the_hole_value())) {
|
||||
builder->LoadTheHole();
|
||||
} else if (obj->SameValue(*factory->null_value())) {
|
||||
builder->LoadNull();
|
||||
} else if (obj->SameValue(*factory->undefined_value())) {
|
||||
builder->LoadUndefined();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else if (obj->IsSmi()) {
|
||||
builder->LoadLiteral(*Handle<Smi>::cast(obj));
|
||||
} else {
|
||||
builder->LoadLiteral(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterToBoolean) {
|
||||
HandleAndZoneScope handles;
|
||||
i::Factory* factory = handles.main_isolate()->factory();
|
||||
|
||||
std::pair<Handle<Object>, bool> object_type_tuples[] = {
|
||||
std::make_pair(factory->undefined_value(), false),
|
||||
std::make_pair(factory->null_value(), false),
|
||||
std::make_pair(factory->false_value(), false),
|
||||
std::make_pair(factory->true_value(), true),
|
||||
std::make_pair(factory->NewNumber(9.1), true),
|
||||
std::make_pair(factory->NewNumberFromInt(0), false),
|
||||
std::make_pair(
|
||||
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
|
||||
true),
|
||||
std::make_pair(
|
||||
Handle<Object>::cast(factory->NewStringFromStaticChars("")), false),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
Register r0(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
builder.EnterBlock();
|
||||
LoadAny(&builder, factory, object_type_tuples[i].first);
|
||||
builder.CastAccumulatorToBoolean();
|
||||
builder.LeaveBlock().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(), object_type_tuples[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterUnaryNotNonBoolean) {
|
||||
HandleAndZoneScope handles;
|
||||
i::Factory* factory = handles.main_isolate()->factory();
|
||||
|
||||
std::pair<Handle<Object>, bool> object_type_tuples[] = {
|
||||
std::make_pair(factory->undefined_value(), true),
|
||||
std::make_pair(factory->null_value(), true),
|
||||
std::make_pair(factory->false_value(), true),
|
||||
std::make_pair(factory->true_value(), false),
|
||||
std::make_pair(factory->NewNumber(9.1), false),
|
||||
std::make_pair(factory->NewNumberFromInt(0), true),
|
||||
std::make_pair(
|
||||
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
|
||||
false),
|
||||
std::make_pair(
|
||||
Handle<Object>::cast(factory->NewStringFromStaticChars("")), true),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
Register r0(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
builder.EnterBlock();
|
||||
LoadAny(&builder, factory, object_type_tuples[i].first);
|
||||
builder.LogicalNot();
|
||||
builder.LeaveBlock().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(), object_type_tuples[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterTypeOf) {
|
||||
HandleAndZoneScope handles;
|
||||
i::Factory* factory = handles.main_isolate()->factory();
|
||||
|
||||
std::pair<Handle<Object>, const char*> object_type_tuples[] = {
|
||||
std::make_pair(factory->undefined_value(), "undefined"),
|
||||
std::make_pair(factory->null_value(), "object"),
|
||||
std::make_pair(factory->true_value(), "boolean"),
|
||||
std::make_pair(factory->false_value(), "boolean"),
|
||||
std::make_pair(factory->NewNumber(9.1), "number"),
|
||||
std::make_pair(factory->NewNumberFromInt(7771), "number"),
|
||||
std::make_pair(
|
||||
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
|
||||
"string"),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
Register r0(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
builder.EnterBlock();
|
||||
LoadAny(&builder, factory, object_type_tuples[i].first);
|
||||
builder.TypeOf();
|
||||
builder.LeaveBlock().Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||
auto callable = tester.GetCallable<>();
|
||||
Handle<v8::internal::String> return_value =
|
||||
Handle<v8::internal::String>::cast(callable().ToHandleChecked());
|
||||
auto actual = return_value->ToCString();
|
||||
CHECK_EQ(strcmp(&actual[0], object_type_tuples[i].second), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterCallRuntime) {
|
||||
HandleAndZoneScope handles;
|
||||
|
||||
|
@ -60,6 +60,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.BinaryOperation(Token::Value::DIV, reg)
|
||||
.BinaryOperation(Token::Value::MOD, reg);
|
||||
|
||||
// Emit unary operator invocations.
|
||||
builder.LogicalNot().TypeOf();
|
||||
|
||||
// Emit test operator invocations.
|
||||
builder.CompareOperation(Token::Value::EQ, reg, LanguageMode::SLOPPY)
|
||||
.CompareOperation(Token::Value::NE, reg, LanguageMode::SLOPPY)
|
||||
|
Loading…
Reference in New Issue
Block a user