Change implementation of eval to make an exact distinction between direct eval and...
authorolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 27 Nov 2008 13:55:06 +0000 (13:55 +0000)
committerolehougaard <olehougaard@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 27 Nov 2008 13:55:06 +0000 (13:55 +0000)
Review URL: http://codereview.chromium.org/12673

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@860 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

25 files changed:
src/ast.cc
src/ast.h
src/codegen-arm.cc
src/codegen-arm.h
src/codegen-ia32.cc
src/codegen-ia32.h
src/contexts.cc
src/debug-delay.js
src/globals.h
src/heap.h
src/parser.cc
src/prettyprinter.cc
src/prettyprinter.h
src/rewriter.cc
src/runtime.cc
src/runtime.h
src/scopes.cc
src/scopes.h
src/usage-analyzer.cc
src/v8natives.js
test/cctest/test-api.cc
test/mjsunit/eval.js [new file with mode: 0644]
test/mjsunit/function-caller.js
test/mjsunit/fuzz-natives.js
test/mozilla/mozilla.status

index 6c25f5aa23c1c6445cc0fe69397d10d92a9a6abf..2ba442158b46dc23307c897694a15e1a7a169525 100644 (file)
@@ -38,14 +38,15 @@ VariableProxySentinel VariableProxySentinel::this_proxy_(true);
 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);                \
   }
@@ -158,17 +159,17 @@ void LabelCollector::AddLabel(Label* label) {
 
 
 // ----------------------------------------------------------------------------
-// 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
index dbb581d85ff0f959bc4c7eda837c95c78b25b127..a301ab7e9e354ce07972dd53e1c187234f8b82ef 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -81,6 +81,7 @@ namespace v8 { namespace internal {
   V(Throw)                                      \
   V(Property)                                   \
   V(Call)                                       \
+  V(CallEval)                                    \
   V(CallNew)                                    \
   V(CallRuntime)                                \
   V(UnaryOperation)                             \
@@ -104,7 +105,7 @@ class Node: public ZoneObject {
  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; }
@@ -168,7 +169,7 @@ class Expression: public Node {
 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_;
@@ -221,7 +222,7 @@ class Block: public BreakableStatement {
         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); }
 
@@ -245,7 +246,7 @@ class Declaration: public Node {
     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_; }
@@ -302,7 +303,7 @@ class LoopStatement: public IterationStatement {
     next_ = next;
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Type type() const  { return type_; }
   Statement* init() const  { return init_; }
@@ -332,7 +333,7 @@ class ForInStatement: public IterationStatement {
     enumerable_ = enumerable;
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Expression* each() const { return each_; }
   Expression* enumerable() const { return enumerable_; }
@@ -348,7 +349,7 @@ class ExpressionStatement: public Statement {
   explicit ExpressionStatement(Expression* expression)
       : expression_(expression) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion.
   virtual ExpressionStatement* AsExpressionStatement() { return this; }
@@ -366,7 +367,7 @@ class ContinueStatement: public Statement {
   explicit ContinueStatement(IterationStatement* target)
       : target_(target) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   IterationStatement* target() const  { return target_; }
 
@@ -380,7 +381,7 @@ class BreakStatement: public Statement {
   explicit BreakStatement(BreakableStatement* target)
       : target_(target) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   BreakableStatement* target() const  { return target_; }
 
@@ -394,7 +395,7 @@ class ReturnStatement: public Statement {
   explicit ReturnStatement(Expression* expression)
       : expression_(expression) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion.
   virtual ReturnStatement* AsReturnStatement() { return this; }
@@ -411,7 +412,7 @@ class WithEnterStatement: public Statement {
   explicit WithEnterStatement(Expression* expression)
       : expression_(expression) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Expression* expression() const  { return expression_; }
 
@@ -424,7 +425,7 @@ class WithExitStatement: public Statement {
  public:
   WithExitStatement() { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 };
 
 
@@ -457,7 +458,7 @@ class SwitchStatement: public BreakableStatement {
     cases_ = cases;
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Expression* tag() const  { return tag_; }
   ZoneList<CaseClause*>* cases() const  { return cases_; }
@@ -482,7 +483,7 @@ class IfStatement: public Statement {
         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(); }
@@ -510,7 +511,7 @@ class LabelCollector: public Node {
   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_; }
@@ -547,7 +548,7 @@ class TryCatch: public TryStatement {
     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_; }
@@ -564,7 +565,7 @@ class TryFinally: public TryStatement {
       : TryStatement(try_block),
         finally_block_(finally_block) { }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Block* finally_block() const { return finally_block_; }
 
@@ -575,13 +576,13 @@ class TryFinally: public TryStatement {
 
 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; }
@@ -592,7 +593,7 @@ class Literal: public Expression {
  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; }
@@ -665,7 +666,7 @@ class ObjectLiteral: public MaterializedLiteral {
         properties_(properties) {
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   Handle<FixedArray> constant_properties() const {
     return constant_properties_;
@@ -688,7 +689,7 @@ class RegExpLiteral: public MaterializedLiteral {
         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_; }
@@ -707,7 +708,7 @@ class ArrayLiteral: public Expression {
       : 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_; }
@@ -720,7 +721,7 @@ class ArrayLiteral: public Expression {
 
 class VariableProxy: public Expression {
  public:
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion
   virtual Property* AsProperty() {
@@ -816,7 +817,7 @@ class Slot: public Expression {
     ASSERT(var != NULL);
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion
   virtual Slot* AsSlot()  { return this; }
@@ -838,7 +839,7 @@ class Property: public Expression {
   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; }
@@ -867,21 +868,18 @@ class Call: public Expression {
  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_; }
@@ -889,7 +887,6 @@ class Call: public Expression {
  private:
   Expression* expression_;
   ZoneList<Expression*>* arguments_;
-  bool is_eval_;
   int pos_;
 
   static Call sentinel_;
@@ -899,9 +896,27 @@ class Call: public Expression {
 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_;
 };
 
 
@@ -916,7 +931,7 @@ class CallRuntime: public Expression {
               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_; }
@@ -936,7 +951,7 @@ class UnaryOperation: public Expression {
     ASSERT(Token::IsUnaryOp(op));
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion
   virtual UnaryOperation* AsUnaryOperation() { return this; }
@@ -957,7 +972,7 @@ class BinaryOperation: public Expression {
     ASSERT(Token::IsBinaryOp(op));
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion
   virtual BinaryOperation* AsBinaryOperation() { return this; }
@@ -1006,7 +1021,7 @@ class CountOperation: public Expression {
     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_; }
@@ -1029,7 +1044,7 @@ class CompareOperation: public Expression {
     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_; }
@@ -1051,7 +1066,7 @@ class Conditional: public Expression {
         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_; }
@@ -1071,7 +1086,7 @@ class Assignment: public 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;
@@ -1094,7 +1109,7 @@ class Throw: public Expression {
   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_; }
 
@@ -1130,7 +1145,7 @@ class FunctionLiteral: public Expression {
         function_token_position_(RelocInfo::kNoPosition) {
   }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
   // Type testing & conversion
   virtual FunctionLiteral* AsFunctionLiteral()  { return this; }
@@ -1179,7 +1194,7 @@ class FunctionBoilerplateLiteral: public Expression {
 
   Handle<JSFunction> boilerplate() const { return boilerplate_; }
 
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 
  private:
   Handle<JSFunction> boilerplate_;
@@ -1188,7 +1203,7 @@ class FunctionBoilerplateLiteral: public Expression {
 
 class ThisFunction: public Expression {
  public:
-  virtual void Accept(Visitor* v);
+  virtual void Accept(AstVisitor* v);
 };
 
 
@@ -1465,10 +1480,10 @@ class RegExpVisitor BASE_EMBEDDED {
 // 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); }
index e5120ae73480e599ad2d0f541a3aeb18b4309f1d..494257a6d397d72f1662c6fb99045e156389b255 100644 (file)
@@ -2303,7 +2303,7 @@ void CodeGenerator::VisitCall(Call* node) {
 
   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.
@@ -2430,6 +2430,58 @@ void CodeGenerator::VisitCall(Call* node) {
 }
 
 
+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");
 
index 2b889cd724e884a0679085dd84e02712d9ab3221..904a3b71e1ba0cd61822178ffb9134c43b4414fe 100644 (file)
@@ -187,7 +187,7 @@ class CodeGenState BASE_EMBEDDED {
 // -------------------------------------------------------------------------
 // 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.
index 230dc174769f2bf5fa80bd1130eb265c0ae62efe..c5987a3cf0ffbd4440587372d54f83d65a769372 100644 (file)
@@ -2836,6 +2836,54 @@ void CodeGenerator::VisitCallNew(CallNew* node) {
 }
 
 
+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));
index ee6df47f713678479c281a191f957aeae92f0339..a1525c70edcff9ec64b7dd86b8bac1890bc9c5dc 100644 (file)
@@ -193,7 +193,7 @@ class CodeGenState BASE_EMBEDDED {
 // -------------------------------------------------------------------------
 // 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.
index 6aaa4ccf377a723ba154056419effeec82b9ee09..0f3f6088d17e7e45d60e1852cfc2b90d8e8ad79f 100644 (file)
@@ -74,14 +74,6 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
                                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;
index 686a81d83e270f2f2a7f1c71a591a9ab8972f211..17b82b573dd32f814de973acb8e92d97142134f8 100644 (file)
@@ -1012,7 +1012,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request,
   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);
@@ -1465,7 +1465,7 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
 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;
index 69e9006bfa9e1d9f4d9913c7d3d01872cf9ed169..e2fb2a9f226439d88f2b96144a8d17a6de12fe66 100644 (file)
@@ -197,7 +197,7 @@ class Statement;
 class String;
 class Struct;
 class SwitchStatement;
-class Visitor;
+class AstVisitor;
 class Variable;
 class VariableProxy;
 class RelocInfo;
index bbd679b3c1a7cf2efcb9aa8f32a68e1cde15b0c0..f17a091340c1b4f6a0f7c8548388a8c00b56fc46 100644 (file)
@@ -193,7 +193,8 @@ namespace v8 { namespace internal {
   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.
index 6c6712d95c8071639cdc1335a85b7ac97f9c57f1..64a87d568c4890e2e5c16f351ae1f2e81190db58 100644 (file)
@@ -667,10 +667,16 @@ class ParserFactory BASE_EMBEDDED {
 
   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;
   }
@@ -717,8 +723,14 @@ class AstBuildingParserFactory : public ParserFactory {
 
   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() {
@@ -1343,7 +1355,7 @@ VariableProxy* AstBuildingParser::Declare(Handle<String> name,
   // 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);
@@ -2624,55 +2636,34 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
         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;
       }
index 39af187ff600cde68a8691544c60b4b0e08c7dba..a9143836a0f9c78dcfebc0367c38577db868368e 100644 (file)
@@ -348,6 +348,11 @@ void PrettyPrinter::VisitCall(Call* node) {
 }
 
 
+void PrettyPrinter::VisitCallEval(CallEval* node) {
+  VisitCall(node);
+}
+
+
 void PrettyPrinter::VisitCallNew(CallNew* node) {
   Print("new (");
   Visit(node->expression());
@@ -1016,6 +1021,11 @@ void AstPrinter::VisitCall(Call* node) {
 }
 
 
+void AstPrinter::VisitCallEval(CallEval* node) {
+  VisitCall(node);
+}
+
+
 void AstPrinter::VisitCallNew(CallNew* node) {
   IndentedScope indent("CALL NEW");
   Visit(node->expression());
index 58cac51b7565a8e3c245dbf5f15bc1cd2878ad7d..1c94635ef49022b2c258a345a6b19f32255dff42 100644 (file)
@@ -34,7 +34,7 @@ namespace v8 { namespace internal {
 
 #ifdef DEBUG
 
-class PrettyPrinter: public Visitor {
+class PrettyPrinter: public AstVisitor {
  public:
   PrettyPrinter();
   virtual ~PrettyPrinter();
index c5ca7cd29294c3c4878542fad2b52f58fc4119ef..f0d53b8e241a8e792ef0ae213a30a6a672b66fe4 100644 (file)
@@ -34,7 +34,7 @@
 namespace v8 { namespace internal {
 
 
-class AstOptimizer: public Visitor {
+class AstOptimizer: public AstVisitor {
  public:
   explicit AstOptimizer() {
   }
@@ -344,6 +344,12 @@ void AstOptimizer::VisitCall(Call* node) {
 }
 
 
+void AstOptimizer::VisitCallEval(CallEval* node) {
+  Visit(node->expression());
+  OptimizeArguments(node->arguments());
+}
+
+
 void AstOptimizer::VisitCallNew(CallNew* node) {
   Visit(node->expression());
   OptimizeArguments(node->arguments());
@@ -469,7 +475,7 @@ void AstOptimizer::VisitThisFunction(ThisFunction* node) {
 }
 
 
-class Processor: public Visitor {
+class Processor: public AstVisitor {
  public:
   explicit Processor(VariableProxy* result)
       : result_(result),
@@ -702,6 +708,12 @@ void Processor::VisitCall(Call* node) {
 }
 
 
+void Processor::VisitCallEval(CallEval* node) {
+  USE(node);
+  UNREACHABLE();
+}
+
+
 void Processor::VisitCallNew(CallNew* node) {
   USE(node);
   UNREACHABLE();
index 5ac92649cac2af6bcad9f37262bfc0cf62c32e27..8ff2f8d44e2d0212d1a6a075ff7991cabd666845 100644 (file)
@@ -3934,57 +3934,6 @@ static Object* Runtime_NumberIsFinite(Arguments args) {
 }
 
 
-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];
@@ -3995,37 +3944,112 @@ static Object* Runtime_GlobalReceiver(Arguments args) {
 
 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);
index 36a7180eb28711d6e1916b9acf8541d4449b0690..98559d44747c78fc9924088fe09a44f934c9b381 100644 (file)
@@ -190,13 +190,13 @@ namespace v8 { namespace internal {
   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 */) \
index 4f84bbb75faa3390003bdffafa9e285e3b5f8f47..fcdc57749cbe33a68d2dfe3208849f3c1291af5f 100644 (file)
@@ -184,11 +184,22 @@ void Scope::Initialize(bool inside_with) {
 
 
 
-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);
@@ -207,7 +218,7 @@ Variable* Scope::Declare(Handle<String> name, Variable::Mode mode) {
 
 void Scope::AddParameter(Variable* var) {
   ASSERT(is_function_scope());
-  ASSERT(Lookup(var->name()) == var);
+  ASSERT(LookupLocal(var->name()) == var);
   params_.Add(var);
 }
 
@@ -258,7 +269,7 @@ void Scope::SetIllegalRedeclaration(Expression* expression) {
 }
 
 
-void Scope::VisitIllegalRedeclaration(Visitor* visitor) {
+void Scope::VisitIllegalRedeclaration(AstVisitor* visitor) {
   ASSERT(HasIllegalRedeclaration());
   illegal_redecl_->Accept(visitor);
 }
@@ -513,7 +524,7 @@ Variable* Scope::LookupRecursive(Handle<String> name, bool inner_lookup) {
   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.
@@ -707,7 +718,7 @@ void Scope::AllocateHeapSlot(Variable* var) {
 
 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
index 05e462b6b51f575b3555089ec04afb8721bdbc9a..1eee225de38a81ca77698c8d63d5eb940c899569 100644 (file)
@@ -88,6 +88,10 @@ class Scope: public ZoneObject {
   // 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
@@ -138,7 +142,7 @@ class Scope: public ZoneObject {
 
   // 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; }
index 43f29a15ac3fee56a1d97ab38787f0e732ca034a..ad26cac008ec6a839c63725a8504e15e15e8f155 100644 (file)
@@ -39,7 +39,7 @@ static const int MaxWeight = 1000000;
 static const int InitialWeight = 100;
 
 
-class UsageComputer: public Visitor {
+class UsageComputer: public AstVisitor {
  public:
   static bool Traverse(Node* node);
 
@@ -73,6 +73,7 @@ class UsageComputer: public Visitor {
   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);
@@ -321,6 +322,11 @@ void UsageComputer::VisitCall(Call* node) {
 }
 
 
+void UsageComputer::VisitCallEval(CallEval* node) {
+  VisitCall(node);
+}
+
+
 void UsageComputer::VisitCallNew(CallNew* node) {
   VisitCall(node);
 }
index f3c98a5f8e684104adc7f6c12321baaeffe6e6f5..9ca3b082233ceef828323938110fbde989014007 100644 (file)
@@ -105,15 +105,15 @@ function GlobalParseFloat(string) {
 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);
 }
 
 
@@ -121,7 +121,7 @@ function GlobalEval(x) {
 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;
@@ -140,7 +140,7 @@ function SetupGlobal() {
 
   // 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,
@@ -521,7 +521,7 @@ function NewFunction(arg1) {  // length == 1
 
   // 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);
 }
index e1549ebcdf71078b6893987531c98723bac3f194..ab2096b76f82802360815a9465bd8dd2e4eefb4a 100644 (file)
@@ -4018,27 +4018,41 @@ THREADED_TEST(FunctionDescriptorException) {
 }
 
 
-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();
 }
 
 
diff --git a/test/mjsunit/eval.js b/test/mjsunit/eval.js
new file mode 100644 (file)
index 0000000..08bd3d0
--- /dev/null
@@ -0,0 +1,136 @@
+// 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);
index f7493467a5a0c80139b0679b2dcb4920f4971668..ddc7b5df28279afd54360ce6f10046b38ab9ec0d 100644 (file)
@@ -44,5 +44,5 @@ h();
 f(null);
 
 // Check called from eval.
-eval('f(eval)');
+eval('f(null)');
 
index d716d18295e7acd4d8169f2a0e3e9404e66c419c..5b5d6d9a2ddf415886390ef55797412128c212f6 100644 (file)
@@ -122,7 +122,8 @@ var knownProblems = {
   "LazyCompile": true,
   "CreateObjectLiteralBoilerplate": true,
   "CloneObjectLiteralBoilerplate": true,
-  "IS_VAR": true
+  "IS_VAR": true,
+  "ResolvePossiblyDirectEval": true
 };
 
 var currentlyUncallable = {
index f6d3c2c54303b8c6c06ce419f843e89aa9656c5d..9e703ae75c32d84d9e7b0b7cd7d68e3262322d0a 100644 (file)
@@ -293,10 +293,6 @@ js1_5/Regress/regress-234389: FAIL_OK
 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
@@ -584,10 +580,6 @@ ecma_3/String/15.5.4.11: FAIL
 # 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