Make invalid LHSs that are calls late errors
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Apr 2014 11:03:05 +0000 (11:03 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Apr 2014 11:03:05 +0000 (11:03 +0000)
Necessary for web legacy compatibility.

Also fold in additional strict mode checks into LHS checks.
Minor constness clean-ups on the way.

R=marja@chromium.org
BUG=chromium:358346
LOG=Y

Review URL: https://codereview.chromium.org/217823003

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

17 files changed:
src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast.cc
src/ast.h
src/heap.h
src/ia32/full-codegen-ia32.cc
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/variables.cc
src/variables.h
src/x64/full-codegen-x64.cc
test/cctest/test-parsing.cc
test/mjsunit/invalid-lhs.js
test/webkit/fast/js/modify-non-references-expected.txt
test/webkit/fast/js/modify-non-references.js

index 3539bbc..4ea2942 100644 (file)
@@ -1890,7 +1890,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
 
 
 void FullCodeGenerator::VisitAssignment(Assignment* expr) {
-  ASSERT(expr->target()->IsValidLeftHandSide());
+  ASSERT(expr->target()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ Assignment");
 
@@ -2432,7 +2432,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
 
 
 void FullCodeGenerator::EmitAssignment(Expression* expr) {
-  ASSERT(expr->IsValidLeftHandSide());
+  ASSERT(expr->IsValidReferenceExpression());
 
   // Left-hand side can only be a property, a global or a (parameter or local)
   // slot.
@@ -4294,7 +4294,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 
 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
-  ASSERT(expr->expression()->IsValidLeftHandSide());
+  ASSERT(expr->expression()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ CountOperation");
   SetSourcePosition(expr->position());
index 46dabae..819868b 100644 (file)
@@ -1890,7 +1890,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
 
 
 void FullCodeGenerator::VisitAssignment(Assignment* expr) {
-  ASSERT(expr->target()->IsValidLeftHandSide());
+  ASSERT(expr->target()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ Assignment");
 
@@ -2131,7 +2131,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
 
 
 void FullCodeGenerator::EmitAssignment(Expression* expr) {
-  ASSERT(expr->IsValidLeftHandSide());
+  ASSERT(expr->IsValidReferenceExpression());
 
   // Left-hand side can only be a property, a global or a (parameter or local)
   // slot.
@@ -4005,7 +4005,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 
 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
-  ASSERT(expr->expression()->IsValidLeftHandSide());
+  ASSERT(expr->expression()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ CountOperation");
   SetSourcePosition(expr->position());
index f6cf189..4781143 100644 (file)
@@ -56,23 +56,23 @@ AST_NODE_LIST(DECL_ACCEPT)
 // Implementation of other node functionality.
 
 
-bool Expression::IsSmiLiteral() {
+bool Expression::IsSmiLiteral() const {
   return AsLiteral() != NULL && AsLiteral()->value()->IsSmi();
 }
 
 
-bool Expression::IsStringLiteral() {
+bool Expression::IsStringLiteral() const {
   return AsLiteral() != NULL && AsLiteral()->value()->IsString();
 }
 
 
-bool Expression::IsNullLiteral() {
+bool Expression::IsNullLiteral() const {
   return AsLiteral() != NULL && AsLiteral()->value()->IsNull();
 }
 
 
-bool Expression::IsUndefinedLiteral(Isolate* isolate) {
-  VariableProxy* var_proxy = AsVariableProxy();
+bool Expression::IsUndefinedLiteral(Isolate* isolate) const {
+  const VariableProxy* var_proxy = AsVariableProxy();
   if (var_proxy == NULL) return false;
   Variable* var = var_proxy->var();
   // The global identifier "undefined" is immutable. Everything
@@ -463,7 +463,7 @@ void BinaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
 }
 
 
-bool BinaryOperation::ResultOverwriteAllowed() {
+bool BinaryOperation::ResultOverwriteAllowed() const {
   switch (op_) {
     case Token::COMMA:
     case Token::OR:
index c6ee71e..486db60 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -215,9 +215,14 @@ class AstNode: public ZoneObject {
   int position() const { return position_; }
 
   // Type testing & conversion functions overridden by concrete subclasses.
-#define DECLARE_NODE_FUNCTIONS(type)                  \
-  bool Is##type() { return node_type() == AstNode::k##type; }          \
-  type* As##type() { return Is##type() ? reinterpret_cast<type*>(this) : NULL; }
+#define DECLARE_NODE_FUNCTIONS(type) \
+  bool Is##type() const { return node_type() == AstNode::k##type; } \
+  type* As##type() { \
+    return Is##type() ? reinterpret_cast<type*>(this) : NULL; \
+  } \
+  const type* As##type() const { \
+    return Is##type() ? reinterpret_cast<const type*>(this) : NULL; \
+  }
   AST_NODE_LIST(DECLARE_NODE_FUNCTIONS)
 #undef DECLARE_NODE_FUNCTIONS
 
@@ -325,35 +330,35 @@ class Expression : public AstNode {
     kTest
   };
 
-  virtual bool IsValidLeftHandSide() { return false; }
+  virtual bool IsValidReferenceExpression() const { return false; }
 
   // Helpers for ToBoolean conversion.
-  virtual bool ToBooleanIsTrue() { return false; }
-  virtual bool ToBooleanIsFalse() { return false; }
+  virtual bool ToBooleanIsTrue() const { return false; }
+  virtual bool ToBooleanIsFalse() const { return false; }
 
   // Symbols that cannot be parsed as array indices are considered property
   // names.  We do not treat symbols that can be array indexes as property
   // names because [] for string objects is handled only by keyed ICs.
-  virtual bool IsPropertyName() { return false; }
+  virtual bool IsPropertyName() const { return false; }
 
   // True iff the result can be safely overwritten (to avoid allocation).
   // False for operations that can return one of their operands.
-  virtual bool ResultOverwriteAllowed() { return false; }
+  virtual bool ResultOverwriteAllowed() const { return false; }
 
   // True iff the expression is a literal represented as a smi.
-  bool IsSmiLiteral();
+  bool IsSmiLiteral() const;
 
   // True iff the expression is a string literal.
-  bool IsStringLiteral();
+  bool IsStringLiteral() const;
 
   // True iff the expression is the null literal.
-  bool IsNullLiteral();
+  bool IsNullLiteral() const;
 
   // True if we can prove that the expression is the undefined literal.
-  bool IsUndefinedLiteral(Isolate* isolate);
+  bool IsUndefinedLiteral(Isolate* isolate) const;
 
   // Expression type bounds
-  Bounds bounds() { return bounds_; }
+  Bounds bounds() const { return bounds_; }
   void set_bounds(Bounds bounds) { bounds_ = bounds; }
 
   // Type feedback information for assignments and properties.
@@ -1346,7 +1351,7 @@ class Literal V8_FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(Literal)
 
-  virtual bool IsPropertyName() V8_OVERRIDE {
+  virtual bool IsPropertyName() const V8_OVERRIDE {
     if (value_->IsInternalizedString()) {
       uint32_t ignored;
       return !String::cast(*value_)->AsArrayIndex(&ignored);
@@ -1359,10 +1364,10 @@ class Literal V8_FINAL : public Expression {
     return Handle<String>::cast(value_);
   }
 
-  virtual bool ToBooleanIsTrue() V8_OVERRIDE {
+  virtual bool ToBooleanIsTrue() const V8_OVERRIDE {
     return value_->BooleanValue();
   }
-  virtual bool ToBooleanIsFalse() V8_OVERRIDE {
+  virtual bool ToBooleanIsFalse() const V8_OVERRIDE {
     return !value_->BooleanValue();
   }
 
@@ -1637,19 +1642,17 @@ class VariableProxy V8_FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(VariableProxy)
 
-  virtual bool IsValidLeftHandSide() V8_OVERRIDE {
-    return var_ == NULL ? true : var_->IsValidLeftHandSide();
+  virtual bool IsValidReferenceExpression() const V8_OVERRIDE {
+    return var_ == NULL ? true : var_->IsValidReference();
   }
 
-  bool IsVariable(Handle<String> n) {
+  bool IsVariable(Handle<String> n) const {
     return !is_this() && name().is_identical_to(n);
   }
 
-  bool IsArguments() { return var_ != NULL && var_->is_arguments(); }
+  bool IsArguments() const { return var_ != NULL && var_->is_arguments(); }
 
-  bool IsLValue() {
-    return is_lvalue_;
-  }
+  bool IsLValue() const { return is_lvalue_; }
 
   Handle<String> name() const { return name_; }
   Variable* var() const { return var_; }
@@ -1687,7 +1690,7 @@ class Property V8_FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(Property)
 
-  virtual bool IsValidLeftHandSide() V8_OVERRIDE { return true; }
+  virtual bool IsValidReferenceExpression() const V8_OVERRIDE { return true; }
 
   Expression* obj() const { return obj_; }
   Expression* key() const { return key_; }
@@ -1970,7 +1973,7 @@ class BinaryOperation V8_FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(BinaryOperation)
 
-  virtual bool ResultOverwriteAllowed();
+  virtual bool ResultOverwriteAllowed() const V8_OVERRIDE;
 
   Token::Value op() const { return op_; }
   Expression* left() const { return left_; }
index 4b67a95..b811092 100644 (file)
@@ -347,11 +347,7 @@ namespace internal {
   V(MakeReferenceError_string, "MakeReferenceError")                     \
   V(MakeSyntaxError_string, "MakeSyntaxError")                           \
   V(MakeTypeError_string, "MakeTypeError")                               \
-  V(illegal_return_string, "illegal_return")                             \
-  V(illegal_break_string, "illegal_break")                               \
-  V(illegal_continue_string, "illegal_continue")                         \
   V(unknown_label_string, "unknown_label")                               \
-  V(redeclaration_string, "redeclaration")                               \
   V(space_string, " ")                                                   \
   V(exec_string, "exec")                                                 \
   V(zero_string, "0")                                                    \
index c3c1bda..b021e46 100644 (file)
@@ -1851,7 +1851,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
 
 
 void FullCodeGenerator::VisitAssignment(Assignment* expr) {
-  ASSERT(expr->target()->IsValidLeftHandSide());
+  ASSERT(expr->target()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ Assignment");
 
@@ -2382,7 +2382,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
 
 
 void FullCodeGenerator::EmitAssignment(Expression* expr) {
-  ASSERT(expr->IsValidLeftHandSide());
+  ASSERT(expr->IsValidReferenceExpression());
 
   // Left-hand side can only be a property, a global or a (parameter or local)
   // slot.
@@ -4297,7 +4297,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 
 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
-  ASSERT(expr->expression()->IsValidLeftHandSide());
+  ASSERT(expr->expression()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ CountOperation");
   SetSourcePosition(expr->position());
index 16a1028..38a4fe5 100644 (file)
@@ -423,10 +423,9 @@ class TargetScope BASE_EMBEDDED {
 // Implementation of Parser
 
 bool ParserTraits::IsEvalOrArguments(Handle<String> identifier) const {
-  return identifier.is_identical_to(
-             parser_->isolate()->factory()->eval_string()) ||
-         identifier.is_identical_to(
-             parser_->isolate()->factory()->arguments_string());
+  Factory* factory = parser_->isolate()->factory();
+  return identifier.is_identical_to(factory->eval_string())
+      || identifier.is_identical_to(factory->arguments_string());
 }
 
 
@@ -485,19 +484,6 @@ Expression* ParserTraits::MarkExpressionAsLValue(Expression* expression) {
 }
 
 
-void ParserTraits::CheckStrictModeLValue(Expression* expression,
-                                         bool* ok) {
-  VariableProxy* lhs = expression != NULL
-      ? expression->AsVariableProxy()
-      : NULL;
-  if (lhs != NULL && !lhs->is_this() && IsEvalOrArguments(lhs->name())) {
-    parser_->ReportMessage("strict_eval_arguments",
-                           Vector<const char*>::empty());
-    *ok = false;
-  }
-}
-
-
 bool ParserTraits::ShortcutNumericLiteralBinaryExpression(
     Expression** x, Expression* y, Token::Value op, int pos,
     AstNodeFactory<AstConstructionVisitor>* factory) {
@@ -604,6 +590,62 @@ Expression* ParserTraits::BuildUnaryExpression(
 }
 
 
+Expression* ParserTraits::NewThrowReferenceError(
+    const char* message, int pos) {
+  return NewThrowError(
+      parser_->isolate()->factory()->MakeReferenceError_string(),
+      message, HandleVector<Object>(NULL, 0), pos);
+}
+
+
+Expression* ParserTraits::NewThrowSyntaxError(
+    const char* message, Handle<Object> arg, int pos) {
+  int argc = arg.is_null() ? 0 : 1;
+  Vector< Handle<Object> > arguments = HandleVector<Object>(&arg, argc);
+  return NewThrowError(
+      parser_->isolate()->factory()->MakeSyntaxError_string(),
+      message, arguments, pos);
+}
+
+
+Expression* ParserTraits::NewThrowTypeError(
+    const char* message, Handle<Object> arg1, Handle<Object> arg2, int pos) {
+  ASSERT(!arg1.is_null() && !arg2.is_null());
+  Handle<Object> elements[] = { arg1, arg2 };
+  Vector< Handle<Object> > arguments =
+      HandleVector<Object>(elements, ARRAY_SIZE(elements));
+  return NewThrowError(
+      parser_->isolate()->factory()->MakeTypeError_string(),
+      message, arguments, pos);
+}
+
+
+Expression* ParserTraits::NewThrowError(
+    Handle<String> constructor, const char* message,
+    Vector<Handle<Object> > arguments, int pos) {
+  Zone* zone = parser_->zone();
+  Factory* factory = parser_->isolate()->factory();
+  int argc = arguments.length();
+  Handle<FixedArray> elements = factory->NewFixedArray(argc, TENURED);
+  for (int i = 0; i < argc; i++) {
+    Handle<Object> element = arguments[i];
+    if (!element.is_null()) {
+      elements->set(i, *element);
+    }
+  }
+  Handle<JSArray> array =
+      factory->NewJSArrayWithElements(elements, FAST_ELEMENTS, TENURED);
+
+  ZoneList<Expression*>* args = new(zone) ZoneList<Expression*>(2, zone);
+  Handle<String> type = factory->InternalizeUtf8String(message);
+  args->Add(parser_->factory()->NewLiteral(type, pos), zone);
+  args->Add(parser_->factory()->NewLiteral(array, pos), zone);
+  CallRuntime* call_constructor =
+      parser_->factory()->NewCallRuntime(constructor, NULL, args, pos);
+  return parser_->factory()->NewThrow(call_constructor, pos);
+}
+
+
 void ParserTraits::ReportMessageAt(Scanner::Location source_location,
                                    const char* message,
                                    Vector<const char*> args,
@@ -680,7 +722,7 @@ Handle<String> ParserTraits::GetSymbol(Scanner* scanner) {
     }
   }
   Handle<String> result =
-      parser_->scanner()->AllocateInternalizedString(parser_->isolate_);
+      parser_->scanner()->AllocateInternalizedString(parser_->isolate());
   ASSERT(!result.is_null());
   return result;
 }
@@ -1722,8 +1764,8 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
           isolate()->factory()->InternalizeOneByteString(
               STATIC_ASCII_VECTOR("Variable"));
       Expression* expression =
-          NewThrowTypeError(isolate()->factory()->redeclaration_string(),
-                            message_string, name);
+          NewThrowTypeError("redeclaration",
+                            message_string, name, declaration->position());
       declaration_scope->SetIllegalRedeclaration(expression);
     }
   }
@@ -2511,9 +2553,8 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
   Scope* declaration_scope = scope_->DeclarationScope();
   if (declaration_scope->is_global_scope() ||
       declaration_scope->is_eval_scope()) {
-    Handle<String> message = isolate()->factory()->illegal_return_string();
     Expression* throw_error =
-        NewThrowSyntaxError(message, Handle<Object>::null());
+        NewThrowSyntaxError("illegal_return", Handle<Object>::null(), pos);
     return factory()->NewExpressionStatement(throw_error, pos);
   }
   return result;
@@ -2992,11 +3033,9 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
       bool accept_OF = expression->AsVariableProxy();
 
       if (CheckInOrOf(accept_OF, &mode)) {
-        if (expression == NULL || !expression->IsValidLeftHandSide()) {
-          ReportMessageAt(lhs_location, "invalid_lhs_in_for", true);
-          *ok = false;
-          return NULL;
-        }
+        expression = this->CheckAndRewriteReferenceExpression(
+            expression, lhs_location, "invalid_lhs_in_for", CHECK_OK);
+
         ForEachStatement* loop =
             factory()->NewForEachStatement(mode, labels, pos);
         Target target(&this->target_stack_, loop);
@@ -3699,58 +3738,6 @@ void Parser::RegisterTargetUse(Label* target, Target* stop) {
 }
 
 
-Expression* Parser::NewThrowReferenceError(Handle<String> message) {
-  return NewThrowError(isolate()->factory()->MakeReferenceError_string(),
-                       message, HandleVector<Object>(NULL, 0));
-}
-
-
-Expression* Parser::NewThrowSyntaxError(Handle<String> message,
-                                        Handle<Object> first) {
-  int argc = first.is_null() ? 0 : 1;
-  Vector< Handle<Object> > arguments = HandleVector<Object>(&first, argc);
-  return NewThrowError(
-      isolate()->factory()->MakeSyntaxError_string(), message, arguments);
-}
-
-
-Expression* Parser::NewThrowTypeError(Handle<String> message,
-                                      Handle<Object> first,
-                                      Handle<Object> second) {
-  ASSERT(!first.is_null() && !second.is_null());
-  Handle<Object> elements[] = { first, second };
-  Vector< Handle<Object> > arguments =
-      HandleVector<Object>(elements, ARRAY_SIZE(elements));
-  return NewThrowError(
-      isolate()->factory()->MakeTypeError_string(), message, arguments);
-}
-
-
-Expression* Parser::NewThrowError(Handle<String> constructor,
-                                  Handle<String> message,
-                                  Vector< Handle<Object> > arguments) {
-  int argc = arguments.length();
-  Handle<FixedArray> elements = isolate()->factory()->NewFixedArray(argc,
-                                                                    TENURED);
-  for (int i = 0; i < argc; i++) {
-    Handle<Object> element = arguments[i];
-    if (!element.is_null()) {
-      elements->set(i, *element);
-    }
-  }
-  Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements(
-      elements, FAST_ELEMENTS, TENURED);
-
-  int pos = position();
-  ZoneList<Expression*>* args = new(zone()) ZoneList<Expression*>(2, zone());
-  args->Add(factory()->NewLiteral(message, pos), zone());
-  args->Add(factory()->NewLiteral(array, pos), zone());
-  CallRuntime* call_constructor =
-      factory()->NewCallRuntime(constructor, NULL, args, pos);
-  return factory()->NewThrow(call_constructor, pos);
-}
-
-
 // ----------------------------------------------------------------------------
 // Regular expressions
 
index 287dfa4..6a164c7 100644 (file)
@@ -461,6 +461,11 @@ class ParserTraits {
 
   static bool IsIdentifier(Expression* expression);
 
+  static Handle<String> AsIdentifier(Expression* expression) {
+    ASSERT(IsIdentifier(expression));
+    return expression->AsVariableProxy()->name();
+  }
+
   static bool IsBoilerplateProperty(ObjectLiteral::Property* property) {
     return ObjectLiteral::IsBoilerplateProperty(property);
   }
@@ -500,10 +505,6 @@ class ParserTraits {
   // used on for the statically checking assignments to harmony const bindings.
   static Expression* MarkExpressionAsLValue(Expression* expression);
 
-  // Checks LHS expression for assignment and prefix/postfix increment/decrement
-  // in strict mode.
-  void CheckStrictModeLValue(Expression* expression, bool* ok);
-
   // Returns true if we have a binary expression between two numeric
   // literals. In that case, *x will be changed to an expression which is the
   // computed value.
@@ -526,6 +527,25 @@ class ParserTraits {
       Expression* expression, Token::Value op, int pos,
       AstNodeFactory<AstConstructionVisitor>* factory);
 
+  // Generate AST node that throws a ReferenceError with the given type.
+  Expression* NewThrowReferenceError(const char* type, int pos);
+
+  // Generate AST node that throws a SyntaxError with the given
+  // type. The first argument may be null (in the handle sense) in
+  // which case no arguments are passed to the constructor.
+  Expression* NewThrowSyntaxError(
+      const char* type, Handle<Object> arg, int pos);
+
+  // Generate AST node that throws a TypeError with the given
+  // type. Both arguments must be non-null (in the handle sense).
+  Expression* NewThrowTypeError(
+      const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos);
+
+  // Generic AST generator for throwing errors from compiled code.
+  Expression* NewThrowError(
+      Handle<String> constructor, const char* type,
+      Vector<Handle<Object> > arguments, int pos);
+
   // Reporting errors.
   void ReportMessageAt(Scanner::Location source_location,
                        const char* message,
@@ -777,25 +797,6 @@ class Parser : public ParserBase<ParserTraits> {
 
   Handle<String> LookupCachedSymbol(int symbol_id);
 
-  // Generate AST node that throw a ReferenceError with the given type.
-  Expression* NewThrowReferenceError(Handle<String> type);
-
-  // Generate AST node that throw a SyntaxError with the given
-  // type. The first argument may be null (in the handle sense) in
-  // which case no arguments are passed to the constructor.
-  Expression* NewThrowSyntaxError(Handle<String> type, Handle<Object> first);
-
-  // Generate AST node that throw a TypeError with the given
-  // type. Both arguments must be non-null (in the handle sense).
-  Expression* NewThrowTypeError(Handle<String> type,
-                                Handle<Object> first,
-                                Handle<Object> second);
-
-  // Generic AST generator for throwing errors from compiled code.
-  Expression* NewThrowError(Handle<String> constructor,
-                            Handle<String> type,
-                            Vector< Handle<Object> > arguments);
-
   PreParser::PreParseResult LazyParseFunctionLiteral(
        SingletonLogger* logger);
 
index 4995ed9..a6811e9 100644 (file)
@@ -56,17 +56,6 @@ namespace v8 {
 namespace internal {
 
 
-void PreParserTraits::CheckStrictModeLValue(PreParserExpression expression,
-                                            bool* ok) {
-  if (expression.IsIdentifier() &&
-      expression.AsIdentifier().IsEvalOrArguments()) {
-    pre_parser_->ReportMessage("strict_eval_arguments",
-                               Vector<const char*>::empty());
-    *ok = false;
-  }
-}
-
-
 void PreParserTraits::ReportMessageAt(Scanner::Location location,
                                       const char* message,
                                       Vector<const char*> args,
index 38d12e1..3781631 100644 (file)
@@ -425,6 +425,13 @@ class ParserBase : public Traits {
   ExpressionT ParseMemberExpressionContinuation(ExpressionT expression,
                                                 bool* ok);
 
+  // Checks if the expression is a valid reference expression (e.g., on the
+  // left-hand side of assignments). Although ruled out by ECMA as early errors,
+  // we allow calls for web compatibility and rewrite them to a runtime throw.
+  ExpressionT CheckAndRewriteReferenceExpression(
+      ExpressionT expression,
+      Scanner::Location location, const char* message, bool* ok);
+
   // Used to detect duplicates in object literals. Each of the values
   // kGetterProperty, kSetterProperty and kValueProperty represents
   // a type of object literal property. When parsing a property, its
@@ -589,10 +596,14 @@ class PreParserExpression {
     return PreParserExpression(kPropertyExpression);
   }
 
+  static PreParserExpression Call() {
+    return PreParserExpression(kCallExpression);
+  }
+
   bool IsIdentifier() { return (code_ & kIdentifierFlag) != 0; }
 
-  // Only works corretly if it is actually an identifier expression.
   PreParserIdentifier AsIdentifier() {
+    ASSERT(IsIdentifier());
     return PreParserIdentifier(
         static_cast<PreParserIdentifier::Type>(code_ >> kIdentifierShift));
   }
@@ -611,13 +622,14 @@ class PreParserExpression {
     return code_ == kPropertyExpression || code_ == kThisPropertyExpression;
   }
 
-  bool IsValidLeftHandSide() {
+  bool IsCall() { return code_ == kCallExpression; }
+
+  bool IsValidReferenceExpression() {
     return IsIdentifier() || IsProperty();
   }
 
   // At the moment PreParser doesn't track these expression types.
   bool IsFunctionLiteral() const { return false; }
-  bool IsCall() const { return false; }
   bool IsCallNew() const { return false; }
 
   PreParserExpression AsFunctionLiteral() { return *this; }
@@ -651,7 +663,8 @@ class PreParserExpression {
     // 2 least significant bits for flags.
     kThisExpression = 1 << 2,
     kThisPropertyExpression = 2 << 2,
-    kPropertyExpression = 3 << 2
+    kPropertyExpression = 3 << 2,
+    kCallExpression = 4 << 2
   };
 
   explicit PreParserExpression(int expression_code) : code_(expression_code) {}
@@ -782,7 +795,7 @@ class PreParserFactory {
   PreParserExpression NewCall(PreParserExpression expression,
                               PreParserExpressionList arguments,
                               int pos) {
-    return PreParserExpression::Default();
+    return PreParserExpression::Call();
   }
   PreParserExpression NewCallNew(PreParserExpression expression,
                                  PreParserExpressionList arguments,
@@ -845,6 +858,10 @@ class PreParserTraits {
     return expression.IsIdentifier();
   }
 
+  static PreParserIdentifier AsIdentifier(PreParserExpression expression) {
+    return expression.AsIdentifier();
+  }
+
   static bool IsBoilerplateProperty(PreParserExpression property) {
     // PreParser doesn't count boilerplate properties.
     return false;
@@ -883,10 +900,6 @@ class PreParserTraits {
     return expression;
   }
 
-  // Checks LHS expression for assignment and prefix/postfix increment/decrement
-  // in strict mode.
-  void CheckStrictModeLValue(PreParserExpression expression, bool* ok);
-
   bool ShortcutNumericLiteralBinaryExpression(PreParserExpression* x,
                                               PreParserExpression y,
                                               Token::Value op,
@@ -901,6 +914,18 @@ class PreParserTraits {
     return PreParserExpression::Default();
   }
 
+  PreParserExpression NewThrowReferenceError(const char* type, int pos) {
+    return PreParserExpression::Default();
+  }
+  PreParserExpression NewThrowSyntaxError(
+      const char* type, Handle<Object> arg, int pos) {
+    return PreParserExpression::Default();
+  }
+  PreParserExpression NewThrowTypeError(
+      const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos) {
+    return PreParserExpression::Default();
+  }
+
   // Reporting errors.
   void ReportMessageAt(Scanner::Location location,
                        const char* message,
@@ -1695,16 +1720,8 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, bool* ok) {
     return expression;
   }
 
-  if (!expression->IsValidLeftHandSide()) {
-    this->ReportMessageAt(lhs_location, "invalid_lhs_in_assignment", true);
-    *ok = false;
-    return this->EmptyExpression();
-  }
-
-  if (strict_mode() == STRICT) {
-    // Assignment to eval or arguments is disallowed in strict mode.
-    this->CheckStrictModeLValue(expression, CHECK_OK);
-  }
+  expression = this->CheckAndRewriteReferenceExpression(
+      expression, lhs_location, "invalid_lhs_in_assignment", CHECK_OK);
   expression = this->MarkExpressionAsLValue(expression);
 
   Token::Value op = Next();  // Get assignment operator.
@@ -1864,17 +1881,9 @@ ParserBase<Traits>::ParseUnaryExpression(bool* ok) {
   } else if (Token::IsCountOp(op)) {
     op = Next();
     Scanner::Location lhs_location = scanner()->peek_location();
-    ExpressionT expression = ParseUnaryExpression(CHECK_OK);
-    if (!expression->IsValidLeftHandSide()) {
-      ReportMessageAt(lhs_location, "invalid_lhs_in_prefix_op", true);
-      *ok = false;
-      return this->EmptyExpression();
-    }
-
-    if (strict_mode() == STRICT) {
-      // Prefix expression operand in strict mode may not be eval or arguments.
-      this->CheckStrictModeLValue(expression, CHECK_OK);
-    }
+    ExpressionT expression = this->ParseUnaryExpression(CHECK_OK);
+    expression = this->CheckAndRewriteReferenceExpression(
+        expression, lhs_location, "invalid_lhs_in_prefix_op", CHECK_OK);
     this->MarkExpressionAsLValue(expression);
 
     return factory()->NewCountOperation(op,
@@ -1898,16 +1907,8 @@ ParserBase<Traits>::ParsePostfixExpression(bool* ok) {
   ExpressionT expression = this->ParseLeftHandSideExpression(CHECK_OK);
   if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
       Token::IsCountOp(peek())) {
-    if (!expression->IsValidLeftHandSide()) {
-      ReportMessageAt(lhs_location, "invalid_lhs_in_postfix_op", true);
-      *ok = false;
-      return this->EmptyExpression();
-    }
-
-    if (strict_mode() == STRICT) {
-      // Postfix expression operand in strict mode may not be eval or arguments.
-      this->CheckStrictModeLValue(expression, CHECK_OK);
-    }
+    expression = this->CheckAndRewriteReferenceExpression(
+        expression, lhs_location, "invalid_lhs_in_postfix_op", CHECK_OK);
     expression = this->MarkExpressionAsLValue(expression);
 
     Token::Value next = Next();
@@ -2117,6 +2118,32 @@ ParserBase<Traits>::ParseMemberExpressionContinuation(ExpressionT expression,
 }
 
 
+template <typename Traits>
+typename ParserBase<Traits>::ExpressionT
+ParserBase<Traits>::CheckAndRewriteReferenceExpression(
+    ExpressionT expression,
+    Scanner::Location location, const char* message, bool* ok) {
+  if (strict_mode() == STRICT && this->IsIdentifier(expression) &&
+      this->IsEvalOrArguments(this->AsIdentifier(expression))) {
+    this->ReportMessageAt(location, "strict_eval_arguments", false);
+    *ok = false;
+    return this->EmptyExpression();
+  } else if (expression->IsValidReferenceExpression()) {
+    return expression;
+  } else if (expression->IsCall()) {
+    // If it is a call, make it a runtime error for legacy web compatibility.
+    // Rewrite `expr' to `expr[throw ReferenceError]'.
+    int pos = location.beg_pos;
+    ExpressionT error = this->NewThrowReferenceError(message, pos);
+    return factory()->NewProperty(expression, error, pos);
+  } else {
+    this->ReportMessageAt(location, message, true);
+    *ok = false;
+    return this->EmptyExpression();
+  }
+}
+
+
 #undef CHECK_OK
 #undef CHECK_OK_CUSTOM
 
index 6c4ea52..47d1099 100644 (file)
@@ -58,7 +58,7 @@ const char* Variable::Mode2String(VariableMode mode) {
 Variable::Variable(Scope* scope,
                    Handle<String> name,
                    VariableMode mode,
-                   bool is_valid_LHS,
+                   bool is_valid_ref,
                    Kind kind,
                    InitializationFlag initialization_flag,
                    Interface* interface)
@@ -70,7 +70,7 @@ Variable::Variable(Scope* scope,
     index_(-1),
     initializer_position_(RelocInfo::kNoPosition),
     local_if_not_shadowed_(NULL),
-    is_valid_LHS_(is_valid_LHS),
+    is_valid_ref_(is_valid_ref),
     force_context_allocation_(false),
     is_used_(false),
     initialization_flag_(initialization_flag),
index 401d044..51b7362 100644 (file)
@@ -77,7 +77,7 @@ class Variable: public ZoneObject {
   Variable(Scope* scope,
            Handle<String> name,
            VariableMode mode,
-           bool is_valid_lhs,
+           bool is_valid_ref,
            Kind kind,
            InitializationFlag initialization_flag,
            Interface* interface = Interface::NewValue());
@@ -85,7 +85,7 @@ class Variable: public ZoneObject {
   // Printing support
   static const char* Mode2String(VariableMode mode);
 
-  bool IsValidLeftHandSide() { return is_valid_LHS_; }
+  bool IsValidReference() { return is_valid_ref_; }
 
   // The source code for an eval() call may refer to a variable that is
   // in an outer scope about which we don't know anything (it may not
@@ -172,8 +172,8 @@ class Variable: public ZoneObject {
   // binding scope (exclusive).
   Variable* local_if_not_shadowed_;
 
-  // Valid as a LHS? (const and this are not valid LHS, for example)
-  bool is_valid_LHS_;
+  // Valid as a reference? (const and this are not valid, for example)
+  bool is_valid_ref_;
 
   // Usage info.
   bool force_context_allocation_;  // set by variable resolver
index 3d4c2a9..e4fa4d9 100644 (file)
@@ -1880,7 +1880,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
 
 
 void FullCodeGenerator::VisitAssignment(Assignment* expr) {
-  ASSERT(expr->target()->IsValidLeftHandSide());
+  ASSERT(expr->target()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ Assignment");
 
@@ -2379,7 +2379,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
 
 
 void FullCodeGenerator::EmitAssignment(Expression* expr) {
-  ASSERT(expr->IsValidLeftHandSide());
+  ASSERT(expr->IsValidReferenceExpression());
 
   // Left-hand side can only be a property, a global or a (parameter or local)
   // slot.
@@ -4298,7 +4298,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 
 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
-  ASSERT(expr->expression()->IsValidLeftHandSide());
+  ASSERT(expr->expression()->IsValidReferenceExpression());
 
   Comment cmnt(masm_, "[ CountOperation");
   SetSourcePosition(expr->position());
index 2746388..ccb5e8c 100644 (file)
@@ -2557,13 +2557,22 @@ TEST(InvalidLeftHandSide) {
     "this[foo]",
     "new foo()[bar]",
     "new foo().bar",
+    "foo()",
+    "foo(bar)",
+    "foo[bar]()",
+    "foo.bar()",
+    "this()",
+    "this.foo()",
+    "this[foo].bar()",
+    "this.foo[foo].bar(this)(bar)[foo]()",
     NULL
   };
 
   // Bad left hand sides for assigment or prefix / postfix operations.
   const char* bad_statement_data_common[] = {
     "2",
-    "foo()",
+    "new foo",
+    "new foo()",
     "null",
     "if",  // Unexpected token
     "{x: 1}",  // Unexpected token
index 92f5c6f..52ee895 100644 (file)
 assertThrows("12 = 12", ReferenceError);
 assertThrows("x++ = 12", ReferenceError);
 assertThrows("eval('var x') = 12", ReferenceError);
-assertThrows("if (false) eval('var x') = 12", ReferenceError);
+assertThrows("if (false) 12 = 12", ReferenceError);
+assertDoesNotThrow("if (false) eval('var x') = 12", ReferenceError);
 
 // Pre- and post-fix operations:
 assertThrows("12++", ReferenceError);
 assertThrows("12--", ReferenceError);
-assertThrows("--12", ReferenceError);
 assertThrows("++12", ReferenceError);
+assertThrows("--12", ReferenceError);
 assertThrows("++(eval('12'))", ReferenceError);
 assertThrows("(eval('12'))++", ReferenceError);
-assertThrows("if (false) ++(eval('12'))", ReferenceError);
-assertThrows("if (false) (eval('12'))++", ReferenceError);
+assertThrows("if (false) 12++", ReferenceError);
+assertThrows("if (false) 12--", ReferenceError);
+assertThrows("if (false) ++12", ReferenceError);
+assertThrows("if (false) --12", ReferenceError);
+assertDoesNotThrow("if (false) ++(eval('12'))", ReferenceError);
+assertDoesNotThrow("if (false) (eval('12'))++", ReferenceError);
 
 // For in:
 assertThrows("for (12 in [1]) print(12);", ReferenceError);
 assertThrows("for (eval('var x') in [1]) print(12);", ReferenceError);
-assertThrows("if (false) for (eval('0') in [1]) print(12);", ReferenceError);
+assertThrows("if (false) for (12 in [1]) print(12);", ReferenceError);
+assertDoesNotThrow("if (false) for (eval('0') in [1]) print(12);", ReferenceError);
 
 // For:
 assertThrows("for (12 = 1;;) print(12);", ReferenceError);
 assertThrows("for (eval('var x') = 1;;) print(12);", ReferenceError);
-assertThrows("if (false) for (eval('var x') = 1;;) print(12);", ReferenceError);
+assertThrows("if (false) for (12 = 1;;) print(12);", ReferenceError);
+assertDoesNotThrow("if (false) for (eval('var x') = 1;;) print(12);", ReferenceError);
 
 // Assignments to 'this'.
 assertThrows("this = 42", ReferenceError);
@@ -63,3 +70,5 @@ assertThrows("this++", ReferenceError);
 assertThrows("++this", ReferenceError);
 assertThrows("this--", ReferenceError);
 assertThrows("--this", ReferenceError);
+assertThrows("if (false) this = 42", ReferenceError);
+assertThrows("if (false) this++", ReferenceError);
index b8c692a..80f2730 100644 (file)
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-FAIL function f() { g()++; } f.toString() should be function f() { g()++; }. Threw exception ReferenceError: Invalid left-hand side expression in postfix operation
-FAIL function f() { g()--; } f.toString() should be function f() { g()--; }. Threw exception ReferenceError: Invalid left-hand side expression in postfix operation
-FAIL function f() { ++g(); } f.toString() should be function f() { ++g(); }. Threw exception ReferenceError: Invalid left-hand side expression in prefix operation
-FAIL function f() { --g(); } f.toString() should be function f() { --g(); }. Threw exception ReferenceError: Invalid left-hand side expression in prefix operation
-FAIL function f() { g() = 1; } f.toString() should be function f() { g() = 1; }. Threw exception ReferenceError: Invalid left-hand side in assignment
-FAIL function f() { g() += 1; } f.toString() should be function f() { g() += 1; }. Threw exception ReferenceError: Invalid left-hand side in assignment
-FAIL g()++ should throw ReferenceError: Postfix ++ operator applied to value that is not a reference.. Threw exception ReferenceError: Invalid left-hand side expression in postfix operation.
-FAIL g()-- should throw ReferenceError: Postfix -- operator applied to value that is not a reference.. Threw exception ReferenceError: Invalid left-hand side expression in postfix operation.
-FAIL ++g() should throw ReferenceError: Prefix ++ operator applied to value that is not a reference.. Threw exception ReferenceError: Invalid left-hand side expression in prefix operation.
-FAIL --g() should throw ReferenceError: Prefix -- operator applied to value that is not a reference.. Threw exception ReferenceError: Invalid left-hand side expression in prefix operation.
-FAIL g() = 1 should throw ReferenceError: Left side of assignment is not a reference.. Threw exception ReferenceError: Invalid left-hand side in assignment.
-FAIL g() += 1 should throw ReferenceError: Left side of assignment is not a reference.. Threw exception ReferenceError: Invalid left-hand side in assignment.
+PASS function f() { g()++; } f.toString() is 'function f() { g()++; }'
+PASS function f() { g()--; } f.toString() is 'function f() { g()--; }'
+PASS function f() { ++g(); } f.toString() is 'function f() { ++g(); }'
+PASS function f() { --g(); } f.toString() is 'function f() { --g(); }'
+PASS function f() { g() = 1; } f.toString() is 'function f() { g() = 1; }'
+PASS function f() { g() += 1; } f.toString() is 'function f() { g() += 1; }'
+PASS Number()++ threw exception ReferenceError: Invalid left-hand side expression in postfix operation.
+PASS Number()-- threw exception ReferenceError: Invalid left-hand side expression in postfix operation.
+PASS ++Number() threw exception ReferenceError: Invalid left-hand side expression in prefix operation.
+PASS --Number() threw exception ReferenceError: Invalid left-hand side expression in prefix operation.
+PASS Number() = 1 threw exception ReferenceError: Invalid left-hand side in assignment.
+PASS Number() += 1 threw exception ReferenceError: Invalid left-hand side in assignment.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index e8b9682..bd9b7aa 100644 (file)
@@ -27,9 +27,9 @@ shouldBe("function f() { ++g(); } f.toString()", "'function f() { ++g(); }'");
 shouldBe("function f() { --g(); } f.toString()", "'function f() { --g(); }'");
 shouldBe("function f() { g() = 1; } f.toString()", "'function f() { g() = 1; }'");
 shouldBe("function f() { g() += 1; } f.toString()", "'function f() { g() += 1; }'");
-shouldThrow("g()++", "'ReferenceError: Postfix ++ operator applied to value that is not a reference.'");
-shouldThrow("g()--", "'ReferenceError: Postfix -- operator applied to value that is not a reference.'");
-shouldThrow("++g()", "'ReferenceError: Prefix ++ operator applied to value that is not a reference.'");
-shouldThrow("--g()", "'ReferenceError: Prefix -- operator applied to value that is not a reference.'");
-shouldThrow("g() = 1", "'ReferenceError: Left side of assignment is not a reference.'");
-shouldThrow("g() += 1", "'ReferenceError: Left side of assignment is not a reference.'");
+shouldThrow("Number()++", "'ReferenceError: Invalid left-hand side expression in postfix operation'");
+shouldThrow("Number()--", "'ReferenceError: Invalid left-hand side expression in postfix operation'");
+shouldThrow("++Number()", "'ReferenceError: Invalid left-hand side expression in prefix operation'");
+shouldThrow("--Number()", "'ReferenceError: Invalid left-hand side expression in prefix operation'");
+shouldThrow("Number() = 1", "'ReferenceError: Invalid left-hand side in assignment'");
+shouldThrow("Number() += 1", "'ReferenceError: Invalid left-hand side in assignment'");