[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(
|
void BytecodeGraphBuilder::VisitTestEqual(
|
||||||
const interpreter::BytecodeArrayIterator& iterator) {
|
const interpreter::BytecodeArrayIterator& iterator) {
|
||||||
UNIMPLEMENTED();
|
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(
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
|
||||||
Token::Value op, Register reg, LanguageMode language_mode) {
|
Token::Value op, Register reg, LanguageMode language_mode) {
|
||||||
if (!is_sloppy(language_mode)) {
|
if (!is_sloppy(language_mode)) {
|
||||||
@ -299,6 +311,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
case Bytecode::kLdaTrue:
|
case Bytecode::kLdaTrue:
|
||||||
case Bytecode::kLdaFalse:
|
case Bytecode::kLdaFalse:
|
||||||
|
case Bytecode::kLogicalNot:
|
||||||
case Bytecode::kTestEqual:
|
case Bytecode::kTestEqual:
|
||||||
case Bytecode::kTestNotEqual:
|
case Bytecode::kTestNotEqual:
|
||||||
case Bytecode::kTestEqualStrict:
|
case Bytecode::kTestEqualStrict:
|
||||||
|
@ -84,6 +84,10 @@ class BytecodeArrayBuilder {
|
|||||||
// Operators (register == lhs, accumulator = rhs).
|
// Operators (register == lhs, accumulator = rhs).
|
||||||
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
|
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
|
||||||
|
|
||||||
|
// Unary Operators.
|
||||||
|
BytecodeArrayBuilder& LogicalNot();
|
||||||
|
BytecodeArrayBuilder& TypeOf();
|
||||||
|
|
||||||
// Tests.
|
// Tests.
|
||||||
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
||||||
LanguageMode language_mode);
|
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) {
|
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 VisitPropertyLoad(Register obj, Property* expr);
|
||||||
void VisitVariableLoad(Variable* variable);
|
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 BytecodeArrayBuilder* builder() { return &builder_; }
|
||||||
inline Scope* scope() const { return scope_; }
|
inline Scope* scope() const { return scope_; }
|
||||||
inline void set_scope(Scope* scope) { scope_ = scope; }
|
inline void set_scope(Scope* scope) { scope_ = scope; }
|
||||||
|
@ -65,6 +65,10 @@ namespace interpreter {
|
|||||||
V(Div, OperandType::kReg8) \
|
V(Div, OperandType::kReg8) \
|
||||||
V(Mod, OperandType::kReg8) \
|
V(Mod, OperandType::kReg8) \
|
||||||
\
|
\
|
||||||
|
/* Unary Operators */ \
|
||||||
|
V(LogicalNot, OperandType::kNone) \
|
||||||
|
V(TypeOf, OperandType::kNone) \
|
||||||
|
\
|
||||||
/* Call operations. */ \
|
/* Call operations. */ \
|
||||||
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
|
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
|
||||||
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
|
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 <callable> <receiver> <arg_count>
|
||||||
//
|
//
|
||||||
// Call a JSfunction or Callable in |callable| with receiver and |arg_count|
|
// Call a JSfunction or Callable in |callable| with the |receiver| and
|
||||||
// arguments in subsequent registers.
|
// |arg_count| arguments in subsequent registers.
|
||||||
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
|
||||||
Node* function_reg = __ BytecodeOperandReg8(0);
|
Node* function_reg = __ BytecodeOperandReg8(0);
|
||||||
Node* function = __ LoadRegister(function_reg);
|
Node* function = __ LoadRegister(function_reg);
|
||||||
@ -352,8 +376,9 @@ void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
|
|||||||
|
|
||||||
// CallRuntime <function_id> <first_arg> <arg_count>
|
// CallRuntime <function_id> <first_arg> <arg_count>
|
||||||
//
|
//
|
||||||
// Call the runtime function |function_id| with first argument in register
|
// Call the runtime function |function_id| with the first argument in
|
||||||
// |first_arg| and |arg_count| arguments in subsequent registers.
|
// register |first_arg| and |arg_count| arguments in subsequent
|
||||||
|
// registers.
|
||||||
void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
|
||||||
Node* function_id = __ BytecodeOperandIdx16(0);
|
Node* function_id = __ BytecodeOperandIdx16(0);
|
||||||
Node* first_arg_reg = __ BytecodeOperandReg8(1);
|
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.
|
// Cast the object referenced by the accumulator to a boolean.
|
||||||
void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) {
|
||||||
// TODO(oth): The next CL for test operations has interpreter specific
|
Node* accumulator = __ GetAccumulator();
|
||||||
// runtime calls. This looks like another candidate.
|
Node* result = __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator);
|
||||||
|
__ SetAccumulator(result);
|
||||||
__ Dispatch();
|
__ Dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Jump <imm8>
|
// 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) {
|
void Interpreter::DoJump(compiler::InterpreterAssembler* assembler) {
|
||||||
Node* relative_jump = __ BytecodeOperandImm8(0);
|
Node* relative_jump = __ BytecodeOperandImm8(0);
|
||||||
__ Jump(relative_jump);
|
__ Jump(relative_jump);
|
||||||
@ -539,7 +565,7 @@ void Interpreter::DoJumpIfFalseConstant(
|
|||||||
|
|
||||||
// Return
|
// Return
|
||||||
//
|
//
|
||||||
// Return the value in register 0.
|
// Return the value in the accumulator.
|
||||||
void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
|
||||||
__ Return();
|
__ Return();
|
||||||
}
|
}
|
||||||
|
@ -381,6 +381,7 @@ Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
|
|||||||
return isolate->factory()->undefined_string();
|
return isolate->factory()->undefined_string();
|
||||||
}
|
}
|
||||||
if (object->IsBoolean()) return isolate->factory()->boolean_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();
|
if (object->IsSymbol()) return isolate->factory()->symbol_string();
|
||||||
#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
|
#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
|
||||||
if (object->Is##Type()) return isolate->factory()->type##_string();
|
if (object->Is##Type()) return isolate->factory()->type##_string();
|
||||||
|
@ -96,7 +96,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterGreaterThanOrEqual) {
|
|||||||
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
|
RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
|
||||||
SealHandleScope scope(isolate);
|
SealHandleScope shs(isolate);
|
||||||
DCHECK_EQ(2, args.length());
|
DCHECK_EQ(2, args.length());
|
||||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||||
CONVERT_ARG_CHECKED(Object, y, 1);
|
CONVERT_ARG_CHECKED(Object, y, 1);
|
||||||
@ -105,7 +105,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
|
|||||||
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
|
RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
|
||||||
SealHandleScope scope(isolate);
|
SealHandleScope shs(isolate);
|
||||||
DCHECK_EQ(2, args.length());
|
DCHECK_EQ(2, args.length());
|
||||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||||
CONVERT_ARG_CHECKED(Object, y, 1);
|
CONVERT_ARG_CHECKED(Object, y, 1);
|
||||||
@ -114,12 +114,28 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
|
|||||||
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
|
RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
|
||||||
SealHandleScope scope(isolate);
|
SealHandleScope shs(isolate);
|
||||||
DCHECK_EQ(1, args.length());
|
DCHECK_EQ(1, args.length());
|
||||||
CONVERT_ARG_CHECKED(Object, x, 0);
|
CONVERT_ARG_CHECKED(Object, x, 0);
|
||||||
return isolate->heap()->ToBoolean(x->BooleanValue());
|
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 internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -225,7 +225,9 @@ namespace internal {
|
|||||||
F(InterpreterGreaterThan, 2, 1) \
|
F(InterpreterGreaterThan, 2, 1) \
|
||||||
F(InterpreterLessThanOrEqual, 2, 1) \
|
F(InterpreterLessThanOrEqual, 2, 1) \
|
||||||
F(InterpreterGreaterThanOrEqual, 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) \
|
#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 interpreter
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // 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) {
|
TEST(InterpreterCallRuntime) {
|
||||||
HandleAndZoneScope handles;
|
HandleAndZoneScope handles;
|
||||||
|
|
||||||
|
@ -60,6 +60,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
|||||||
.BinaryOperation(Token::Value::DIV, reg)
|
.BinaryOperation(Token::Value::DIV, reg)
|
||||||
.BinaryOperation(Token::Value::MOD, reg);
|
.BinaryOperation(Token::Value::MOD, reg);
|
||||||
|
|
||||||
|
// Emit unary operator invocations.
|
||||||
|
builder.LogicalNot().TypeOf();
|
||||||
|
|
||||||
// Emit test operator invocations.
|
// Emit test operator invocations.
|
||||||
builder.CompareOperation(Token::Value::EQ, reg, LanguageMode::SLOPPY)
|
builder.CompareOperation(Token::Value::EQ, reg, LanguageMode::SLOPPY)
|
||||||
.CompareOperation(Token::Value::NE, reg, LanguageMode::SLOPPY)
|
.CompareOperation(Token::Value::NE, reg, LanguageMode::SLOPPY)
|
||||||
|
Loading…
Reference in New Issue
Block a user