Make the ResolvePossiblyDirectEval faster by avoiding the
stack traversal code. Review URL: http://codereview.chromium.org/523051 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3533 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
b74daccff0
commit
15fe7a8ad6
@ -2892,13 +2892,15 @@ void CodeGenerator::VisitCall(Call* node) {
|
||||
frame_->EmitPush(r2);
|
||||
}
|
||||
|
||||
// Push the receiver.
|
||||
__ ldr(r1, frame_->Receiver());
|
||||
frame_->EmitPush(r1);
|
||||
|
||||
// Resolve the call.
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
|
||||
|
||||
// Touch up stack with the right values for the function and the receiver.
|
||||
__ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize));
|
||||
__ str(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize + kPointerSize));
|
||||
__ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ str(r1, MemOperand(sp, arg_count * kPointerSize));
|
||||
|
||||
// Call the function.
|
||||
|
@ -992,6 +992,7 @@ void Genesis::InstallNativeFunctions() {
|
||||
INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun);
|
||||
INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun);
|
||||
INSTALL_NATIVE(JSFunction, "ToBoolean", to_boolean_fun);
|
||||
INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun);
|
||||
INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun);
|
||||
INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance",
|
||||
configure_instance_fun);
|
||||
|
@ -77,6 +77,7 @@ enum ContextLookupFlags {
|
||||
V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
|
||||
V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
|
||||
V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \
|
||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||
V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
|
||||
V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
|
||||
V(FUNCTION_MAP_INDEX, Map, function_map) \
|
||||
@ -202,6 +203,7 @@ class Context: public FixedArray {
|
||||
TO_UINT32_FUN_INDEX,
|
||||
TO_INT32_FUN_INDEX,
|
||||
TO_BOOLEAN_FUN_INDEX,
|
||||
GLOBAL_EVAL_FUN_INDEX,
|
||||
INSTANTIATE_FUN_INDEX,
|
||||
CONFIGURE_INSTANCE_FUN_INDEX,
|
||||
SPECIAL_FUNCTION_TABLE_INDEX,
|
||||
|
@ -4580,22 +4580,19 @@ void CodeGenerator::VisitCall(Call* node) {
|
||||
frame_->Push(Factory::undefined_value());
|
||||
}
|
||||
|
||||
// Push the receiver.
|
||||
frame_->PushParameterAt(-1);
|
||||
|
||||
// Resolve the call.
|
||||
Result result =
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
|
||||
|
||||
// Touch up the stack with the right values for the function and the
|
||||
// receiver. Use a scratch register to avoid destroying the result.
|
||||
Result scratch = allocator_->Allocate();
|
||||
ASSERT(scratch.is_valid());
|
||||
__ mov(scratch.reg(), FieldOperand(result.reg(), FixedArray::kHeaderSize));
|
||||
frame_->SetElementAt(arg_count + 1, &scratch);
|
||||
|
||||
// We can reuse the result register now.
|
||||
frame_->Spill(result.reg());
|
||||
__ mov(result.reg(),
|
||||
FieldOperand(result.reg(), FixedArray::kHeaderSize + kPointerSize));
|
||||
frame_->SetElementAt(arg_count, &result);
|
||||
// The runtime call returns a pair of values in eax (function) and
|
||||
// edx (receiver). Touch up the stack with the right values.
|
||||
Result receiver = allocator_->Allocate(edx);
|
||||
frame_->SetElementAt(arg_count + 1, &result);
|
||||
frame_->SetElementAt(arg_count, &receiver);
|
||||
receiver.Unuse();
|
||||
|
||||
// Call the function.
|
||||
CodeForSourcePosition(node->position());
|
||||
|
112
src/runtime.cc
112
src/runtime.cc
@ -5233,51 +5233,31 @@ static Object* Runtime_CompileString(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSFunction> GetBuiltinFunction(String* name) {
|
||||
LookupResult result;
|
||||
Top::global_context()->builtins()->LocalLookup(name, &result);
|
||||
return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
|
||||
}
|
||||
static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
|
||||
ASSERT(args.length() == 3);
|
||||
if (!args[0]->IsJSFunction()) {
|
||||
return MakePair(Top::ThrowIllegalOperation(), NULL);
|
||||
}
|
||||
|
||||
|
||||
static Object* CompileDirectEval(Handle<String> source) {
|
||||
// Compute the eval context.
|
||||
HandleScope scope;
|
||||
Handle<JSFunction> callee = args.at<JSFunction>(0);
|
||||
Handle<Object> receiver; // Will be overwritten.
|
||||
|
||||
// Compute the calling context.
|
||||
Handle<Context> context = Handle<Context>(Top::context());
|
||||
#ifdef DEBUG
|
||||
// Make sure Top::context() agrees with the old code that traversed
|
||||
// the stack frames to compute the context.
|
||||
StackFrameLocator locator;
|
||||
JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
|
||||
Handle<Context> context(Context::cast(frame->context()));
|
||||
bool is_global = context->IsGlobalContext();
|
||||
|
||||
// Compile source string in the current context.
|
||||
Handle<JSFunction> boilerplate = Compiler::CompileEval(
|
||||
source,
|
||||
context,
|
||||
is_global,
|
||||
Compiler::DONT_VALIDATE_JSON);
|
||||
if (boilerplate.is_null()) return Failure::Exception();
|
||||
Handle<JSFunction> fun =
|
||||
Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
|
||||
return *fun;
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
|
||||
ASSERT(args.length() == 2);
|
||||
|
||||
HandleScope scope;
|
||||
|
||||
CONVERT_ARG_CHECKED(JSFunction, callee, 0);
|
||||
|
||||
Handle<Object> receiver;
|
||||
ASSERT(Context::cast(frame->context()) == *context);
|
||||
#endif
|
||||
|
||||
// Find where the 'eval' symbol is bound. It is unaliased only if
|
||||
// it is bound in the global context.
|
||||
StackFrameLocator locator;
|
||||
JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
|
||||
Handle<Context> context(Context::cast(frame->context()));
|
||||
int index;
|
||||
PropertyAttributes attributes;
|
||||
while (!context.is_null()) {
|
||||
int index = -1;
|
||||
PropertyAttributes attributes = ABSENT;
|
||||
while (true) {
|
||||
receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
|
||||
&index, &attributes);
|
||||
// Stop search when eval is found or when the global context is
|
||||
@ -5296,46 +5276,42 @@ static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
|
||||
Handle<Object> name = Factory::eval_symbol();
|
||||
Handle<Object> reference_error =
|
||||
Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
|
||||
return Top::Throw(*reference_error);
|
||||
return MakePair(Top::Throw(*reference_error), NULL);
|
||||
}
|
||||
|
||||
if (context->IsGlobalContext()) {
|
||||
// 'eval' is bound in the global context, but it may have been overwritten.
|
||||
// Compare it to the builtin 'GlobalEval' function to make sure.
|
||||
Handle<JSFunction> global_eval =
|
||||
GetBuiltinFunction(Heap::global_eval_symbol());
|
||||
if (global_eval.is_identical_to(callee)) {
|
||||
// A direct eval call.
|
||||
if (args[1]->IsString()) {
|
||||
CONVERT_ARG_CHECKED(String, source, 1);
|
||||
// A normal eval call on a string. Compile it and return the
|
||||
// compiled function bound in the local context.
|
||||
Object* compiled_source = CompileDirectEval(source);
|
||||
if (compiled_source->IsFailure()) return compiled_source;
|
||||
receiver = Handle<Object>(frame->receiver());
|
||||
callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
|
||||
} else {
|
||||
// An eval call that is not called on a string. Global eval
|
||||
// deals better with this.
|
||||
receiver = Handle<Object>(Top::global_context()->global());
|
||||
}
|
||||
} else {
|
||||
// 'eval' is overwritten. Just call the function with the given arguments.
|
||||
receiver = Handle<Object>(Top::global_context()->global());
|
||||
}
|
||||
} else {
|
||||
if (!context->IsGlobalContext()) {
|
||||
// 'eval' is not bound in the global context. Just call the function
|
||||
// with the given arguments. This is not necessarily the global eval.
|
||||
if (receiver->IsContext()) {
|
||||
context = Handle<Context>::cast(receiver);
|
||||
receiver = Handle<Object>(context->get(index));
|
||||
} else if (receiver->IsJSContextExtensionObject()) {
|
||||
receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
|
||||
}
|
||||
return MakePair(*callee, *receiver);
|
||||
}
|
||||
|
||||
Handle<FixedArray> call = Factory::NewFixedArray(2);
|
||||
call->set(0, *callee);
|
||||
call->set(1, *receiver);
|
||||
return *call;
|
||||
// 'eval' is bound in the global context, but it may have been overwritten.
|
||||
// Compare it to the builtin 'GlobalEval' function to make sure.
|
||||
if (*callee != Top::global_context()->global_eval_fun() ||
|
||||
!args[1]->IsString()) {
|
||||
return MakePair(*callee, Top::context()->global()->global_receiver());
|
||||
}
|
||||
|
||||
// Deal with a normal eval call with a string argument. Compile it
|
||||
// and return the compiled function bound in the local context.
|
||||
Handle<String> source = args.at<String>(1);
|
||||
Handle<JSFunction> boilerplate = Compiler::CompileEval(
|
||||
source,
|
||||
Handle<Context>(Top::context()),
|
||||
Top::context()->IsGlobalContext(),
|
||||
Compiler::DONT_VALIDATE_JSON);
|
||||
if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
|
||||
callee = Factory::NewFunctionFromBoilerplate(
|
||||
boilerplate,
|
||||
Handle<Context>(Top::context()),
|
||||
NOT_TENURED);
|
||||
return MakePair(*callee, args[2]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,7 +202,7 @@ namespace internal {
|
||||
\
|
||||
/* Eval */ \
|
||||
F(GlobalReceiver, 1, 1) \
|
||||
F(ResolvePossiblyDirectEval, 2, 1) \
|
||||
F(ResolvePossiblyDirectEval, 3, 1) \
|
||||
\
|
||||
F(SetProperty, -1 /* 3 or 4 */, 1) \
|
||||
F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */, 1) \
|
||||
|
@ -236,7 +236,7 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) {
|
||||
|
||||
Variable* Scope::DeclareGlobal(Handle<String> name) {
|
||||
ASSERT(is_global_scope());
|
||||
return variables_.Declare(this, name, Variable::DYNAMIC, true,
|
||||
return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, true,
|
||||
Variable::NORMAL);
|
||||
}
|
||||
|
||||
|
@ -2671,23 +2671,19 @@ void CodeGenerator::VisitCall(Call* node) {
|
||||
frame_->Push(Factory::undefined_value());
|
||||
}
|
||||
|
||||
// Push the receiver.
|
||||
frame_->PushParameterAt(-1);
|
||||
|
||||
// Resolve the call.
|
||||
Result result =
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
|
||||
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
|
||||
|
||||
// Touch up the stack with the right values for the function and the
|
||||
// receiver. Use a scratch register to avoid destroying the result.
|
||||
Result scratch = allocator_->Allocate();
|
||||
ASSERT(scratch.is_valid());
|
||||
__ movq(scratch.reg(),
|
||||
FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(0)));
|
||||
frame_->SetElementAt(arg_count + 1, &scratch);
|
||||
|
||||
// We can reuse the result register now.
|
||||
frame_->Spill(result.reg());
|
||||
__ movq(result.reg(),
|
||||
FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(1)));
|
||||
frame_->SetElementAt(arg_count, &result);
|
||||
// The runtime call returns a pair of values in rax (function) and
|
||||
// rdx (receiver). Touch up the stack with the right values.
|
||||
Result receiver = allocator_->Allocate(rdx);
|
||||
frame_->SetElementAt(arg_count + 1, &result);
|
||||
frame_->SetElementAt(arg_count, &receiver);
|
||||
receiver.Unuse();
|
||||
|
||||
// Call the function.
|
||||
CodeForSourcePosition(node->position());
|
||||
|
@ -58,16 +58,16 @@ eval = global_eval;
|
||||
|
||||
// Test that un-aliased eval reads from local context.
|
||||
foo = 0;
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
var foo = 2;
|
||||
return eval('foo');
|
||||
})();
|
||||
assertEquals(2, result);
|
||||
|
||||
//Test that un-aliased eval writes to local context.
|
||||
// Test that un-aliased eval writes to local context.
|
||||
foo = 0;
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
var foo = 1;
|
||||
eval('foo = 2');
|
||||
@ -84,7 +84,7 @@ assertTrue(o === o.self);
|
||||
// Test that aliased eval reads from global context.
|
||||
var e = eval;
|
||||
foo = 0;
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
var foo = 2;
|
||||
return e('foo');
|
||||
@ -105,7 +105,7 @@ assertTrue(this === o.self);
|
||||
// Try to cheat the 'aliased eval' detection.
|
||||
var x = this;
|
||||
foo = 0;
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
var foo = 2;
|
||||
return x.eval('foo');
|
||||
@ -113,7 +113,7 @@ result =
|
||||
assertEquals(0, result);
|
||||
|
||||
foo = 0;
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
var eval = function(x) { return x; };
|
||||
var foo = eval(2);
|
||||
@ -128,8 +128,29 @@ result =
|
||||
})();
|
||||
assertEquals(4, result);
|
||||
|
||||
result =
|
||||
(function() {
|
||||
eval("var eval = function(s) { return this; }");
|
||||
return eval("42"); // Should return the global object
|
||||
})();
|
||||
assertEquals(this, result);
|
||||
|
||||
result =
|
||||
(function() {
|
||||
var obj = { f: function(eval) { return eval("this"); } };
|
||||
return obj.f(eval);
|
||||
})();
|
||||
assertEquals(this, result);
|
||||
|
||||
result =
|
||||
(function() {
|
||||
var obj = { f: function(eval) { arguments; return eval("this"); } };
|
||||
return obj.f(eval);
|
||||
})();
|
||||
assertEquals(this, result);
|
||||
|
||||
eval = function(x) { return 2 * x; };
|
||||
result =
|
||||
result =
|
||||
(function() {
|
||||
return (function() { return eval(2); })();
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user