blob: 868f595a8d6868154e0f0fba99a64e77f12dd556 [file] [log] [blame]
// 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.
// TODO(rmcilroy): Remove this define after this flag is turned on globally
#define V8_IMMINENT_DEPRECATION_WARNINGS
#include "src/v8.h"
#include "src/execution.h"
#include "src/handles.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-feedback-vector.h"
namespace v8 {
namespace internal {
namespace interpreter {
static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function) {
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), 0, nullptr);
}
template <class... A>
static MaybeHandle<Object> CallInterpreter(Isolate* isolate,
Handle<JSFunction> function,
A... args) {
Handle<Object> argv[] = { args... };
return Execution::Call(isolate, function,
isolate->factory()->undefined_value(), sizeof...(args),
argv);
}
template <class... A>
class InterpreterCallable {
public:
InterpreterCallable(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate), function_(function) {}
virtual ~InterpreterCallable() {}
MaybeHandle<Object> operator()(A... args) {
return CallInterpreter(isolate_, function_, args...);
}
private:
Isolate* isolate_;
Handle<JSFunction> function_;
};
static const char* kFunctionName = "f";
class InterpreterTester {
public:
InterpreterTester(Isolate* isolate, const char* source,
MaybeHandle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector,
const char* filter)
: isolate_(isolate),
source_(source),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_vector_stores = true;
i::FLAG_ignition = true;
i::FLAG_ignition_fake_try_catch = true;
i::FLAG_always_opt = false;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
SNPrintF(ignition_filter, "--ignition-filter=%s", filter);
FlagList::SetFlagsFromString(ignition_filter.start(),
ignition_filter.length());
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
}
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>(),
const char* filter = kFunctionName)
: InterpreterTester(isolate, nullptr, bytecode, feedback_vector, filter) {
}
InterpreterTester(Isolate* isolate, const char* source,
const char* filter = kFunctionName)
: InterpreterTester(isolate, source, MaybeHandle<BytecodeArray>(),
MaybeHandle<TypeFeedbackVector>(), filter) {}
virtual ~InterpreterTester() {}
template <class... A>
InterpreterCallable<A...> GetCallable() {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
}
static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
static Handle<String> GetName(Isolate* isolate, const char* name) {
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(name);
return isolate->factory()->string_table()->LookupString(isolate, result);
}
static std::string SourceForBody(const char* body) {
return "function " + function_name() + "() {\n" + std::string(body) + "\n}";
}
static std::string function_name() {
return std::string(kFunctionName);
}
private:
Isolate* isolate_;
const char* source_;
MaybeHandle<BytecodeArray> bytecode_;
MaybeHandle<TypeFeedbackVector> feedback_vector_;
template <class... A>
Handle<JSFunction> GetBytecodeFunction() {
Handle<JSFunction> function;
if (source_) {
CompileRun(source_);
v8::Local<v8::Context> context =
v8::Isolate::GetCurrent()->GetCurrentContext();
Local<Function> api_function =
Local<Function>::Cast(CcTest::global()
->Get(context, v8_str(kFunctionName))
.ToLocalChecked());
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
} else {
int arg_count = sizeof...(A);
std::string source("(function " + function_name() + "(");
for (int i = 0; i < arg_count; i++) {
source += i == 0 ? "a" : ", a";
}
source += "){})";
function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source.c_str()))));
function->ReplaceCode(
*isolate_->builtins()->InterpreterEntryTrampoline());
}
if (!bytecode_.is_null()) {
function->shared()->set_function_data(*bytecode_.ToHandleChecked());
}
if (!feedback_vector_.is_null()) {
function->shared()->set_feedback_vector(
*feedback_vector_.ToHandleChecked());
}
return function;
}
DISALLOW_COPY_AND_ASSIGN(InterpreterTester);
};
TEST(InterpreterReturn) {
HandleAndZoneScope handles;
Handle<Object> undefined_value =
handles.main_isolate()->factory()->undefined_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(undefined_value));
}
TEST(InterpreterLoadUndefined) {
HandleAndZoneScope handles;
Handle<Object> undefined_value =
handles.main_isolate()->factory()->undefined_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadUndefined().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(undefined_value));
}
TEST(InterpreterLoadNull) {
HandleAndZoneScope handles;
Handle<Object> null_value = handles.main_isolate()->factory()->null_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadNull().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(null_value));
}
TEST(InterpreterLoadTheHole) {
HandleAndZoneScope handles;
Handle<Object> the_hole_value =
handles.main_isolate()->factory()->the_hole_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadTheHole().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(the_hole_value));
}
TEST(InterpreterLoadTrue) {
HandleAndZoneScope handles;
Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadTrue().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
}
TEST(InterpreterLoadFalse) {
HandleAndZoneScope handles;
Handle<Object> false_value = handles.main_isolate()->factory()->false_value();
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadFalse().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(false_value));
}
TEST(InterpreterLoadLiteral) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
// Small Smis.
for (int i = -128; i < 128; i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(i)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(i));
}
// Large Smis.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(0x12345678)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x12345678));
}
// Heap numbers.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(factory->NewHeapNumber(-2.1e19)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(i::HeapNumber::cast(*return_val)->value(), -2.1e19);
}
// Strings.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
Handle<i::String> string = factory->NewStringFromAsciiChecked("String");
builder.LoadLiteral(string).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(i::String::cast(*return_val)->Equals(*string));
}
}
TEST(InterpreterLoadStoreRegisters) {
HandleAndZoneScope handles;
Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
for (int i = 0; i <= Register::kMaxRegisterIndex; i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(i + 1);
builder.set_context_count(0);
builder.set_parameter_count(1);
Register reg(i);
builder.LoadTrue()
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.LoadAccumulatorWithRegister(reg)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
}
}
static const Token::Value kShiftOperators[] = {
Token::Value::SHL, Token::Value::SAR, Token::Value::SHR};
static const Token::Value kArithmeticOperators[] = {
Token::Value::BIT_OR, Token::Value::BIT_XOR, Token::Value::BIT_AND,
Token::Value::SHL, Token::Value::SAR, Token::Value::SHR,
Token::Value::ADD, Token::Value::SUB, Token::Value::MUL,
Token::Value::DIV, Token::Value::MOD};
static double BinaryOpC(Token::Value op, double lhs, double rhs) {
switch (op) {
case Token::Value::ADD:
return lhs + rhs;
case Token::Value::SUB:
return lhs - rhs;
case Token::Value::MUL:
return lhs * rhs;
case Token::Value::DIV:
return lhs / rhs;
case Token::Value::MOD:
return std::fmod(lhs, rhs);
case Token::Value::BIT_OR:
return (v8::internal::DoubleToInt32(lhs) |
v8::internal::DoubleToInt32(rhs));
case Token::Value::BIT_XOR:
return (v8::internal::DoubleToInt32(lhs) ^
v8::internal::DoubleToInt32(rhs));
case Token::Value::BIT_AND:
return (v8::internal::DoubleToInt32(lhs) &
v8::internal::DoubleToInt32(rhs));
case Token::Value::SHL: {
int32_t val = v8::internal::DoubleToInt32(lhs);
uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F;
int32_t result = val << count;
return result;
}
case Token::Value::SAR: {
int32_t val = v8::internal::DoubleToInt32(lhs);
uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F;
int32_t result = val >> count;
return result;
}
case Token::Value::SHR: {
uint32_t val = v8::internal::DoubleToUint32(lhs);
uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F;
uint32_t result = val >> count;
return result;
}
default:
UNREACHABLE();
return std::numeric_limits<double>::min();
}
}
TEST(InterpreterShiftOpsSmi) {
int lhs_inputs[] = {0, -17, -182, 1073741823, -1};
int rhs_inputs[] = {5, 2, 1, -1, -2, 0, 31, 32, -32, 64, 37};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kShiftOperators); o++) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
BytecodeArrayBuilder builder(handles.main_isolate(),
handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
Register reg(0);
int lhs = lhs_inputs[l];
int rhs = rhs_inputs[r];
builder.LoadLiteral(Smi::FromInt(lhs))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(rhs))
.BinaryOperation(kShiftOperators[o], reg, Strength::WEAK)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kShiftOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterBinaryOpsSmi) {
int lhs_inputs[] = {3266, 1024, 0, -17, -18000};
int rhs_inputs[] = {3266, 5, 4, 3, 2, 1, -1, -2};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kArithmeticOperators); o++) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
BytecodeArrayBuilder builder(handles.main_isolate(),
handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
Register reg(0);
int lhs = lhs_inputs[l];
int rhs = rhs_inputs[r];
builder.LoadLiteral(Smi::FromInt(lhs))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(rhs))
.BinaryOperation(kArithmeticOperators[o], reg, Strength::WEAK)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kArithmeticOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterBinaryOpsHeapNumber) {
double lhs_inputs[] = {3266.101, 1024.12, 0.01, -17.99, -18000.833, 9.1e17};
double rhs_inputs[] = {3266.101, 5.999, 4.778, 3.331, 2.643,
1.1, -1.8, -2.9, 8.3e-27};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kArithmeticOperators); o++) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
BytecodeArrayBuilder builder(handles.main_isolate(),
handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
Register reg(0);
double lhs = lhs_inputs[l];
double rhs = rhs_inputs[r];
builder.LoadLiteral(factory->NewNumber(lhs))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(factory->NewNumber(rhs))
.BinaryOperation(kArithmeticOperators[o], reg, Strength::WEAK)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kArithmeticOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterStringAdd) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
struct TestCase {
Handle<Object> lhs;
Handle<Object> rhs;
Handle<Object> expected_value;
} test_cases[] = {
{factory->NewStringFromStaticChars("a"),
factory->NewStringFromStaticChars("b"),
factory->NewStringFromStaticChars("ab")},
{factory->NewStringFromStaticChars("aaaaaa"),
factory->NewStringFromStaticChars("b"),
factory->NewStringFromStaticChars("aaaaaab")},
{factory->NewStringFromStaticChars("aaa"),
factory->NewStringFromStaticChars("bbbbb"),
factory->NewStringFromStaticChars("aaabbbbb")},
{factory->NewStringFromStaticChars(""),
factory->NewStringFromStaticChars("b"),
factory->NewStringFromStaticChars("b")},
{factory->NewStringFromStaticChars("a"),
factory->NewStringFromStaticChars(""),
factory->NewStringFromStaticChars("a")},
{factory->NewStringFromStaticChars("1.11"), factory->NewHeapNumber(2.5),
factory->NewStringFromStaticChars("1.112.5")},
{factory->NewStringFromStaticChars("-1.11"), factory->NewHeapNumber(2.56),
factory->NewStringFromStaticChars("-1.112.56")},
{factory->NewStringFromStaticChars(""), factory->NewHeapNumber(2.5),
factory->NewStringFromStaticChars("2.5")},
};
for (size_t i = 0; i < arraysize(test_cases); i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
Register reg(0);
builder.LoadLiteral(test_cases[i].lhs)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(test_cases[i].rhs)
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
.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->SameValue(*test_cases[i].expected_value));
}
}
TEST(InterpreterParameter1) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadAccumulatorWithRegister(builder.Parameter(0)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
// Check for heap objects.
Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
Handle<Object> return_val = callable(true_value).ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
// Check for Smis.
return_val = callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
}
TEST(InterpreterParameter8) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(8);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.BinaryOperation(Token::Value::ADD, builder.Parameter(1), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(2), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(3), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(4), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(5), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(6), Strength::WEAK)
.BinaryOperation(Token::Value::ADD, builder.Parameter(7), Strength::WEAK)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H, H, H, H, H>();
Handle<Smi> arg1 = Handle<Smi>(Smi::FromInt(1), handles.main_isolate());
Handle<Smi> arg2 = Handle<Smi>(Smi::FromInt(2), handles.main_isolate());
Handle<Smi> arg3 = Handle<Smi>(Smi::FromInt(3), handles.main_isolate());
Handle<Smi> arg4 = Handle<Smi>(Smi::FromInt(4), handles.main_isolate());
Handle<Smi> arg5 = Handle<Smi>(Smi::FromInt(5), handles.main_isolate());
Handle<Smi> arg6 = Handle<Smi>(Smi::FromInt(6), handles.main_isolate());
Handle<Smi> arg7 = Handle<Smi>(Smi::FromInt(7), handles.main_isolate());
Handle<Smi> arg8 = Handle<Smi>(Smi::FromInt(8), handles.main_isolate());
// Check for Smis.
Handle<Object> return_val =
callable(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(36));
}
TEST(InterpreterParameter1Assign) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(5))
.StoreAccumulatorInRegister(builder.Parameter(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5));
}
TEST(InterpreterLoadGlobal) {
HandleAndZoneScope handles;
// Test loading a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return global;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(321));
}
TEST(InterpreterStoreGlobal) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
// Test storing to a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" global = 999;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
callable().ToHandleChecked();
Handle<i::String> name = factory->InternalizeUtf8String("global");
Handle<i::Object> global_obj =
Object::GetProperty(isolate->global_object(), name).ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterCallGlobal) {
HandleAndZoneScope handles;
// Test calling a global function.
std::string source(
"function g_add(a, b) { return a + b; }\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return g_add(5, 10);\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15));
}
TEST(InterpreterLoadUnallocated) {
HandleAndZoneScope handles;
// Test loading an unallocated global.
std::string source(
"unallocated = 123;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return unallocated;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
}
TEST(InterpreterStoreUnallocated) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
// Test storing to an unallocated global.
std::string source(
"unallocated = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" unallocated = 999;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
callable().ToHandleChecked();
Handle<i::String> name = factory->InternalizeUtf8String("unallocated");
Handle<i::Object> global_obj =
Object::GetProperty(isolate->global_object(), name).ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterLoadNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Zone zone;
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadNamedProperty(builder.Parameter(0), name_index,
vector->GetIndex(slot), i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to polymorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
return_val = callable(object2).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(456));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
return_val = callable(object5).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterLoadKeyedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Zone zone;
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> key = factory->NewStringFromAsciiChecked("key");
key = factory->string_table()->LookupString(isolate, key);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(key)
.LoadKeyedProperty(builder.Parameter(0), vector->GetIndex(slot),
i::STRICT)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ key : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ key : 789, val2 : 123 })");
return_val = callable(object3).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterStoreNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Zone zone;
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadLiteral(Smi::FromInt(999))
.StoreNamedProperty(builder.Parameter(0), name_index,
vector->GetIndex(slot), i::STRICT)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to polymorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
callable(object5).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object5, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterStoreKeyedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Zone zone;
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot = feedback_spec.AddKeyedStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(999))
.StoreKeyedProperty(builder.Parameter(0), Register(0),
vector->GetIndex(slot), i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterCall) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Zone zone;
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
int slot_index = vector->GetIndex(slot);
Handle<i::String> name = factory->NewStringFromAsciiChecked("func");
name = factory->string_table()->LookupString(isolate, name);
// Check with no args.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { this.func = function() { return 0x265; }})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x265));
}
// Check that receiver is passed properly.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() {"
" this.val = 1234;"
" this.func = function() { return this.val; };"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(1234));
}
// Check with two parameters (+ receiver).
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(4);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.StoreAccumulatorInRegister(Register(1))
.LoadLiteral(Smi::FromInt(51))
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(Register(3))
.Call(Register(0), Register(1), 2)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.func = function(a, b) { return a - b; }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK(return_val->SameValue(Smi::FromInt(40)));
}
// Check with 10 parameters (+ receiver).
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(12);
builder.set_context_count(0);
builder.set_parameter_count(1);
size_t name_index = builder.GetConstantPoolEntry(name);
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.StoreAccumulatorInRegister(Register(1))
.LoadLiteral(factory->NewStringFromAsciiChecked("a"))
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(factory->NewStringFromAsciiChecked("b"))
.StoreAccumulatorInRegister(Register(3))
.LoadLiteral(factory->NewStringFromAsciiChecked("c"))
.StoreAccumulatorInRegister(Register(4))
.LoadLiteral(factory->NewStringFromAsciiChecked("d"))
.StoreAccumulatorInRegister(Register(5))
.LoadLiteral(factory->NewStringFromAsciiChecked("e"))
.StoreAccumulatorInRegister(Register(6))
.LoadLiteral(factory->NewStringFromAsciiChecked("f"))
.StoreAccumulatorInRegister(Register(7))
.LoadLiteral(factory->NewStringFromAsciiChecked("g"))
.StoreAccumulatorInRegister(Register(8))
.LoadLiteral(factory->NewStringFromAsciiChecked("h"))
.StoreAccumulatorInRegister(Register(9))
.LoadLiteral(factory->NewStringFromAsciiChecked("i"))
.StoreAccumulatorInRegister(Register(10))
.LoadLiteral(factory->NewStringFromAsciiChecked("j"))
.StoreAccumulatorInRegister(Register(11))
.Call(Register(0), Register(1), 10)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.prefix = \"prefix_\";"
" this.func = function(a, b, c, d, e, f, g, h, i, j) {"
" return this.prefix + a + b + c + d + e + f + g + h + i + j;"
" }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
Handle<i::String> expected =
factory->NewStringFromAsciiChecked("prefix_abcdefghij");
CHECK(i::String::cast(*return_val)->Equals(*expected));
}
}
static BytecodeArrayBuilder& SetRegister(BytecodeArrayBuilder& builder,
Register reg, int value,
Register scratch) {
return builder.StoreAccumulatorInRegister(scratch)
.LoadLiteral(Smi::FromInt(value))
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(scratch);
}
static BytecodeArrayBuilder& IncrementRegister(BytecodeArrayBuilder& builder,
Register reg, int value,
Register scratch) {
return builder.StoreAccumulatorInRegister(scratch)
.LoadLiteral(Smi::FromInt(value))
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(scratch);
}
TEST(InterpreterJumps) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(2);
builder.set_context_count(0);
builder.set_parameter_count(0);
Register reg(0), scratch(1);
BytecodeLabel label[3];
builder.LoadLiteral(Smi::FromInt(0))
.StoreAccumulatorInRegister(reg)
.Jump(&label[1]);
SetRegister(builder, reg, 1024, scratch).Bind(&label[0]);
IncrementRegister(builder, reg, 1, scratch).Jump(&label[2]);
SetRegister(builder, reg, 2048, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch).Jump(&label[0]);
SetRegister(builder, reg, 4096, scratch).Bind(&label[2]);
IncrementRegister(builder, reg, 4, scratch)
.LoadAccumulatorWithRegister(reg)
.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_EQ(Smi::cast(*return_value)->value(), 7);
}
TEST(InterpreterConditionalJumps) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(2);
builder.set_context_count(0);
builder.set_parameter_count(0);
Register reg(0), scratch(1);
BytecodeLabel label[2];
BytecodeLabel done, done1;
builder.LoadLiteral(Smi::FromInt(0))
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.JumpIfFalse(&label[0]);
IncrementRegister(builder, reg, 1024, scratch)
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(&done);
IncrementRegister(builder, reg, 1, scratch).LoadTrue().JumpIfTrue(&label[1]);
IncrementRegister(builder, reg, 2048, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch).LoadFalse().JumpIfTrue(&done1);
IncrementRegister(builder, reg, 4, scratch)
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.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_EQ(Smi::cast(*return_value)->value(), 7);
}
TEST(InterpreterConditionalJumps2) {
// TODO(oth): Add tests for all conditional jumps near and far.
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(2);
builder.set_context_count(0);
builder.set_parameter_count(0);
Register reg(0), scratch(1);
BytecodeLabel label[2];
BytecodeLabel done, done1;
builder.LoadLiteral(Smi::FromInt(0))
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.JumpIfFalse(&label[0]);
IncrementRegister(builder, reg, 1024, scratch)
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(&done);
IncrementRegister(builder, reg, 1, scratch).LoadTrue().JumpIfTrue(&label[1]);
IncrementRegister(builder, reg, 2048, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch).LoadFalse().JumpIfTrue(&done1);
IncrementRegister(builder, reg, 4, scratch)
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.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_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_context_count(0);
builder.set_parameter_count(0);
builder.LoadLiteral(Smi::FromInt(inputs[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(Smi::FromInt(inputs[j]))
.CompareOperation(comparison, r0, Strength::WEAK)
.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_context_count(0);
builder.set_parameter_count(0);
builder.LoadLiteral(factory->NewHeapNumber(inputs[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(factory->NewHeapNumber(inputs[j]))
.CompareOperation(comparison, r0, Strength::WEAK)
.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_context_count(0);
builder.set_parameter_count(0);
builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(factory->NewStringFromAsciiChecked(rhs))
.CompareOperation(comparison, r0, Strength::WEAK)
.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_context_count(0);
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, Strength::WEAK)
.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, Strength::WEAK)
.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_context_count(0);
builder.set_parameter_count(0);
builder.LoadLiteral(cases[i]);
builder.StoreAccumulatorInRegister(r0)
.LoadLiteral(func)
.CompareOperation(Token::Value::INSTANCEOF, r0, Strength::WEAK)
.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_context_count(0);
builder.set_parameter_count(0);
builder.LoadLiteral(factory->NewStringFromAsciiChecked(properties[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(Handle<Object>::cast(array))
.CompareOperation(Token::Value::IN, r0, Strength::WEAK)
.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(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_context_count(0);
builder.set_parameter_count(0);
builder.LoadFalse();
for (size_t j = 0; j < i; j++) {
builder.LogicalNot();
}
builder.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_context_count(0);
builder.set_parameter_count(0);
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.CastAccumulatorToBoolean();
builder.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_context_count(0);
builder.set_parameter_count(0);
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.LogicalNot();
builder.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_context_count(0);
builder.set_parameter_count(0);
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.TypeOf();
builder.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;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(2);
builder.set_context_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(15))
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(40))
.StoreAccumulatorInRegister(Register(1))
.CallRuntime(Runtime::kAdd, Register(0), 2)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55));
}
TEST(InterpreterFunctionLiteral) {
HandleAndZoneScope handles;
// Test calling a function literal.
std::string source(
"function " + InterpreterTester::function_name() + "(a) {\n"
" return (function(x){ return x + 2; })(a);\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<i::Object> return_val = callable(
Handle<Smi>(Smi::FromInt(3), handles.main_isolate())).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5));
}
TEST(InterpreterRegExpLiterals) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return /abd/.exec('cccabbdd');\n",
factory->null_value()),
std::make_pair("return /ab+d/.exec('cccabbdd')[0];\n",
factory->NewStringFromStaticChars("abbd")),
std::make_pair("return /AbC/i.exec('ssaBC')[0];\n",
factory->NewStringFromStaticChars("aBC")),
std::make_pair("return 'ssaBC'.match(/AbC/i)[0];\n",
factory->NewStringFromStaticChars("aBC")),
std::make_pair("return 'ssaBCtAbC'.match(/(AbC)/gi)[1];\n",
factory->NewStringFromStaticChars("AbC")),
};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterArrayLiterals) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return [][0];\n",
factory->undefined_value()),
std::make_pair("return [1, 3, 2][1];\n",
handle(Smi::FromInt(3), isolate)),
std::make_pair("return ['a', 'b', 'c'][2];\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("var a = 100; return [a, a + 1, a + 2, a + 3][2];\n",
handle(Smi::FromInt(102), isolate)),
std::make_pair("return [[1, 2, 3], ['a', 'b', 'c']][1][0];\n",
factory->NewStringFromStaticChars("a")),
std::make_pair("var t = 't'; return [[t, t + 'est'], [1 + t]][0][1];\n",
factory->NewStringFromStaticChars("test"))
};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterObjectLiterals) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return { }.name;",
factory->undefined_value()),
std::make_pair("return { name: 'string', val: 9.2 }.name;",
factory->NewStringFromStaticChars("string")),
std::make_pair("var a = 15; return { name: 'string', val: a }.val;",
handle(Smi::FromInt(15), isolate)),
std::make_pair("var a = 5; return { val: a, val: a + 1 }.val;",
handle(Smi::FromInt(6), isolate)),
std::make_pair("return { func: function() { return 'test' } }.func();",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { func(a) { return a + 'st'; } }.func('te');",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { get a() { return 22; } }.a;",
handle(Smi::FromInt(22), isolate)),
std::make_pair("var a = { get b() { return this.x + 't'; },\n"
" set b(val) { this.x = val + 's' } };\n"
"a.b = 'te';\n"
"return a.b;",
factory->NewStringFromStaticChars("test")),
std::make_pair("var a = 123; return { 1: a }[1];",
handle(Smi::FromInt(123), isolate)),
std::make_pair("return Object.getPrototypeOf({ __proto__: null });",
factory->null_value()),
std::make_pair("var a = 'test'; return { [a]: 1 }.test;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 'test'; return { b: a, [a]: a + 'ing' }['test']",
factory->NewStringFromStaticChars("testing")),
std::make_pair("var a = 'proto_str';\n"
"var b = { [a]: 1, __proto__: { var : a } };\n"
"return Object.getPrototypeOf(b).var",
factory->NewStringFromStaticChars("proto_str")),
std::make_pair("var n = 'name';\n"
"return { [n]: 'val', get a() { return 987 } }['a'];",
handle(Smi::FromInt(987), isolate)),
};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterConstruct) {
HandleAndZoneScope handles;
std::string source(
"function counter() { this.count = 0; }\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter();\n"
" return c.count;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0));
}
TEST(InterpreterConstructWithArgument) {
HandleAndZoneScope handles;
std::string source(
"function counter(arg0) { this.count = 17; this.x = arg0; }\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter(3);\n"
" return c.x;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
}
TEST(InterpreterConstructWithArguments) {
HandleAndZoneScope handles;
std::string source(
"function counter(arg0, arg1) {\n"
" this.count = 7; this.x = arg0; this.y = arg1;\n"
"}\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter(3, 5);\n"
" return c.count + c.x + c.y;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15));
}
TEST(InterpreterContextVariables) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_vars[] = {
std::make_pair("var a; (function() { a = 1; })(); return a;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 10; (function() { a; })(); return a;",
handle(Smi::FromInt(10), isolate)),
std::make_pair("var a = 20; var b = 30;\n"
"return (function() { return a + b; })();",
handle(Smi::FromInt(50), isolate)),
std::make_pair("'use strict'; let a = 1;\n"
"{ let b = 2; return (function() { return a + b; })(); }",
handle(Smi::FromInt(3), isolate)),
std::make_pair("'use strict'; let a = 10;\n"
"{ let b = 20; var c = function() { [a, b] };\n"
" return a + b; }",
handle(Smi::FromInt(30), isolate)),
};
for (size_t i = 0; i < arraysize(context_vars); i++) {
std::string source(InterpreterTester::SourceForBody(context_vars[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*context_vars[i].second));
}
}
TEST(InterpreterContextParameters) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_params[] = {
std::make_pair("return (function() { return arg1; })();",
handle(Smi::FromInt(1), isolate)),
std::make_pair("(function() { arg1 = 4; })(); return arg1;",
handle(Smi::FromInt(4), isolate)),
std::make_pair("(function() { arg3 = arg2 - arg1; })(); return arg3;",
handle(Smi::FromInt(1), isolate)),
};
for (size_t i = 0; i < arraysize(context_params); i++) {
std::string source = "function " + InterpreterTester::function_name() +
"(arg1, arg2, arg3) {" + context_params[i].first + "}";
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable =
tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>();
Handle<Object> a1 = handle(Smi::FromInt(1), isolate);
Handle<Object> a2 = handle(Smi::FromInt(2), isolate);
Handle<Object> a3 = handle(Smi::FromInt(3), isolate);
Handle<i::Object> return_value = callable(a1, a2, a3).ToHandleChecked();
CHECK(return_value->SameValue(*context_params[i].second));
}
}
TEST(InterpreterOuterContextVariables) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_vars[] = {
std::make_pair("return outerVar * innerArg;",
handle(Smi::FromInt(200), isolate)),
std::make_pair("outerVar = innerArg; return outerVar",
handle(Smi::FromInt(20), isolate)),
};
std::string header(
"function Outer() {"
" var outerVar = 10;"
" function Inner(innerArg) {"
" this.innerFunc = function() { ");
std::string footer(
" }}"
" this.getInnerFunc = function() { return new Inner(20).innerFunc; }"
"}"
"var f = new Outer().getInnerFunc();");
for (size_t i = 0; i < arraysize(context_vars); i++) {
std::string source = header + context_vars[i].first + footer;
InterpreterTester tester(handles.main_isolate(), source.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*context_vars[i].second));
}
}
TEST(InterpreterComma) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a; return 0, a;\n", factory->undefined_value()),
std::make_pair("return 'a', 2.2, 3;\n",
handle(Smi::FromInt(3), isolate)),
std::make_pair("return 'a', 'b', 'c';\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("return 3.2, 2.3, 4.5;\n", factory->NewNumber(4.5)),
std::make_pair("var a = 10; return b = a, b = b+1;\n",
handle(Smi::FromInt(11), isolate)),
std::make_pair("var a = 10; return b = a, b = b+1, b + 10;\n",
handle(Smi::FromInt(21), isolate))};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterLogicalOr) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a, b; return a || b;\n", factory->undefined_value()),
std::make_pair("var a, b = 10; return a || b;\n",
handle(Smi::FromInt(10), isolate)),
std::make_pair("var a = '0', b = 10; return a || b;\n",
factory->NewStringFromStaticChars("0")),
std::make_pair("return 0 || 3.2;\n", factory->NewNumber(3.2)),
std::make_pair("return 'a' || 0;\n",
factory->NewStringFromStaticChars("a")),
std::make_pair("var a = '0', b = 10; return (a == 0) || b;\n",
factory->true_value())};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterLogicalAnd) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a, b = 10; return a && b;\n",
factory->undefined_value()),
std::make_pair("var a = 0, b = 10; return a && b / a;\n",
handle(Smi::FromInt(0), isolate)),
std::make_pair("var a = '0', b = 10; return a && b;\n",
handle(Smi::FromInt(10), isolate)),
std::make_pair("return 0.0 && 3.2;\n", handle(Smi::FromInt(0), isolate)),
std::make_pair("return 'a' && 'b';\n",
factory->NewStringFromStaticChars("b")),
std::make_pair("return 'a' && 0 || 'b', 'c';\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("var x = 1, y = 3; return x && 0 + 1 || y;\n",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var x = 1, y = 3; return (x == 1) && (3 == 3) || y;\n",
factory->true_value())};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*literals[i].second));
}
}
TEST(InterpreterTryCatch) {
HandleAndZoneScope handles;
// TODO(rmcilroy): modify tests when we have real try catch support.
std::string source(InterpreterTester::SourceForBody(
"var a = 1; try { a = a + 1; } catch(e) { a = a + 2; }; return a;"));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(2));
}
TEST(InterpreterTryFinally) {
HandleAndZoneScope handles;
// TODO(rmcilroy): modify tests when we have real try finally support.
std::string source(InterpreterTester::SourceForBody(
"var a = 1; try { a = a + 1; } finally { a = a + 2; }; return a;"));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(4));
}
TEST(InterpreterThrow) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
// TODO(rmcilroy): modify tests when we have real try catch support.
std::pair<const char*, Handle<Object>> throws[] = {
std::make_pair("throw undefined;\n",
factory->undefined_value()),
std::make_pair("throw 1;\n",
handle(Smi::FromInt(1), isolate)),
std::make_pair("throw 'Error';\n",
factory->NewStringFromStaticChars("Error")),
std::make_pair("var a = true; if (a) { throw 'Error'; }\n",
factory->NewStringFromStaticChars("Error")),
std::make_pair("var a = false; if (a) { throw 'Error'; }\n",
factory->undefined_value()),
std::make_pair("throw 'Error1'; throw 'Error2'\n",
factory->NewStringFromStaticChars("Error1")),
};
const char* try_wrapper =
"(function() { try { f(); } catch(e) { return e; }})()";
for (size_t i = 0; i < arraysize(throws); i++) {
std::string source(InterpreterTester::SourceForBody(throws[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
tester.GetCallable<>();
Handle<Object> thrown_obj = v8::Utils::OpenHandle(*CompileRun(try_wrapper));
CHECK(thrown_obj->SameValue(*throws[i].second));
}
}
TEST(InterpreterCountOperators) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> count_ops[] = {
std::make_pair("var a = 1; return ++a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 1; return a++;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 5; return --a;",
handle(Smi::FromInt(4), isolate)),
std::make_pair("var a = 5; return a--;",
handle(Smi::FromInt(5), isolate)),
std::make_pair("var a = 5.2; return --a;",
factory->NewHeapNumber(4.2)),
std::make_pair("var a = 'string'; return ++a;",
factory->nan_value()),
std::make_pair("var a = 'string'; return a--;",
factory->nan_value()),
std::make_pair("var a = true; return ++a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = false; return a--;",
handle(Smi::FromInt(0), isolate)),
std::make_pair("var a = { val: 11 }; return ++a.val;",
handle(Smi::FromInt(12), isolate)),
std::make_pair("var a = { val: 11 }; return a.val--;",
handle(Smi::FromInt(11), isolate)),
std::make_pair("var a = { val: 11 }; return ++a.val;",
handle(Smi::FromInt(12), isolate)),
std::make_pair("var name = 'val'; var a = { val: 22 }; return --a[name];",
handle(Smi::FromInt(21), isolate)),
std::make_pair("var name = 'val'; var a = { val: 22 }; return a[name]++;",
handle(Smi::FromInt(22), isolate)),
std::make_pair("var a = 1; (function() { a = 2 })(); return ++a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1; (function() { a = 2 })(); return a--;",
handle(Smi::FromInt(2), isolate)),
};
for (size_t i = 0; i < arraysize(count_ops); i++) {
std::string source(InterpreterTester::SourceForBody(count_ops[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*count_ops[i].second));
}
}
TEST(InterpreterGlobalCountOperators) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> count_ops[] = {
std::make_pair("var global = 100;function f(){ return ++global; }",
handle(Smi::FromInt(101), isolate)),
std::make_pair("var global = 100; function f(){ return --global; }",
handle(Smi::FromInt(99), isolate)),
std::make_pair("var global = 100; function f(){ return global++; }",
handle(Smi::FromInt(100), isolate)),
std::make_pair("unallocated = 200; function f(){ return ++unallocated; }",
handle(Smi::FromInt(201), isolate)),
std::make_pair("unallocated = 200; function f(){ return --unallocated; }",
handle(Smi::FromInt(199), isolate)),
std::make_pair("unallocated = 200; function f(){ return unallocated++; }",
handle(Smi::FromInt(200), isolate)),
};
for (size_t i = 0; i < arraysize(count_ops); i++) {
InterpreterTester tester(handles.main_isolate(), count_ops[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*count_ops[i].second));
}
}
TEST(InterpreterCompoundExpressions) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> compound_expr[] = {
std::make_pair("var a = 1; a += 2; return a;",
Handle<Object>(Smi::FromInt(3), isolate)),
std::make_pair("var a = 10; a /= 2; return a;",
Handle<Object>(Smi::FromInt(5), isolate)),
std::make_pair("var a = 'test'; a += 'ing'; return a;",
factory->NewStringFromStaticChars("testing")),
std::make_pair("var a = { val: 2 }; a.val *= 2; return a.val;",
Handle<Object>(Smi::FromInt(4), isolate)),
std::make_pair("var a = 1; (function f() { a = 2; })(); a += 24;"
"return a;",
Handle<Object>(Smi::FromInt(26), isolate)),
};
for (size_t i = 0; i < arraysize(compound_expr); i++) {
std::string source(
InterpreterTester::SourceForBody(compound_expr[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*compound_expr[i].second));
}
}
TEST(InterpreterGlobalCompoundExpressions) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> compound_expr[2] = {
std::make_pair("var global = 100;"
"function f() { global += 20; return global; }",
Handle<Object>(Smi::FromInt(120), isolate)),
std::make_pair("unallocated = 100;"
"function f() { unallocated -= 20; return unallocated; }",
Handle<Object>(Smi::FromInt(80), isolate)),
};
for (size_t i = 0; i < arraysize(compound_expr); i++) {
InterpreterTester tester(handles.main_isolate(), compound_expr[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*compound_expr[i].second));
}
}
TEST(InterpreterCreateArguments) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, int> create_args[] = {
std::make_pair("function f() { return arguments[0]; }", 0),
std::make_pair("function f(a) { return arguments[0]; }", 0),
std::make_pair("function f() { return arguments[2]; }", 2),
std::make_pair("function f(a) { return arguments[2]; }", 2),
std::make_pair("function f(a, b, c, d) { return arguments[2]; }", 2),
std::make_pair("function f(a) {"
"'use strict'; return arguments[0]; }",
0),
std::make_pair("function f(a, b, c, d) {"
"'use strict'; return arguments[2]; }",
2),
// Check arguments are mapped in sloppy mode and unmapped in strict.
std::make_pair("function f(a, b, c, d) {"
" c = b; return arguments[2]; }",
1),
std::make_pair("function f(a, b, c, d) {"
" 'use strict'; c = b; return arguments[2]; }",
2),
};
// Test passing no arguments.
for (size_t i = 0; i < arraysize(create_args); i++) {
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(factory->undefined_value()));
}
// Test passing one argument.
for (size_t i = 0; i < arraysize(create_args); i++) {
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(handle(Smi::FromInt(40), isolate)).ToHandleChecked();
if (create_args[i].second == 0) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(40));
} else {
CHECK(return_val.is_identical_to(factory->undefined_value()));
}
}
// Test passing three argument.
for (size_t i = 0; i < arraysize(create_args); i++) {
Handle<Object> args[3] = {
handle(Smi::FromInt(40), isolate),
handle(Smi::FromInt(60), isolate),
handle(Smi::FromInt(80), isolate),
};
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable =
tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>();
Handle<Object> return_val =
callable(args[0], args[1], args[2]).ToHandleChecked();
CHECK(return_val->SameValue(*args[create_args[i].second]));
}
}
TEST(InterpreterConditional) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> conditional[] = {
std::make_pair("return true ? 2 : 3;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("return false ? 2 : 3;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1; return a ? 20 : 30;",
handle(Smi::FromInt(20), isolate)),
std::make_pair("var a = 1; return a ? 20 : 30;",
handle(Smi::FromInt(20), isolate)),
std::make_pair("var a = 'string'; return a ? 20 : 30;",
handle(Smi::FromInt(20), isolate)),
std::make_pair("var a = undefined; return a ? 20 : 30;",
handle(Smi::FromInt(30), isolate)),
std::make_pair("return 1 ? 2 ? 3 : 4 : 5;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("return 0 ? 2 ? 3 : 4 : 5;",
handle(Smi::FromInt(5), isolate)),
};
for (size_t i = 0; i < arraysize(conditional); i++) {
std::string source(InterpreterTester::SourceForBody(conditional[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*conditional[i].second));
}
}
TEST(InterpreterDelete) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
// Tests for delete for local variables that work both in strict
// and sloppy modes
std::pair<const char*, Handle<Object>> test_delete[] = {
std::make_pair(
"var a = { x:10, y:'abc', z:30.2}; delete a.x; return a.x;\n",
factory->undefined_value()),
std::make_pair(
"var b = { x:10, y:'abc', z:30.2}; delete b.x; return b.y;\n",
factory->NewStringFromStaticChars("abc")),
std::make_pair("var c = { x:10, y:'abc', z:30.2}; var d = c; delete d.x; "
"return c.x;\n",
factory->undefined_value()),
std::make_pair("var e = { x:10, y:'abc', z:30.2}; var g = e; delete g.x; "
"return e.y;\n",
factory->NewStringFromStaticChars("abc")),
std::make_pair("var a = { x:10, y:'abc', z:30.2};\n"
"var b = a;"
"delete b.x;"
"return b.x;\n",
factory->undefined_value()),
std::make_pair("var a = {1:10};\n"
"(function f1() {return a;});"
"return delete a[1];",
factory->ToBoolean(true)),
std::make_pair("return delete this;", factory->ToBoolean(true)),
std::make_pair("return delete 'test';", factory->ToBoolean(true))};
// Test delete in sloppy mode
for (size_t i = 0; i < arraysize(test_delete); i++) {
std::string source(InterpreterTester::SourceForBody(test_delete[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_delete[i].second));
}
// Test delete in strict mode
for (size_t i = 0; i < arraysize(test_delete); i++) {
std::string strict_test =
"'use strict'; " + std::string(test_delete[i].first);
std::string source(InterpreterTester::SourceForBody(strict_test.c_str()));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_delete[i].second));
}
}
TEST(InterpreterDeleteSloppyUnqualifiedIdentifier) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
// These tests generate a syntax error for strict mode. We don't
// test for it here.
std::pair<const char*, Handle<Object>> test_delete[] = {
std::make_pair("var sloppy_a = { x:10, y:'abc'};\n"
"var sloppy_b = delete sloppy_a;\n"
"if (delete sloppy_a) {\n"
" return undefined;\n"
"} else {\n"
" return sloppy_a.x;\n"
"}\n",
Handle<Object>(Smi::FromInt(10), isolate)),
// TODO(mythria) When try-catch is implemented change the tests to check
// if delete actually deletes
std::make_pair("sloppy_a = { x:10, y:'abc'};\n"
"var sloppy_b = delete sloppy_a;\n"
// "try{return a.x;} catch(e) {return b;}\n"
"return sloppy_b;",
factory->ToBoolean(true)),
std::make_pair("sloppy_a = { x:10, y:'abc'};\n"
"var sloppy_b = delete sloppy_c;\n"
"return sloppy_b;",
factory->ToBoolean(true))};
for (size_t i = 0; i < arraysize(test_delete); i++) {
std::string source(InterpreterTester::SourceForBody(test_delete[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_delete[i].second));
}
}
TEST(InterpreterGlobalDelete) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> test_global_delete[] = {
std::make_pair("var a = { x:10, y:'abc', z:30.2 };\n"
"function f() {\n"
" delete a.x;\n"
" return a.x;\n"
"}\n"
"f();\n",
factory->undefined_value()),
std::make_pair("var b = {1:10, 2:'abc', 3:30.2 };\n"
"function f() {\n"
" delete b[2];\n"
" return b[1];\n"
" }\n"
"f();\n",
Handle<Object>(Smi::FromInt(10), isolate)),
std::make_pair("var c = { x:10, y:'abc', z:30.2 };\n"
"function f() {\n"
" var d = c;\n"
" delete d.y;\n"
" return d.x;\n"
"}\n"
"f();\n",
Handle<Object>(Smi::FromInt(10), isolate)),
std::make_pair("e = { x:10, y:'abc' };\n"
"function f() {\n"
" return delete e;\n"
"}\n"
"f();\n",
factory->ToBoolean(true)),
std::make_pair("var g = { x:10, y:'abc' };\n"
"function f() {\n"
" return delete g;\n"
"}\n"
"f();\n",
factory->ToBoolean(false)),
std::make_pair("function f() {\n"
" var obj = {h:10, f1() {return delete this;}};\n"
" return obj.f1();\n"
"}\n"
"f();",
factory->ToBoolean(true)),
std::make_pair("function f() {\n"
" var obj = {h:10,\n"
" f1() {\n"
" 'use strict';\n"
" return delete this.h;}};\n"
" return obj.f1();\n"
"}\n"
"f();",
factory->ToBoolean(true))};
for (size_t i = 0; i < arraysize(test_global_delete); i++) {
InterpreterTester tester(handles.main_isolate(),
test_global_delete[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_global_delete[i].second));
}
}
TEST(InterpreterBasicLoops) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> loops[] = {
std::make_pair("var a = 10; var b = 1;\n"
"while (a) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
"};\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 1; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" --a;\n"
"} while(a);\n"
"return b;\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var b = 1;\n"
"for ( var a = 10; a; a--) {\n"
" b *= 2;\n"
"}\n"
"return b;",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"while (a > 0) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
"};\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 1; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" --a;\n"
"} while(a);\n"
"return b;\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var b = 1;\n"
"for ( var a = 10; a > 0; a--) {\n"
" b *= 2;\n"
"}\n"
"return b;",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"while (false) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
"}\n"
"return b;\n",
Handle<Object>(Smi::FromInt(1), isolate)),
std::make_pair("var a = 10; var b = 1;\n"
"while (true) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
" continue;"
"}\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
"} while(true);\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
"} while(false);\n"
"return b;\n",
Handle<Object>(Smi::FromInt(2), isolate)),
std::make_pair("var a = 10; var b = 1;\n"
"for ( a = 1, b = 30; false; ) {\n"
" b = b * 2;\n"
"}\n"
"return b;\n",
Handle<Object>(Smi::FromInt(30), isolate))};
for (size_t i = 0; i < arraysize(loops); i++) {
std::string source(InterpreterTester::SourceForBody(loops[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*loops[i].second));
}
}
TEST(InterpreterForIn) {
HandleAndZoneScope handles;
// TODO(oth): Add a test here for delete mid-loop when delete is ready.
std::pair<const char*, int> for_in_samples[] = {
{"function f() {\n"
" var r = -1;\n"
" for (var a in null) { r = a; }\n"
" return r;\n"
"}",
-1},
{"function f() {\n"
" var r = -1;\n"
" for (var a in undefined) { r = a; }\n"
" return r;\n"
"}",
-1},
{"function f() {\n"
" var r = 0;\n"
" for (var a in [0,6,7,9]) { r = r + (1 << a); }\n"
" return r;\n"
"}",
0xf},
{"function f() {\n"
" var r = 0;\n"
" for (var a in [0,6,7,9]) { r = r + (1 << a); }\n"
" var r = 0;\n"
" for (var a in [0,6,7,9]) { r = r + (1 << a); }\n"
" return r;\n"
"}",
0xf},
{"function f() {\n"
" var r = 0;\n"
" for (var a in 'foobar') { r = r + (1 << a); }\n"
" return r;\n"
"}",
0x3f},
{"function f() {\n"
" var r = 0;\n"
" for (var a in {1:0, 10:1, 100:2, 1000:3}) {\n"
" r = r + Number(a);\n"
" }\n"
" return r;\n"
"}",
1111},
{"function f() {\n"
" var r = 0;\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (var a in data) {\n"
" if (a == 1) delete data[1];\n"
" r = r + Number(a);\n"
" }\n"
" return r;\n"
"}",
1111},
{"function f() {\n"
" var r = 0;\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (var a in data) {\n"
" if (a == 10) delete data[100];\n"
" r = r + Number(a);\n"
" }\n"
" return r;\n"
"}",
1011},
{"function f() {\n"
" var r = 0;\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (var a in data) {\n"
" if (a == 10) data[10000] = 4;\n"
" r = r + Number(a);\n"
" }\n"
" return r;\n"
"}",
1111},
{"function f() {\n"
" var r = 0;\n"
" var input = 'foobar';\n"
" for (var a in input) {\n"
" if (input[a] == 'b') break;\n"
" r = r + (1 << a);\n"
" }\n"
" return r;\n"
"}",
0x7},
{"function f() {\n"
"var r = 0;\n"
"var input = 'foobar';\n"
"for (var a in input) {\n"
" if (input[a] == 'b') continue;\n"
" r = r + (1 << a);\n"
"}\n"
"return r;\n"
"}",
0x37},
{"function f() {\n"
" var r = 0;\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (var a in data) {\n"
" if (a == 10) {\n"
" data[10000] = 4;\n"
" }\n"
" r = r + Number(a);\n"
" }\n"
" return r;\n"
"}",
1111},
{"function f() {\n"
" var r = [ 3 ];\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (r[10] in data) {\n"
" }\n"
" return Number(r[10]);\n"
"}",
1000},
{"function f() {\n"
" var r = [ 3 ];\n"
" var data = {1:0, 10:1, 100:2, 1000:3};\n"
" for (r['100'] in data) {\n"
" }\n"
" return Number(r['100']);\n"
"}",
1000},
{"function f() {\n"
" var obj = {}\n"
" var descObj = new Boolean(false);\n"
" var accessed = 0;\n"
" descObj.enumerable = true;\n"
" Object.defineProperties(obj, { prop:descObj });\n"
" for (var p in obj) {\n"
" if (p === 'prop') { accessed = 1; }\n"
" }\n"
" return accessed;"
"}",
1},
{"function f() {\n"
" var appointment = {};\n"
" Object.defineProperty(appointment, 'startTime', {\n"
" value: 1001,\n"
" writable: false,\n"
" enumerable: false,\n"
" configurable: true\n"
" });\n"
" Object.defineProperty(appointment, 'name', {\n"
" value: 'NAME',\n"
" writable: false,\n"
" enumerable: false,\n"
" configurable: true\n"
" });\n"
" var meeting = Object.create(appointment);\n"
" Object.defineProperty(meeting, 'conferenceCall', {\n"
" value: 'In-person meeting',\n"
" writable: false,\n"
" enumerable: false,\n"
" configurable: true\n"
" });\n"
"\n"
" var teamMeeting = Object.create(meeting);\n"
"\n"
" var flags = 0;\n"
" for (var p in teamMeeting) {\n"
" if (p === 'startTime') {\n"
" flags |= 1;\n"
" }\n"
" if (p === 'name') {\n"
" flags |= 2;\n"
" }\n"
" if (p === 'conferenceCall') {\n"
" flags |= 4;\n"
" }\n"
" }\n"
"\n"
" var hasOwnProperty = !teamMeeting.hasOwnProperty('name') &&\n"
" !teamMeeting.hasOwnProperty('startTime') &&\n"
" !teamMeeting.hasOwnProperty('conferenceCall');\n"
" if (!hasOwnProperty) {\n"
" flags |= 8;\n"
" }\n"
" return flags;\n"
" }",
0}};
for (size_t i = 0; i < arraysize(for_in_samples); i++) {
InterpreterTester tester(handles.main_isolate(), for_in_samples[i].first);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), for_in_samples[i].second);
}
}
TEST(InterpreterSwitch) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> switch_ops[] = {
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: return 2;\n"
" case 2: return 3;\n"
"}\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 2: a = 2; break;\n"
" case 1: a = 3; break;\n"
"}\n"
"return a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: a = 2; // fall-through\n"
" case 2: a = 3; break;\n"
"}\n"
"return a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case 1: return 100;\n"
" case 2: return 200;\n"
"}\n"
"return undefined;",
factory->undefined_value()),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case 1: return 100;\n"
" case 2: return 200;\n"
" default: return 300;\n"
"}\n"
"return undefined;",
handle(Smi::FromInt(300), isolate)),
std::make_pair("var a = 100;\n"
"switch(typeof(a)) {\n"
" case 'string': return 1;\n"
" case 'number': return 2;\n"
" default: return 3;\n"
"}\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case a += 20: return 1;\n"
" case a -= 10: return 2;\n"
" case a -= 10: return 3;\n"
" default: return 3;\n"
"}\n",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: \n"
" switch(a + 1) {\n"
" case 2 : a += 1; break;\n"
" default : a += 2; break;\n"
" } // fall-through\n"
" case 2: a += 3;\n"
"}\n"
"return a;",
handle(Smi::FromInt(5), isolate)),
};
for (size_t i = 0; i < arraysize(switch_ops); i++) {
std::string source(InterpreterTester::SourceForBody(switch_ops[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*switch_ops[i].second));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8