Revision: 3533
Author: [email protected]
Date: Tue Jan 5 01:38:02 2010
Log: Make the ResolvePossiblyDirectEval faster by avoiding the
stack traversal code.
Review URL: http://codereview.chromium.org/523051
http://code.google.com/p/v8/source/detail?r=3533
Modified:
/branches/bleeding_edge/src/arm/codegen-arm.cc
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/contexts.h
/branches/bleeding_edge/src/ia32/codegen-ia32.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/src/scopes.cc
/branches/bleeding_edge/src/x64/codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/eval.js
=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc Tue Dec 22 04:41:45 2009
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc Tue Jan 5 01:38:02 2010
@@ -2891,14 +2891,16 @@
} else {
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.
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Fri Dec 11 02:40:01 2009
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue Jan 5 01:38:02 2010
@@ -992,6 +992,7 @@
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);
=======================================
--- /branches/bleeding_edge/src/contexts.h Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/contexts.h Tue Jan 5 01:38:02 2010
@@ -77,6 +77,7 @@
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 @@
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,
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Dec 22 05:10:24
2009
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Jan 5 01:38:02
2010
@@ -4579,23 +4579,20 @@
} else {
frame_->Push(Factory::undefined_value());
}
+
+ // Push the receiver.
+ frame_->PushParameterAt(-1);
// Resolve the call.
Result result =
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
-
- // 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);
+ frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+
+ // 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());
=======================================
--- /branches/bleeding_edge/src/runtime.cc Mon Dec 28 05:01:03 2009
+++ /branches/bleeding_edge/src/runtime.cc Tue Jan 5 01:38:02 2010
@@ -5233,51 +5233,31 @@
}
-static Handle<JSFunction> GetBuiltinFunction(String* name) {
- LookupResult result;
- Top::global_context()->builtins()->LocalLookup(name, &result);
- return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
-}
-
-
-static Object* CompileDirectEval(Handle<String> source) {
- // Compute the eval context.
+static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
+ ASSERT(args.length() == 3);
+ if (!args[0]->IsJSFunction()) {
+ return MakePair(Top::ThrowIllegalOperation(), NULL);
+ }
+
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 @@
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]);
}
=======================================
--- /branches/bleeding_edge/src/runtime.h Wed Dec 9 05:06:08 2009
+++ /branches/bleeding_edge/src/runtime.h Tue Jan 5 01:38:02 2010
@@ -202,7 +202,7 @@
\
/* 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) \
=======================================
--- /branches/bleeding_edge/src/scopes.cc Tue Dec 8 01:43:51 2009
+++ /branches/bleeding_edge/src/scopes.cc Tue Jan 5 01:38:02 2010
@@ -236,7 +236,7 @@
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);
}
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc Tue Dec 22 04:41:45 2009
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc Tue Jan 5 01:38:02 2010
@@ -2670,24 +2670,20 @@
} else {
frame_->Push(Factory::undefined_value());
}
+
+ // Push the receiver.
+ frame_->PushParameterAt(-1);
// Resolve the call.
Result result =
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
-
- // 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);
+ frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+
+ // 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());
=======================================
--- /branches/bleeding_edge/test/mjsunit/eval.js Thu Nov 27 05:55:06 2008
+++ /branches/bleeding_edge/test/mjsunit/eval.js Tue Jan 5 01:38:02 2010
@@ -58,16 +58,16 @@
// 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 @@
// 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 @@
// 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 @@
assertEquals(0, result);
foo = 0;
-result =
+result =
(function() {
var eval = function(x) { return x; };
var foo = eval(2);
@@ -128,8 +128,29 @@
})();
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); })();
})();
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev