Two changes, one a refactoring and one that affects V8's JS semantics.

1. Change the AST node type CallNew to be a subclass of Expression
   rather than Call.  It's not really a call but it just happens to
   have the same fields.

2. Change our error reporting for invalid left-hand sides in for-in
   statements, pre- and postfix count expressions, and assignments.
   Before we signaled a syntax error at compile time *unless* the LHS
   was a function call or 'new' expression, in which case we signaled
   a reference error at runtime.  Now we signal a reference error at
   runtime in all cases.  This matches the JSC behavior in Safari 4.

Review URL: http://codereview.chromium.org/249039

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2994 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kmillikin@chromium.org 2009-09-30 09:49:36 +00:00
parent b1a5bf4c5d
commit fb038bf146
4 changed files with 64 additions and 138 deletions

View File

@ -954,12 +954,8 @@ class Property: public Expression {
class Call: public Expression {
public:
Call(Expression* expression,
ZoneList<Expression*>* arguments,
int pos)
: expression_(expression),
arguments_(arguments),
pos_(pos) { }
Call(Expression* expression, ZoneList<Expression*>* arguments, int pos)
: expression_(expression), arguments_(arguments), pos_(pos) { }
virtual void Accept(AstVisitor* v);
@ -981,12 +977,21 @@ class Call: public Expression {
};
class CallNew: public Call {
class CallNew: public Expression {
public:
CallNew(Expression* expression, ZoneList<Expression*>* arguments, int pos)
: Call(expression, arguments, pos) { }
: expression_(expression), arguments_(arguments), pos_(pos) { }
virtual void Accept(AstVisitor* v);
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
int position() { return pos_; }
private:
Expression* expression_;
ZoneList<Expression*>* arguments_;
int pos_;
};

View File

@ -2660,25 +2660,13 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
} else {
Expression* expression = ParseExpression(false, CHECK_OK);
if (peek() == Token::IN) {
// Report syntax error if the expression is an invalid
// left-hand side expression.
// Signal a reference error if the expression is an invalid
// left-hand side expression. We could report this as a syntax
// error here but for compatibility with JSC we choose to report
// the error at runtime.
if (expression == NULL || !expression->IsValidLeftHandSide()) {
if (expression != NULL && expression->AsCall() != NULL) {
// According to ECMA-262 host function calls are permitted to
// return references. This cannot happen in our system so we
// will always get an error. We could report this as a syntax
// error here but for compatibility with KJS and SpiderMonkey we
// choose to report the error at runtime.
Handle<String> type = Factory::invalid_lhs_in_for_in_symbol();
expression = NewThrowReferenceError(type);
} else {
// Invalid left hand side expressions that are not function
// calls are reported as syntax errors at compile time.
ReportMessage("invalid_lhs_in_for_in",
Vector<const char*>::empty());
*ok = false;
return NULL;
}
}
ForInStatement* loop = NEW(ForInStatement(labels));
Target target(this, loop);
@ -2755,29 +2743,14 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
return expression;
}
// Signal a reference error if the expression is an invalid left-hand
// side expression. We could report this as a syntax error here but
// for compatibility with JSC we choose to report the error at
// runtime.
if (expression == NULL || !expression->IsValidLeftHandSide()) {
if (expression != NULL && expression->AsCall() != NULL) {
// According to ECMA-262 host function calls are permitted to
// return references. This cannot happen in our system so we
// will always get an error. We could report this as a syntax
// error here but for compatibility with KJS and SpiderMonkey we
// choose to report the error at runtime.
Handle<String> type = Factory::invalid_lhs_in_assignment_symbol();
expression = NewThrowReferenceError(type);
} else {
// Invalid left hand side expressions that are not function
// calls are reported as syntax errors at compile time.
//
// NOTE: KJS sometimes delay the error reporting to runtime. If
// we want to be completely compatible we should do the same.
// For example: "(x++) = 42" gives a reference error at runtime
// with KJS whereas we report a syntax error at compile time.
ReportMessage("invalid_lhs_in_assignment", Vector<const char*>::empty());
*ok = false;
return NULL;
}
}
Token::Value op = Next(); // Get assignment operator.
int pos = scanner().location().beg_pos;
@ -2951,45 +2924,37 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
Token::Value op = peek();
if (Token::IsUnaryOp(op)) {
op = Next();
Expression* x = ParseUnaryExpression(CHECK_OK);
Expression* expression = ParseUnaryExpression(CHECK_OK);
// Compute some expressions involving only number literals.
if (x && x->AsLiteral() && x->AsLiteral()->handle()->IsNumber()) {
double x_val = x->AsLiteral()->handle()->Number();
if (expression != NULL && expression->AsLiteral() &&
expression->AsLiteral()->handle()->IsNumber()) {
double value = expression->AsLiteral()->handle()->Number();
switch (op) {
case Token::ADD:
return x;
return expression;
case Token::SUB:
return NewNumberLiteral(-x_val);
return NewNumberLiteral(-value);
case Token::BIT_NOT:
return NewNumberLiteral(~DoubleToInt32(x_val));
return NewNumberLiteral(~DoubleToInt32(value));
default: break;
}
}
return NEW(UnaryOperation(op, x));
return NEW(UnaryOperation(op, expression));
} else if (Token::IsCountOp(op)) {
op = Next();
Expression* x = ParseUnaryExpression(CHECK_OK);
if (x == NULL || !x->IsValidLeftHandSide()) {
if (x != NULL && x->AsCall() != NULL) {
// According to ECMA-262 host function calls are permitted to
// return references. This cannot happen in our system so we
// will always get an error. We could report this as a syntax
// error here but for compatibility with KJS and SpiderMonkey we
// choose to report the error at runtime.
Expression* expression = ParseUnaryExpression(CHECK_OK);
// Signal a reference error if the expression is an invalid
// left-hand side expression. We could report this as a syntax
// error here but for compatibility with JSC we choose to report the
// error at runtime.
if (expression == NULL || !expression->IsValidLeftHandSide()) {
Handle<String> type = Factory::invalid_lhs_in_prefix_op_symbol();
x = NewThrowReferenceError(type);
} else {
// Invalid left hand side expressions that are not function
// calls are reported as syntax errors at compile time.
ReportMessage("invalid_lhs_in_prefix_op", Vector<const char*>::empty());
*ok = false;
return NULL;
expression = NewThrowReferenceError(type);
}
}
return NEW(CountOperation(true /* prefix */, op, x));
return NEW(CountOperation(true /* prefix */, op, expression));
} else {
return ParsePostfixExpression(ok);
@ -3001,30 +2966,20 @@ Expression* Parser::ParsePostfixExpression(bool* ok) {
// PostfixExpression ::
// LeftHandSideExpression ('++' | '--')?
Expression* result = ParseLeftHandSideExpression(CHECK_OK);
Expression* expression = ParseLeftHandSideExpression(CHECK_OK);
if (!scanner_.has_line_terminator_before_next() && Token::IsCountOp(peek())) {
if (result == NULL || !result->IsValidLeftHandSide()) {
if (result != NULL && result->AsCall() != NULL) {
// According to ECMA-262 host function calls are permitted to
// return references. This cannot happen in our system so we
// will always get an error. We could report this as a syntax
// error here but for compatibility with KJS and SpiderMonkey we
// choose to report the error at runtime.
// Signal a reference error if the expression is an invalid
// left-hand side expression. We could report this as a syntax
// error here but for compatibility with JSC we choose to report the
// error at runtime.
if (expression == NULL || !expression->IsValidLeftHandSide()) {
Handle<String> type = Factory::invalid_lhs_in_postfix_op_symbol();
result = NewThrowReferenceError(type);
} else {
// Invalid left hand side expressions that are not function
// calls are reported as syntax errors at compile time.
ReportMessage("invalid_lhs_in_postfix_op",
Vector<const char*>::empty());
*ok = false;
return NULL;
}
expression = NewThrowReferenceError(type);
}
Token::Value next = Next();
result = NEW(CountOperation(false /* postfix */, next, result));
expression = NEW(CountOperation(false /* postfix */, next, expression));
}
return result;
return expression;
}

View File

@ -44,44 +44,12 @@ class UsageComputer: public AstVisitor {
public:
static bool Traverse(AstNode* node);
void VisitBlock(Block* node);
void VisitDeclaration(Declaration* node);
void VisitExpressionStatement(ExpressionStatement* node);
void VisitEmptyStatement(EmptyStatement* node);
void VisitIfStatement(IfStatement* node);
void VisitContinueStatement(ContinueStatement* node);
void VisitBreakStatement(BreakStatement* node);
void VisitReturnStatement(ReturnStatement* node);
void VisitWithEnterStatement(WithEnterStatement* node);
void VisitWithExitStatement(WithExitStatement* node);
void VisitSwitchStatement(SwitchStatement* node);
void VisitLoopStatement(LoopStatement* node);
void VisitForInStatement(ForInStatement* node);
void VisitTryCatch(TryCatch* node);
void VisitTryFinally(TryFinally* node);
void VisitDebuggerStatement(DebuggerStatement* node);
void VisitFunctionLiteral(FunctionLiteral* node);
void VisitFunctionBoilerplateLiteral(FunctionBoilerplateLiteral* node);
void VisitConditional(Conditional* node);
void VisitSlot(Slot* node);
void VisitVariable(Variable* node);
void VisitVariableProxy(VariableProxy* node);
void VisitLiteral(Literal* node);
void VisitRegExpLiteral(RegExpLiteral* node);
void VisitObjectLiteral(ObjectLiteral* node);
void VisitArrayLiteral(ArrayLiteral* node);
void VisitCatchExtensionObject(CatchExtensionObject* node);
void VisitAssignment(Assignment* node);
void VisitThrow(Throw* node);
void VisitProperty(Property* node);
void VisitCall(Call* node);
void VisitCallNew(CallNew* node);
void VisitCallRuntime(CallRuntime* node);
void VisitUnaryOperation(UnaryOperation* node);
void VisitCountOperation(CountOperation* node);
void VisitBinaryOperation(BinaryOperation* node);
void VisitCompareOperation(CompareOperation* node);
void VisitThisFunction(ThisFunction* node);
// AST node visit functions.
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
void VisitVariable(Variable* var);
private:
int weight_;
@ -329,7 +297,8 @@ void UsageComputer::VisitCall(Call* node) {
void UsageComputer::VisitCallNew(CallNew* node) {
VisitCall(node);
Read(node->expression());
ReadList(node->arguments());
}

View File

@ -25,9 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test that we get exceptions for invalid left-hand sides. Also
// tests that if the invalid left-hand side is a function call, the
// exception is delayed until runtime.
// Test that we get exceptions for invalid left-hand sides. The
// exceptions are delayed until runtime.
// Normal assignments:
assertThrows("12 = 12");
@ -57,12 +56,10 @@ assertDoesNotThrow("if (false) for (eval('var x') = 1;;) print(12);");
// Assignments to 'this'.
assertThrows("this = 42");
assertThrows("function f() { this = 12; }");
assertThrows("for (this in Array) ;");
assertDoesNotThrow("function f() { this = 12; }");
assertThrows("for (this in {x:3, y:4, z:5}) ;");
assertThrows("for (this = 0;;) ;");
assertThrows("this++");
assertThrows("++this");
assertThrows("this--");
assertThrows("--this");