Commit 32879ae0 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for calling eval.

Adds support for calling eval to the interpreter.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1508293003

Cr-Commit-Position: refs/heads/master@{#33184}
parent 0406fa22
......@@ -2465,8 +2465,8 @@ void AstGraphBuilder::VisitCall(Call* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args);
// Resolve callee and receiver for a potential direct eval call. This block
// will mutate the callee and receiver values pushed onto the environment.
// Resolve callee for a potential direct eval call. This block will mutate the
// callee value pushed onto the environment.
if (possibly_eval && args->length() > 0) {
int arg_count = args->length();
......
......@@ -1615,6 +1615,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
execution_result()->PrepareForConsecutiveAllocations(args->length() + 1);
Register receiver = execution_result()->NextConsecutiveRegister();
bool possibly_eval = false;
switch (call_type) {
case Call::NAMED_PROPERTY_CALL:
case Call::KEYED_PROPERTY_CALL: {
......@@ -1635,6 +1636,31 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->StoreAccumulatorInRegister(callee);
break;
}
case Call::POSSIBLY_EVAL_CALL: {
possibly_eval = true;
if (callee_expr->AsVariableProxy()->var()->IsLookupSlot()) {
TemporaryRegisterScope temporary_register_scope(builder());
temporary_register_scope.PrepareForConsecutiveAllocations(2);
Register context = temporary_register_scope.NextConsecutiveRegister();
Register name = temporary_register_scope.NextConsecutiveRegister();
Variable* variable = callee_expr->AsVariableProxy()->var();
builder()
->MoveRegister(Register::function_context(), context)
.LoadLiteral(variable->name())
.StoreAccumulatorInRegister(name);
// Call LoadLookupSlot to get the callee and receiver. Reuse the context
// and name arguments as the return registers (since these are
// consecutive), and them move into callee and receiver registers.
builder()
->CallRuntimeForPair(Runtime::kLoadLookupSlot, context, 2, context)
.MoveRegister(context, callee)
.MoveRegister(name, receiver);
break;
}
// Fall through.
}
case Call::OTHER_CALL: {
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
VisitForAccumulatorValue(callee_expr);
......@@ -1645,7 +1671,6 @@ void BytecodeGenerator::VisitCall(Call* expr) {
case Call::KEYED_SUPER_PROPERTY_CALL:
case Call::LOOKUP_SLOT_CALL:
case Call::SUPER_CALL:
case Call::POSSIBLY_EVAL_CALL:
UNIMPLEMENTED();
}
......@@ -1654,7 +1679,37 @@ void BytecodeGenerator::VisitCall(Call* expr) {
Register arg = VisitArguments(args);
CHECK(args->length() == 0 || arg.index() == receiver.index() + 1);
// TODO(rmcilroy): Deal with possible direct eval here?
// Resolve callee for a potential direct eval call. This block will mutate the
// callee value.
if (possibly_eval && args->length() > 0) {
TemporaryRegisterScope temporary_register_scope(builder());
temporary_register_scope.PrepareForConsecutiveAllocations(5);
Register callee_for_eval =
temporary_register_scope.NextConsecutiveRegister();
Register source = temporary_register_scope.NextConsecutiveRegister();
Register function = temporary_register_scope.NextConsecutiveRegister();
Register language = temporary_register_scope.NextConsecutiveRegister();
Register position = temporary_register_scope.NextConsecutiveRegister();
// Set up arguments for ResolvePossiblyDirectEval by copying callee, source
// strings and function closure, and loading language and
// position.
builder()
->MoveRegister(callee, callee_for_eval)
.MoveRegister(arg, source)
.MoveRegister(Register::function_closure(), function)
.LoadLiteral(Smi::FromInt(language_mode()))
.StoreAccumulatorInRegister(language)
.LoadLiteral(
Smi::FromInt(execution_context()->scope()->start_position()))
.StoreAccumulatorInRegister(position);
// Call ResolvePossiblyDirectEval and modify the callee.
builder()
->CallRuntime(Runtime::kResolvePossiblyDirectEval, callee_for_eval, 5)
.StoreAccumulatorInRegister(callee);
}
// TODO(rmcilroy): Use CallIC to allow call type feedback.
builder()->Call(callee, receiver, args->length(),
feedback_index(expr->CallFeedbackICSlot()));
......@@ -2052,7 +2107,12 @@ void BytecodeGenerator::VisitBuildLocalActivationContext() {
Scope* scope = this->scope();
if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
UNIMPLEMENTED();
Variable* variable = scope->receiver();
Register receiver(builder()->Parameter(0));
// Context variable (at bottom of the context chain).
DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
builder()->LoadAccumulatorWithRegister(receiver).StoreContextSlot(
execution_context()->reg(), variable->index());
}
// Copy parameters into context if necessary.
......
......@@ -25,6 +25,7 @@ class BytecodeGeneratorHelper {
BytecodeGeneratorHelper() {
i::FLAG_ignition = true;
i::FLAG_ignition_fake_try_catch = true;
i::FLAG_ignition_fallback_on_eval_and_catch = false;
i::FLAG_ignition_filter = StrDup(kFunctionName);
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
......@@ -1241,7 +1242,6 @@ TEST(PropertyCall) {
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddCallICSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot();
USE(slot1);
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
......@@ -1556,7 +1556,6 @@ TEST(CallGlobal) {
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddCallICSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddLoadICSlot();
USE(slot1);
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
......@@ -5843,6 +5842,205 @@ TEST(AssignmentsInBinaryExpression) {
}
TEST(Eval) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
int closure = Register::function_closure().index();
int context = Register::function_context().index();
int new_target = Register::new_target().index();
int first_context_slot = Context::MIN_CONTEXT_SLOTS;
ExpectedSnippet<const char*> snippets[] = {
{"return eval('1;');",
9 * kPointerSize,
1,
73,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(0), //
B(Ldar), THIS(1), //
B(StaContextSlot), R(0), U8(first_context_slot), //
B(CreateMappedArguments), //
B(StaContextSlot), R(0), U8(first_context_slot + 1), //
B(Ldar), R(new_target), //
B(StaContextSlot), R(0), U8(first_context_slot + 2), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
B(Mov), R(3), R(5), //
B(Mov), R(closure), R(6), //
B(LdaZero), //
B(Star), R(7), //
B(LdaSmi8), U8(10), //
B(Star), R(8), //
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), //
U8(5), //
B(Star), R(1), //
B(Call), R(1), R(2), U8(1), U8(0), //
B(Return), //
},
2,
{"eval", "1;"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LookupSlot) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int closure = Register::function_closure().index();
int first_context_slot = Context::MIN_CONTEXT_SLOTS;
int context = Register::function_context().index();
int new_target = Register::new_target().index();
ExpectedSnippet<const char*> snippets[] = {
{"eval('var x = 10;'); return x;",
9 * kPointerSize,
1,
75,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(0), //
B(Ldar), THIS(1), //
B(StaContextSlot), R(0), U8(first_context_slot), //
B(CreateMappedArguments), //
B(StaContextSlot), R(0), U8(first_context_slot + 1), //
B(Ldar), R(new_target), //
B(StaContextSlot), R(0), U8(first_context_slot + 2), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
B(Mov), R(3), R(5), //
B(Mov), R(closure), R(6), //
B(LdaZero), //
B(Star), R(7), //
B(LdaSmi8), U8(10), //
B(Star), R(8), //
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), //
U8(5), //
B(Star), R(1), //
B(Call), R(1), R(2), U8(1), U8(0), //
B(LdaLookupSlot), U8(2), //
B(Return), //
},
3,
{"eval", "var x = 10;", "x"}},
{"eval('var x = 10;'); return typeof x;",
9 * kPointerSize,
1,
76,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(0), //
B(Ldar), THIS(1), //
B(StaContextSlot), R(0), U8(first_context_slot), //
B(CreateMappedArguments), //
B(StaContextSlot), R(0), U8(first_context_slot + 1), //
B(Ldar), R(new_target), //
B(StaContextSlot), R(0), U8(first_context_slot + 2), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
B(Mov), R(3), R(5), //
B(Mov), R(closure), R(6), //
B(LdaZero), //
B(Star), R(7), //
B(LdaSmi8), U8(10), //
B(Star), R(8), //
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), //
U8(5), //
B(Star), R(1), //
B(Call), R(1), R(2), U8(1), U8(0), //
B(LdaLookupSlotInsideTypeof), U8(2), //
B(TypeOf), //
B(Return), //
},
3,
{"eval", "var x = 10;", "x"}},
{"x = 20; return eval('');",
9 * kPointerSize,
1,
77,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(0), //
B(Ldar), THIS(1), //
B(StaContextSlot), R(0), U8(first_context_slot), //
B(CreateMappedArguments), //
B(StaContextSlot), R(0), U8(first_context_slot + 1), //
B(Ldar), R(new_target), //
B(StaContextSlot), R(0), U8(first_context_slot + 2), //
B(LdaSmi8), U8(20), //
B(StaLookupSlotSloppy), U8(0), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(1), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
B(LdaConstant), U8(2), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
B(Mov), R(3), R(5), //
B(Mov), R(closure), R(6), //
B(LdaZero), //
B(Star), R(7), //
B(LdaSmi8), U8(10), //
B(Star), R(8), //
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), //
U8(5), //
B(Star), R(1), //
B(Call), R(1), R(2), U8(1), U8(0), //
B(Return), //
},
3,
{"x", "eval", ""}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LookupSlotInEval) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......@@ -6013,7 +6211,7 @@ TEST(LookupSlotWideInEval) {
}
TEST(DeleteLookupSlot) {
TEST(DeleteLookupSlotInEval) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......
......@@ -66,6 +66,7 @@ class InterpreterTester {
feedback_vector_(feedback_vector) {
i::FLAG_ignition = true;
i::FLAG_ignition_fake_try_catch = true;
i::FLAG_ignition_fallback_on_eval_and_catch = false;
i::FLAG_always_opt = false;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
......@@ -3195,6 +3196,40 @@ TEST(InterpreterToName) {
}
TEST(TemporaryRegisterAllocation) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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;
i::Isolate* isolate = handles.main_isolate();
......@@ -3276,40 +3311,6 @@ TEST(InterpreterLookupSlotWide) {
}
TEST(TemporaryRegisterAllocation) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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(InterpreterDeleteLookupSlot) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
......@@ -3387,6 +3388,112 @@ TEST(JumpWithConstantsAndWideConstants) {
}
}
TEST(InterpreterEval) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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;
i::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(handles.main_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;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment