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 { class Call: public Expression {
public: public:
Call(Expression* expression, Call(Expression* expression, ZoneList<Expression*>* arguments, int pos)
ZoneList<Expression*>* arguments, : expression_(expression), arguments_(arguments), pos_(pos) { }
int pos)
: expression_(expression),
arguments_(arguments),
pos_(pos) { }
virtual void Accept(AstVisitor* v); virtual void Accept(AstVisitor* v);
@ -981,12 +977,21 @@ class Call: public Expression {
}; };
class CallNew: public Call { class CallNew: public Expression {
public: public:
CallNew(Expression* expression, ZoneList<Expression*>* arguments, int pos) CallNew(Expression* expression, ZoneList<Expression*>* arguments, int pos)
: Call(expression, arguments, pos) { } : expression_(expression), arguments_(arguments), pos_(pos) { }
virtual void Accept(AstVisitor* v); 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 { } else {
Expression* expression = ParseExpression(false, CHECK_OK); Expression* expression = ParseExpression(false, CHECK_OK);
if (peek() == Token::IN) { if (peek() == Token::IN) {
// Report syntax error if the expression is an invalid // Signal a reference error if the expression is an invalid
// left-hand side expression. // 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->IsValidLeftHandSide()) {
if (expression != NULL && expression->AsCall() != NULL) { Handle<String> type = Factory::invalid_lhs_in_for_in_symbol();
// According to ECMA-262 host function calls are permitted to expression = NewThrowReferenceError(type);
// 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)); ForInStatement* loop = NEW(ForInStatement(labels));
Target target(this, loop); Target target(this, loop);
@ -2755,30 +2743,15 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
return expression; 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->IsValidLeftHandSide()) {
if (expression != NULL && expression->AsCall() != NULL) { Handle<String> type = Factory::invalid_lhs_in_assignment_symbol();
// According to ECMA-262 host function calls are permitted to expression = NewThrowReferenceError(type);
// 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. Token::Value op = Next(); // Get assignment operator.
int pos = scanner().location().beg_pos; int pos = scanner().location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
@ -2951,45 +2924,37 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
Token::Value op = peek(); Token::Value op = peek();
if (Token::IsUnaryOp(op)) { if (Token::IsUnaryOp(op)) {
op = Next(); op = Next();
Expression* x = ParseUnaryExpression(CHECK_OK); Expression* expression = ParseUnaryExpression(CHECK_OK);
// Compute some expressions involving only number literals. // Compute some expressions involving only number literals.
if (x && x->AsLiteral() && x->AsLiteral()->handle()->IsNumber()) { if (expression != NULL && expression->AsLiteral() &&
double x_val = x->AsLiteral()->handle()->Number(); expression->AsLiteral()->handle()->IsNumber()) {
double value = expression->AsLiteral()->handle()->Number();
switch (op) { switch (op) {
case Token::ADD: case Token::ADD:
return x; return expression;
case Token::SUB: case Token::SUB:
return NewNumberLiteral(-x_val); return NewNumberLiteral(-value);
case Token::BIT_NOT: case Token::BIT_NOT:
return NewNumberLiteral(~DoubleToInt32(x_val)); return NewNumberLiteral(~DoubleToInt32(value));
default: break; default: break;
} }
} }
return NEW(UnaryOperation(op, x)); return NEW(UnaryOperation(op, expression));
} else if (Token::IsCountOp(op)) { } else if (Token::IsCountOp(op)) {
op = Next(); op = Next();
Expression* x = ParseUnaryExpression(CHECK_OK); Expression* expression = ParseUnaryExpression(CHECK_OK);
if (x == NULL || !x->IsValidLeftHandSide()) { // Signal a reference error if the expression is an invalid
if (x != NULL && x->AsCall() != NULL) { // left-hand side expression. We could report this as a syntax
// According to ECMA-262 host function calls are permitted to // error here but for compatibility with JSC we choose to report the
// return references. This cannot happen in our system so we // error at runtime.
// will always get an error. We could report this as a syntax if (expression == NULL || !expression->IsValidLeftHandSide()) {
// error here but for compatibility with KJS and SpiderMonkey we Handle<String> type = Factory::invalid_lhs_in_prefix_op_symbol();
// choose to report the error at runtime. expression = NewThrowReferenceError(type);
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;
}
} }
return NEW(CountOperation(true /* prefix */, op, x)); return NEW(CountOperation(true /* prefix */, op, expression));
} else { } else {
return ParsePostfixExpression(ok); return ParsePostfixExpression(ok);
@ -3001,30 +2966,20 @@ Expression* Parser::ParsePostfixExpression(bool* ok) {
// PostfixExpression :: // PostfixExpression ::
// LeftHandSideExpression ('++' | '--')? // LeftHandSideExpression ('++' | '--')?
Expression* result = ParseLeftHandSideExpression(CHECK_OK); Expression* expression = ParseLeftHandSideExpression(CHECK_OK);
if (!scanner_.has_line_terminator_before_next() && Token::IsCountOp(peek())) { if (!scanner_.has_line_terminator_before_next() && Token::IsCountOp(peek())) {
if (result == NULL || !result->IsValidLeftHandSide()) { // Signal a reference error if the expression is an invalid
if (result != NULL && result->AsCall() != NULL) { // left-hand side expression. We could report this as a syntax
// According to ECMA-262 host function calls are permitted to // error here but for compatibility with JSC we choose to report the
// return references. This cannot happen in our system so we // error at runtime.
// will always get an error. We could report this as a syntax if (expression == NULL || !expression->IsValidLeftHandSide()) {
// error here but for compatibility with KJS and SpiderMonkey we Handle<String> type = Factory::invalid_lhs_in_postfix_op_symbol();
// choose to report the error at runtime. expression = NewThrowReferenceError(type);
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;
}
} }
Token::Value next = Next(); 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: public:
static bool Traverse(AstNode* node); static bool Traverse(AstNode* node);
void VisitBlock(Block* node); // AST node visit functions.
void VisitDeclaration(Declaration* node); #define DECLARE_VISIT(type) void Visit##type(type* node);
void VisitExpressionStatement(ExpressionStatement* node); AST_NODE_LIST(DECLARE_VISIT)
void VisitEmptyStatement(EmptyStatement* node); #undef DECLARE_VISIT
void VisitIfStatement(IfStatement* node);
void VisitContinueStatement(ContinueStatement* node); void VisitVariable(Variable* var);
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);
private: private:
int weight_; int weight_;
@ -329,7 +297,8 @@ void UsageComputer::VisitCall(Call* node) {
void UsageComputer::VisitCallNew(CallNew* 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 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test that we get exceptions for invalid left-hand sides. Also // Test that we get exceptions for invalid left-hand sides. The
// tests that if the invalid left-hand side is a function call, the // exceptions are delayed until runtime.
// exception is delayed until runtime.
// Normal assignments: // Normal assignments:
assertThrows("12 = 12"); assertThrows("12 = 12");
@ -57,12 +56,10 @@ assertDoesNotThrow("if (false) for (eval('var x') = 1;;) print(12);");
// Assignments to 'this'. // Assignments to 'this'.
assertThrows("this = 42"); assertThrows("this = 42");
assertThrows("function f() { this = 12; }"); assertDoesNotThrow("function f() { this = 12; }");
assertThrows("for (this in Array) ;"); assertThrows("for (this in {x:3, y:4, z:5}) ;");
assertThrows("for (this = 0;;) ;"); assertThrows("for (this = 0;;) ;");
assertThrows("this++"); assertThrows("this++");
assertThrows("++this"); assertThrows("++this");
assertThrows("this--"); assertThrows("this--");
assertThrows("--this"); assertThrows("--this");