Commit 45118bfd authored by rossberg@chromium.org's avatar rossberg@chromium.org

Make invalid LHSs that are calls late errors

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
parent fec19557
......@@ -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());
......
......@@ -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());
......
......@@ -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:
......
......@@ -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_; }
......
......@@ -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") \
......
......@@ -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());
......
......@@ -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
......
......@@ -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);
......
......@@ -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,
......
......@@ -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
......
......@@ -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),
......
......@@ -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
......
......@@ -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());
......
......@@ -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
......
......@@ -32,27 +32,34 @@
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);
......@@ -21,18 +21,18 @@
# (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
......
......@@ -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'");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment