VariableProxySentinel VariableProxySentinel::identifier_proxy_(false);
ValidLeftHandSideSentinel ValidLeftHandSideSentinel::instance_;
Property Property::this_property_(VariableProxySentinel::this_proxy(), NULL, 0);
-Call Call::sentinel_(NULL, NULL, false, 0);
+Call Call::sentinel_(NULL, NULL, 0);
+CallEval CallEval::sentinel_(NULL, NULL, 0);
// ----------------------------------------------------------------------------
// All the Accept member functions for each syntax tree node type.
#define DECL_ACCEPT(type) \
- void type::Accept(Visitor* v) { \
+ void type::Accept(AstVisitor* v) { \
if (v->CheckStackOverflow()) return; \
v->Visit##type(this); \
}
// ----------------------------------------------------------------------------
-// Implementation of Visitor
+// Implementation of AstVisitor
-void Visitor::VisitStatements(ZoneList<Statement*>* statements) {
+void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
Visit(statements->at(i));
}
}
-void Visitor::VisitExpressions(ZoneList<Expression*>* expressions) {
+void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) {
for (int i = 0; i < expressions->length(); i++) {
// The variable statement visiting code may pass NULL expressions
// to this code. Maybe this should be handled by introducing an
V(Throw) \
V(Property) \
V(Call) \
+ V(CallEval) \
V(CallNew) \
V(CallRuntime) \
V(UnaryOperation) \
public:
Node(): statement_pos_(RelocInfo::kNoPosition) { }
virtual ~Node() { }
- virtual void Accept(Visitor* v) = 0;
+ virtual void Accept(AstVisitor* v) = 0;
// Type testing & conversion.
virtual Statement* AsStatement() { return NULL; }
class ValidLeftHandSideSentinel: public Expression {
public:
virtual bool IsValidLeftHandSide() { return true; }
- virtual void Accept(Visitor* v) { UNREACHABLE(); }
+ virtual void Accept(AstVisitor* v) { UNREACHABLE(); }
static ValidLeftHandSideSentinel* instance() { return &instance_; }
private:
static ValidLeftHandSideSentinel instance_;
statements_(capacity),
is_initializer_block_(is_initializer_block) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
void AddStatement(Statement* statement) { statements_.Add(statement); }
ASSERT(fun == NULL || mode == Variable::VAR);
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
VariableProxy* proxy() const { return proxy_; }
Variable::Mode mode() const { return mode_; }
next_ = next;
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Type type() const { return type_; }
Statement* init() const { return init_; }
enumerable_ = enumerable;
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* each() const { return each_; }
Expression* enumerable() const { return enumerable_; }
explicit ExpressionStatement(Expression* expression)
: expression_(expression) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion.
virtual ExpressionStatement* AsExpressionStatement() { return this; }
explicit ContinueStatement(IterationStatement* target)
: target_(target) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
IterationStatement* target() const { return target_; }
explicit BreakStatement(BreakableStatement* target)
: target_(target) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
BreakableStatement* target() const { return target_; }
explicit ReturnStatement(Expression* expression)
: expression_(expression) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion.
virtual ReturnStatement* AsReturnStatement() { return this; }
explicit WithEnterStatement(Expression* expression)
: expression_(expression) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* expression() const { return expression_; }
public:
WithExitStatement() { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
};
cases_ = cases;
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* tag() const { return tag_; }
ZoneList<CaseClause*>* cases() const { return cases_; }
then_statement_(then_statement),
else_statement_(else_statement) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
bool HasThenStatement() const { return !then_statement()->IsEmpty(); }
bool HasElseStatement() const { return !else_statement()->IsEmpty(); }
void AddLabel(Label* label);
// Virtual behaviour. LabelCollectors are never part of the AST.
- virtual void Accept(Visitor* v) { UNREACHABLE(); }
+ virtual void Accept(AstVisitor* v) { UNREACHABLE(); }
virtual LabelCollector* AsLabelCollector() { return this; }
ZoneList<Label*>* labels() { return labels_; }
ASSERT(catch_var->AsVariableProxy() != NULL);
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* catch_var() const { return catch_var_; }
Block* catch_block() const { return catch_block_; }
: TryStatement(try_block),
finally_block_(finally_block) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Block* finally_block() const { return finally_block_; }
class DebuggerStatement: public Statement {
public:
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
};
class EmptyStatement: public Statement {
public:
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion.
virtual EmptyStatement* AsEmptyStatement() { return this; }
public:
explicit Literal(Handle<Object> handle) : handle_(handle) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion.
virtual Literal* AsLiteral() { return this; }
properties_(properties) {
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Handle<FixedArray> constant_properties() const {
return constant_properties_;
pattern_(pattern),
flags_(flags) {}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Handle<String> pattern() const { return pattern_; }
Handle<String> flags() const { return flags_; }
: literals_(literals), values_(values) {
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Handle<FixedArray> literals() const { return literals_; }
ZoneList<Expression*>* values() const { return values_; }
class VariableProxy: public Expression {
public:
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual Property* AsProperty() {
ASSERT(var != NULL);
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual Slot* AsSlot() { return this; }
Property(Expression* obj, Expression* key, int pos)
: obj_(obj), key_(key), pos_(pos) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual Property* AsProperty() { return this; }
public:
Call(Expression* expression,
ZoneList<Expression*>* arguments,
- bool is_eval,
int pos)
: expression_(expression),
arguments_(arguments),
- is_eval_(is_eval),
pos_(pos) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing and conversion.
virtual Call* AsCall() { return this; }
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
- bool is_eval() { return is_eval_; }
int position() { return pos_; }
static Call* sentinel() { return &sentinel_; }
private:
Expression* expression_;
ZoneList<Expression*>* arguments_;
- bool is_eval_;
int pos_;
static Call sentinel_;
class CallNew: public Call {
public:
CallNew(Expression* expression, ZoneList<Expression*>* arguments, int pos)
- : Call(expression, arguments, false, pos) { }
+ : Call(expression, arguments, pos) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
+};
+
+
+// The CallEval class represents a call of the form 'eval(...)' where eval
+// cannot be seen to be overwritten at compile time. It is potentially a
+// direct (i.e. not aliased) eval call. The real nature of the call is
+// determined at runtime.
+class CallEval: public Call {
+ public:
+ CallEval(Expression* expression, ZoneList<Expression*>* arguments, int pos)
+ : Call(expression, arguments, pos) { }
+
+ virtual void Accept(AstVisitor* v);
+
+ static CallEval* sentinel() { return &sentinel_; }
+
+ private:
+ static CallEval sentinel_;
};
ZoneList<Expression*>* arguments)
: name_(name), function_(function), arguments_(arguments) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Handle<String> name() const { return name_; }
Runtime::Function* function() const { return function_; }
ASSERT(Token::IsUnaryOp(op));
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual UnaryOperation* AsUnaryOperation() { return this; }
ASSERT(Token::IsBinaryOp(op));
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual BinaryOperation* AsBinaryOperation() { return this; }
ASSERT(Token::IsCountOp(op));
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
bool is_prefix() const { return is_prefix_; }
bool is_postfix() const { return !is_prefix_; }
ASSERT(Token::IsCompareOp(op));
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Token::Value op() const { return op_; }
Expression* left() const { return left_; }
then_expression_(then_expression),
else_expression_(else_expression) { }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* condition() const { return condition_; }
Expression* then_expression() const { return then_expression_; }
ASSERT(Token::IsAssignmentOp(op));
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
virtual Assignment* AsAssignment() { return this; }
Token::Value binary_op() const;
Throw(Expression* exception, int pos)
: exception_(exception), pos_(pos) {}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
Expression* exception() const { return exception_; }
int position() const { return pos_; }
function_token_position_(RelocInfo::kNoPosition) {
}
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
// Type testing & conversion
virtual FunctionLiteral* AsFunctionLiteral() { return this; }
Handle<JSFunction> boilerplate() const { return boilerplate_; }
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
private:
Handle<JSFunction> boilerplate_;
class ThisFunction: public Expression {
public:
- virtual void Accept(Visitor* v);
+ virtual void Accept(AstVisitor* v);
};
// Basic visitor
// - leaf node visitors are abstract.
-class Visitor BASE_EMBEDDED {
+class AstVisitor BASE_EMBEDDED {
public:
- Visitor() : stack_overflow_(false) { }
- virtual ~Visitor() { }
+ AstVisitor() : stack_overflow_(false) { }
+ virtual ~AstVisitor() { }
// Dispatch
void Visit(Node* node) { node->Accept(this); }
ZoneList<Expression*>* args = node->arguments();
- if (FLAG_debug_info) RecordStatementPosition(node);
+ RecordStatementPosition(node);
// Standard function call.
// Check if the function is a variable or a property.
}
+void CodeGenerator::VisitCallEval(CallEval* node) {
+ Comment cmnt(masm_, "[ CallEval");
+
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to resolve
+ // the function we need to call and the receiver of the call.
+ // Then we call the resolved function using the given arguments.
+
+ ZoneList<Expression*>* args = node->arguments();
+ Expression* function = node->expression();
+
+ RecordStatementPosition(node);
+
+ // Prepare stack for call to resolved function.
+ Load(function);
+ __ mov(r2, Operand(Factory::undefined_value()));
+ __ push(r2); // Slot for receiver
+ for (int i = 0; i < args->length(); i++) {
+ Load(args->at(i));
+ }
+
+ // Prepare stack for call to ResolvePossiblyDirectEval.
+ __ ldr(r1, MemOperand(sp, args->length() * kPointerSize + kPointerSize));
+ __ push(r1);
+ if (args->length() > 0) {
+ __ ldr(r1, MemOperand(sp, args->length() * kPointerSize));
+ __ push(r1);
+ } else {
+ __ push(r2);
+ }
+
+ // Resolve the call.
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
+
+ // Touch up stack with the right values for the function and the receiver.
+ __ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize));
+ __ str(r1, MemOperand(sp, (args->length() + 1) * kPointerSize));
+ __ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize + kPointerSize));
+ __ str(r1, MemOperand(sp, args->length() * kPointerSize));
+
+ // Call the function.
+ __ RecordPosition(node->position());
+
+ CallFunctionStub call_function(args->length());
+ __ CallStub(&call_function);
+
+ __ ldr(cp, frame_->Context());
+ // Remove the function from the stack.
+ frame_->Pop();
+ frame_->Push(r0);
+}
+
+
void CodeGenerator::VisitCallNew(CallNew* node) {
Comment cmnt(masm_, "[ CallNew");
// -------------------------------------------------------------------------
// CodeGenerator
-class CodeGenerator: public Visitor {
+class CodeGenerator: public AstVisitor {
public:
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
}
+void CodeGenerator::VisitCallEval(CallEval* node) {
+ Comment cmnt(masm_, "[ CallEval");
+
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to resolve
+ // the function we need to call and the receiver of the call.
+ // Then we call the resolved function using the given arguments.
+
+ ZoneList<Expression*>* args = node->arguments();
+ Expression* function = node->expression();
+
+ RecordStatementPosition(node);
+
+ // Prepare stack for call to resolved function.
+ Load(function);
+ __ push(Immediate(Factory::undefined_value())); // Slot for receiver
+ for (int i = 0; i < args->length(); i++) {
+ Load(args->at(i));
+ }
+
+ // Prepare stack for call to ResolvePossiblyDirectEval.
+ __ push(Operand(esp, args->length() * kPointerSize + kPointerSize));
+ if (args->length() > 0) {
+ __ push(Operand(esp, args->length() * kPointerSize));
+ } else {
+ __ push(Immediate(Factory::undefined_value()));
+ }
+
+ // Resolve the call.
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
+
+ // Touch up stack with the right values for the function and the receiver.
+ __ mov(edx, FieldOperand(eax, FixedArray::kHeaderSize));
+ __ mov(Operand(esp, (args->length() + 1) * kPointerSize), edx);
+ __ mov(edx, FieldOperand(eax, FixedArray::kHeaderSize + kPointerSize));
+ __ mov(Operand(esp, args->length() * kPointerSize), edx);
+
+ // Call the function.
+ __ RecordPosition(node->position());
+
+ CallFunctionStub call_function(args->length());
+ __ CallStub(&call_function);
+
+ // Restore context and pop function from the stack.
+ __ mov(esi, frame_->Context());
+ __ mov(frame_->Top(), eax);
+}
+
+
void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
// -------------------------------------------------------------------------
// CodeGenerator
-class CodeGenerator: public Visitor {
+class CodeGenerator: public AstVisitor {
public:
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
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;
try {
try {
// Convert the JSON string to an object.
- request = %CompileString('(' + json_request + ')', 0, false)();
+ request = %CompileString('(' + json_request + ')', 0)();
// 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, false)();
+ response = %CompileString('(' + json_response + ')', 0)();
// Return whether VM should be running after this request.
return response.running;
class String;
class Struct;
class SwitchStatement;
-class Visitor;
+class AstVisitor;
class Variable;
class VariableProxy;
class RelocInfo;
V(failure_symbol, "<failure>") \
V(space_symbol, " ") \
V(exec_symbol, "exec") \
- V(zero_symbol, "0")
+ V(zero_symbol, "0") \
+ V(global_eval_symbol, "GlobalEval")
// Forward declaration of the GCTracer class.
virtual Expression* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
- bool is_eval, int pos) {
+ int pos) {
return Call::sentinel();
}
+ virtual Expression* NewCallEval(Expression* expression,
+ ZoneList<Expression*>* arguments,
+ int pos) {
+ return CallEval::sentinel();
+ }
+
virtual Statement* EmptyStatement() {
return NULL;
}
virtual Expression* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
- bool is_eval, int pos) {
- return new Call(expression, arguments, is_eval, pos);
+ int pos) {
+ return new Call(expression, arguments, pos);
+ }
+
+ virtual Expression* NewCallEval(Expression* expression,
+ ZoneList<Expression*>* arguments,
+ int pos) {
+ return new CallEval(expression, arguments, pos);
}
virtual Statement* EmptyStatement() {
// to the calling function context.
if (top_scope_->is_function_scope()) {
// Declare the variable in the function scope.
- var = top_scope_->Lookup(name);
+ var = top_scope_->LookupLocal(name);
if (var == NULL) {
// Declare the name.
var = top_scope_->Declare(name, mode);
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
// Keep track of eval() calls since they disable all local variable
- // 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).
-
+ // 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.
+
+ bool is_potentially_direct_eval = false;
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())) {
- // 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();
- }
+ Handle<String> name = callee->name();
+ Variable* var = top_scope_->Lookup(name);
+ 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();
+ is_potentially_direct_eval = true;
}
}
}
- // Optimize the eval() case w/o arguments so we
- // don't need to handle it every time at runtime.
- //
- // Note: For now we don't do static eval analysis
- // as it appears that we need to be able to call
- // eval() via alias names. We leave the code as
- // is, in case we want to enable this again in the
- // future.
- const bool is_eval = false;
- if (is_eval && args->length() == 0) {
- result = NEW(Literal(Factory::undefined_value()));
+ if (is_potentially_direct_eval) {
+ result = factory()->NewCallEval(result, args, pos);
} else {
- result = factory()->NewCall(result, args, is_eval, pos);
+ result = factory()->NewCall(result, args, pos);
}
break;
}
}
+void PrettyPrinter::VisitCallEval(CallEval* node) {
+ VisitCall(node);
+}
+
+
void PrettyPrinter::VisitCallNew(CallNew* node) {
Print("new (");
Visit(node->expression());
}
+void AstPrinter::VisitCallEval(CallEval* node) {
+ VisitCall(node);
+}
+
+
void AstPrinter::VisitCallNew(CallNew* node) {
IndentedScope indent("CALL NEW");
Visit(node->expression());
#ifdef DEBUG
-class PrettyPrinter: public Visitor {
+class PrettyPrinter: public AstVisitor {
public:
PrettyPrinter();
virtual ~PrettyPrinter();
namespace v8 { namespace internal {
-class AstOptimizer: public Visitor {
+class AstOptimizer: public AstVisitor {
public:
explicit AstOptimizer() {
}
}
+void AstOptimizer::VisitCallEval(CallEval* node) {
+ Visit(node->expression());
+ OptimizeArguments(node->arguments());
+}
+
+
void AstOptimizer::VisitCallNew(CallNew* node) {
Visit(node->expression());
OptimizeArguments(node->arguments());
}
-class Processor: public Visitor {
+class Processor: public AstVisitor {
public:
explicit Processor(VariableProxy* result)
: result_(result),
}
+void Processor::VisitCallEval(CallEval* node) {
+ USE(node);
+ UNREACHABLE();
+}
+
+
void Processor::VisitCallNew(CallNew* node) {
USE(node);
UNREACHABLE();
}
-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);
-
- // 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;
- 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()));
-
- // 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;
-
- // Otherwise, use the global context from the other environment.
- return *target;
-}
-
-
-static Object* Runtime_EvalReceiver(Arguments args) {
- ASSERT(args.length() == 1);
- StackFrameLocator locator;
- JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
- // Fetch the caller context from the frame.
- Context* caller = Context::cast(frame->context());
-
- // 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();
- }
-
- // Otherwise use the given argument (the global object of the
- // receiving context).
- return args[0];
-}
-
-
static Object* Runtime_GlobalReceiver(Arguments args) {
ASSERT(args.length() == 1);
Object* global = args[0];
static Object* Runtime_CompileString(Arguments args) {
HandleScope scope;
- ASSERT(args.length() == 3);
+ ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(String, source, 0);
CONVERT_ARG_CHECKED(Smi, line_offset, 1);
- bool contextual = args[2]->IsTrue();
- RUNTIME_ASSERT(contextual || args[2]->IsFalse());
-
- // 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());
- }
-
// Compile source string.
- bool is_global = context->IsGlobalContext();
Handle<JSFunction> boilerplate =
- Compiler::CompileEval(source, line_offset->value(), is_global);
+ 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;
}
+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.
+ HandleScope scope;
+ StackFrameLocator locator;
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ Handle<Context> context(Context::cast(frame->context()));
+ bool is_global = context->IsGlobalContext();
+
+ // 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);
+ return *fun;
+}
+
+
+static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
+ ASSERT(args.length() == 2);
+
+ HandleScope scope;
+
+ CONVERT_ARG_CHECKED(JSFunction, callee, 0);
+
+ Handle<Object> receiver;
+
+ // 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()) {
+ receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
+ &index, &attributes);
+ if (attributes != ABSENT) break;
+ if (context->is_function_context()) {
+ context = Handle<Context>(Context::cast(context->closure()->context()));
+ } else {
+ context = Handle<Context>(context->previous());
+ }
+ }
+
+ 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 {
+ // '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));
+ }
+ }
+
+ Handle<FixedArray> call = Factory::NewFixedArray(2);
+ call->set(0, *callee);
+ call->set(1, *receiver);
+ return *call;
+}
+
+
static Object* Runtime_CompileScript(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
F(NumberIsFinite, 1) \
\
/* Globals */ \
- F(CompileString, 3) \
+ F(CompileString, 2) \
F(CompileScript, 4) \
F(GlobalPrint, 1) \
\
/* Eval */ \
- F(EvalReceiver, 1) \
F(GlobalReceiver, 1) \
+ F(ResolvePossiblyDirectEval, 2) \
\
F(SetProperty, -1 /* 3 or 4 */) \
F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */) \
-Variable* Scope::Lookup(Handle<String> name) {
+Variable* Scope::LookupLocal(Handle<String> name) {
return locals_.Lookup(name);
}
+Variable* Scope::Lookup(Handle<String> name) {
+ for (Scope* scope = this;
+ scope != NULL;
+ scope = scope->outer_scope()) {
+ Variable* var = scope->LookupLocal(name);
+ if (var != NULL) return var;
+ }
+ return NULL;
+}
+
+
Variable* Scope::DeclareFunctionVar(Handle<String> name) {
ASSERT(is_function_scope() && function_ == NULL);
function_ = new Variable(this, name, Variable::CONST, true, false);
void Scope::AddParameter(Variable* var) {
ASSERT(is_function_scope());
- ASSERT(Lookup(var->name()) == var);
+ ASSERT(LookupLocal(var->name()) == var);
params_.Add(var);
}
}
-void Scope::VisitIllegalRedeclaration(Visitor* visitor) {
+void Scope::VisitIllegalRedeclaration(AstVisitor* visitor) {
ASSERT(HasIllegalRedeclaration());
illegal_redecl_->Accept(visitor);
}
bool guess = scope_calls_eval_;
// Try to find the variable in this scope.
- Variable* var = Lookup(name);
+ Variable* var = LookupLocal(name);
if (var != NULL) {
// We found a variable. If this is not an inner lookup, we are done.
void Scope::AllocateParameterLocals() {
ASSERT(is_function_scope());
- Variable* arguments = Lookup(Factory::arguments_symbol());
+ Variable* arguments = LookupLocal(Factory::arguments_symbol());
ASSERT(arguments != NULL); // functions have 'arguments' declared implicitly
if (MustAllocate(arguments) && !HasArgumentsParameter()) {
// 'arguments' is used. Unless there is also a parameter called
// Declarations
// Lookup a variable in this scope. Returns the variable or NULL if not found.
+ virtual Variable* LookupLocal(Handle<String> name);
+
+ // Lookup a variable in this scope or outer scopes.
+ // Returns the variable or NULL if not found.
virtual Variable* Lookup(Handle<String> name);
// Declare the function variable for a function literal. This variable
// Visit the illegal redeclaration expression. Do not call if the
// scope doesn't have an illegal redeclaration node.
- void VisitIllegalRedeclaration(Visitor* visitor);
+ void VisitIllegalRedeclaration(AstVisitor* visitor);
// Check if the scope has (at least) one illegal redeclaration.
bool HasIllegalRedeclaration() const { return illegal_redecl_ != NULL; }
static const int InitialWeight = 100;
-class UsageComputer: public Visitor {
+class UsageComputer: public AstVisitor {
public:
static bool Traverse(Node* node);
void VisitThrow(Throw* node);
void VisitProperty(Property* node);
void VisitCall(Call* node);
+ void VisitCallEval(CallEval* node);
void VisitCallNew(CallNew* node);
void VisitCallRuntime(CallRuntime* node);
void VisitUnaryOperation(UnaryOperation* node);
}
+void UsageComputer::VisitCallEval(CallEval* node) {
+ VisitCall(node);
+}
+
+
void UsageComputer::VisitCallNew(CallNew* node) {
VisitCall(node);
}
function GlobalEval(x) {
if (!IS_STRING(x)) return x;
- if (this !== %GlobalReceiver(global)) {
+ if (this !== global && this !== %GlobalReceiver(global)) {
throw new $EvalError('The "this" object passed to eval must ' +
'be the global object from which eval originated');
}
- var f = %CompileString(x, 0, true);
+ var f = %CompileString(x, 0);
if (!IS_FUNCTION(f)) return f;
- return f.call(%EvalReceiver(this));
+ return f.call(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, false);
+ var f = %CompileString(ToString(expr), 0);
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, false)();
+ var f = %CompileString(source, -1)();
%FunctionSetName(f, "anonymous");
return %SetNewFunctionAttributes(f);
}
}
-THREADED_TEST(Eval) {
+THREADED_TEST(EvalAliasedDynamic) {
v8::HandleScope scope;
LocalContext current;
- // Test that un-aliased eval uses local context.
+ // 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("foo = 0;"
- "(function() {"
+ Script::Compile(v8_str("function f(x) { "
" var foo = 2;"
- " return eval('foo');"
- "})();"));
- Local<Value> result = script->Run();
- CHECK_EQ(2, result->Int32Value());
+ " with (x) { return eval('foo'); }"
+ "}"
+ "foo = 0;"
+ "result1 = f(new Object());"
+ "result2 = f(global);"
+ "var x = new Object();"
+ "x.eval = function(x) { return 1; };"
+ "result3 = f(x);"));
+ script->Run();
+ CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
+ CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
+ CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
- // Test that un-aliased eval has right this.
+ v8::TryCatch try_catch;
script =
- Script::Compile(v8_str("function MyObject() { this.self = eval('this'); }"
- "var o = new MyObject();"
- "o === o.self"));
- result = script->Run();
- CHECK(result->IsTrue());
+ Script::Compile(v8_str("function f(x) { "
+ " var bar = 2;"
+ " with (x) { return eval('bar'); }"
+ "}"
+ "f(global)"));
+ script->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
}
--- /dev/null
+// Copyright 2008 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.
+
+assertEquals(void 0, eval());
+assertEquals(4, eval(4));
+
+function f() { return 'The f function'; };
+assertTrue(f === eval(f));
+
+function g(x, y) { return 4; };
+
+count = 0;
+assertEquals(4, eval('2 + 2', count++));
+assertEquals(1, count);
+
+try {
+ eval('hest 7 &*^*&^');
+ assertTrue(false, 'Did not throw on syntax error.');
+} catch (e) {
+ assertEquals('SyntaxError', e.name);
+}
+
+
+// eval has special evaluation order for consistency with other browsers.
+global_eval = eval;
+assertEquals(void 0, eval(eval("var eval = function f(x) { return 'hest';}")))
+eval = global_eval;
+
+//Test eval with different number of parameters.
+global_eval = eval;
+eval = function(x, y) { return x + y; };
+assertEquals(4, eval(2, 2));
+eval = global_eval;
+
+// Test that un-aliased eval reads from local context.
+foo = 0;
+result =
+ (function() {
+ var foo = 2;
+ return eval('foo');
+ })();
+assertEquals(2, result);
+
+//Test that un-aliased eval writes to local context.
+foo = 0;
+result =
+ (function() {
+ var foo = 1;
+ eval('foo = 2');
+ return foo;
+ })();
+assertEquals(2, result);
+assertEquals(0, foo);
+
+// Test that un-aliased eval has right receiver.
+function MyObject() { this.self = eval('this'); }
+var o = new MyObject();
+assertTrue(o === o.self);
+
+// Test that aliased eval reads from global context.
+var e = eval;
+foo = 0;
+result =
+ (function() {
+ var foo = 2;
+ return e('foo');
+ })();
+assertEquals(0, result);
+
+// Test that aliased eval writes to global context.
+var e = eval;
+foo = 0;
+(function() { e('var foo = 2;'); })();
+assertEquals(2, foo);
+
+// Test that aliased eval has right receiver.
+function MyOtherObject() { this.self = e('this'); }
+var o = new MyOtherObject();
+assertTrue(this === o.self);
+
+// Try to cheat the 'aliased eval' detection.
+var x = this;
+foo = 0;
+result =
+ (function() {
+ var foo = 2;
+ return x.eval('foo');
+ })();
+assertEquals(0, result);
+
+foo = 0;
+result =
+ (function() {
+ var eval = function(x) { return x; };
+ var foo = eval(2);
+ return e('foo');
+ })();
+assertEquals(0, result);
+
+result =
+ (function() {
+ var eval = function(x) { return 2 * x; };
+ return (function() { return eval(2); })();
+ })();
+assertEquals(4, result);
+
+eval = function(x) { return 2 * x; };
+result =
+ (function() {
+ return (function() { return eval(2); })();
+ })();
+assertEquals(4, result);
f(null);
// Check called from eval.
-eval('f(eval)');
+eval('f(null)');
"LazyCompile": true,
"CreateObjectLiteralBoilerplate": true,
"CloneObjectLiteralBoilerplate": true,
- "IS_VAR": true
+ "IS_VAR": true,
+ "ResolvePossiblyDirectEval": true
};
var currentlyUncallable = {
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