v8/test/cctest/interpreter/test-interpreter.cc

4731 lines
176 KiB
C++
Raw Normal View History

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/v8.h"
#include "src/execution.h"
#include "src/handles.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/interpreter/interpreter-tester.h"
#include "test/cctest/test-feedback-vector.h"
namespace v8 {
namespace internal {
namespace interpreter {
TEST(InterpreterReturn) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Handle<Object> undefined_value = isolate->factory()->undefined_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> undefined_value = isolate->factory()->undefined_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadUndefined().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> null_value = isolate->factory()->null_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadNull().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> the_hole_value = isolate->factory()->the_hole_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadTheHole().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> true_value = isolate->factory()->true_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadTrue().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> false_value = isolate->factory()->false_value();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadFalse().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// Small Smis.
for (int i = -128; i < 128; i++) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadLiteral(Smi::FromInt(i)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadLiteral(Smi::FromInt(0x12345678)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadLiteral(factory->NewHeapNumber(-2.1e19)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 0);
Handle<i::String> string = factory->NewStringFromAsciiChecked("String");
builder.LoadLiteral(string).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Handle<Object> true_value = isolate->factory()->true_value();
for (int i = 0; i <= kMaxInt8; i++) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, i + 1);
Register reg(i);
builder.LoadTrue()
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.LoadAccumulatorWithRegister(reg)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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,
vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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,
vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
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(isolate, handles.main_zone(), 1, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Register reg(0);
builder.LoadLiteral(test_cases[i].lhs)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(test_cases[i].rhs)
.BinaryOperation(Token::Value::ADD, reg, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
[turbofan] Collect invocation counts and compute relative call frequencies. Add a notion of "invocation count" to the baseline compilers, which increment a special slot in the TypeFeedbackVector for each invocation of a given function (the optimized code doesn't currently collect this information). Use this invocation count to relativize the call counts on the call sites within the function, so that the inlining heuristic has a view of relative importance of a call site rather than some absolute numbers with unclear meaning for the current function. Also apply the call site frequency as a factor to all frequencies in the inlinee by passing this to the graph builders so that the importance of a call site in an inlinee is relative to the topmost optimized function. Note that all functions that neither have literals nor need type feedback slots will share a single invocation count cell in the canonical empty type feedback vector, so their invocation count is meaningless, but that doesn't matter since we only use the invocation count to relativize call counts within the function, which we only have if we have at least one type feedback vector (the CallIC slot). See the design document for additional details on this change: https://docs.google.com/document/d/1VoYBhpDhJC4VlqMXCKvae-8IGuheBGxy32EOgC2LnT8 BUG=v8:5267,v8:5372 R=mvstanton@chromium.org,rmcilroy@chromium.org,mstarzinger@chromium.org Review-Url: https://codereview.chromium.org/2337123003 Cr-Commit-Position: refs/heads/master@{#39410}
2016-09-14 10:20:08 +00:00
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_cases[i].expected_value));
}
}
TEST(InterpreterParameter1) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadAccumulatorWithRegister(builder.Parameter(0)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
// Check for heap objects.
Handle<Object> true_value = 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;
Isolate* isolate = handles.main_isolate();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 8, 0, 0);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot3 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot4 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot5 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot6 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.BinaryOperation(Token::Value::ADD, builder.Parameter(1),
vector->GetIndex(slot))
.BinaryOperation(Token::Value::ADD, builder.Parameter(2),
vector->GetIndex(slot1))
.BinaryOperation(Token::Value::ADD, builder.Parameter(3),
vector->GetIndex(slot2))
.BinaryOperation(Token::Value::ADD, builder.Parameter(4),
vector->GetIndex(slot3))
.BinaryOperation(Token::Value::ADD, builder.Parameter(5),
vector->GetIndex(slot4))
.BinaryOperation(Token::Value::ADD, builder.Parameter(6),
vector->GetIndex(slot5))
.BinaryOperation(Token::Value::ADD, builder.Parameter(7),
vector->GetIndex(slot6))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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(InterpreterBinaryOpTypeFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
struct BinaryOpExpectation {
Token::Value op;
Handle<Object> arg1;
Handle<Object> arg2;
Handle<Object> result;
int32_t feedback;
};
BinaryOpExpectation const kTestCases[] = {
// ADD
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(5), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(Smi::kMaxValue), isolate),
Handle<Smi>(Smi::FromInt(1), isolate),
isolate->factory()->NewHeapNumber(Smi::kMaxValue + 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 + 3),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 + 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("2"),
isolate->factory()->NewStringFromAsciiChecked("22"),
BinaryOperationFeedback::kAny},
// SUB
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(-1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate),
Handle<Smi>(Smi::FromInt(1), isolate),
isolate->factory()->NewHeapNumber(Smi::kMinValue - 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 - 3),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 - 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny},
// MUL
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(2), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(6), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(Smi::kMinValue * 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3 * 3.1415),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 * 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// DIV
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(6), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(3.0 / 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(3.1415 / 3),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, isolate->factory()->NewHeapNumber(3.1415),
isolate->factory()->NewHeapNumber(
-std::numeric_limits<double>::infinity()),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("1"),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// MOD
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(5), isolate),
Handle<Smi>(Smi::FromInt(3), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(-4), isolate),
Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, isolate->factory()->NewHeapNumber(3.1415),
Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewHeapNumber(fmod(3.1415, 3.0)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, isolate->factory()->NewHeapNumber(-3.1415),
isolate->factory()->NewHeapNumber(-1.4142),
isolate->factory()->NewHeapNumber(fmod(-3.1415, -1.4142)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, Handle<Smi>(Smi::FromInt(3), isolate),
isolate->factory()->NewStringFromAsciiChecked("-2"),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny}};
for (const BinaryOpExpectation& test_case : kTestCases) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Register reg(0);
builder.LoadLiteral(test_case.arg1)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(test_case.arg2)
.BinaryOperation(test_case.op, reg, vector->GetIndex(slot0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(test_case.feedback, static_cast<Smi*>(feedback0)->value());
CHECK(Object::Equals(test_case.result, return_val).ToChecked());
}
}
TEST(InterpreterBinaryOpSmiTypeFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
struct BinaryOpExpectation {
Token::Value op;
Handle<Object> arg1;
int32_t arg2;
Handle<Object> result;
int32_t feedback;
};
BinaryOpExpectation const kTestCases[] = {
// ADD
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate), 42,
Handle<Smi>(Smi::FromInt(44), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate), Smi::kMaxValue,
isolate->factory()->NewHeapNumber(Smi::kMaxValue + 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewHeapNumber(3.1415), 2,
isolate->factory()->NewHeapNumber(3.1415 + 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewStringFromAsciiChecked("2"), 2,
isolate->factory()->NewStringFromAsciiChecked("22"),
BinaryOperationFeedback::kAny},
// SUB
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(2), isolate), 42,
Handle<Smi>(Smi::FromInt(-40), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SUB, Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate), 1,
isolate->factory()->NewHeapNumber(Smi::kMinValue - 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewHeapNumber(3.1415), 2,
isolate->factory()->NewHeapNumber(3.1415 - 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, isolate->factory()->NewStringFromAsciiChecked("2"), 2,
Handle<Smi>(Smi::FromInt(0), isolate), BinaryOperationFeedback::kAny},
// BIT_OR
{Token::Value::BIT_OR, Handle<Smi>(Smi::FromInt(4), isolate), 1,
Handle<Smi>(Smi::FromInt(5), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::BIT_OR, isolate->factory()->NewHeapNumber(3.1415), 8,
Handle<Smi>(Smi::FromInt(11), isolate),
BinaryOperationFeedback::kNumber},
{Token::Value::BIT_OR, isolate->factory()->NewStringFromAsciiChecked("2"),
1, Handle<Smi>(Smi::FromInt(3), isolate), BinaryOperationFeedback::kAny},
// BIT_AND
{Token::Value::BIT_AND, Handle<Smi>(Smi::FromInt(3), isolate), 1,
Handle<Smi>(Smi::FromInt(1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::BIT_AND, isolate->factory()->NewHeapNumber(3.1415), 2,
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kNumber},
{Token::Value::BIT_AND,
isolate->factory()->NewStringFromAsciiChecked("2"), 1,
Handle<Smi>(Smi::FromInt(0), isolate), BinaryOperationFeedback::kAny},
// SHL
{Token::Value::SHL, Handle<Smi>(Smi::FromInt(3), isolate), 1,
Handle<Smi>(Smi::FromInt(6), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SHL, isolate->factory()->NewHeapNumber(3.1415), 2,
Handle<Smi>(Smi::FromInt(12), isolate),
BinaryOperationFeedback::kNumber},
{Token::Value::SHL, isolate->factory()->NewStringFromAsciiChecked("2"), 1,
Handle<Smi>(Smi::FromInt(4), isolate), BinaryOperationFeedback::kAny},
// SAR
{Token::Value::SAR, Handle<Smi>(Smi::FromInt(3), isolate), 1,
Handle<Smi>(Smi::FromInt(1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SAR, isolate->factory()->NewHeapNumber(3.1415), 2,
Handle<Smi>(Smi::FromInt(0), isolate), BinaryOperationFeedback::kNumber},
{Token::Value::SAR, isolate->factory()->NewStringFromAsciiChecked("2"), 1,
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny}};
for (const BinaryOpExpectation& test_case : kTestCases) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
Register reg(0);
builder.LoadLiteral(test_case.arg1)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(test_case.arg2))
.BinaryOperation(test_case.op, reg, vector->GetIndex(slot0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(test_case.feedback, static_cast<Smi*>(feedback0)->value());
CHECK(Object::Equals(test_case.result, return_val).ToChecked());
}
}
TEST(InterpreterUnaryOpFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
Handle<Smi> smi_one = Handle<Smi>(Smi::FromInt(1), isolate);
Handle<Smi> smi_max = Handle<Smi>(Smi::FromInt(Smi::kMaxValue), isolate);
Handle<Smi> smi_min = Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate);
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(2.1);
Handle<String> str = isolate->factory()->NewStringFromAsciiChecked("42");
struct TestCase {
Token::Value op;
Handle<Smi> smi_feedback_value;
Handle<Smi> smi_to_number_feedback_value;
Handle<HeapNumber> number_feedback_value;
Handle<Object> any_feedback_value;
};
TestCase const kTestCases[] = {
{Token::Value::ADD, smi_one, smi_max, number, str},
{Token::Value::SUB, smi_one, smi_min, number, str}};
for (TestCase const& test_case : kTestCases) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 4, 0, 0);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot3 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.CountOperation(test_case.op, vector->GetIndex(slot0))
.LoadAccumulatorWithRegister(builder.Parameter(1))
.CountOperation(test_case.op, vector->GetIndex(slot1))
.LoadAccumulatorWithRegister(builder.Parameter(2))
.CountOperation(test_case.op, vector->GetIndex(slot2))
.LoadAccumulatorWithRegister(builder.Parameter(3))
.CountOperation(test_case.op, vector->GetIndex(slot3))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H>();
Handle<Object> return_val =
callable(test_case.smi_feedback_value,
test_case.smi_to_number_feedback_value,
test_case.number_feedback_value, test_case.any_feedback_value)
.ToHandleChecked();
USE(return_val);
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall,
static_cast<Smi*>(feedback0)->value());
Object* feedback1 = vector->Get(slot1);
CHECK(feedback1->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback1)->value());
Object* feedback2 = vector->Get(slot2);
CHECK(feedback2->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback2)->value());
Object* feedback3 = vector->Get(slot3);
CHECK(feedback3->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny,
static_cast<Smi*>(feedback3)->value());
}
}
TEST(InterpreterBitwiseTypeFeedback) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Zone zone(isolate->allocator());
const Token::Value kBitwiseBinaryOperators[] = {
Token::Value::BIT_OR, Token::Value::BIT_XOR, Token::Value::BIT_AND,
Token::Value::SHL, Token::Value::SHR, Token::Value::SAR};
for (Token::Value op : kBitwiseBinaryOperators) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 4, 0, 0);
i::FeedbackVectorSpec feedback_spec(&zone);
i::FeedbackVectorSlot slot0 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
i::FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.BinaryOperation(op, builder.Parameter(1), vector->GetIndex(slot0))
.BinaryOperation(op, builder.Parameter(2), vector->GetIndex(slot1))
.BinaryOperation(op, builder.Parameter(3), vector->GetIndex(slot2))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H>();
Handle<Smi> arg1 = Handle<Smi>(Smi::FromInt(2), isolate);
Handle<Smi> arg2 = Handle<Smi>(Smi::FromInt(2), isolate);
Handle<HeapNumber> arg3 = isolate->factory()->NewHeapNumber(2.2);
Handle<String> arg4 = isolate->factory()->NewStringFromAsciiChecked("2");
Handle<Object> return_val =
callable(arg1, arg2, arg3, arg4).ToHandleChecked();
USE(return_val);
Object* feedback0 = vector->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall,
static_cast<Smi*>(feedback0)->value());
Object* feedback1 = vector->Get(slot1);
CHECK(feedback1->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback1)->value());
Object* feedback2 = vector->Get(slot2);
CHECK(feedback2->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny,
static_cast<Smi*>(feedback2)->value());
}
}
TEST(InterpreterParameter1Assign) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadLiteral(Smi::FromInt(5))
.StoreAccumulatorInRegister(builder.Parameter(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
// Test loading a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return global;\n"
"}");
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
// 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(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;
Isolate* isolate = handles.main_isolate();
// Test loading an unallocated global.
std::string source(
"unallocated = 123;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return unallocated;\n"
"}");
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadNamedProperty(builder.Parameter(0), name, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> key = factory->NewStringFromAsciiChecked("key");
key = factory->string_table()->LookupString(isolate, key);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
builder.LoadLiteral(key)
.LoadKeyedProperty(builder.Parameter(0), vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 0);
builder.LoadLiteral(Smi::FromInt(999))
.StoreNamedProperty(builder.Parameter(0), name, vector->GetIndex(slot),
STRICT)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddKeyedStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 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(isolate);
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));
}
static void TestInterpreterCall(TailCallMode tail_call_mode) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
FeedbackVectorSlot call_slot = feedback_spec.AddCallICSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
int slot_index = vector->GetIndex(slot);
int call_slot_index = -1;
call_slot_index = vector->GetIndex(call_slot);
Handle<i::String> name = factory->NewStringFromAsciiChecked("func");
name = factory->string_table()->LookupString(isolate, name);
// Check with no args.
{
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.StoreAccumulatorInRegister(Register(0));
builder.Call(Register(0), builder.Parameter(0), 1, call_slot_index,
tail_call_mode);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 1);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.StoreAccumulatorInRegister(Register(0));
builder.Call(Register(0), builder.Parameter(0), 1, call_slot_index,
tail_call_mode);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 4);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.StoreAccumulatorInRegister(Register(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.StoreAccumulatorInRegister(Register(1))
.LoadLiteral(Smi::FromInt(51))
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(Register(3));
builder.Call(Register(0), Register(1), 3, call_slot_index, tail_call_mode);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(isolate, handles.main_zone(), 1, 0, 12);
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
.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));
builder.Call(Register(0), Register(1), 11, call_slot_index, tail_call_mode);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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));
}
}
TEST(InterpreterCall) { TestInterpreterCall(TailCallMode::kDisallow); }
TEST(InterpreterTailCall) { TestInterpreterCall(TailCallMode::kAllow); }
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,
int slot_index) {
return builder.StoreAccumulatorInRegister(scratch)
.LoadLiteral(Smi::FromInt(value))
.BinaryOperation(Token::Value::ADD, reg, slot_index)
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(scratch);
}
TEST(InterpreterJumps) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 2);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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, vector->GetIndex(slot))
.Jump(&label[2]);
SetRegister(builder, reg, 2048, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch, vector->GetIndex(slot1))
.JumpLoop(&label[0], 0);
SetRegister(builder, reg, 4096, scratch).Bind(&label[2]);
IncrementRegister(builder, reg, 4, scratch, vector->GetIndex(slot2))
.LoadAccumulatorWithRegister(reg)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_value)->value(), 7);
}
TEST(InterpreterConditionalJumps) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 2);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot3 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot4 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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, vector->GetIndex(slot))
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(&done);
IncrementRegister(builder, reg, 1, scratch, vector->GetIndex(slot1))
.LoadTrue()
.JumpIfTrue(&label[1]);
IncrementRegister(builder, reg, 2048, scratch, vector->GetIndex(slot2))
.Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch, vector->GetIndex(slot3))
.LoadFalse()
.JumpIfTrue(&done1);
IncrementRegister(builder, reg, 4, scratch, vector->GetIndex(slot4))
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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;
Isolate* isolate = handles.main_isolate();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 2);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot1 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot3 = feedback_spec.AddGeneralSlot();
FeedbackVectorSlot slot4 = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
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, vector->GetIndex(slot))
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(&done);
IncrementRegister(builder, reg, 1, scratch, vector->GetIndex(slot1))
.LoadTrue()
.JumpIfTrue(&label[1]);
IncrementRegister(builder, reg, 2048, scratch, vector->GetIndex(slot2))
.Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch, vector->GetIndex(slot3))
.LoadFalse()
.JumpIfTrue(&done1);
IncrementRegister(builder, reg, 4, scratch, vector->GetIndex(slot4))
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_value)->value(), 7);
}
TEST(InterpreterJumpConstantWith16BitOperand) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 257);
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Register reg(0), scratch(256);
BytecodeLabel done, fake;
builder.LoadLiteral(Smi::FromInt(0));
builder.StoreAccumulatorInRegister(reg);
// Consume all 8-bit operands
for (int i = 1; i <= 256; i++) {
builder.LoadLiteral(isolate->factory()->NewNumber(i));
builder.BinaryOperation(Token::Value::ADD, reg, vector->GetIndex(slot));
builder.StoreAccumulatorInRegister(reg);
}
builder.Jump(&done);
// Emit more than 16-bit immediate operands worth of code to jump over.
builder.Bind(&fake);
for (int i = 0; i < 6600; i++) {
builder.LoadLiteral(Smi::FromInt(0)); // 1-byte
builder.BinaryOperation(Token::Value::ADD, scratch,
vector->GetIndex(slot)); // 6-bytes
builder.StoreAccumulatorInRegister(scratch); // 4-bytes
builder.MoveRegister(scratch, reg); // 6-bytes
}
builder.Bind(&done);
builder.LoadAccumulatorWithRegister(reg);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
BytecodeArrayIterator iterator(bytecode_array);
bool found_16bit_constant_jump = false;
while (!iterator.done()) {
if (iterator.current_bytecode() == Bytecode::kJumpConstant &&
iterator.current_operand_scale() == OperandScale::kDouble) {
found_16bit_constant_jump = true;
break;
}
iterator.Advance();
}
CHECK(found_16bit_constant_jump);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_value)->value(), 256.0 / 2 * (1 + 256));
}
TEST(InterpreterJumpWith32BitOperand) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
Register reg(0);
BytecodeLabel done;
builder.LoadLiteral(Smi::FromInt(0));
builder.StoreAccumulatorInRegister(reg);
// Consume all 16-bit constant pool entries
for (int i = 1; i <= 65536; i++) {
builder.LoadLiteral(isolate->factory()->NewNumber(i));
}
builder.Jump(&done);
builder.LoadLiteral(Smi::FromInt(0));
builder.Bind(&done);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
BytecodeArrayIterator iterator(bytecode_array);
bool found_32bit_jump = false;
while (!iterator.done()) {
if (iterator.current_bytecode() == Bytecode::kJump &&
iterator.current_operand_scale() == OperandScale::kQuadruple) {
found_32bit_jump = true;
break;
}
iterator.Advance();
}
CHECK(found_32bit_jump);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_value)->value(), 65536.0);
}
static const Token::Value kComparisonTypes[] = {
Token::Value::EQ, Token::Value::NE, Token::Value::EQ_STRICT,
Token::Value::LT, 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;
Isolate* isolate = handles.main_isolate();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Register r0(0);
builder.LoadLiteral(Smi::FromInt(inputs[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(Smi::FromInt(inputs[j]))
.CompareOperation(comparison, r0, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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]));
Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall,
static_cast<Smi*>(feedback)->value());
}
}
}
}
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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 1);
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Register r0(0);
builder.LoadLiteral(factory->NewHeapNumber(inputs[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(factory->NewHeapNumber(inputs[j]))
.CompareOperation(comparison, r0, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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]));
Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber,
static_cast<Smi*>(feedback)->value());
}
}
}
}
TEST(InterpreterStringComparisons) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone zone(isolate->allocator());
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++) {
CanonicalHandleScope canonical(isolate);
const char* lhs = inputs[i].c_str();
const char* rhs = inputs[j].c_str();
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 1);
Register r0(0);
builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(factory->NewStringFromAsciiChecked(rhs))
.CompareOperation(comparison, r0, vector->GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
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]));
Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny,
static_cast<Smi*>(feedback)->value());
}
}
}
}
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"};
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,
ConversionFlags::NO_FLAGS);
double rhs = StringToDouble(&unicode_cache, rhs_cstr,
ConversionFlags::NO_FLAGS);
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 1);
Zone zone(isolate->allocator());
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddGeneralSlot();
Handle<i::TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate, &feedback_spec);
Register r0(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, vector->GetIndex(slot))
.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, vector->GetIndex(slot))
.Return();
}
Handle<BytecodeArray> bytecode_array =
builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(),
CompareC(comparison, lhs, rhs, true));
Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny,
static_cast<Smi*>(feedback)->value());
}
}
}
}
}
TEST(InterpreterStrictNotEqual) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
const char* code_snippet =
"function f(lhs, rhs) {\n"
" return lhs !== rhs;\n"
"}\n"
"f(0, 0);\n";
InterpreterTester tester(isolate, code_snippet);
auto callable = tester.GetCallable<Handle<Object>, Handle<Object>>();
// Test passing different types.
const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e5", "2.01"};
UnicodeCache unicode_cache;
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
double lhs =
StringToDouble(&unicode_cache, inputs[i], ConversionFlags::NO_FLAGS);
double rhs =
StringToDouble(&unicode_cache, inputs[j], ConversionFlags::NO_FLAGS);
Handle<Object> lhs_obj = factory->NewNumber(lhs);
Handle<Object> rhs_obj = factory->NewStringFromAsciiChecked(inputs[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(),
CompareC(Token::Value::NE_STRICT, lhs, rhs, true));
}
}
// Test passing string types.
const char* inputs_str[] = {"A", "abc", "z", "", "Foo!", "Foo"};
for (size_t i = 0; i < arraysize(inputs_str); i++) {
for (size_t j = 0; j < arraysize(inputs_str); j++) {
Handle<Object> lhs_obj =
factory->NewStringFromAsciiChecked(inputs_str[i]);
Handle<Object> rhs_obj =
factory->NewStringFromAsciiChecked(inputs_str[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(),
CompareC(Token::Value::NE_STRICT, inputs_str[i], inputs_str[j]));
}
}
// Test passing doubles.
double inputs_number[] = {std::numeric_limits<double>::min(),
std::numeric_limits<double>::max(),
-0.001,
0.01,
0.1000001,
1e99,
-1e-99};
for (size_t i = 0; i < arraysize(inputs_number); i++) {
for (size_t j = 0; j < arraysize(inputs_number); j++) {
Handle<Object> lhs_obj = factory->NewNumber(inputs_number[i]);
Handle<Object> rhs_obj = factory->NewNumber(inputs_number[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(),
CompareC(Token::Value::NE_STRICT, inputs_number[i],
inputs_number[j]));
}
}
}
TEST(InterpreterInstanceOf) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = 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(isolate, handles.main_zone(), 0, 0, 1);
Register r0(0);
builder.LoadLiteral(cases[i]);
builder.StoreAccumulatorInRegister(r0)
.LoadLiteral(func)
.CompareOperation(Token::Value::INSTANCEOF, r0, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// Allocate an array
Handle<i::JSArray> array =
factory->NewJSArray(0, 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(isolate, handles.main_zone(), 0, 0, 1);
Register r0(0);
builder.LoadLiteral(factory->NewStringFromAsciiChecked(properties[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(Handle<Object>::cast(array))
.CompareOperation(Token::Value::IN, r0, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
for (size_t i = 1; i < 10; i++) {
bool expected_value = ((i & 1) == 1);
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 0);
Register r0(0);
builder.LoadFalse();
for (size_t j = 0; j < i; j++) {
builder.LogicalNot();
}
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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(InterpreterUnaryNotNonBoolean) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = 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(isolate, handles.main_zone(), 0, 0, 0);
Register r0(0);
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.LogicalNot();
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, const char*> typeof_vals[] = {
std::make_pair("return typeof undefined;", "undefined"),
std::make_pair("return typeof null;", "object"),
std::make_pair("return typeof true;", "boolean"),
std::make_pair("return typeof false;", "boolean"),
std::make_pair("return typeof 9.1;", "number"),
std::make_pair("return typeof 7771;", "number"),
std::make_pair("return typeof 'hello';", "string"),
std::make_pair("return typeof global_unallocated;", "undefined"),
};
for (size_t i = 0; i < arraysize(typeof_vals); i++) {
std::string source(InterpreterTester::SourceForBody(typeof_vals[i].first));
InterpreterTester tester(isolate, source.c_str());
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], typeof_vals[i].second), 0);
}
}
TEST(InterpreterCallRuntime) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 2);
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(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55));
}
TEST(InterpreterInvokeIntrinsic) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 2);
builder.LoadLiteral(Smi::FromInt(15))
.StoreAccumulatorInRegister(Register(0))
.CallRuntime(Runtime::kInlineIsArray, Register(0), 1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val->IsBoolean());
CHECK_EQ(return_val->BooleanValue(), false);
}
TEST(InterpreterFunctionLiteral) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
// Test calling a function literal.
std::string source(
"function " + InterpreterTester::function_name() + "(a) {\n"
" return (function(x){ return x + 2; })(a);\n"
"}");
InterpreterTester tester(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
std::ostringstream unique_vars;
for (int i = 0; i < 250; i++) {
unique_vars << "var a" << i << " = 0;";
}
std::pair<std::string, 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)),
std::make_pair("'use strict';" + unique_vars.str() +
"eval(); var b = 100; return b;",
handle(Smi::FromInt(100), isolate)),
};
for (size_t i = 0; i < arraysize(context_vars); i++) {
std::string source(
InterpreterTester::SourceForBody(context_vars[i].first.c_str()));
InterpreterTester tester(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;
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(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;
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> catches[] = {
std::make_pair("var a = 1; try { a = 2 } catch(e) { a = 3 }; return a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a; try { undef.x } catch(e) { a = 2 }; return a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a; try { throw 1 } catch(e) { a = e + 2 }; return a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a; try { throw 1 } catch(e) { a = e + 2 };"
" try { throw a } catch(e) { a = e + 3 }; return a;",
handle(Smi::FromInt(6), isolate)),
};
for (size_t i = 0; i < arraysize(catches); i++) {
std::string source(InterpreterTester::SourceForBody(catches[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*catches[i].second));
}
}
TEST(InterpreterTryFinally) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> finallies[] = {
std::make_pair(
"var a = 1; try { a = a + 1; } finally { a = a + 2; }; return a;",
factory->NewStringFromStaticChars("R4")),
std::make_pair(
"var a = 1; try { a = 2; return 23; } finally { a = 3 }; return a;",
factory->NewStringFromStaticChars("R23")),
std::make_pair(
"var a = 1; try { a = 2; throw 23; } finally { a = 3 }; return a;",
factory->NewStringFromStaticChars("E23")),
std::make_pair(
"var a = 1; try { a = 2; throw 23; } finally { return a; };",
factory->NewStringFromStaticChars("R2")),
std::make_pair(
"var a = 1; try { a = 2; throw 23; } finally { throw 42; };",
factory->NewStringFromStaticChars("E42")),
std::make_pair("var a = 1; for (var i = 10; i < 20; i += 5) {"
" try { a = 2; break; } finally { a = 3; }"
"} return a + i;",
factory->NewStringFromStaticChars("R13")),
std::make_pair("var a = 1; for (var i = 10; i < 20; i += 5) {"
" try { a = 2; continue; } finally { a = 3; }"
"} return a + i;",
factory->NewStringFromStaticChars("R23")),
std::make_pair("var a = 1; try { a = 2;"
" try { a = 3; throw 23; } finally { a = 4; }"
"} catch(e) { a = a + e; } return a;",
factory->NewStringFromStaticChars("R27")),
std::make_pair("var func_name;"
"function tcf2(a) {"
" try { throw new Error('boom');} "
" catch(e) {return 153; } "
" finally {func_name = tcf2.name;}"
"}"
"tcf2();"
"return func_name;",
factory->NewStringFromStaticChars("Rtcf2")),
};
const char* try_wrapper =
"(function() { try { return 'R' + f() } catch(e) { return 'E' + e }})()";
for (size_t i = 0; i < arraysize(finallies); i++) {
std::string source(InterpreterTester::SourceForBody(finallies[i].first));
InterpreterTester tester(isolate, source.c_str());
tester.GetCallable<>();
Handle<Object> wrapped = v8::Utils::OpenHandle(*CompileRun(try_wrapper));
CHECK(wrapped->SameValue(*finallies[i].second));
}
}
TEST(InterpreterThrow) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
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(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;
Isolate* isolate = handles.main_isolate();
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)),
std::make_pair("var i = 5; while(i--) {}; return i;",
handle(Smi::FromInt(-1), isolate)),
std::make_pair("var i = 1; if(i--) { return 1; } else { return 2; };",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var i = -2; do {} while(i++) {}; return i;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var i = -1; for(; i++; ) {}; return i",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var i = 20; switch(i++) {\n"
" case 20: return 1;\n"
" default: return 2;\n"
"}",
handle(Smi::FromInt(1), isolate)),
};
for (size_t i = 0; i < arraysize(count_ops); i++) {
std::string source(InterpreterTester::SourceForBody(count_ops[i].first));
InterpreterTester tester(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;
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
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(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;
Isolate* isolate = handles.main_isolate();
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),
// Check arguments for duplicate parameters in sloppy mode.
std::make_pair("function f(a, a, b) { return arguments[1]; }", 1),
// check rest parameters
std::make_pair("function f(...restArray) { return restArray[0]; }", 0),
std::make_pair("function f(a, ...restArray) { return restArray[0]; }", 1),
std::make_pair("function f(a, ...restArray) { return arguments[0]; }", 0),
std::make_pair("function f(a, ...restArray) { return arguments[1]; }", 1),
std::make_pair("function f(a, ...restArray) { return restArray[1]; }", 2),
std::make_pair("function f(a, ...arguments) { return arguments[0]; }", 1),
std::make_pair("function f(a, b, ...restArray) { return restArray[0]; }",
2),
};
// Test passing no arguments.
for (size_t i = 0; i < arraysize(create_args); i++) {
InterpreterTester tester(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(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(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;
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(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;
Isolate* isolate = handles.main_isolate();
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(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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(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;
Isolate* isolate = handles.main_isolate();
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(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*loops[i].second));
}
}
TEST(InterpreterForIn) {
std::pair<const char*, int> for_in_samples[] = {
{"var r = -1;\n"
"for (var a in null) { r = a; }\n"
"return r;\n",
-1},
{"var r = -1;\n"
"for (var a in undefined) { r = a; }\n"
"return r;\n",
-1},
{"var r = 0;\n"
"for (var a in [0,6,7,9]) { r = r + (1 << a); }\n"
"return r;\n",
0xf},
{"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},
{"var r = 0;\n"
"for (var a in 'foobar') { r = r + (1 << a); }\n"
"return r;\n",
0x3f},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"var data = {x:23, y:34};\n"
" var result = 0;\n"
"var o = {};\n"
"var arr = [o];\n"
"for (arr[0].p in data)\n" // This is to test if value is loaded
" result += data[arr[0].p];\n" // back from accumulator before storing
"return result;\n", // named properties.
57},
{"var data = {x:23, y:34};\n"
"var result = 0;\n"
"var o = {};\n"
"var i = 0;\n"
"for (o[i++] in data)\n" // This is to test if value is loaded
" result += data[o[i-1]];\n" // back from accumulator before
"return result;\n", // storing keyed properties.
57}};
// Two passes are made for this test. On the first, 8-bit register
// operands are employed, and on the 16-bit register operands are
// used.
for (int pass = 0; pass < 2; pass++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::ostringstream wide_os;
if (pass == 1) {
for (int i = 0; i < 200; i++) {
wide_os << "var local" << i << " = 0;\n";
}
}
for (size_t i = 0; i < arraysize(for_in_samples); i++) {
std::ostringstream body_os;
body_os << wide_os.str() << for_in_samples[i].first;
std::string body(body_os.str());
std::string function = InterpreterTester::SourceForBody(body.c_str());
InterpreterTester tester(isolate, function.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Handle<Smi>::cast(return_val)->value(),
for_in_samples[i].second);
}
}
}
TEST(InterpreterForOf) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> for_of[] = {
{"function f() {\n"
" var r = 0;\n"
" for (var a of [0,6,7,9]) { r += a; }\n"
" return r;\n"
"}",
handle(Smi::FromInt(22), isolate)},
{"function f() {\n"
" var r = '';\n"
" for (var a of 'foobar') { r = a + r; }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("raboof")},
{"function f() {\n"
" var a = [1, 2, 3];\n"
" a.name = 4;\n"
" var r = 0;\n"
" for (var x of a) { r += x; }\n"
" return r;\n"
"}",
handle(Smi::FromInt(6), isolate)},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3]; \n"
" for (a of data) { delete data[0]; r += a; } return r; }",
factory->NewStringFromStaticChars("123")},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3]; \n"
" for (a of data) { delete data[2]; r += a; } return r; }",
factory->NewStringFromStaticChars("12undefined")},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3]; \n"
" for (a of data) { delete data; r += a; } return r; }",
factory->NewStringFromStaticChars("123")},
{"function f() {\n"
" var r = '';\n"
" var input = 'foobar';\n"
" for (var a of input) {\n"
" if (a == 'b') break;\n"
" r += a;\n"
" }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("foo")},
{"function f() {\n"
" var r = '';\n"
" var input = 'foobar';\n"
" for (var a of input) {\n"
" if (a == 'b') continue;\n"
" r += a;\n"
" }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("fooar")},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3, 4]; \n"
" for (a of data) { data[2] = 567; r += a; }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("125674")},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3, 4]; \n"
" for (a of data) { data[4] = 567; r += a; }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("1234567")},
{"function f() {\n"
" var r = '';\n"
" var data = [1, 2, 3, 4]; \n"
" for (a of data) { data[5] = 567; r += a; }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("1234undefined567")},
{"function f() {\n"
" var r = '';\n"
" var obj = new Object();\n"
" obj[Symbol.iterator] = function() { return {\n"
" index: 3,\n"
" data: ['a', 'b', 'c', 'd'],"
" next: function() {"
" return {"
" done: this.index == -1,\n"
" value: this.index < 0 ? undefined : this.data[this.index--]\n"
" }\n"
" }\n"
" }}\n"
" for (a of obj) { r += a }\n"
" return r;\n"
"}",
factory->NewStringFromStaticChars("dcba")},
};
for (size_t i = 0; i < arraysize(for_of); i++) {
InterpreterTester tester(isolate, for_of[i].first);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val->SameValue(*for_of[i].second));
}
}
TEST(InterpreterSwitch) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
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(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*switch_ops[i].second));
}
}
TEST(InterpreterSloppyThis) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> sloppy_this[] = {
std::make_pair("var global_val = 100;\n"
"function f() { return this.global_val; }\n",
handle(Smi::FromInt(100), isolate)),
std::make_pair("var global_val = 110;\n"
"function g() { return this.global_val; };"
"function f() { return g(); }\n",
handle(Smi::FromInt(110), isolate)),
std::make_pair("var global_val = 110;\n"
"function g() { return this.global_val };"
"function f() { 'use strict'; return g(); }\n",
handle(Smi::FromInt(110), isolate)),
std::make_pair("function f() { 'use strict'; return this; }\n",
factory->undefined_value()),
std::make_pair("function g() { 'use strict'; return this; };"
"function f() { return g(); }\n",
factory->undefined_value()),
};
for (size_t i = 0; i < arraysize(sloppy_this); i++) {
InterpreterTester tester(isolate, sloppy_this[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*sloppy_this[i].second));
}
}
TEST(InterpreterThisFunction) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
InterpreterTester tester(isolate,
"var f;\n f = function f() { return f.name; }");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*factory->NewStringFromStaticChars("f")));
}
TEST(InterpreterNewTarget) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// TODO(rmcilroy): Add tests that we get the original constructor for
// superclass constructors once we have class support.
InterpreterTester tester(isolate, "function f() { this.a = new.target; }");
auto callable = tester.GetCallable<>();
callable().ToHandleChecked();
Handle<Object> new_target_name = v8::Utils::OpenHandle(
*CompileRun("(function() { return (new f()).a.name; })();"));
CHECK(new_target_name->SameValue(*factory->NewStringFromStaticChars("f")));
}
TEST(InterpreterAssignmentInExpressions) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, int> samples[] = {
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return y;\n"
"}",
10},
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 55;\n"
" x = x + (x = 100) + (x = 101);\n"
" return x;\n"
"}",
256},
{"function f() {\n"
" var x = 7;\n"
" return ++x + x + x++;\n"
"}",
24},
{"function f() {\n"
" var x = 7;\n"
" var y = 1 + ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7;\n"
" var y = ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7, y = 100, z = 1000;\n"
" return x + (x += 3) + y + (y *= 10) + (z *= 7) + z;\n"
"}",
15117},
{"function f() {\n"
" var inner = function (x) { return x + (x = 2) + (x = 4) + x; };\n"
" return inner(1);\n"
"}",
11},
{"function f() {\n"
" var x = 1, y = 2;\n"
" x = x + (x = 3) + y + (y = 4), y = y + (y = 5) + y + x;\n"
" return x + y;\n"
"}",
10 + 24},
{"function f() {\n"
" var x = 0;\n"
" var y = x | (x = 1) | (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 0;\n"
" var y = x || (x = 1);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" var y = x && (x = 2) && (x = 3);\n"
" return x;\n"
"}",
3},
{"function f() {\n"
" var x = 1;\n"
" var y = x || (x = 2);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" x = (x << (x = 3)) | (x = 16);\n"
" return x;\n"
"}",
24},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" var u = r + s + t + (r = 10) + (s = 20) +"
" (t = (r + s)) + r + s + t;\n"
" return r + s + t + u;\n"
"}",
211},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 1)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
11},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 0)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
27},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return (r + (r = 5)) > s ? r : t;\n"
"}",
5},
{"function f(a) {\n"
" return a + (arguments[0] = 10);\n"
"}",
50},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + a;\n"
"}",
60},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + arguments[0];\n"
"}",
60},
};
const int arg_value = 40;
for (size_t i = 0; i < arraysize(samples); i++) {
InterpreterTester tester(isolate, samples[i].first);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(handle(Smi::FromInt(arg_value), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), samples[i].second);
}
}
TEST(InterpreterToName) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> to_name_tests[] = {
{"var a = 'val'; var obj = {[a] : 10}; return obj.val;",
factory->NewNumberFromInt(10)},
{"var a = 20; var obj = {[a] : 10}; return obj['20'];",
factory->NewNumberFromInt(10)},
{"var a = 20; var obj = {[a] : 10}; return obj[20];",
factory->NewNumberFromInt(10)},
{"var a = {val:23}; var obj = {[a] : 10}; return obj[a];",
factory->NewNumberFromInt(10)},
{"var a = {val:23}; var obj = {[a] : 10};\n"
"return obj['[object Object]'];",
factory->NewNumberFromInt(10)},
{"var a = {toString : function() { return 'x'}};\n"
"var obj = {[a] : 10};\n"
"return obj.x;",
factory->NewNumberFromInt(10)},
{"var a = {valueOf : function() { return 'x'}};\n"
"var obj = {[a] : 10};\n"
"return obj.x;",
factory->undefined_value()},
{"var a = {[Symbol.toPrimitive] : function() { return 'x'}};\n"
"var obj = {[a] : 10};\n"
"return obj.x;",
factory->NewNumberFromInt(10)},
};
for (size_t i = 0; i < arraysize(to_name_tests); i++) {
std::string source(
InterpreterTester::SourceForBody(to_name_tests[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*to_name_tests[i].second));
}
}
TEST(TemporaryRegisterAllocation) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> reg_tests[] = {
{"function add(a, b, c) {"
" return a + b + c;"
"}"
"function f() {"
" var a = 10, b = 10;"
" return add(a, b++, b);"
"}",
factory->NewNumberFromInt(31)},
{"function add(a, b, c, d) {"
" return a + b + c + d;"
"}"
"function f() {"
" var x = 10, y = 20, z = 30;"
" return x + add(x, (y= x++), x, z);"
"}",
factory->NewNumberFromInt(71)},
};
for (size_t i = 0; i < arraysize(reg_tests); i++) {
InterpreterTester tester(isolate, reg_tests[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*reg_tests[i].second));
}
}
TEST(InterpreterLookupSlot) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// TODO(mythria): Add more tests when we have support for eval/with.
const char* function_prologue = "var f;"
"var x = 1;"
"function f1() {"
" eval(\"function t() {";
const char* function_epilogue = " }; f = t;\");"
"}"
"f1();";
std::pair<const char*, Handle<Object>> lookup_slot[] = {
{"return x;", handle(Smi::FromInt(1), isolate)},
{"return typeof x;", factory->NewStringFromStaticChars("number")},
{"return typeof dummy;", factory->NewStringFromStaticChars("undefined")},
{"x = 10; return x;", handle(Smi::FromInt(10), isolate)},
{"'use strict'; x = 20; return x;", handle(Smi::FromInt(20), isolate)},
};
for (size_t i = 0; i < arraysize(lookup_slot); i++) {
std::string script = std::string(function_prologue) +
std::string(lookup_slot[i].first) +
std::string(function_epilogue);
InterpreterTester tester(isolate, script.c_str(), "t");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*lookup_slot[i].second));
}
}
TEST(InterpreterCallLookupSlot) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> call_lookup[] = {
{"g = function(){ return 2 }; eval(''); return g();",
handle(Smi::FromInt(2), isolate)},
{"g = function(){ return 2 }; eval('g = function() {return 3}');\n"
"return g();",
handle(Smi::FromInt(3), isolate)},
{"g = { x: function(){ return this.y }, y: 20 };\n"
"eval('g = { x: g.x, y: 30 }');\n"
"return g.x();",
handle(Smi::FromInt(30), isolate)},
};
for (size_t i = 0; i < arraysize(call_lookup); i++) {
std::string source(InterpreterTester::SourceForBody(call_lookup[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*call_lookup[i].second));
}
}
TEST(InterpreterLookupSlotWide) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
const char* function_prologue =
"var f;"
"var x = 1;"
"function f1() {"
" eval(\"function t() {";
const char* function_epilogue =
" }; f = t;\");"
"}"
"f1();";
std::ostringstream str;
str << "var y = 2.3;";
for (int i = 1; i < 256; i++) {
str << "y = " << 2.3 + i << ";";
}
std::string init_function_body = str.str();
std::pair<std::string, Handle<Object>> lookup_slot[] = {
{init_function_body + "return x;", handle(Smi::FromInt(1), isolate)},
{init_function_body + "return typeof x;",
factory->NewStringFromStaticChars("number")},
{init_function_body + "return x = 10;",
handle(Smi::FromInt(10), isolate)},
{"'use strict';" + init_function_body + "x = 20; return x;",
handle(Smi::FromInt(20), isolate)},
};
for (size_t i = 0; i < arraysize(lookup_slot); i++) {
std::string script = std::string(function_prologue) + lookup_slot[i].first +
std::string(function_epilogue);
InterpreterTester tester(isolate, script.c_str(), "t");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*lookup_slot[i].second));
}
}
TEST(InterpreterDeleteLookupSlot) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// TODO(mythria): Add more tests when we have support for eval/with.
const char* function_prologue = "var f;"
"var x = 1;"
"y = 10;"
"var obj = {val:10};"
"var z = 30;"
"function f1() {"
" var z = 20;"
" eval(\"function t() {";
const char* function_epilogue = " }; f = t;\");"
"}"
"f1();";
std::pair<const char*, Handle<Object>> delete_lookup_slot[] = {
{"return delete x;", factory->false_value()},
{"return delete y;", factory->true_value()},
{"return delete z;", factory->false_value()},
{"return delete obj.val;", factory->true_value()},
{"'use strict'; return delete obj.val;", factory->true_value()},
};
for (size_t i = 0; i < arraysize(delete_lookup_slot); i++) {
std::string script = std::string(function_prologue) +
std::string(delete_lookup_slot[i].first) +
std::string(function_epilogue);
InterpreterTester tester(isolate, script.c_str(), "t");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*delete_lookup_slot[i].second));
}
}
TEST(JumpWithConstantsAndWideConstants) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
const int kStep = 13;
for (int constants = 11; constants < 256 + 3 * kStep; constants += kStep) {
std::ostringstream filler_os;
// Generate a string that consumes constant pool entries and
// spread out branch distances in script below.
for (int i = 0; i < constants; i++) {
filler_os << "var x_ = 'x_" << i << "';\n";
}
std::string filler(filler_os.str());
std::ostringstream script_os;
script_os << "function " << InterpreterTester::function_name() << "(a) {\n";
script_os << " " << filler;
script_os << " for (var i = a; i < 2; i++) {\n";
script_os << " " << filler;
script_os << " if (i == 0) { " << filler << "i = 10; continue; }\n";
script_os << " else if (i == a) { " << filler << "i = 12; break; }\n";
script_os << " else { " << filler << " }\n";
script_os << " }\n";
script_os << " return i;\n";
script_os << "}\n";
std::string script(script_os.str());
for (int a = 0; a < 3; a++) {
InterpreterTester tester(isolate, script.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> argument = factory->NewNumberFromInt(a);
Handle<Object> return_val = callable(argument).ToHandleChecked();
static const int results[] = {11, 12, 2};
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), results[a]);
}
}
}
TEST(InterpreterEval) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> eval[] = {
{"return eval('1;');", handle(Smi::FromInt(1), isolate)},
{"return eval('100 * 20;');", handle(Smi::FromInt(2000), isolate)},
{"var x = 10; return eval('x + 20;');",
handle(Smi::FromInt(30), isolate)},
{"var x = 10; eval('x = 33;'); return x;",
handle(Smi::FromInt(33), isolate)},
{"'use strict'; var x = 20; var z = 0;\n"
"eval('var x = 33; z = x;'); return x + z;",
handle(Smi::FromInt(53), isolate)},
{"eval('var x = 33;'); eval('var y = x + 20'); return x + y;",
handle(Smi::FromInt(86), isolate)},
{"var x = 1; eval('for(i = 0; i < 10; i++) x = x + 1;'); return x",
handle(Smi::FromInt(11), isolate)},
{"var x = 10; eval('var x = 20;'); return x;",
handle(Smi::FromInt(20), isolate)},
{"var x = 1; eval('\"use strict\"; var x = 2;'); return x;",
handle(Smi::FromInt(1), isolate)},
{"'use strict'; var x = 1; eval('var x = 2;'); return x;",
handle(Smi::FromInt(1), isolate)},
{"var x = 10; eval('x + 20;'); return typeof x;",
factory->NewStringFromStaticChars("number")},
{"eval('var y = 10;'); return typeof unallocated;",
factory->NewStringFromStaticChars("undefined")},
{"'use strict'; eval('var y = 10;'); return typeof unallocated;",
factory->NewStringFromStaticChars("undefined")},
{"eval('var x = 10;'); return typeof x;",
factory->NewStringFromStaticChars("number")},
{"var x = {}; eval('var x = 10;'); return typeof x;",
factory->NewStringFromStaticChars("number")},
{"'use strict'; var x = {}; eval('var x = 10;'); return typeof x;",
factory->NewStringFromStaticChars("object")},
};
for (size_t i = 0; i < arraysize(eval); i++) {
std::string source(InterpreterTester::SourceForBody(eval[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*eval[i].second));
}
}
TEST(InterpreterEvalParams) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> eval_params[] = {
{"var x = 10; return eval('x + p1;');",
handle(Smi::FromInt(30), isolate)},
{"var x = 10; eval('p1 = x;'); return p1;",
handle(Smi::FromInt(10), isolate)},
{"var a = 10;"
"function inner() { return eval('a + p1;');}"
"return inner();",
handle(Smi::FromInt(30), isolate)},
};
for (size_t i = 0; i < arraysize(eval_params); i++) {
std::string source = "function " + InterpreterTester::function_name() +
"(p1) {" + eval_params[i].first + "}";
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<i::Object> return_value =
callable(handle(Smi::FromInt(20), isolate)).ToHandleChecked();
CHECK(return_value->SameValue(*eval_params[i].second));
}
}
TEST(InterpreterEvalGlobal) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> eval_global[] = {
{"function add_global() { eval('function test() { z = 33; }; test()'); };"
"function f() { add_global(); return z; }; f();",
handle(Smi::FromInt(33), isolate)},
{"function add_global() {\n"
" eval('\"use strict\"; function test() { y = 33; };"
" try { test() } catch(e) {}');\n"
"}\n"
"function f() { add_global(); return typeof y; } f();",
factory->NewStringFromStaticChars("undefined")},
};
for (size_t i = 0; i < arraysize(eval_global); i++) {
InterpreterTester tester(isolate, eval_global[i].first, "test");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*eval_global[i].second));
}
}
TEST(InterpreterEvalVariableDecl) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> eval_global[] = {
{"function f() { eval('var x = 10; x++;'); return x; }",
handle(Smi::FromInt(11), isolate)},
{"function f() { var x = 20; eval('var x = 10; x++;'); return x; }",
handle(Smi::FromInt(11), isolate)},
{"function f() {"
" var x = 20;"
" eval('\"use strict\"; var x = 10; x++;');"
" return x; }",
handle(Smi::FromInt(20), isolate)},
{"function f() {"
" var y = 30;"
" eval('var x = {1:20}; x[2]=y;');"
" return x[2]; }",
handle(Smi::FromInt(30), isolate)},
{"function f() {"
" eval('var x = {name:\"test\"};');"
" return x.name; }",
factory->NewStringFromStaticChars("test")},
{"function f() {"
" eval('var x = [{name:\"test\"}, {type:\"cc\"}];');"
" return x[1].type+x[0].name; }",
factory->NewStringFromStaticChars("cctest")},
{"function f() {\n"
" var x = 3;\n"
" var get_eval_x;\n"
" eval('\"use strict\"; "
" var x = 20; "
" get_eval_x = function func() {return x;};');\n"
" return get_eval_x() + x;\n"
"}",
handle(Smi::FromInt(23), isolate)},
// TODO(mythria): Add tests with const declarations.
};
for (size_t i = 0; i < arraysize(eval_global); i++) {
InterpreterTester tester(isolate, eval_global[i].first, "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*eval_global[i].second));
}
}
TEST(InterpreterEvalFunctionDecl) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> eval_func_decl[] = {
{"function f() {\n"
" var x = 3;\n"
" eval('var x = 20;"
" function get_x() {return x;};');\n"
" return get_x() + x;\n"
"}",
handle(Smi::FromInt(40), isolate)},
};
for (size_t i = 0; i < arraysize(eval_func_decl); i++) {
InterpreterTester tester(isolate, eval_func_decl[i].first, "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*eval_func_decl[i].second));
}
}
TEST(InterpreterWideRegisterArithmetic) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
static const size_t kMaxRegisterForTest = 150;
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " var retval = -77;\n";
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
os << " var x" << i << " = " << i << ";\n";
}
for (size_t i = 0; i < kMaxRegisterForTest / 2; i++) {
size_t j = kMaxRegisterForTest - i - 1;
os << " var tmp = x" << j << ";\n";
os << " var x" << j << " = x" << i << ";\n";
os << " var x" << i << " = tmp;\n";
}
for (size_t i = 0; i < kMaxRegisterForTest / 2; i++) {
size_t j = kMaxRegisterForTest - i - 1;
os << " var tmp = x" << j << ";\n";
os << " var x" << j << " = x" << i << ";\n";
os << " var x" << i << " = tmp;\n";
}
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
os << " if (arg == " << i << ") {\n" //
<< " retval = x" << i << ";\n" //
<< " }\n"; //
}
os << " return retval;\n";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
Handle<Object> arg = handle(Smi::FromInt(static_cast<int>(i)), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
CHECK(return_value->SameValue(*arg));
}
}
TEST(InterpreterCallWideRegisters) {
static const int kPeriod = 25;
static const int kLength = 512;
static const int kStartChar = 65;
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
for (int pass = 0; pass < 3; pass += 1) {
std::ostringstream os;
for (int i = 0; i < pass * 97; i += 1) {
os << "var x" << i << " = " << i << "\n";
}
os << "return String.fromCharCode(";
os << kStartChar;
for (int i = 1; i < kLength; i += 1) {
os << "," << kStartChar + (i % kPeriod);
}
os << ");";
std::string source = InterpreterTester::SourceForBody(os.str().c_str());
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable();
Handle<Object> return_val = callable().ToHandleChecked();
Handle<String> return_string = Handle<String>::cast(return_val);
CHECK_EQ(return_string->length(), kLength);
for (int i = 0; i < kLength; i += 1) {
CHECK_EQ(return_string->Get(i), 65 + (i % kPeriod));
}
}
}
TEST(InterpreterWideParametersPickOne) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
static const int kParameterCount = 130;
for (int parameter = 0; parameter < 10; parameter++) {
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " function selector(i";
for (int i = 0; i < kParameterCount; i++) {
os << ","
<< "a" << i;
}
os << ") {\n";
os << " return a" << parameter << ";\n";
os << " };\n";
os << " return selector(arg";
for (int i = 0; i < kParameterCount; i++) {
os << "," << i;
}
os << ");";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(isolate, source.c_str(), "*");
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> arg = handle(Smi::FromInt(0xaa55), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
Handle<Smi> actual = Handle<Smi>::cast(return_value);
CHECK_EQ(actual->value(), parameter);
}
}
TEST(InterpreterWideParametersSummation) {
static int kParameterCount = 200;
static int kBaseValue = 17000;
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " function summation(i";
for (int i = 0; i < kParameterCount; i++) {
os << ","
<< "a" << i;
}
os << ") {\n";
os << " var sum = " << kBaseValue << ";\n";
os << " switch(i) {\n";
for (int i = 0; i < kParameterCount; i++) {
int j = kParameterCount - i - 1;
os << " case " << j << ": sum += a" << j << ";\n";
}
os << " }\n";
os << " return sum;\n";
os << " };\n";
os << " return summation(arg";
for (int i = 0; i < kParameterCount; i++) {
os << "," << i;
}
os << ");";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(isolate, source.c_str(), "*");
auto callable = tester.GetCallable<Handle<Object>>();
for (int i = 0; i < kParameterCount; i++) {
Handle<Object> arg = handle(Smi::FromInt(i), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
int expected = kBaseValue + i * (i + 1) / 2;
Handle<Smi> actual = Handle<Smi>::cast(return_value);
CHECK_EQ(actual->value(), expected);
}
}
TEST(InterpreterDoExpression) {
bool old_flag = FLAG_harmony_do_expressions;
FLAG_harmony_do_expressions = true;
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> do_expr[] = {
{"var a = do {}; return a;", factory->undefined_value()},
{"var a = do { var x = 100; }; return a;", factory->undefined_value()},
{"var a = do { var x = 100; }; return a;", factory->undefined_value()},
{"var a = do { var x = 100; x++; }; return a;",
handle(Smi::FromInt(100), isolate)},
{"var i = 0; for (; i < 5;) { i = do { if (i == 3) { break; }; i + 1; }};"
"return i;",
handle(Smi::FromInt(3), isolate)},
};
for (size_t i = 0; i < arraysize(do_expr); i++) {
std::string source(InterpreterTester::SourceForBody(do_expr[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*do_expr[i].second));
}
FLAG_harmony_do_expressions = old_flag;
}
TEST(InterpreterWithStatement) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> with_stmt[] = {
{"with({x:42}) return x;", handle(Smi::FromInt(42), isolate)},
{"with({}) { var y = 10; return y;}", handle(Smi::FromInt(10), isolate)},
{"var y = {x:42};"
" function inner() {"
" var x = 20;"
" with(y) return x;"
"}"
"return inner();",
handle(Smi::FromInt(42), isolate)},
{"var y = {x:42};"
" function inner(o) {"
" var x = 20;"
" with(o) return x;"
"}"
"return inner(y);",
handle(Smi::FromInt(42), isolate)},
};
for (size_t i = 0; i < arraysize(with_stmt); i++) {
std::string source(InterpreterTester::SourceForBody(with_stmt[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*with_stmt[i].second));
}
}
TEST(InterpreterClassLiterals) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> examples[] = {
{"class C {\n"
" constructor(x) { this.x_ = x; }\n"
" method() { return this.x_; }\n"
"}\n"
"return new C(99).method();",
handle(Smi::FromInt(99), isolate)},
{"class C {\n"
" constructor(x) { this.x_ = x; }\n"
" static static_method(x) { return x; }\n"
"}\n"
"return C.static_method(101);",
handle(Smi::FromInt(101), isolate)},
{"class C {\n"
" get x() { return 102; }\n"
"}\n"
"return new C().x",
handle(Smi::FromInt(102), isolate)},
{"class C {\n"
" static get x() { return 103; }\n"
"}\n"
"return C.x",
handle(Smi::FromInt(103), isolate)},
{"class C {\n"
" constructor() { this.x_ = 0; }"
" set x(value) { this.x_ = value; }\n"
" get x() { return this.x_; }\n"
"}\n"
"var c = new C();"
"c.x = 104;"
"return c.x;",
handle(Smi::FromInt(104), isolate)},
{"var x = 0;"
"class C {\n"
" static set x(value) { x = value; }\n"
" static get x() { return x; }\n"
"}\n"
"C.x = 105;"
"return C.x;",
handle(Smi::FromInt(105), isolate)},
{"var method = 'f';"
"class C {\n"
" [method]() { return 106; }\n"
"}\n"
"return new C().f();",
handle(Smi::FromInt(106), isolate)},
};
for (size_t i = 0; i < arraysize(examples); ++i) {
std::string source(InterpreterTester::SourceForBody(examples[i].first));
InterpreterTester tester(isolate, source.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*examples[i].second));
}
}
TEST(InterpreterClassAndSuperClass) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> examples[] = {
{"class A {\n"
" constructor(x) { this.x_ = x; }\n"
" method() { return this.x_; }\n"
"}\n"
"class B extends A {\n"
" constructor(x, y) { super(x); this.y_ = y; }\n"
" method() { return super.method() + 1; }\n"
"}\n"
"return new B(998, 0).method();\n",
handle(Smi::FromInt(999), isolate)},
{"class A {\n"
" constructor() { this.x_ = 2; this.y_ = 3; }\n"
"}\n"
"class B extends A {\n"
" constructor() { super(); }"
" method() { this.x_++; this.y_++; return this.x_ + this.y_; }\n"
"}\n"
"return new B().method();\n",
handle(Smi::FromInt(7), isolate)},
{"var calls = 0;\n"
"class B {}\n"
"B.prototype.x = 42;\n"
"class C extends B {\n"
" constructor() {\n"
" super();\n"
" calls++;\n"
" }\n"
"}\n"
"new C;\n"
"return calls;\n",
handle(Smi::FromInt(1), isolate)},
{"class A {\n"
" method() { return 1; }\n"
" get x() { return 2; }\n"
"}\n"
"class B extends A {\n"
" method() { return super.x === 2 ? super.method() : -1; }\n"
"}\n"
"return new B().method();\n",
handle(Smi::FromInt(1), isolate)},
{"var object = { setY(v) { super.y = v; }};\n"
"object.setY(10);\n"
"return object.y;\n",
handle(Smi::FromInt(10), isolate)},
};
for (size_t i = 0; i < arraysize(examples); ++i) {
std::string source(InterpreterTester::SourceForBody(examples[i].first));
InterpreterTester tester(isolate, source.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*examples[i].second));
}
}
TEST(InterpreterConstDeclaration) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> const_decl[] = {
{"const x = 3; return x;", handle(Smi::FromInt(3), isolate)},
{"let x = 10; x = x + 20; return x;", handle(Smi::FromInt(30), isolate)},
{"let x = 10; x = 20; return x;", handle(Smi::FromInt(20), isolate)},
{"let x; x = 20; return x;", handle(Smi::FromInt(20), isolate)},
{"let x; return x;", factory->undefined_value()},
{"var x = 10; { let x = 30; } return x;",
handle(Smi::FromInt(10), isolate)},
{"let x = 10; { let x = 20; } return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; eval('let x = 20;'); return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; eval('const x = 20;'); return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; { const x = 20; } return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; { const x = 20; return x;} return -1;",
handle(Smi::FromInt(20), isolate)},
{"var a = 10;\n"
"for (var i = 0; i < 10; ++i) {\n"
" const x = i;\n" // const declarations are block scoped.
" a = a + x;\n"
"}\n"
"return a;\n",
handle(Smi::FromInt(55), isolate)},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
}
TEST(InterpreterConstDeclarationLookupSlots) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> const_decl[] = {
{"const x = 3; function f1() {return x;}; return x;",
handle(Smi::FromInt(3), isolate)},
{"let x = 10; x = x + 20; function f1() {return x;}; return x;",
handle(Smi::FromInt(30), isolate)},
{"let x; x = 20; function f1() {return x;}; return x;",
handle(Smi::FromInt(20), isolate)},
{"let x; function f1() {return x;}; return x;",
factory->undefined_value()},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
}
TEST(InterpreterConstInLookupContextChain) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
const char* prologue =
"function OuterMost() {\n"
" const outerConst = 10;\n"
" let outerLet = 20;\n"
" function Outer() {\n"
" function Inner() {\n"
" this.innerFunc = function() { ";
const char* epilogue =
" }\n"
" }\n"
" this.getInnerFunc ="
" function() {return new Inner().innerFunc;}\n"
" }\n"
" this.getOuterFunc ="
" function() {return new Outer().getInnerFunc();}"
"}\n"
"var f = new OuterMost().getOuterFunc();\n"
"f();\n";
std::pair<const char*, Handle<Object>> const_decl[] = {
{"return outerConst;", handle(Smi::FromInt(10), isolate)},
{"return outerLet;", handle(Smi::FromInt(20), isolate)},
{"outerLet = 30; return outerLet;", handle(Smi::FromInt(30), isolate)},
{"var outerLet = 40; return outerLet;",
handle(Smi::FromInt(40), isolate)},
{"var outerConst = 50; return outerConst;",
handle(Smi::FromInt(50), isolate)},
{"try { outerConst = 30 } catch(e) { return -1; }",
handle(Smi::FromInt(-1), isolate)}};
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string script = std::string(prologue) +
std::string(const_decl[i].first) +
std::string(epilogue);
InterpreterTester tester(isolate, script.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
}
TEST(InterpreterIllegalConstDeclaration) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
std::pair<const char*, const char*> const_decl[] = {
{"const x = x = 10 + 3; return x;",
"Uncaught ReferenceError: x is not defined"},
{"const x = 10; x = 20; return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"const x = 10; { x = 20; } return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"const x = 10; eval('x = 20;'); return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"let x = x + 10; return x;",
"Uncaught ReferenceError: x is not defined"},
{"'use strict'; (function f1() { f1 = 123; })() ",
"Uncaught TypeError: Assignment to constant variable."},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(isolate, source.c_str());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string = v8_str(const_decl[i].second);
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(isolate, source.c_str());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string = v8_str(const_decl[i].second);
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
}
TEST(InterpreterGenerators) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> tests[] = {
{"function* f() { }; return f().next().value",
factory->undefined_value()},
{"function* f() { yield 42 }; return f().next().value",
factory->NewNumberFromInt(42)},
{"function* f() { for (let x of [42]) yield x}; return f().next().value",
factory->NewNumberFromInt(42)},
};
for (size_t i = 0; i < arraysize(tests); i++) {
std::string source(InterpreterTester::SourceForBody(tests[i].first));
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*tests[i].second));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8