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.
......
......@@ -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