VariableProxySentinel VariableProxySentinel::identifier_proxy_(false);
ValidLeftHandSideSentinel ValidLeftHandSideSentinel::instance_;
Property Property::this_property_(VariableProxySentinel::this_proxy(), NULL, 0);
-Call Call::sentinel_(NULL, NULL, Call::ALIASED, 0);
+Call Call::sentinel_(NULL, NULL, false, 0);
// ----------------------------------------------------------------------------
class Call: public Expression {
public:
- enum EvalType {
- ALIASED, // Either not eval or an aliased eval.
- POTENTIALLY_DIRECT // Looks like a direct eval at codegen time.
- // Needs to be determined at runtime whether the
- // eval is direct.
- };
-
Call(Expression* expression,
ZoneList<Expression*>* arguments,
- EvalType eval_type,
+ bool is_eval,
int pos)
: expression_(expression),
arguments_(arguments),
- eval_type_(eval_type),
+ is_eval_(is_eval),
pos_(pos) { }
virtual void Accept(Visitor* v);
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
- EvalType eval_type() { return eval_type_; }
+ bool is_eval() { return is_eval_; }
int position() { return pos_; }
static Call* sentinel() { return &sentinel_; }
private:
Expression* expression_;
ZoneList<Expression*>* arguments_;
- EvalType eval_type_;
+ bool is_eval_;
int pos_;
static Call sentinel_;
class CallNew: public Call {
public:
CallNew(Expression* expression, ZoneList<Expression*>* arguments, int pos)
- : Call(expression, arguments, ALIASED, pos) { }
+ : Call(expression, arguments, false, pos) { }
virtual void Accept(Visitor* v);
};
// is resolved in cache misses (this also holds for megamorphic calls).
// ------------------------------------------------------------------------
- if (node->eval_type() == Call::POTENTIALLY_DIRECT) {
- __ CallRuntime(Runtime::kSetInPotentiallyDirectEval, 0);
- } else {
- __ CallRuntime(Runtime::kClearInPotentiallyDirectEval, 0);
- }
-
if (var != NULL && !var->is_this() && var->is_global()) {
// ----------------------------------
// JavaScript example: 'foo(1, 2, 3)' // foo is global
// is resolved in cache misses (this also holds for megamorphic calls).
// ------------------------------------------------------------------------
- if (node->eval_type() == Call::POTENTIALLY_DIRECT) {
- __ CallRuntime(Runtime::kSetInPotentiallyDirectEval, 0);
- } else {
- __ CallRuntime(Runtime::kClearInPotentiallyDirectEval, 0);
- }
-
if (var != NULL && !var->is_this() && var->is_global()) {
// ----------------------------------
// JavaScript example: 'foo(1, 2, 3)' // foo is global
int* index_, PropertyAttributes* attributes) {
Handle<Context> context(this);
+ // The context must be in frame slot 0 (if not debugging).
+ if (kDebug && !Debug::InDebugger()) {
+ StackFrameLocator locator;
+ ASSERT(context->fcontext() ==
+ Context::cast(
+ locator.FindJavaScriptFrame(0)->context())->fcontext());
+ }
+
bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
*index_ = -1;
*attributes = ABSENT;
DebugCommandProcessor.prototype.responseToText = function(json_response) {
try {
// Convert the JSON string to an object.
- response = %CompileString('(' + json_response + ')', 0)();
+ response = %CompileString('(' + json_response + ')', 0, false)();
if (!response.success) {
return response.message;
try {
try {
// Convert the JSON string to an object.
- request = %CompileString('(' + json_request + ')', 0)();
+ request = %CompileString('(' + json_request + ')', 0, false)();
// Create an initial response.
response = this.createResponse(request);
DebugCommandProcessor.prototype.isRunning = function(json_response) {
try {
// Convert the JSON string to an object.
- response = %CompileString('(' + json_response + ')', 0)();
+ response = %CompileString('(' + json_response + ')', 0, false)();
// Return whether VM should be running after this request.
return response.running;
return Handle<String>();
}
-
-
virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) {
if (obj == VariableProxySentinel::this_proxy()) {
return Property::this_property();
virtual Expression* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
- Call::EvalType eval_type, int pos) {
+ bool is_eval, int pos) {
return Call::sentinel();
}
virtual Expression* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
- Call::EvalType eval_type, int pos) {
- return new Call(expression, arguments, eval_type, pos);
+ bool is_eval, int pos) {
+ return new Call(expression, arguments, is_eval, pos);
}
virtual Statement* EmptyStatement() {
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
// 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. These calls are marked as
- // potentially direct eval calls. Whether they are actually direct calls
- // to eval is determined at run time.
-
- Call::EvalType eval_type = Call::ALIASED;
+ // optimizations. We can ignore locally declared variables with
+ // name 'eval' since they override the global 'eval' function. We
+ // only need to look at unresolved variables (VariableProxies).
+
if (!is_pre_parsing_) {
+ // We assume that only a function called 'eval' can be used
+ // to invoke the global eval() implementation. This permits
+ // for massive optimizations.
VariableProxy* callee = result->AsVariableProxy();
if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) {
- Handle<String> name = callee->name();
- Variable* var = NULL;
- for (Scope* scope = top_scope_;
- scope != NULL;
- scope = scope->outer_scope()) {
- var = scope->Lookup(callee->name());
- if (var != NULL) break;
- }
- if (var == NULL) {
- // We do not allow direct calls to 'eval' in our internal
- // JS files. Use builtin functions instead.
- ASSERT(!Bootstrapper::IsActive());
- top_scope_->RecordEvalCall();
- eval_type = Call::POTENTIALLY_DIRECT;
+ // We do not allow direct calls to 'eval' in our internal
+ // JS files. Use builtin functions instead.
+ ASSERT(!Bootstrapper::IsActive());
+ top_scope_->RecordEvalCall();
+ } else {
+ // This is rather convoluted code to check if we're calling
+ // a function named 'eval' through a property access. If so,
+ // we mark it as a possible eval call (we don't know if the
+ // receiver will resolve to the global object or not), but
+ // we do not treat the call as an eval() call - we let the
+ // call get through to the JavaScript eval code defined in
+ // v8natives.js.
+ Property* property = result->AsProperty();
+ if (property != NULL) {
+ Literal* key = property->key()->AsLiteral();
+ if (key != NULL &&
+ key->handle().is_identical_to(Factory::eval_symbol())) {
+ // We do not allow direct calls to 'eval' in our
+ // internal JS files. Use builtin functions instead.
+ ASSERT(!Bootstrapper::IsActive());
+ top_scope_->RecordEvalCall();
+ }
}
}
}
if (is_eval && args->length() == 0) {
result = NEW(Literal(Factory::undefined_value()));
} else {
- result = factory()->NewCall(result, args, eval_type, pos);
+ result = factory()->NewCall(result, args, is_eval, pos);
}
break;
}
}
-static Object* Runtime_GlobalReceiver(Arguments args) {
- ASSERT(args.length() == 1);
- Object* global = args[0];
- if (!global->IsJSGlobalObject()) return Heap::null_value();
- return JSGlobalObject::cast(global)->global_receiver();
-}
-
+static Object* EvalContext() {
+ // The topmost JS frame belongs to the eval function which called
+ // the CompileString runtime function. We need to unwind one level
+ // to get to the caller of eval.
+ StackFrameLocator locator;
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
-static Object* Runtime_CompileString(Arguments args) {
+ // TODO(900055): Right now we check if the caller of eval() supports
+ // eval to determine if it's an aliased eval or not. This may not be
+ // entirely correct in the unlikely case where a function uses both
+ // aliased and direct eval calls.
HandleScope scope;
- ASSERT(args.length() == 2);
- CONVERT_ARG_CHECKED(String, source, 0);
- CONVERT_ARG_CHECKED(Smi, line_offset, 1);
-
- // Compile source string.
- Handle<JSFunction> boilerplate =
- Compiler::CompileEval(source, line_offset->value(), true);
- if (boilerplate.is_null()) return Failure::Exception();
- Handle<Context> context(Top::context()->global_context());
- Handle<JSFunction> fun =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
- return *fun;
-}
+ if (!ScopeInfo<>::SupportsEval(frame->FindCode())) {
+ // Aliased eval: Evaluate in the global context of the eval
+ // function to support aliased, cross environment evals.
+ return *Top::global_context();
+ }
+ // Fetch the caller context from the frame.
+ Handle<Context> caller(Context::cast(frame->context()));
-static Object* Runtime_ExecDirectEval(Arguments args) {
- ASSERT(args.length() == 1);
+ // Check for eval() invocations that cross environments. Use the
+ // context from the stack if evaluating in current environment.
+ Handle<Context> target = Top::global_context();
+ if (caller->global_context() == *target) return *caller;
- if (!args[0]->IsString()) return args[0];
+ // Otherwise, use the global context from the other environment.
+ return *target;
+}
- Handle<String> source = args.at<String>(0);
- // Compute the eval context.
- HandleScope scope;
+static Object* Runtime_EvalReceiver(Arguments args) {
+ ASSERT(args.length() == 1);
StackFrameLocator locator;
JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
- Handle<Context> context(Context::cast(frame->context()));
- bool is_global = context->IsGlobalContext();
+ // Fetch the caller context from the frame.
+ Context* caller = Context::cast(frame->context());
- // Compile source string.
- Handle<JSFunction> boilerplate =
- Compiler::CompileEval(source, 0, is_global);
- if (boilerplate.is_null()) return Failure::Exception();
- Handle<JSFunction> fun =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
-
- // Call generated function
- Handle<Object> receiver(frame->receiver());
- bool has_pending_exception = false;
- Handle<Object> result =
- Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
- if (has_pending_exception) {
- ASSERT(Top::has_pending_exception());
- return Failure::Exception();
+ // Check for eval() invocations that cross environments. Use the
+ // top frames receiver if evaluating in current environment.
+ Context* global_context = Top::context()->global()->global_context();
+ if (caller->global_context() == global_context) {
+ return frame->receiver();
}
- return *result;
-}
-
-static Object* Runtime_SetInPotentiallyDirectEval(Arguments args) {
- ASSERT(args.length() == 0);
- Top::set_in_potentially_direct_eval(true);
- return Heap::undefined_value();
+ // Otherwise use the given argument (the global object of the
+ // receiving context).
+ return args[0];
}
-static Object* Runtime_ClearInPotentiallyDirectEval(Arguments args) {
- ASSERT(args.length() == 0);
- Top::set_in_potentially_direct_eval(false);
- return Heap::undefined_value();
+static Object* Runtime_GlobalReceiver(Arguments args) {
+ ASSERT(args.length() == 1);
+ Object* global = args[0];
+ if (!global->IsJSGlobalObject()) return Heap::null_value();
+ return JSGlobalObject::cast(global)->global_receiver();
}
-static Object* Runtime_InDirectEval(Arguments args) {
- ASSERT(args.length() == 0);
+static Object* Runtime_CompileString(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(String, source, 0);
+ CONVERT_ARG_CHECKED(Smi, line_offset, 1);
+ bool contextual = args[2]->IsTrue();
+ RUNTIME_ASSERT(contextual || args[2]->IsFalse());
- // No need to look further if the static analysis showed that this
- // is aliased.
- if (!Top::is_in_potentially_direct_eval()) {
- return Heap::false_value();
+ // Compute the eval context.
+ Handle<Context> context;
+ if (contextual) {
+ // Get eval context. May not be available if we are calling eval
+ // through an alias, and the corresponding frame doesn't have a
+ // proper eval context set up.
+ Object* eval_context = EvalContext();
+ if (eval_context->IsFailure()) return eval_context;
+ context = Handle<Context>(Context::cast(eval_context));
+ } else {
+ context = Handle<Context>(Top::context()->global_context());
}
- // Find where the 'eval' symbol is bound. It is unaliased only if
- // it is bound in the global object.
- HandleScope scope;
- StackFrameLocator locator;
- JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
- Context* context = Context::cast(frame->context());
- int index;
- PropertyAttributes attributes;
- while (context && !context->IsGlobalContext()) {
- context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
- &index, &attributes);
- if (attributes != ABSENT) return Heap::false_value();
- if (context->is_function_context()) {
- context = Context::cast(context->closure()->context());
- } else {
- context = context->previous();
- }
- }
- return Heap::true_value();
+
+ // Compile source string.
+ bool is_global = context->IsGlobalContext();
+ Handle<JSFunction> boilerplate =
+ Compiler::CompileEval(source, line_offset->value(), is_global);
+ if (boilerplate.is_null()) return Failure::Exception();
+ Handle<JSFunction> fun =
+ Factory::NewFunctionFromBoilerplate(boilerplate, context);
+ return *fun;
}
F(NumberIsFinite, 1) \
\
/* Globals */ \
- F(CompileString, 2) \
+ F(CompileString, 3) \
F(CompileScript, 4) \
F(GlobalPrint, 1) \
\
/* Eval */ \
+ F(EvalReceiver, 1) \
F(GlobalReceiver, 1) \
- F(SetInPotentiallyDirectEval, 0) \
- F(ClearInPotentiallyDirectEval, 0) \
- F(InDirectEval, 0) \
- F(ExecDirectEval, 1) \
\
F(SetProperty, -1 /* 3 or 4 */) \
F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */) \
clear_scheduled_exception();
thread_local_.save_context_ = NULL;
thread_local_.catcher_ = NULL;
- thread_local_.in_potentially_direct_eval_ = false;
}
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;
-
- // Flag for whether we are currently in a call that the static analysis
- // marked as a potentially direct eval.
- bool in_potentially_direct_eval_;
};
#define TOP_ADDRESS_LIST(C) \
}
static inline Address* handler_address() { return &thread_local_.handler_; }
- static inline void set_in_potentially_direct_eval(bool b) {
- thread_local_.in_potentially_direct_eval_ = b;
- }
-
- static inline bool is_in_potentially_direct_eval() {
- return thread_local_.in_potentially_direct_eval_;
- }
-
// Generated code scratch locations.
static void* formal_count_address() { return &thread_local_.formal_count_; }
return %StringParseFloat(ToString(string));
}
+
function GlobalEval(x) {
if (!IS_STRING(x)) return x;
'be the global object from which eval originated');
}
- if (%InDirectEval()) {
- return %ExecDirectEval(x);
- }
-
- var f = %CompileString(x, 0);
+ var f = %CompileString(x, 0, true);
if (!IS_FUNCTION(f)) return f;
- return f.call(this);
+ return f.call(%EvalReceiver(this));
}
function GlobalExecScript(expr, lang) {
// NOTE: We don't care about the character casing.
if (!lang || /javascript/i.test(lang)) {
- var f = %CompileString(ToString(expr), 0);
+ var f = %CompileString(ToString(expr), 0, false);
f.call(%GlobalReceiver(global));
}
return null;
// ECMA-262 - 15.1.1.3.
%SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE);
-
+
// Setup non-enumerable function on the global object.
InstallFunctions(global, DONT_ENUM, $Array(
"isNaN", GlobalIsNaN,
// The call to SetNewFunctionAttributes will ensure the prototype
// property of the resulting function is enumerable (ECMA262, 15.3.5.2).
- var f = %CompileString(source, -1)();
+ var f = %CompileString(source, -1, false)();
%FunctionSetName(f, "anonymous");
return %SetNewFunctionAttributes(f);
}
v8::HandleScope scope;
LocalContext current;
- // Test that un-aliased eval reads from local context.
+ // Test that un-aliased eval uses local context.
Local<Script> script =
Script::Compile(v8_str("foo = 0;"
"(function() {"
Local<Value> result = script->Run();
CHECK_EQ(2, result->Int32Value());
- // Test that un-aliased eval writes to local context.
- script =
- Script::Compile(v8_str("foo = 0;"
- "(function() {"
- " var foo = 1;"
- " eval('foo = 2;');"
- " return foo;"
- "})();"));
- result = script->Run();
- CHECK_EQ(0, current->Global()->Get(v8_str("foo"))->Int32Value());
- CHECK(result->IsInt32());
- CHECK_EQ(2, result->Int32Value());
-
// Test that un-aliased eval has right this.
script =
Script::Compile(v8_str("function MyObject() { this.self = eval('this'); }"
"o === o.self"));
result = script->Run();
CHECK(result->IsTrue());
-
- // Test that aliased eval reads from global context.
- script =
- Script::Compile(v8_str("var e = eval;"
- "foo = 0;"
- "(function() {"
- " var foo = 2;"
- " return e('foo');"
- "})();"));
- result = script->Run();
- CHECK_EQ(0, result->Int32Value());
-
- // Test that aliased eval writes to global context.
- script =
- Script::Compile(v8_str("var e = eval;"
- "foo = 0;"
- "(function() {"
- " e('var foo = 2;');"
- "})();"
- "foo"));
- result = script->Run();
- CHECK_EQ(2, result->Int32Value());
-
- // Test that aliased eval has right this.
- script =
- Script::Compile(v8_str("var e = eval;"
- "function MyObject() { this.self = e('this'); }"
- "var o = new MyObject();"
- "this === o.self"));
- result = script->Run();
- CHECK(result->IsTrue());
-
- // Try to cheat the 'aliased eval' detection.
- script =
- Script::Compile(v8_str("var x = this;"
- "foo = 0;"
- "(function() {"
- " var foo = 2;"
- " return x.eval('foo');"
- "})();"));
- result = script->Run();
- CHECK_EQ(0, result->Int32Value());
-
- script =
- Script::Compile(v8_str("var e = eval;"
- "foo = 0;"
- "(function() {"
- " var eval = function(x) { return x; };"
- " var foo = eval(2);"
- " return e('foo');"
- "})();"));
- result = script->Run();
- CHECK_EQ(0, result->Int32Value());
-
- script =
- Script::Compile(v8_str("(function() {"
- " var eval = function(x) { return 2 * x; };"
- " return (function() { return eval(2); })();"
- "})();"));
- result = script->Run();
- CHECK_EQ(4, result->Int32Value());
-
- script =
- Script::Compile(v8_str("eval = function(x) { return 2 * x; };"
- "(function() {"
- " return (function() { return eval(2); })();"
- "})();"));
- result = script->Run();
- CHECK_EQ(4, result->Int32Value());
-}
-
-
-THREADED_TEST(EvalAliasedDynamic) {
- v8::HandleScope scope;
- LocalContext current;
-
- // This sets 'global' to the real global object (as opposed to the
- // proxy). It is highly implementation dependent, so take care.
- current->Global()->Set(v8_str("global"), current->Global()->GetPrototype());
-
- // Tests where aliased eval can only be resolved dynamically.
- Local<Script> script =
- Script::Compile(v8_str("function f(x) { "
- " var foo = 2;"
- " with (x) { return eval('foo'); }"
- "}"
- "foo = 0;"
- "var result = f(new Object()) == 2;"
- "result = result && (f(global) == 0);"
- "var x = new Object();"
- "x.eval = function(x) { return 1; };"
- "result = result && (f(x) == 1);"
- "result"));
- Local<Value> result = script->Run();
- CHECK(result->IsTrue());
-
- v8::TryCatch try_catch;
- script =
- Script::Compile(v8_str("function f(x) { "
- " var bar = 2;"
- " with (x) { return eval('bar'); }"
- "}"
- "f(global)"));
- result = script->Run();
- CHECK(try_catch.HasCaught());
- try_catch.Reset();
-
- script =
- Script::Compile(v8_str("this.eval = function (x) { return x + x; };"
- "foo = 2;"
- "eval('foo')"));
- result = script->Run();
- CHECK(result->IsString());
- CHECK_EQ(v8_str("foofoo"), result);
}
js1_5/Regress/regress-320119: FAIL_OK
+# We do not support explicit global evals through <global>.eval(...).
+js1_5/Regress/regress-68498-003: FAIL_OK
+
+
# No support for toSource().
js1_5/Regress/regress-248444: FAIL_OK
js1_5/Regress/regress-313967-01: FAIL_OK
# Marked as: Will not fix. V8 throws an acceptable RangeError.
js1_5/Expressions/regress-394673: FAIL
+# Bug 1202598: New mozilla test js1_5/Regress/regress-383682 fails.
+js1_5/Regress/regress-383682: FAIL
+
+
##################### MOZILLA EXTENSION TESTS #####################
ecma/extensions/15.1.2.1-1: FAIL_OK