Revision: 9838
Author: [email protected]
Date: Mon Oct 31 02:38:52 2011
Log: Make eval consider anything on the form eval(args...) a potential
direct cal
Previously we omitted all cases where the global eval property was shadowed,
even if by a variable holding the same value. ES5 requires us to treat these
as direct calls.
We still throw if calling indirect eval with a detached global object.
BUG=v8:994
TEST=mjsunit/eval.js
Review URL: http://codereview.chromium.org/8343054
http://code.google.com/p/v8/source/detail?r=9838
Deleted:
/branches/bleeding_edge/test/mjsunit/regress/regress-221.js
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/mips/full-codegen-mips.cc
/branches/bleeding_edge/src/parser.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/src/v8natives.js
/branches/bleeding_edge/src/variables.h
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/cctest/test-api.cc
/branches/bleeding_edge/test/mjsunit/eval.js
/branches/bleeding_edge/test/mjsunit/strict-mode-implicit-receiver.js
=======================================
--- /branches/bleeding_edge/test/mjsunit/regress/regress-221.js Tue Dec 7
03:01:02 2010
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Test that direct eval calls handle the case where eval has been
-// deleted correctly.
-
-// See http://code.google.com/p/v8/issues/detail?id=221
-
-assertThrows('eval(delete eval)');
-
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Oct 25 01:33:08
2011
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Oct 31 02:38:52
2011
@@ -2200,8 +2200,7 @@
}
-void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag,
- int arg_count) {
+void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
@@ -2221,9 +2220,7 @@
__ mov(r1, Operand(Smi::FromInt(strict_mode)));
__ push(r1);
- __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP
- ? Runtime::kResolvePossiblyDirectEvalNoLookup
- : Runtime::kResolvePossiblyDirectEval, 4);
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
}
@@ -2256,29 +2253,12 @@
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly
- // in generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- Label done;
- Variable* var = proxy->var();
- if (!var->IsUnallocated() && var->mode() == DYNAMIC_GLOBAL) {
- Label slow;
- EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow);
- // Push the function and resolve eval.
- __ push(r0);
- EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count);
- __ jmp(&done);
- __ bind(&slow);
- }
// Push a copy of the function (found below the arguments) and
// resolve eval.
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ push(r1);
- EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count);
- __ bind(&done);
+ EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in r0 (function) and
// r1 (receiver). Touch up the stack with the right values.
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/full-codegen.h Mon Oct 31 02:38:52 2011
@@ -466,13 +466,8 @@
Label* done);
void EmitVariableLoad(VariableProxy* proxy);
- enum ResolveEvalFlag {
- SKIP_CONTEXT_LOOKUP,
- PERFORM_CONTEXT_LOOKUP
- };
-
// Expects the arguments and the function already pushed.
- void EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, int arg_count);
+ void EmitResolvePossiblyDirectEval(int arg_count);
// Platform-specific support for allocating a new closure based on
// the given function info.
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Thu Oct 27
08:46:25 2011
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Oct 31
02:38:52 2011
@@ -2217,8 +2217,7 @@
}
-void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag,
- int arg_count) {
+void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(esp, arg_count * kPointerSize));
@@ -2235,9 +2234,7 @@
FLAG_harmony_scoping ? kStrictMode : strict_mode_flag();
__ push(Immediate(Smi::FromInt(strict_mode)));
- __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP
- ? Runtime::kResolvePossiblyDirectEvalNoLookup
- : Runtime::kResolvePossiblyDirectEval, 4);
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
}
@@ -2268,28 +2265,11 @@
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly in
- // generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- Label done;
- Variable* var = proxy->var();
- if (!var->IsUnallocated() && var->mode() == DYNAMIC_GLOBAL) {
- Label slow;
- EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow);
- // Push the function and resolve eval.
- __ push(eax);
- EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count);
- __ jmp(&done);
- __ bind(&slow);
- }
// Push a copy of the function (found below the arguments) and
// resolve eval.
__ push(Operand(esp, (arg_count + 1) * kPointerSize));
- EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count);
- __ bind(&done);
+ EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
=======================================
--- /branches/bleeding_edge/src/mips/full-codegen-mips.cc Wed Oct 26
08:02:00 2011
+++ /branches/bleeding_edge/src/mips/full-codegen-mips.cc Mon Oct 31
02:38:52 2011
@@ -2225,8 +2225,7 @@
}
-void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag,
- int arg_count) {
+void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ lw(a1, MemOperand(sp, arg_count * kPointerSize));
@@ -2246,9 +2245,7 @@
__ li(a1, Operand(Smi::FromInt(strict_mode)));
__ push(a1);
- __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP
- ? Runtime::kResolvePossiblyDirectEvalNoLookup
- : Runtime::kResolvePossiblyDirectEval, 4);
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
}
@@ -2281,29 +2278,12 @@
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly
- // in generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- Label done;
- Variable* var = proxy->var();
- if (!var->IsUnallocated() && var->mode() == DYNAMIC_GLOBAL) {
- Label slow;
- EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow);
- // Push the function and resolve eval.
- __ push(v0);
- EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count);
- __ jmp(&done);
- __ bind(&slow);
- }
// Push a copy of the function (found below the arguments) and
// resolve eval.
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ push(a1);
- EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count);
- __ bind(&done);
+ EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in v0 (function) and
// v1 (receiver). Touch up the stack with the right values.
=======================================
--- /branches/bleeding_edge/src/parser.cc Thu Oct 27 06:08:51 2011
+++ /branches/bleeding_edge/src/parser.cc Mon Oct 31 02:38:52 2011
@@ -2932,23 +2932,14 @@
// Keep track of eval() calls since they disable all local variable
// optimizations.
// The calls that need special treatment are the
- // direct (i.e. not aliased) eval calls. These calls are all of the
- // form eval(...) with no explicit receiver object where eval is
not
- // declared in the current scope chain.
+ // direct eval calls. These calls are all of the form eval(...),
with
+ // no explicit receiver.
// These calls are marked as potentially direct eval calls. Whether
// they are actually direct calls to eval is determined at run
time.
- // TODO(994): In ES5, it doesn't matter if the "eval" var is
declared
- // in the local scope chain. It only matters that it's
called "eval",
- // is called without a receiver and it refers to the original eval
- // function.
VariableProxy* callee = result->AsVariableProxy();
if (callee != NULL &&
callee->IsVariable(isolate()->factory()->eval_symbol())) {
- Handle<String> name = callee->name();
- Variable* var = top_scope_->Lookup(name);
- if (var == NULL) {
- top_scope_->DeclarationScope()->RecordEvalCall();
- }
+ top_scope_->DeclarationScope()->RecordEvalCall();
}
result = NewCall(result, args, pos);
break;
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri Oct 28 07:08:43 2011
+++ /branches/bleeding_edge/src/runtime.cc Mon Oct 31 02:38:52 2011
@@ -9452,80 +9452,12 @@
HandleScope scope(isolate);
Handle<Object> callee = args.at<Object>(0);
- Handle<Object> receiver; // Will be overwritten.
-
- // Compute the calling context.
- Handle<Context> context = Handle<Context>(isolate->context(), isolate);
-#ifdef DEBUG
- // Make sure Isolate::context() agrees with the old code that traversed
- // the stack frames to compute the context.
- StackFrameLocator locator;
- JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
- 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.
- int index = -1;
- PropertyAttributes attributes = ABSENT;
- BindingFlags binding_flags;
- while (true) {
- // Don't follow context chains in Context::Lookup and implement the
loop
- // up the context chain here, so that we can know the context where
eval
- // was found.
- receiver = context->Lookup(isolate->factory()->eval_symbol(),
- FOLLOW_PROTOTYPE_CHAIN,
- &index,
- &attributes,
- &binding_flags);
- // Stop search when eval is found or when the global context is
- // reached.
- if (attributes != ABSENT || context->IsGlobalContext()) break;
- context = Handle<Context>(context->previous(), isolate);
- }
-
- // If eval could not be resolved, it has been deleted and we need to
- // throw a reference error.
- if (attributes == ABSENT) {
- Handle<Object> name = isolate->factory()->eval_symbol();
- Handle<Object> reference_error =
- isolate->factory()->NewReferenceError("not_defined",
- HandleVector(&name, 1));
- return MakePair(isolate->Throw(*reference_error), NULL);
- }
-
- 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() || receiver->IsJSContextExtensionObject()) {
- receiver = isolate->factory()->the_hole_value();
- }
- return MakePair(*callee, *receiver);
- }
-
- // '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 != isolate->global_context()->global_eval_fun() ||
- !args[1]->IsString()) {
- return MakePair(*callee, isolate->heap()->the_hole_value());
- }
-
- CONVERT_STRICT_MODE_ARG(strict_mode, 3);
- return CompileGlobalEval(isolate,
- args.at<String>(1),
- args.at<Object>(2),
- strict_mode);
-}
-
-
-RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) {
- ASSERT(args.length() == 4);
-
- HandleScope scope(isolate);
- Handle<Object> callee = args.at<Object>(0);
-
- // 'eval' is bound in the global context, but it may have been
overwritten.
- // Compare it to the builtin 'GlobalEval' function to make sure.
+
+ // If "eval" didn't refer to the original GlobalEval, it's not a
+ // direct call to eval.
+ // (And even if it is, but the first argument isn't a string, just let
+ // execution default to an indirect call to eval, which will also return
+ // the first argument without doing anything).
if (*callee != isolate->global_context()->global_eval_fun() ||
!args[1]->IsString()) {
return MakePair(*callee, isolate->heap()->the_hole_value());
=======================================
--- /branches/bleeding_edge/src/runtime.h Fri Oct 28 01:45:04 2011
+++ /branches/bleeding_edge/src/runtime.h Mon Oct 31 02:38:52 2011
@@ -258,7 +258,6 @@
/* Eval */ \
F(GlobalReceiver, 1, 1) \
F(ResolvePossiblyDirectEval, 4, 2) \
- F(ResolvePossiblyDirectEvalNoLookup, 4, 2) \
\
F(SetProperty, -1 /* 4 or 5 */, 1) \
F(DefineOrRedefineDataProperty, 4, 1) \
=======================================
--- /branches/bleeding_edge/src/v8natives.js Mon Oct 24 08:56:18 2011
+++ /branches/bleeding_edge/src/v8natives.js Mon Oct 31 02:38:52 2011
@@ -162,28 +162,23 @@
function GlobalEval(x) {
if (!IS_STRING(x)) return x;
- var receiver = this;
var global_receiver = %GlobalReceiver(global);
-
- if (receiver == null && !IS_UNDETECTABLE(receiver)) {
- receiver = global_receiver;
- }
-
- var this_is_global_receiver = (receiver === global_receiver);
var global_is_detached = (global === global_receiver);
// For consistency with JSC we require the global object passed to
// eval to be the global object from which 'eval' originated. This
// is not mandated by the spec.
- if (!this_is_global_receiver || global_is_detached) {
- throw new $EvalError('The "this" object passed to eval must ' +
+ // We only throw if the global has been detached, since we need the
+ // receiver as this-value for the call.
+ if (global_is_detached) {
+ throw new $EvalError('The "this" value passed to eval must ' +
'be the global object from which eval
originated');
}
var f = %CompileString(x);
if (!IS_FUNCTION(f)) return f;
- return %_CallFunction(receiver, f);
+ return %_CallFunction(global_receiver, f);
}
=======================================
--- /branches/bleeding_edge/src/variables.h Tue Oct 25 01:33:08 2011
+++ /branches/bleeding_edge/src/variables.h Mon Oct 31 02:38:52 2011
@@ -134,8 +134,7 @@
// True if the variable is named eval and not known to be shadowed.
bool is_possibly_eval() const {
- return IsVariable(FACTORY->eval_symbol()) &&
- (mode_ == DYNAMIC || mode_ == DYNAMIC_GLOBAL);
+ return IsVariable(FACTORY->eval_symbol());
}
Variable* local_if_not_shadowed() const {
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Oct 25 01:33:08
2011
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Oct 31 02:38:52
2011
@@ -2099,8 +2099,7 @@
}
-void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag,
- int arg_count) {
+void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(rsp, arg_count * kPointerSize));
@@ -2117,9 +2116,7 @@
FLAG_harmony_scoping ? kStrictMode : strict_mode_flag();
__ Push(Smi::FromInt(strict_mode));
- __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP
- ? Runtime::kResolvePossiblyDirectEvalNoLookup
- : Runtime::kResolvePossiblyDirectEval, 4);
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
}
@@ -2149,28 +2146,11 @@
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly in
- // generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- Label done;
- Variable* var = proxy->var();
- if (!var->IsUnallocated() && var->mode() == DYNAMIC_GLOBAL) {
- Label slow;
- EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow);
- // Push the function and resolve eval.
- __ push(rax);
- EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count);
- __ jmp(&done);
- __ bind(&slow);
- }
// Push a copy of the function (found below the arguments) and
resolve
// eval.
__ push(Operand(rsp, (arg_count + 1) * kPointerSize));
- EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count);
- __ bind(&done);
+ EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in rax (function) and
// rdx (receiver). Touch up the stack with the right values.
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Mon Oct 17 05:48:31 2011
+++ /branches/bleeding_edge/test/cctest/test-api.cc Mon Oct 31 02:38:52 2011
@@ -7787,9 +7787,11 @@
" var bar = 2;"
" with (x) { return eval('bar'); }"
"}"
- "f(this)"));
+ "result4 = f(this)"));
script->Run();
- CHECK(try_catch.HasCaught());
+ CHECK(!try_catch.HasCaught());
+ CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value());
+
try_catch.Reset();
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/eval.js Wed Apr 27 22:04:48 2011
+++ /branches/bleeding_edge/test/mjsunit/eval.js Mon Oct 31 02:38:52 2011
@@ -39,7 +39,7 @@
try {
eval('hest 7 &*^*&^');
- assertTrue(false, 'Did not throw on syntax error.');
+ assertUnreachable('Did not throw on syntax error.');
} catch (e) {
assertEquals('SyntaxError', e.name);
}
@@ -108,19 +108,41 @@
result =
(function() {
var foo = 2;
+ // Should be non-direct call.
return x.eval('foo');
})();
assertEquals(0, result);
+foo = 0;
+result =
+ (function() {
+ var foo = 2;
+ // Should be non-direct call.
+ return (1,eval)('foo');
+ })();
+assertEquals(0, result);
+
foo = 0;
result =
(function() {
var eval = function(x) { return x; };
var foo = eval(2);
+ // Should be non-direct call.
return e('foo');
})();
assertEquals(0, result);
+foo = 0;
+result =
+ (function() {
+ var foo = 2;
+ // Should be direct call.
+ with ({ eval : e }) {
+ return eval('foo');
+ }
+ })();
+assertEquals(2, result);
+
result =
(function() {
var eval = function(x) { return 2 * x; };
@@ -135,19 +157,17 @@
})();
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);
+(function() {
+ var obj = { f: function(eval) { return eval("this"); } };
+ result = obj.f(eval);
+ assertEquals(obj, result);
+})();
+
+(function() {
+ var obj = { f: function(eval) { arguments; return eval("this"); } };
+ result = obj.f(eval);
+ assertEquals(obj, result);
+})();
eval = function(x) { return 2 * x; };
result =
@@ -156,6 +176,9 @@
})();
assertEquals(4, result);
+
+
+
// Regression test: calling a function named eval found in a context that
is
// not the global context should get the global object as receiver.
result =
=======================================
--- /branches/bleeding_edge/test/mjsunit/strict-mode-implicit-receiver.js
Mon May 30 06:23:17 2011
+++ /branches/bleeding_edge/test/mjsunit/strict-mode-implicit-receiver.js
Mon Oct 31 02:38:52 2011
@@ -168,12 +168,7 @@
outer_eval_conversion3(strict_eval, 'undefined');
outer_eval_conversion3(non_strict_eval, 'object');
-// TODO(ager): I'm not sure this is in accordance with the spec. At
-// the moment, any call to eval where eval is not bound in the global
-// context is treated as an indirect call to eval which means that the
-// global context is used and the global object is passed as the
-// receiver.
-outer_eval_conversion3(eval, 'object');
+outer_eval_conversion3(eval, 'undefined');
function test_constant_function() {
var o = { f: function() { "use strict"; return this; } };
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev