JavaScriptCore:
authordarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 28 Oct 2007 22:50:59 +0000 (22:50 +0000)
committerdarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 28 Oct 2007 22:50:59 +0000 (22:50 +0000)
        Reviewed by Maciej.

        - http://bugs.webkit.org/show_bug.cgi?id=15735
          remove GroupNode to simplify AST and possibly get a modest speedup

        This patch removes 4 node types: GroupNode, PropertyNameNode,
        FunctionCallParenBracketNode, and FunctionCallParenDotNode.

        To remove GroupNode, we add knowledge of precedence to the tree nodes,
        and use that when serializing to determine where parentheses are needed.
        This means we no longer have to represent parentheses in the tree.

        The precedence values are named after productions in the grammar from the
        JavaScript standard.

        SunSpider says this is an 0.4% speedup.

        * kjs/function.h:
        * kjs/function.cpp: Removed escapeStringForPrettyPrinting -- it's part of
        serialization, so I moved it to the file that takes care of that.

        * kjs/grammar.y: Changed makeGetterOrSetterPropertyNode to use 0 to
        indicate failure instead of a separate boolean. Got rid of PropertyNameNode
        by merging the PropertyName rule into the Property rule (which was easier
        than figuring out how to pass the Identifier from one node to another).
        Got rid of GroupNode, nodeInsideAllParens(), FunctionCallParenBracketNode,
        and FunctionCallParenDotNode.

        * kjs/nodes.h: Removed unused forward declarations and Operator values.
        Added Precedence enum, and precedence function to all nodes. Removed
        nodeInsideAllParens. Added streamBinaryOperator function for serialization.
        Removed GroupNode and PropertyNameNode. Made PropertyNode store an Identifier.
        Removed FunctionCallParenBracketNode and FunctionCallParenDotNode.

        * kjs/nodes.cpp: Removed Node::nodinsideAllParens, GroupNode, and PropertyNameNode.
        (KJS::PropertyListNode::evaluate): Changed code to get name directly instead
        of converting it from an Identifier to a jsString then back to a UString
        then into an Identifier again!

        * kjs/nodes2string.cpp: Changed special-token implementation to use a separate
        function for each of Endl, Indent, Unindent, and DotExpr instead of using a
        single function with a switch. Added a precedence that you can stream in, to
        cause the next node serialized to add parentheses based on that precedence value.
        (KJS::operatorString): Moved to the top of the file.
        (KJS::escapeStringForPrettyPrinting): Moved here from function.cpp. Removed old
        workaround for snprintf, since StringExtras.h takes care of that.
        (KJS::operator<<): Made the char and char* versions faster by using UString's
        character append functions instead of constructing a UString. Added the logic
        to the Node* version to add parentheses if needed.
        (KJS::Node::streamLeftAssociativeBinaryOperator): Added helper function.
        (KJS::ElementNode::streamTo): Use PrecAssignment for the elements.
        (KJS::BracketAccessorNode::streamTo): Use PrecCall for the expression before
        the bracket.
        (KJS::DotAccessorNode::streamTo): Use PrecCall for the expression before the dot.
        (KJS::ArgumentListNode::streamTo): Use PrecAssignment for the arguments.
        (KJS::NewExprNode::streamTo): Use PrecMember for the expression.
        (KJS::FunctionCallValueNode::streamTo): Use PrecCall.
        (KJS::FunctionCallBracketNode::streamTo): Ditto.
        (KJS::FunctionCallDotNode::streamTo): Ditto.
        (KJS::PostfixBracketNode::streamTo): Ditto.
        (KJS::PostfixDotNode::streamTo): Ditto.
        (KJS::PostfixErrorNode::streamTo): Use PrecLeftHandSide.
        (KJS::DeleteBracketNode::streamTo): Use PrecCall.
        (KJS::DeleteDotNode::streamTo): Ditto.
        (KJS::DeleteValueNode::streamTo): Use PrecUnary.
        (KJS::VoidNode::streamTo): Ditto.
        (KJS::TypeOfValueNode::streamTo): Ditto.
        (KJS::PrefixBracketNode::streamTo): Use PrecCall.
        (KJS::PrefixDotNode::streamTo): Ditto.
        (KJS::PrefixErrorNode::streamTo): Use PrecUnary.
        (KJS::UnaryPlusNode::streamTo): Ditto.
        (KJS::NegateNode::streamTo): Ditto.
        (KJS::BitwiseNotNode::streamTo): Ditto.
        (KJS::LogicalNotNode::streamTo): Ditto.
        (KJS::MultNode::streamTo): Use streamLeftAssociativeBinaryOperator.
        (KJS::DivNode::streamTo): Ditto.
        (KJS::ModNode::streamTo): Ditto.
        (KJS::AddNode::streamTo): Ditto.
        (KJS::SubNode::streamTo): Ditto.
        (KJS::LeftShiftNode::streamTo): Ditto.
        (KJS::RightShiftNode::streamTo): Ditto.
        (KJS::UnsignedRightShiftNode::streamTo): Ditto.
        (KJS::LessNode::streamTo): Ditto.
        (KJS::GreaterNode::streamTo): Ditto.
        (KJS::LessEqNode::streamTo): Ditto.
        (KJS::GreaterEqNode::streamTo): Ditto.
        (KJS::InstanceOfNode::streamTo): Ditto.
        (KJS::InNode::streamTo): Ditto.
        (KJS::EqualNode::streamTo): Ditto.
        (KJS::NotEqualNode::streamTo): Ditto.
        (KJS::StrictEqualNode::streamTo): Ditto.
        (KJS::NotStrictEqualNode::streamTo): Ditto.
        (KJS::BitAndNode::streamTo): Ditto.
        (KJS::BitXOrNode::streamTo): Ditto.
        (KJS::BitOrNode::streamTo): Ditto.
        (KJS::LogicalAndNode::streamTo): Ditto.
        (KJS::LogicalOrNode::streamTo): Ditto.
        (KJS::ConditionalNode::streamTo): Ditto.
        (KJS::AssignResolveNode::streamTo): Use PrecAssignment for the right side.
        (KJS::AssignBracketNode::streamTo): Use PrecCall for the expression before
        the bracket and PrecAssignment for the right side.
        (KJS::AssignDotNode::streamTo): Ditto.
        (KJS::AssignErrorNode::streamTo): Use PrecLeftHandSide for the left side
        and PrecAssignment for the right side.
        (KJS::CommaNode::streamTo): Use PrecAssignment for both expressions.
        (KJS::AssignExprNode::streamTo): Use PrecAssignment.

LayoutTests:

        Reviewed by Maciej.

        - test for http://bugs.webkit.org/show_bug.cgi?id=15735
          remove GroupNode to simplify AST and possibly get a modest speedup

        One test is a start at testing that parentheses are added when needed.
        The other test checks some aspects of object literals, since I changed
        the way the property names is handled in those. More tests are needed.

        * fast/js/function-toString-object-literals-expected.txt: Added.
        * fast/js/function-toString-object-literals.html: Added.
        * fast/js/function-toString-parentheses-expected.txt: Added.
        * fast/js/function-toString-parentheses.html: Added.
        * fast/js/resources/function-toString-object-literals.js: Added.
        * fast/js/resources/function-toString-parentheses.js: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@27191 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/kjs/function.cpp
JavaScriptCore/kjs/function.h
JavaScriptCore/kjs/grammar.y
JavaScriptCore/kjs/nodes.cpp
JavaScriptCore/kjs/nodes.h
JavaScriptCore/kjs/nodes2string.cpp
LayoutTests/ChangeLog
LayoutTests/fast/js/function-toString-object-literals-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/function-toString-object-literals.html [new file with mode: 0644]
LayoutTests/fast/js/function-toString-parentheses-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/function-toString-parentheses.html [new file with mode: 0644]
LayoutTests/fast/js/resources/function-toString-object-literals.js [new file with mode: 0644]
LayoutTests/fast/js/resources/function-toString-parentheses.js [new file with mode: 0644]

index a80be2b2576b1b6f98aca59f511616d0c3a7d67b..e55cb88ddd97a48bea60dacd51af256385f878d0 100644 (file)
@@ -1,3 +1,112 @@
+2007-10-28  Darin Adler  <darin@apple.com>
+
+        Reviewed by Maciej.
+
+        - http://bugs.webkit.org/show_bug.cgi?id=15735
+          remove GroupNode to simplify AST and possibly get a modest speedup
+
+        This patch removes 4 node types: GroupNode, PropertyNameNode,
+        FunctionCallParenBracketNode, and FunctionCallParenDotNode.
+
+        To remove GroupNode, we add knowledge of precedence to the tree nodes,
+        and use that when serializing to determine where parentheses are needed.
+        This means we no longer have to represent parentheses in the tree.
+
+        The precedence values are named after productions in the grammar from the
+        JavaScript standard.
+
+        SunSpider says this is an 0.4% speedup.
+
+        * kjs/function.h:
+        * kjs/function.cpp: Removed escapeStringForPrettyPrinting -- it's part of
+        serialization, so I moved it to the file that takes care of that.
+
+        * kjs/grammar.y: Changed makeGetterOrSetterPropertyNode to use 0 to
+        indicate failure instead of a separate boolean. Got rid of PropertyNameNode
+        by merging the PropertyName rule into the Property rule (which was easier
+        than figuring out how to pass the Identifier from one node to another).
+        Got rid of GroupNode, nodeInsideAllParens(), FunctionCallParenBracketNode,
+        and FunctionCallParenDotNode.
+
+        * kjs/nodes.h: Removed unused forward declarations and Operator values.
+        Added Precedence enum, and precedence function to all nodes. Removed
+        nodeInsideAllParens. Added streamBinaryOperator function for serialization.
+        Removed GroupNode and PropertyNameNode. Made PropertyNode store an Identifier.
+        Removed FunctionCallParenBracketNode and FunctionCallParenDotNode.
+
+        * kjs/nodes.cpp: Removed Node::nodinsideAllParens, GroupNode, and PropertyNameNode.
+        (KJS::PropertyListNode::evaluate): Changed code to get name directly instead
+        of converting it from an Identifier to a jsString then back to a UString
+        then into an Identifier again!
+
+        * kjs/nodes2string.cpp: Changed special-token implementation to use a separate
+        function for each of Endl, Indent, Unindent, and DotExpr instead of using a
+        single function with a switch. Added a precedence that you can stream in, to
+        cause the next node serialized to add parentheses based on that precedence value.
+        (KJS::operatorString): Moved to the top of the file.
+        (KJS::escapeStringForPrettyPrinting): Moved here from function.cpp. Removed old
+        workaround for snprintf, since StringExtras.h takes care of that.
+        (KJS::operator<<): Made the char and char* versions faster by using UString's
+        character append functions instead of constructing a UString. Added the logic
+        to the Node* version to add parentheses if needed.
+        (KJS::Node::streamLeftAssociativeBinaryOperator): Added helper function.
+        (KJS::ElementNode::streamTo): Use PrecAssignment for the elements.
+        (KJS::BracketAccessorNode::streamTo): Use PrecCall for the expression before
+        the bracket.
+        (KJS::DotAccessorNode::streamTo): Use PrecCall for the expression before the dot.
+        (KJS::ArgumentListNode::streamTo): Use PrecAssignment for the arguments.
+        (KJS::NewExprNode::streamTo): Use PrecMember for the expression.
+        (KJS::FunctionCallValueNode::streamTo): Use PrecCall.
+        (KJS::FunctionCallBracketNode::streamTo): Ditto.
+        (KJS::FunctionCallDotNode::streamTo): Ditto.
+        (KJS::PostfixBracketNode::streamTo): Ditto.
+        (KJS::PostfixDotNode::streamTo): Ditto.
+        (KJS::PostfixErrorNode::streamTo): Use PrecLeftHandSide.
+        (KJS::DeleteBracketNode::streamTo): Use PrecCall.
+        (KJS::DeleteDotNode::streamTo): Ditto.
+        (KJS::DeleteValueNode::streamTo): Use PrecUnary.
+        (KJS::VoidNode::streamTo): Ditto.
+        (KJS::TypeOfValueNode::streamTo): Ditto.
+        (KJS::PrefixBracketNode::streamTo): Use PrecCall.
+        (KJS::PrefixDotNode::streamTo): Ditto.
+        (KJS::PrefixErrorNode::streamTo): Use PrecUnary.
+        (KJS::UnaryPlusNode::streamTo): Ditto.
+        (KJS::NegateNode::streamTo): Ditto.
+        (KJS::BitwiseNotNode::streamTo): Ditto.
+        (KJS::LogicalNotNode::streamTo): Ditto.
+        (KJS::MultNode::streamTo): Use streamLeftAssociativeBinaryOperator.
+        (KJS::DivNode::streamTo): Ditto.
+        (KJS::ModNode::streamTo): Ditto.
+        (KJS::AddNode::streamTo): Ditto.
+        (KJS::SubNode::streamTo): Ditto.
+        (KJS::LeftShiftNode::streamTo): Ditto.
+        (KJS::RightShiftNode::streamTo): Ditto.
+        (KJS::UnsignedRightShiftNode::streamTo): Ditto.
+        (KJS::LessNode::streamTo): Ditto.
+        (KJS::GreaterNode::streamTo): Ditto.
+        (KJS::LessEqNode::streamTo): Ditto.
+        (KJS::GreaterEqNode::streamTo): Ditto.
+        (KJS::InstanceOfNode::streamTo): Ditto.
+        (KJS::InNode::streamTo): Ditto.
+        (KJS::EqualNode::streamTo): Ditto.
+        (KJS::NotEqualNode::streamTo): Ditto.
+        (KJS::StrictEqualNode::streamTo): Ditto.
+        (KJS::NotStrictEqualNode::streamTo): Ditto.
+        (KJS::BitAndNode::streamTo): Ditto.
+        (KJS::BitXOrNode::streamTo): Ditto.
+        (KJS::BitOrNode::streamTo): Ditto.
+        (KJS::LogicalAndNode::streamTo): Ditto.
+        (KJS::LogicalOrNode::streamTo): Ditto.
+        (KJS::ConditionalNode::streamTo): Ditto.
+        (KJS::AssignResolveNode::streamTo): Use PrecAssignment for the right side.
+        (KJS::AssignBracketNode::streamTo): Use PrecCall for the expression before
+        the bracket and PrecAssignment for the right side.
+        (KJS::AssignDotNode::streamTo): Ditto.
+        (KJS::AssignErrorNode::streamTo): Use PrecLeftHandSide for the left side
+        and PrecAssignment for the right side.
+        (KJS::CommaNode::streamTo): Use PrecAssignment for both expressions.
+        (KJS::AssignExprNode::streamTo): Use PrecAssignment.
+
 2007-10-28  Kevin Ollivier  <kevino@theolliviers.com>
 
         Define wx port and set wx port USE options.
         - Made a special constructor for iterators that knows it points to
         a valid filled cell and so skips updating itself.
 
-        - Reordered memory accesses in the various lookup functions for better codegetion
+        - Reordered memory accesses in the various lookup functions for better code generation
         
         - Made simple translators avoid passing a hash code around
         
index e714412d39d161465933044d888bd089102fffa6..648ba3ae35060b1722c4e7ecd0493ce75bc4c87c 100644 (file)
@@ -889,46 +889,4 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
   return res;
 }
 
-UString escapeStringForPrettyPrinting(const UString& s)
-{
-    UString escapedString;
-    
-    for (int i = 0; i < s.size(); i++) {
-        unsigned short c = s.data()[i].unicode();
-        
-        switch (c) {
-        case '\"':
-            escapedString += "\\\"";
-            break;
-        case '\n':
-            escapedString += "\\n";
-            break;
-        case '\r':
-            escapedString += "\\r";
-            break;
-        case '\t':
-            escapedString += "\\t";
-            break;
-        case '\\':
-            escapedString += "\\\\";
-            break;
-        default:
-            if (c < 128 && isPrintableChar(c))
-                escapedString.append(c);
-            else {
-                char hexValue[7];
-            
-#if PLATFORM(WIN_OS)
-                _snprintf(hexValue, 7, "\\u%04x", c);
-#else
-                snprintf(hexValue, 7, "\\u%04x", c);
-#endif
-                escapedString += hexValue;
-            }
-        }
-    }
-    
-    return escapedString;    
-}
-
 } // namespace
index 78ee4d3110b4901a975bf57185af604c81fe99f4..7c414ca8d7e781fea24d8d1978968897b3544661 100644 (file)
@@ -1,8 +1,7 @@
 // -*- c-basic-offset: 2 -*-
 /*
- *  This file is part of the KDE libraries
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2003, 2006 Apple Computer, Inc.
+ *  Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -216,8 +215,6 @@ namespace KJS {
   static const double mantissaOverflowLowerBound = 9007199254740992.0;
   double parseIntOverflow(const char* s, int length, int radix);
 
-UString escapeStringForPrettyPrinting(const UString& s);
-
 } // namespace
 
 #endif
index 5970f925905207c3d012bb8abc6726e7de5142d0..4d01e0e8de5d29e1c0f5858388cfb8f1605a8ad7 100644 (file)
@@ -1,7 +1,6 @@
 %{
 
 /*
- *  This file is part of the KDE libraries
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
  *  Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
  *
@@ -60,7 +59,7 @@ using namespace KJS;
 static Node* makeAssignNode(Node* loc, Operator, Node* expr);
 static Node* makePrefixNode(Node* expr, Operator);
 static Node* makePostfixNode(Node* expr, Operator);
-static bool makeGetterOrSetterPropertyNode(PropertyNode*& result, Identifier &getOrSet, Identifier& name, ParameterNode *params, FunctionBodyNode *body);
+static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier &getOrSet, const Identifier& name, ParameterNode*, FunctionBodyNode*);
 static Node *makeFunctionCallNode(Node *func, ArgumentsNode *args);
 static Node *makeTypeOfNode(Node *expr);
 static Node *makeDeleteNode(Node *expr);
@@ -108,7 +107,6 @@ static Node* makeNumberNode(double);
   Operator            op;
   PropertyListNode   *plist;
   PropertyNode       *pnode;
-  PropertyNameNode   *pname;
 }
 
 %start Program
@@ -202,7 +200,6 @@ static Node* makeNumberNode(double);
 %type <clist> CaseClauses  CaseClausesOpt
 %type <ival>  Elision ElisionOpt
 %type <elm>   ElementList
-%type <pname> PropertyName
 %type <pnode> Property
 %type <plist> PropertyList
 %%
@@ -225,17 +222,13 @@ Literal:
                                         }
 ;
 
-PropertyName:
-    IDENT                               { $$ = new PropertyNameNode(*$1); }
-  | STRING                              { $$ = new PropertyNameNode(Identifier(*$1)); }
-  | NUMBER                              { $$ = new PropertyNameNode($1); }
-;
-
 Property:
-    PropertyName ':' AssignmentExpr     { $$ = new PropertyNode($1, $3, PropertyNode::Constant); }
-  | IDENT IDENT '(' ')' FunctionBody    { if (!makeGetterOrSetterPropertyNode($$, *$1, *$2, 0, $5)) YYABORT; }
+    IDENT ':' AssignmentExpr            { $$ = new PropertyNode(*$1, $3, PropertyNode::Constant); }
+  | STRING ':' AssignmentExpr           { $$ = new PropertyNode(Identifier(*$1), $3, PropertyNode::Constant); }
+  | NUMBER ':' AssignmentExpr           { $$ = new PropertyNode(Identifier(UString::from($1)), $3, PropertyNode::Constant); }
+  | IDENT IDENT '(' ')' FunctionBody    { $$ = makeGetterOrSetterPropertyNode(*$1, *$2, 0, $5); if (!$$) YYABORT; }
   | IDENT IDENT '(' FormalParameterList ')' FunctionBody
-                                        { if (!makeGetterOrSetterPropertyNode($$, *$1, *$2, $4, $6)) YYABORT; }
+                                        { $$ = makeGetterOrSetterPropertyNode(*$1, *$2, $4, $6); if (!$$) YYABORT; }
 ;
 
 PropertyList:
@@ -256,8 +249,7 @@ PrimaryExprNoBrace:
   | Literal
   | ArrayLiteral
   | IDENT                               { $$ = new ResolveNode(*$1); }
-  | '(' Expr ')'                        { $$ = ($2->isResolveNode() || $2->isGroupNode()) ?
-                                            $2 : new GroupNode($2); }
+  | '(' Expr ')'                        { $$ = $2; }
 ;
 
 ArrayLiteral:
@@ -734,7 +726,7 @@ IterationStatement:
                                         { $$ = new ForNode($4, $6, $8, $10); DBG($$, @1, @9); }
   | FOR '(' LeftHandSideExpr INTOKEN Expr ')' Statement
                                         {
-                                            Node *n = $3->nodeInsideAllParens();
+                                            Node *n = $3;
                                             if (!n->isLocation())
                                                 YYABORT;
                                             $$ = new ForInNode(n, $5, $7);
@@ -875,135 +867,112 @@ SourceElement:
 %%
 
 static Node* makeAssignNode(Node* loc, Operator op, Node* expr)
-{ 
-    Node *n = loc->nodeInsideAllParens();
-
-    if (!n->isLocation())
+{
+    if (!loc->isLocation())
         return new AssignErrorNode(loc, op, expr);
 
-    if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (loc->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(loc);
         return new AssignResolveNode(resolve->identifier(), op, expr);
     }
-    if (n->isBracketAccessorNode()) {
-        BracketAccessorNode *bracket = static_cast<BracketAccessorNode *>(n);
+    if (loc->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(loc);
         return new AssignBracketNode(bracket->base(), bracket->subscript(), op, expr);
     }
-    ASSERT(n->isDotAccessorNode());
-    DotAccessorNode *dot = static_cast<DotAccessorNode *>(n);
+    ASSERT(loc->isDotAccessorNode());
+    DotAccessorNode* dot = static_cast<DotAccessorNode*>(loc);
     return new AssignDotNode(dot->base(), dot->identifier(), op, expr);
 }
 
-static Node* makePrefixNode(Node *expr, Operator op)
+static Node* makePrefixNode(Nodeexpr, Operator op)
 { 
-    Node *n = expr->nodeInsideAllParens();
-
-    if (!n->isLocation())
+    if (!expr->isLocation())
         return new PrefixErrorNode(expr, op);
     
-    if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (expr->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(expr);
         return new PrefixResolveNode(resolve->identifier(), op);
     }
-    if (n->isBracketAccessorNode()) {
-        BracketAccessorNode *bracket = static_cast<BracketAccessorNode *>(n);
+    if (expr->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr);
         return new PrefixBracketNode(bracket->base(), bracket->subscript(), op);
     }
-    ASSERT(n->isDotAccessorNode());
-    DotAccessorNode *dot = static_cast<DotAccessorNode *>(n);
+    ASSERT(expr->isDotAccessorNode());
+    DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr);
     return new PrefixDotNode(dot->base(), dot->identifier(), op);
 }
 
 static Node* makePostfixNode(Node* expr, Operator op)
 { 
-    Node *n = expr->nodeInsideAllParens();
-
-    if (!n->isLocation())
+    if (!expr->isLocation())
         return new PostfixErrorNode(expr, op);
     
-    if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (expr->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(expr);
         return new PostfixResolveNode(resolve->identifier(), op);
     }
-    if (n->isBracketAccessorNode()) {
-        BracketAccessorNode *bracket = static_cast<BracketAccessorNode *>(n);
+    if (expr->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr);
         return new PostfixBracketNode(bracket->base(), bracket->subscript(), op);
     }
-    ASSERT(n->isDotAccessorNode());
-    DotAccessorNode *dot = static_cast<DotAccessorNode *>(n);
+    ASSERT(expr->isDotAccessorNode());
+    DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr);
     return new PostfixDotNode(dot->base(), dot->identifier(), op);
 }
 
-static Node *makeFunctionCallNode(Node *func, ArgumentsNode *args)
+static Node* makeFunctionCallNode(Node* func, ArgumentsNode* args)
 {
-    Node *n = func->nodeInsideAllParens();
-    
-    if (!n->isLocation())
+    if (!func->isLocation())
         return new FunctionCallValueNode(func, args);
-    else if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (func->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(func);
         return new FunctionCallResolveNode(resolve->identifier(), args);
-    } else if (n->isBracketAccessorNode()) {
-        BracketAccessorNode *bracket = static_cast<BracketAccessorNode *>(n);
-        if (n != func)
-            return new FunctionCallParenBracketNode(bracket->base(), bracket->subscript(), args);
-        else
-            return new FunctionCallBracketNode(bracket->base(), bracket->subscript(), args);
-    } else {
-        ASSERT(n->isDotAccessorNode());
-        DotAccessorNode *dot = static_cast<DotAccessorNode *>(n);
-        if (n != func)
-            return new FunctionCallParenDotNode(dot->base(), dot->identifier(), args);
-        else
-            return new FunctionCallDotNode(dot->base(), dot->identifier(), args);
     }
+    if (func->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func);
+        return new FunctionCallBracketNode(bracket->base(), bracket->subscript(), args);
+    }
+    ASSERT(func->isDotAccessorNode());
+    DotAccessorNode* dot = static_cast<DotAccessorNode*>(func);
+    return new FunctionCallDotNode(dot->base(), dot->identifier(), args);
 }
 
-static Node *makeTypeOfNode(Node *expr)
+static Node* makeTypeOfNode(Node* expr)
 {
-    Node *n = expr->nodeInsideAllParens();
-
-    if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (expr->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(expr);
         return new TypeOfResolveNode(resolve->identifier());
-    } else
-        return new TypeOfValueNode(expr);
+    }
+    return new TypeOfValueNode(expr);
 }
 
-static Node *makeDeleteNode(Node *expr)
+static Node* makeDeleteNode(Node* expr)
 {
-    Node *n = expr->nodeInsideAllParens();
-    
-    if (!n->isLocation())
+    if (!expr->isLocation())
         return new DeleteValueNode(expr);
-    else if (n->isResolveNode()) {
-        ResolveNode *resolve = static_cast<ResolveNode *>(n);
+    if (expr->isResolveNode()) {
+        ResolveNode* resolve = static_cast<ResolveNode*>(expr);
         return new DeleteResolveNode(resolve->identifier());
-    } else if (n->isBracketAccessorNode()) {
-        BracketAccessorNode *bracket = static_cast<BracketAccessorNode *>(n);
+    }
+    if (expr->isBracketAccessorNode()) {
+        BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr);
         return new DeleteBracketNode(bracket->base(), bracket->subscript());
-    } else {
-        ASSERT(n->isDotAccessorNode());
-        DotAccessorNode *dot = static_cast<DotAccessorNode *>(n);
-        return new DeleteDotNode(dot->base(), dot->identifier());
     }
+    ASSERT(expr->isDotAccessorNode());
+    DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr);
+    return new DeleteDotNode(dot->base(), dot->identifier());
 }
 
-static bool makeGetterOrSetterPropertyNode(PropertyNode*& result, Identifier& getOrSet, Identifier& name, ParameterNode *params, FunctionBodyNode *body)
+static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier& getOrSet, const Identifier& name, ParameterNode* params, FunctionBodyNode* body)
 {
     PropertyNode::Type type;
-    
     if (getOrSet == "get")
         type = PropertyNode::Getter;
     else if (getOrSet == "set")
         type = PropertyNode::Setter;
     else
-        return false;
-    
-    result = new PropertyNode(new PropertyNameNode(name), 
-                              new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, body, params), type);
-
-    return true;
+        return 0;
+    return new PropertyNode(name, new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, body, params), type);
 }
 
 static Node* makeNegateNode(Node *n)
index 9301d173eccd0090bd11d1fcffbb29529a977ce9..e4f81c2cb8c35d091e6df422f3ed56eba969470b 100644 (file)
@@ -290,11 +290,6 @@ Completion Node::rethrowException(ExecState* exec)
     return Completion(Throw, exception);
 }
 
-Node *Node::nodeInsideAllParens()
-{
-    return this;
-}
-
 // ------------------------------ StatementNode --------------------------------
 
 StatementNode::StatementNode() 
@@ -398,23 +393,6 @@ JSValue *ResolveNode::evaluate(ExecState *exec)
   return throwUndefinedVariableError(exec, ident);
 }
 
-// ------------------------------ GroupNode ------------------------------------
-
-// ECMA 11.1.6
-JSValue *GroupNode::evaluate(ExecState *exec)
-{
-  return group->evaluate(exec);
-}
-
-Node *GroupNode::nodeInsideAllParens()
-{
-    Node *n = this;
-    do
-        n = static_cast<GroupNode *>(n)->group.get();
-    while (n->isGroupNode());
-    return n;
-}
-
 // ------------------------------ ElementNode ----------------------------------
 
 // ECMA 11.1.4
@@ -479,23 +457,20 @@ JSValue *PropertyListNode::evaluate(ExecState *exec)
   JSObject *obj = exec->lexicalInterpreter()->builtinObject()->construct(exec, List::empty());
   
   for (PropertyListNode *p = this; p; p = p->next.get()) {
-    JSValue *n = p->node->name->evaluate(exec);
-    KJS_CHECKEXCEPTIONVALUE
     JSValue *v = p->node->assign->evaluate(exec);
     KJS_CHECKEXCEPTIONVALUE
     
-    Identifier propertyName = Identifier(n->toString(exec));
     switch (p->node->type) {
       case PropertyNode::Getter:
         ASSERT(v->isObject());
-        obj->defineGetter(exec, propertyName, static_cast<JSObject *>(v));
+        obj->defineGetter(exec, p->node->name(), static_cast<JSObject *>(v));
         break;
       case PropertyNode::Setter:
         ASSERT(v->isObject());
-        obj->defineSetter(exec, propertyName, static_cast<JSObject *>(v));
+        obj->defineSetter(exec, p->node->name(), static_cast<JSObject *>(v));
         break;
       case PropertyNode::Constant:
-        obj->put(exec, propertyName, v);
+        obj->put(exec, p->node->name(), v);
         break;
     }
   }
@@ -516,22 +491,6 @@ JSValue *PropertyNode::evaluate(ExecState*)
   return jsNull();
 }
 
-// ---------------------------- PropertyNameNode -------------------------------
-
-// ECMA 11.1.5
-JSValue *PropertyNameNode::evaluate(ExecState*)
-{
-  JSValue *s;
-
-  if (str.isNull()) {
-    s = jsString(UString::from(numeric));
-  } else {
-    s = jsOwnedString(str.ustring());
-  }
-
-  return s;
-}
-
 // ------------------------------ BracketAccessorNode --------------------------------
 
 // ECMA 11.2.1a
index 2eebd4303c8ac02dc83ece394622ff5e3f96873a..057b670469db674a31052478da7740d22461d9bb 100644 (file)
 
 namespace KJS {
 
-  class FuncDeclNode;
-  class ProgramNode;
-  class PropertyNameNode;
-  class PropertyListNode;
-  class RegExp;
-  class SourceElementsNode;
-  class SourceStream;
-  class VarDeclNode;
-
-  enum Operator { OpEqual,
-                  OpEqEq,
-                  OpNotEq,
-                  OpStrEq,
-                  OpStrNEq,
-                  OpPlusEq,
-                  OpMinusEq,
-                  OpMultEq,
-                  OpDivEq,
-                  OpPlusPlus,
-                  OpMinusMinus,
-                  OpLess,
-                  OpLessEq,
-                  OpGreater,
-                  OpGreaterEq,
-                  OpAndEq,
-                  OpXOrEq,
-                  OpOrEq,
-                  OpModEq,
-                  OpAnd,
-                  OpOr,
-                  OpBitAnd,
-                  OpBitXOr,
-                  OpBitOr,
-                  OpLShift,
-                  OpRShift,
-                  OpURShift,
-                  OpIn,
-                  OpInstanceOf
-  };
+    class FuncDeclNode;
+    class PropertyListNode;
+    class SourceElementsNode;
+    class SourceStream;
+    class VarDeclNode;
+
+    enum Operator {
+        OpEqual,
+        OpPlusEq,
+        OpMinusEq,
+        OpMultEq,
+        OpDivEq,
+        OpPlusPlus,
+        OpMinusMinus,
+        OpAndEq,
+        OpXOrEq,
+        OpOrEq,
+        OpModEq,
+        OpLShift,
+        OpRShift,
+        OpURShift,
+    };
+
+    enum Precedence {
+        PrecPrimary,
+        PrecMember,
+        PrecCall,
+        PrecLeftHandSide,
+        PrecPostfix,
+        PrecUnary,
+        PrecMultiplicitave,
+        PrecAdditive,
+        PrecShift,
+        PrecRelational,
+        PrecEquality,
+        PrecBitwiseAnd,
+        PrecBitwiseXor,
+        PrecBitwiseOr,
+        PrecLogicalAnd,
+        PrecLogicalOr,
+        PrecConditional,
+        PrecAssignment,
+        PrecExpression
+    };
   
   struct DeclarationStacks {
       typedef Vector<Node*, 16> NodeStack;
@@ -110,25 +115,25 @@ namespace KJS {
 
     virtual JSValue *evaluate(ExecState *exec) KJS_FAST_CALL = 0;
     UString toString() const KJS_FAST_CALL;
-    virtual void streamTo(SourceStream&) const KJS_FAST_CALL = 0;
     int lineNo() const KJS_FAST_CALL { return m_line; }
     void ref() KJS_FAST_CALL;
     void deref() KJS_FAST_CALL;
     unsigned refcount() KJS_FAST_CALL;
     static void clearNewNodes() KJS_FAST_CALL;
 
-    virtual Node *nodeInsideAllParens() KJS_FAST_CALL;
-
     virtual bool isNumber() const KJS_FAST_CALL { return false; }
     virtual bool isImmediateValue() const KJS_FAST_CALL { return false; }
     virtual bool isLocation() const KJS_FAST_CALL { return false; }
     virtual bool isResolveNode() const KJS_FAST_CALL { return false; }
     virtual bool isBracketAccessorNode() const KJS_FAST_CALL { return false; }
     virtual bool isDotAccessorNode() const KJS_FAST_CALL { return false; }
-    virtual bool isGroupNode() const KJS_FAST_CALL { return false; }
+
+    // Serialization.
+    virtual void streamTo(SourceStream&) const KJS_FAST_CALL = 0;
+    virtual Precedence precedence() const = 0;
 
     // Used for iterative, depth-first traversal of the node tree. Does not cross function call boundaries.
-    bool mayHaveDeclarations() { return m_mayHaveDeclarations; }
+    bool mayHaveDeclarations() const { return m_mayHaveDeclarations; }
     virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL { ASSERT_NOT_REACHED(); }
 
     virtual void breakCycle() KJS_FAST_CALL { }
@@ -169,6 +174,7 @@ namespace KJS {
     bool hitStatement(ExecState*) KJS_FAST_CALL;
     virtual Completion execute(ExecState *exec) KJS_FAST_CALL = 0;
     void pushLabel(const Identifier &id) KJS_FAST_CALL { ls.push(id); }
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   protected:
     LabelStack ls;
   private:
@@ -181,6 +187,7 @@ namespace KJS {
     NullNode() KJS_FAST_CALL {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   };
 
   class BooleanNode : public Node {
@@ -188,6 +195,7 @@ namespace KJS {
     BooleanNode(bool v) KJS_FAST_CALL : value(v) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   private:
     bool value;
   };
@@ -197,6 +205,7 @@ namespace KJS {
     NumberNode(double v) KJS_FAST_CALL : val(v) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
 
     virtual bool isNumber() const KJS_FAST_CALL { return true; }
     double value() const KJS_FAST_CALL { return val; }
@@ -210,6 +219,7 @@ namespace KJS {
       ImmediateNumberNode(JSValue* v) KJS_FAST_CALL : m_value(v) {}
       JSValue* evaluate(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecPrimary; }
       
       virtual bool isImmediateValue() const KJS_FAST_CALL { return true; }
       double value() const KJS_FAST_CALL { return JSImmediate::toDouble(m_value); }
@@ -223,6 +233,7 @@ namespace KJS {
     StringNode(const UString *v) KJS_FAST_CALL { value = *v; }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   private:
     UString value;
   };
@@ -233,6 +244,7 @@ namespace KJS {
       : pattern(p), flags(f) { }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   private:
     UString pattern, flags;
   };
@@ -242,13 +254,15 @@ namespace KJS {
     ThisNode() KJS_FAST_CALL {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
-  };
+    virtual Precedence precedence() const { return PrecPrimary; }
+ };
 
   class ResolveNode : public Node {
   public:
     ResolveNode(const Identifier &s) KJS_FAST_CALL : ident(s) { }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
 
     virtual bool isLocation() const KJS_FAST_CALL { return true; }
     virtual bool isResolveNode() const KJS_FAST_CALL { return true; }
@@ -258,17 +272,6 @@ namespace KJS {
     Identifier ident;
   };
 
-  class GroupNode : public Node {
-  public:
-    GroupNode(Node *g) KJS_FAST_CALL : group(g) { }
-    virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
-    virtual Node *nodeInsideAllParens() KJS_FAST_CALL;
-    virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
-    virtual bool isGroupNode() const KJS_FAST_CALL { return true; }
-  private:
-    RefPtr<Node> group;
-  };
-
   class ElementNode : public Node {
   public:
     // list pointer is tail of a circular list, cracked in the ArrayNode ctor
@@ -279,6 +282,7 @@ namespace KJS {
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     PassRefPtr<ElementNode> releaseNext() KJS_FAST_CALL { return next.release(); }
     virtual void breakCycle() KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     friend class ArrayNode;
     ListRefPtr<ElementNode> next;
@@ -295,33 +299,25 @@ namespace KJS {
       : element(ele->next.release()), elision(eli), opt(true) { Parser::removeNodeCycle(element.get()); }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   private:
     RefPtr<ElementNode> element;
     int elision;
     bool opt;
   };
 
-  class PropertyNameNode : public Node {
-  public:
-    PropertyNameNode(double d) KJS_FAST_CALL : numeric(d) { }
-    PropertyNameNode(const Identifier &s) KJS_FAST_CALL : str(s) { }
-    JSValue* evaluate(ExecState*) KJS_FAST_CALL;
-    virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
-  private:
-    double numeric;
-    Identifier str;
-  };
-  
   class PropertyNode : public Node {
   public:
     enum Type { Constant, Getter, Setter };
-    PropertyNode(PropertyNameNode *n, Node *a, Type t) KJS_FAST_CALL
-      : name(n), assign(a), type(t) { }
+    PropertyNode(const Identifier& n, Node *a, Type t) KJS_FAST_CALL
+      : m_name(n), assign(a), type(t) { }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     friend class PropertyListNode;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+    const Identifier& name() const { return m_name; }
   private:
-    RefPtr<PropertyNameNode> name;
+    Identifier m_name;
     RefPtr<Node> assign;
     Type type;
   };
@@ -337,6 +333,7 @@ namespace KJS {
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     PassRefPtr<PropertyListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
     virtual void breakCycle() KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     friend class ObjectLiteralNode;
     RefPtr<PropertyNode> node;
@@ -349,6 +346,7 @@ namespace KJS {
     ObjectLiteralNode(PropertyListNode *l) KJS_FAST_CALL : list(l->next.release()) { Parser::removeNodeCycle(list.get()); }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPrimary; }
   private:
     RefPtr<PropertyListNode> list;
   };
@@ -358,6 +356,7 @@ namespace KJS {
     BracketAccessorNode(Node *e1, Node *e2) KJS_FAST_CALL : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecMember; }
 
     virtual bool isLocation() const KJS_FAST_CALL { return true; }
     virtual bool isBracketAccessorNode() const KJS_FAST_CALL { return true; }
@@ -374,6 +373,7 @@ namespace KJS {
     DotAccessorNode(Node *e, const Identifier &s) KJS_FAST_CALL : expr(e), ident(s) { }
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecMember; }
 
     virtual bool isLocation() const KJS_FAST_CALL { return true; }
     virtual bool isDotAccessorNode() const KJS_FAST_CALL { return true; }
@@ -396,6 +396,7 @@ namespace KJS {
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     PassRefPtr<ArgumentListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
     virtual void breakCycle() KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     friend class ArgumentsNode;
     ListRefPtr<ArgumentListNode> next;
@@ -410,6 +411,7 @@ namespace KJS {
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     List evaluateList(ExecState *exec) KJS_FAST_CALL { return list ? list->evaluateList(exec) : List(); }
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     RefPtr<ArgumentListNode> list;
   };
@@ -420,6 +422,7 @@ namespace KJS {
     NewExprNode(Node *e, ArgumentsNode *a) KJS_FAST_CALL : expr(e), args(a) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecLeftHandSide; }
   private:
     RefPtr<Node> expr;
     RefPtr<ArgumentsNode> args;
@@ -430,6 +433,7 @@ namespace KJS {
     FunctionCallValueNode(Node *e, ArgumentsNode *a) KJS_FAST_CALL : expr(e), args(a) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecCall; }
   private:
     RefPtr<Node> expr;
     RefPtr<ArgumentsNode> args;
@@ -440,6 +444,7 @@ namespace KJS {
     FunctionCallResolveNode(const Identifier& i, ArgumentsNode *a) KJS_FAST_CALL : ident(i), args(a) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecCall; }
   private:
     Identifier ident;
     RefPtr<ArgumentsNode> args;
@@ -450,40 +455,31 @@ namespace KJS {
     FunctionCallBracketNode(Node *b, Node *s, ArgumentsNode *a) KJS_FAST_CALL : base(b), subscript(s), args(a) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecCall; }
   protected:
     RefPtr<Node> base;
     RefPtr<Node> subscript;
     RefPtr<ArgumentsNode> args;
   };
 
-  class FunctionCallParenBracketNode : public FunctionCallBracketNode {
-  public:
-    FunctionCallParenBracketNode(Node *b, Node *s, ArgumentsNode *a) KJS_FAST_CALL : FunctionCallBracketNode(b, s, a) {}
-    virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
-  };
-
   class FunctionCallDotNode : public Node {
   public:
     FunctionCallDotNode(Node *b, const Identifier &i, ArgumentsNode *a) KJS_FAST_CALL : base(b), ident(i), args(a) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecCall; }
   protected:
     RefPtr<Node> base;
     Identifier ident;
     RefPtr<ArgumentsNode> args;
   };
 
-  class FunctionCallParenDotNode : public FunctionCallDotNode {
-  public:
-    FunctionCallParenDotNode(Node *b, const Identifier &i, ArgumentsNode *a) KJS_FAST_CALL : FunctionCallDotNode(b, i, a) {}
-    virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
-  };
-
   class PostfixResolveNode : public Node {
   public:
     PostfixResolveNode(const Identifier& i, Operator o) KJS_FAST_CALL : m_ident(i), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPostfix; }
   private:
     Identifier m_ident;
     Operator m_oper;
@@ -494,6 +490,7 @@ namespace KJS {
     PostfixBracketNode(Node *b, Node *s, Operator o) KJS_FAST_CALL : m_base(b), m_subscript(s), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPostfix; }
   private:
     RefPtr<Node> m_base;
     RefPtr<Node> m_subscript;
@@ -505,6 +502,7 @@ namespace KJS {
     PostfixDotNode(Node *b, const Identifier& i, Operator o) KJS_FAST_CALL : m_base(b), m_ident(i), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPostfix; }
   private:
     RefPtr<Node> m_base;
     Identifier m_ident;
@@ -516,6 +514,7 @@ namespace KJS {
     PostfixErrorNode(Node* e, Operator o) KJS_FAST_CALL : m_expr(e), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecPostfix; }
   private:
     RefPtr<Node> m_expr;
     Operator m_oper;
@@ -526,6 +525,7 @@ namespace KJS {
     DeleteResolveNode(const Identifier& i) KJS_FAST_CALL : m_ident(i) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     Identifier m_ident;
   };
@@ -535,6 +535,7 @@ namespace KJS {
     DeleteBracketNode(Node *base, Node *subscript) KJS_FAST_CALL : m_base(base), m_subscript(subscript) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_base;
     RefPtr<Node> m_subscript;
@@ -545,6 +546,7 @@ namespace KJS {
     DeleteDotNode(Node *base, const Identifier& i) KJS_FAST_CALL : m_base(base), m_ident(i) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_base;
     Identifier m_ident;
@@ -555,6 +557,7 @@ namespace KJS {
     DeleteValueNode(Node *e) KJS_FAST_CALL : m_expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_expr;
   };
@@ -564,6 +567,7 @@ namespace KJS {
     VoidNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> expr;
   };
@@ -573,6 +577,7 @@ namespace KJS {
     TypeOfResolveNode(const Identifier& i) KJS_FAST_CALL : m_ident(i) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     Identifier m_ident;
   };
@@ -582,6 +587,7 @@ namespace KJS {
     TypeOfValueNode(Node *e) KJS_FAST_CALL : m_expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_expr;
   };
@@ -591,6 +597,7 @@ namespace KJS {
     PrefixResolveNode(const Identifier& i, Operator o) KJS_FAST_CALL : m_ident(i), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     Identifier m_ident;
     Operator m_oper;
@@ -601,6 +608,7 @@ namespace KJS {
     PrefixBracketNode(Node *b, Node *s, Operator o) KJS_FAST_CALL : m_base(b), m_subscript(s), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_base;
     RefPtr<Node> m_subscript;
@@ -612,6 +620,7 @@ namespace KJS {
     PrefixDotNode(Node *b, const Identifier& i, Operator o) KJS_FAST_CALL : m_base(b), m_ident(i), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_base;
     Identifier m_ident;
@@ -623,6 +632,7 @@ namespace KJS {
     PrefixErrorNode(Node* e, Operator o) KJS_FAST_CALL : m_expr(e), m_oper(o) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> m_expr;
     Operator m_oper;
@@ -633,6 +643,7 @@ namespace KJS {
     UnaryPlusNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> expr;
   };
@@ -642,6 +653,7 @@ namespace KJS {
     NegateNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> expr;
   };
@@ -651,6 +663,7 @@ namespace KJS {
     BitwiseNotNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> expr;
   };
@@ -660,6 +673,7 @@ namespace KJS {
     LogicalNotNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecUnary; }
   private:
     RefPtr<Node> expr;
   };
@@ -669,6 +683,7 @@ namespace KJS {
       MultNode(Node *t1, Node *t2) KJS_FAST_CALL : term1(t1), term2(t2) {}
       JSValue* evaluate(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecMultiplicitave; }
   private:
       RefPtr<Node> term1;
       RefPtr<Node> term2;
@@ -679,6 +694,7 @@ namespace KJS {
       DivNode(Node *t1, Node *t2) KJS_FAST_CALL : term1(t1), term2(t2) {}
       JSValue* evaluate(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecMultiplicitave; }
   private:
       RefPtr<Node> term1;
       RefPtr<Node> term2;
@@ -689,6 +705,7 @@ namespace KJS {
       ModNode(Node *t1, Node *t2) KJS_FAST_CALL : term1(t1), term2(t2) {}
       JSValue* evaluate(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecMultiplicitave; }
   private:
       RefPtr<Node> term1;
       RefPtr<Node> term2;
@@ -699,6 +716,7 @@ namespace KJS {
     AddNode(Node *t1, Node *t2) KJS_FAST_CALL : term1(t1), term2(t2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecAdditive; }
   private:
     RefPtr<Node> term1;
     RefPtr<Node> term2;
@@ -709,6 +727,7 @@ namespace KJS {
       SubNode(Node *t1, Node *t2) KJS_FAST_CALL : term1(t1), term2(t2) {}
       JSValue* evaluate(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+      virtual Precedence precedence() const { return PrecAdditive; }
   private:
       RefPtr<Node> term1;
       RefPtr<Node> term2;
@@ -720,6 +739,7 @@ namespace KJS {
       : term1(t1), term2(t2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecShift; }
   private:
     RefPtr<Node> term1;
     RefPtr<Node> term2;
@@ -731,6 +751,7 @@ namespace KJS {
       : term1(t1), term2(t2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecShift; }
   private:
     RefPtr<Node> term1;
     RefPtr<Node> term2;
@@ -742,6 +763,7 @@ namespace KJS {
       : term1(t1), term2(t2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecShift; }
   private:
     RefPtr<Node> term1;
     RefPtr<Node> term2;
@@ -753,6 +775,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -764,6 +787,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -775,6 +799,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -786,6 +811,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -797,6 +823,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -808,6 +835,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecRelational; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -819,6 +847,7 @@ namespace KJS {
       : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecEquality; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -830,6 +859,7 @@ namespace KJS {
       : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecEquality; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -841,6 +871,7 @@ namespace KJS {
       : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecEquality; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -852,6 +883,7 @@ namespace KJS {
       : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecEquality; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -863,6 +895,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecBitwiseAnd; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -874,6 +907,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecBitwiseOr; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -885,6 +919,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecBitwiseXor; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -899,6 +934,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecLogicalAnd; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -910,6 +946,7 @@ namespace KJS {
       expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecLogicalOr; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -924,6 +961,7 @@ namespace KJS {
       logical(l), expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecConditional; }
   private:
     RefPtr<Node> logical;
     RefPtr<Node> expr1;
@@ -936,6 +974,7 @@ namespace KJS {
       : m_ident(ident), m_oper(oper), m_right(right) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecAssignment; }
   protected:
     Identifier m_ident;
     Operator m_oper;
@@ -948,6 +987,7 @@ namespace KJS {
       : m_base(base), m_subscript(subscript), m_oper(oper), m_right(right) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecAssignment; }
   protected:
     RefPtr<Node> m_base;
     RefPtr<Node> m_subscript;
@@ -961,6 +1001,7 @@ namespace KJS {
       : m_base(base), m_ident(ident), m_oper(oper), m_right(right) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecAssignment; }
   protected:
     RefPtr<Node> m_base;
     Identifier m_ident;
@@ -974,6 +1015,7 @@ namespace KJS {
       : m_left(left), m_oper(oper), m_right(right) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecAssignment; }
   protected:
     RefPtr<Node> m_left;
     Operator m_oper;
@@ -985,6 +1027,7 @@ namespace KJS {
     CommaNode(Node *e1, Node *e2) KJS_FAST_CALL : expr1(e1), expr2(e2) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecExpression; }
   private:
     RefPtr<Node> expr1;
     RefPtr<Node> expr2;
@@ -995,17 +1038,19 @@ namespace KJS {
     AssignExprNode(Node *e) KJS_FAST_CALL : expr(e) {}
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     RefPtr<Node> expr;
   };
 
-  class VarDeclNode: public Node {
+  class VarDeclNode : public Node {
   public:
     enum Type { Variable, Constant };
     VarDeclNode(const Identifier &id, AssignExprNode *in, Type t) KJS_FAST_CALL;
     JSValue* evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
     Type varType;
     Identifier ident;
   private:
@@ -1024,6 +1069,7 @@ namespace KJS {
     PassRefPtr<VarDeclListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
     virtual void breakCycle() KJS_FAST_CALL;
     virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     friend class ForNode;
     friend class VarStatementNode;
@@ -1220,6 +1266,7 @@ namespace KJS {
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
     PassRefPtr<ParameterNode> releaseNext() KJS_FAST_CALL { return next.release(); }
     virtual void breakCycle() KJS_FAST_CALL;
+    virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
     friend class FuncDeclNode;
     friend class FuncExprNode;
@@ -1271,6 +1318,7 @@ namespace KJS {
       : ident(i), param(p ? p->next.release() : 0), body(b) { if (p) { Parser::removeNodeCycle(param.get()); } addParams(); }
     virtual JSValue *evaluate(ExecState*) KJS_FAST_CALL;
     virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+    virtual Precedence precedence() const { return PrecMember; }
   private:
     void addParams() KJS_FAST_CALL;
     // Used for streamTo
@@ -1326,6 +1374,7 @@ namespace KJS {
       Completion evalStatements(ExecState*) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
       virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL;
+      virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
       RefPtr<Node> expr;
       RefPtr<SourceElementsNode> source;
@@ -1344,6 +1393,7 @@ namespace KJS {
       PassRefPtr<ClauseListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
       virtual void breakCycle() KJS_FAST_CALL;
       virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL;
+      virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
       friend class CaseBlockNode;
       RefPtr<CaseClauseNode> clause;
@@ -1357,6 +1407,7 @@ namespace KJS {
       Completion evalBlock(ExecState *exec, JSValue *input) KJS_FAST_CALL;
       virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
       virtual void getDeclarations(DeclarationStacks&) KJS_FAST_CALL;
+      virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
   private:
       RefPtr<ClauseListNode> list1;
       RefPtr<CaseClauseNode> def;
index d1a728e1470f285c4c56b13babef84c6dab3e4dc..47b9e2f70d78dc5ea5e5a16024410021b49c7800 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *  This file is part of the KDE libraries
  *  Copyright (C) 2002 Harri Porten (porten@kde.org)
  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
  *
 
 #include "config.h"
 #include "nodes.h"
-#include "function.h"
+
+#include <wtf/StringExtras.h>
+#include <wtf/unicode/Unicode.h>
+
+using namespace WTF;
+using namespace Unicode;
 
 namespace KJS {
-  /**
-   * A simple text streaming class that helps with code indentation.
-   */
-  class SourceStream {
-  public:
-    enum Format {
-      Endl, Indent, Unindent, DotExpr
-    };
-    SourceStream() : m_groupIfNumber(false) {}
-    UString toString() const { return str; }
-    SourceStream& operator<<(const Identifier &);
-    SourceStream& operator<<(const UString &);
-    SourceStream& operator<<(const char *);
+
+// A simple text streaming class that helps with code indentation.
+
+enum EndlType { Endl };
+enum IndentType { Indent };
+enum UnindentType { Unindent };
+enum DotExprType { DotExpr };
+
+class SourceStream {
+public:
+    SourceStream() : m_numberNeedsParens(false), m_precedence(PrecExpression) { }
+    UString toString() const { return m_string; }
+    SourceStream& operator<<(const Identifier&);
+    SourceStream& operator<<(const UString&);
+    SourceStream& operator<<(const char*);
     SourceStream& operator<<(double);
     SourceStream& operator<<(char);
-    SourceStream& operator<<(Format f);
-    SourceStream& operator<<(const Node *);
-    template <typename T> SourceStream& operator<<(RefPtr<T> n) { return this->operator<<(n.get()); }
+    SourceStream& operator<<(EndlType);
+    SourceStream& operator<<(IndentType);
+    SourceStream& operator<<(UnindentType);
+    SourceStream& operator<<(DotExprType);
+    SourceStream& operator<<(Precedence);
+    SourceStream& operator<<(const Node*);
+    template <typename T> SourceStream& operator<<(const RefPtr<T>& n) { return *this << n.get(); }
+
+private:
+    UString m_string;
+    UString m_spacesForIndentation;
+    bool m_numberNeedsParens;
+    Precedence m_precedence;
+};
+
+// --------
+
+static UString escapeStringForPrettyPrinting(const UString& s)
+{
+    UString escapedString;
+
+    for (int i = 0; i < s.size(); i++) {
+        unsigned short c = s.data()[i].unicode();
+        switch (c) {
+            case '\"':
+                escapedString += "\\\"";
+                break;
+            case '\n':
+                escapedString += "\\n";
+                break;
+            case '\r':
+                escapedString += "\\r";
+                break;
+            case '\t':
+                escapedString += "\\t";
+                break;
+            case '\\':
+                escapedString += "\\\\";
+                break;
+            default:
+                if (c < 128 && isPrintableChar(c))
+                    escapedString.append(c);
+                else {
+                    char hexValue[7];
+                    snprintf(hexValue, 7, "\\u%04x", c);
+                    escapedString += hexValue;
+                }
+        }
+    }
 
-  private:
-    UString str; /* TODO: buffer */
-    UString ind;
-    bool m_groupIfNumber;
-  };
+    return escapedString;    
+}
+
+static const char* operatorString(Operator oper)
+{
+    switch (oper) {
+        case OpEqual:
+            return "=";
+        case OpMultEq:
+            return "*=";
+        case OpDivEq:
+            return "/=";
+        case OpPlusEq:
+            return "+=";
+        case OpMinusEq:
+            return "-=";
+        case OpLShift:
+            return "<<=";
+        case OpRShift:
+            return ">>=";
+        case OpURShift:
+            return ">>>=";
+        case OpAndEq:
+            return "&=";
+        case OpXOrEq:
+            return "^=";
+        case OpOrEq:
+            return "|=";
+        case OpModEq:
+            return "%=";
+        case OpPlusPlus:
+            return "++";
+        case OpMinusMinus:
+            return "--";
+    }
+    ASSERT_NOT_REACHED();
+    return "???";
+}
+
+static bool isParserRoundTripNumber(const UString& string)
+{
+    double number = string.toDouble(false, false);
+    if (isnan(number) || isinf(number))
+        return false;
+    return string == UString::from(number);
 }
 
-using namespace KJS;
+// --------
 
 SourceStream& SourceStream::operator<<(char c)
 {
-  m_groupIfNumber = false;
-  UChar ch(c);
-  str += UString(&ch, 1);
-  return *this;
+    m_numberNeedsParens = false;
+    UChar ch(c);
+    m_string.append(ch);
+    return *this;
 }
 
-SourceStream& SourceStream::operator<<(const char *s)
+SourceStream& SourceStream::operator<<(const chars)
 {
-  m_groupIfNumber = false;
-  str += UString(s);
-  return *this;
+    m_numberNeedsParens = false;
+    m_string += s;
+    return *this;
 }
 
 SourceStream& SourceStream::operator<<(double value)
 {
-  if (m_groupIfNumber)
-    str.append("(");
+    bool needParens = m_numberNeedsParens;
+    m_numberNeedsParens = false;
 
-  str += UString::from(value);
+    if (needParens)
+        m_string.append('(');
+    m_string += UString::from(value);
+    if (needParens)
+        m_string.append(')');
 
-  if (m_groupIfNumber)
-    str.append(")");
-
-  m_groupIfNumber = false;
-  return *this;
+    return *this;
 }
 
-SourceStream& SourceStream::operator<<(const UString &s)
+SourceStream& SourceStream::operator<<(const UStrings)
 {
-  m_groupIfNumber = false;
-  str += s;
-  return *this;
+    m_numberNeedsParens = false;
+    m_string += s;
+    return *this;
 }
 
-SourceStream& SourceStream::operator<<(const Identifier &s)
+SourceStream& SourceStream::operator<<(const Identifiers)
 {
-  m_groupIfNumber = false;
-  str += s.ustring();
-  return *this;
+    m_numberNeedsParens = false;
+    m_string += s.ustring();
+    return *this;
 }
 
-SourceStream& SourceStream::operator<<(const Node *n)
+SourceStream& SourceStream::operator<<(const Noden)
 {
-  if (n)
-    n->streamTo(*this);
-  m_groupIfNumber = false;
-  return *this;
+    bool needParens = m_precedence != PrecExpression && n->precedence() > m_precedence;
+    m_precedence = PrecExpression;
+    if (n) {
+        if (needParens)
+            m_string.append('(');
+        n->streamTo(*this);
+        if (needParens)
+            m_string.append(')');
+    }
+    return *this;
 }
 
-SourceStream& SourceStream::operator<<(Format f)
+SourceStream& SourceStream::operator<<(EndlType)
 {
-  m_groupIfNumber = false;
-  switch (f) {
-    case Endl:
-      str += "\n" + ind;
-      break;
-    case Indent:
-      ind += "  ";
-      break;
-    case Unindent:
-      ind = ind.substr(0, ind.size() - 2);
-      break;
-  case DotExpr:
-      m_groupIfNumber = true;
-      break;
-  }
-
-  return *this;
+    m_numberNeedsParens = false;
+    m_string.append('\n');
+    m_string.append(m_spacesForIndentation);
+    return *this;
 }
 
-UString Node::toString() const
+SourceStream& SourceStream::operator<<(IndentType)
 {
-  SourceStream str;
-  streamTo(str);
-
-  return str.toString();
+    m_numberNeedsParens = false;
+    m_spacesForIndentation += "  ";
+    return *this;
 }
 
-void NullNode::streamTo(SourceStream &s) const { s << "null"; }
-
-void BooleanNode::streamTo(SourceStream &s) const
+SourceStream& SourceStream::operator<<(UnindentType)
 {
-  s << (value ? "true" : "false");
+    m_numberNeedsParens = false;
+    m_spacesForIndentation = m_spacesForIndentation.substr(0, m_spacesForIndentation.size() - 2);
+    return *this;
 }
 
-void NumberNode::streamTo(SourceStream &s) const { s << val; }
-
-void ImmediateNumberNode::streamTo(SourceStream& s) const
+inline SourceStream& SourceStream::operator<<(DotExprType)
 {
-    s << value();
+    m_numberNeedsParens = true;
+    return *this;
 }
 
-void StringNode::streamTo(SourceStream &s) const
+inline SourceStream& SourceStream::operator<<(Precedence precedence)
 {
-  s << '"' << escapeStringForPrettyPrinting(value) << '"';
+    m_precedence = precedence;
+    return *this;
 }
 
-void RegExpNode::streamTo(SourceStream &s) const
-{ 
-    s << "/" <<  pattern << "/" << flags; 
+static void streamLeftAssociativeBinaryOperator(SourceStream& s, Precedence precedence,
+    const char* operatorString, const Node* left, const Node* right)
+{
+    s << precedence << left
+        << ' ' << operatorString << ' '
+        << static_cast<Precedence>(precedence - 1) << right;
 }
 
-void ThisNode::streamTo(SourceStream &s) const { s << "this"; }
+template <typename T> static inline void streamLeftAssociativeBinaryOperator(SourceStream& s,
+    Precedence p, const char* o, const RefPtr<T>& l, const RefPtr<T>& r)
+{
+    streamLeftAssociativeBinaryOperator(s, p, o, l.get(), r.get());
+}
 
-void ResolveNode::streamTo(SourceStream &s) const { s << ident; }
+// --------
 
-void GroupNode::streamTo(SourceStream &s) const
+UString Node::toString() const
 {
-  s << "(" << group << ")"; 
+    SourceStream stream;
+    streamTo(stream);
+    return stream.toString();
 }
 
-void ElementNode::streamTo(SourceStream &s) const
+// --------
+
+void NullNode::streamTo(SourceStream& s) const
 {
-  for (const ElementNode *n = this; n; n = n->next.get()) {
-    for (int i = 0; i < n->elision; i++)
-      s << ",";
-    s << n->node;
-    if (n->next)
-        s << ",";
-  }
+    s << "null";
 }
 
-void ArrayNode::streamTo(SourceStream &s) const
+void BooleanNode::streamTo(SourceStream& s) const
 {
-  s << "[" << element;
-  for (int i = 0; i < elision; i++)
-    s << ",";
-  // Parser consumes one elision comma if there's array elements 
-  // present in the expression.
-  if (opt && element)
-    s << ",";
-  s << "]";
+    s << (value ? "true" : "false");
 }
 
-void ObjectLiteralNode::streamTo(SourceStream &s) const
+void NumberNode::streamTo(SourceStream& s) const
 {
-  if (list)
-    s << "{ " << list << " }";
-  else
-    s << "{ }";
+    s << val;
 }
 
-void PropertyListNode::streamTo(SourceStream &s) const
+void ImmediateNumberNode::streamTo(SourceStream& s) const
 {
-  s << node;
-  
-  for (const PropertyListNode *n = next.get(); n; n = n->next.get())
-    s << ", " << n->node;
+    s << value();
 }
 
-void PropertyNode::streamTo(SourceStream &s) const
+void StringNode::streamTo(SourceStream& s) const
 {
-  switch (type) {
-    case Constant:
-      s << name << ": " << assign;
-      break;
-    case Getter:
-    case Setter: {
-      const FuncExprNode *func = static_cast<const FuncExprNode *>(assign.get());
-      if (type == Getter)
-        s << "get "; 
-      else
-        s << "set ";
-      
-      s << name << "(" << func->param << ")" << func->body;
-      break;
-    }
-  }
+    s << '"' << escapeStringForPrettyPrinting(value) << '"';
 }
 
-void PropertyNameNode::streamTo(SourceStream &s) const
-{
-  if (str.isNull())
-    s << UString::from(numeric);
-  else
-    s << '"' << escapeStringForPrettyPrinting(str.ustring()) << '"';
+void RegExpNode::streamTo(SourceStream& s) const
+{ 
+    s << '/' <<  pattern << '/' << flags; 
 }
 
-void BracketAccessorNode::streamTo(SourceStream &s) const
+void ThisNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << "[" << expr2 << "]";
+    s << "this";
 }
 
-void DotAccessorNode::streamTo(SourceStream &s) const
+void ResolveNode::streamTo(SourceStream& s) const
 {
-  s << SourceStream::DotExpr << expr << "." << ident;
+    s << ident;
 }
 
-void ArgumentListNode::streamTo(SourceStream &s) const
+void ElementNode::streamTo(SourceStream& s) const
 {
-  s << expr;
-  for (ArgumentListNode *n = next.get(); n; n = n->next.get())
-    s << ", " << n->expr;
+    for (const ElementNode* n = this; n; n = n->next.get()) {
+        for (int i = 0; i < n->elision; i++)
+            s << ',';
+        s << PrecAssignment << n->node;
+        if (n->next)
+            s << ',';
+    }
 }
 
-void ArgumentsNode::streamTo(SourceStream &s) const
+void ArrayNode::streamTo(SourceStream& s) const
+{
+    s << '[' << element;
+    for (int i = 0; i < elision; i++)
+        s << ',';
+    // Parser consumes one elision comma if there's array elements 
+    // present in the expression.
+    if (opt && element)
+        s << ',';
+    s << ']';
+}
+
+void ObjectLiteralNode::streamTo(SourceStream& s) const
+{
+    if (list)
+        s << "{ " << list << " }";
+    else
+        s << "{ }";
+}
+
+void PropertyListNode::streamTo(SourceStream& s) const
+{
+    s << node;
+    for (const PropertyListNode* n = next.get(); n; n = n->next.get())
+        s << ", " << n->node;
+}
+
+void PropertyNode::streamTo(SourceStream& s) const
+{
+    switch (type) {
+        case Constant: {
+            UString propertyName = name().ustring();
+            if (isParserRoundTripNumber(propertyName))
+                s << propertyName;
+            else
+                s << '"' << escapeStringForPrettyPrinting(propertyName) << '"';
+            s << ": " << PrecAssignment << assign;
+            break;
+        }
+        case Getter:
+        case Setter: {
+            const FuncExprNode* func = static_cast<const FuncExprNode*>(assign.get());
+            if (type == Getter)
+                s << "get \""; 
+            else
+                s << "set \"";
+            s << escapeStringForPrettyPrinting(name().ustring())
+                << "\"(" << func->param << ')' << func->body;
+            break;
+        }
+    }
+}
+
+void BracketAccessorNode::streamTo(SourceStream& s) const
 {
-  s << "(" << list << ")";
+    s << PrecCall << expr1 << "[" << expr2 << "]";
 }
 
-void NewExprNode::streamTo(SourceStream &s) const
+void DotAccessorNode::streamTo(SourceStream& s) const
 {
-  s << "new " << expr << args;
+    s << DotExpr << PrecCall << expr << "." << ident;
 }
 
-void FunctionCallValueNode::streamTo(SourceStream &s) const
+void ArgumentListNode::streamTo(SourceStream& s) const
 {
-  s << expr << args;
+    s << PrecAssignment << expr;
+    for (ArgumentListNode* n = next.get(); n; n = n->next.get())
+        s << ", " << PrecAssignment << n->expr;
 }
 
-void FunctionCallResolveNode::streamTo(SourceStream &s) const
+void ArgumentsNode::streamTo(SourceStream& s) const
 {
-  s << ident << args;
+    s << '(' << list << ')';
 }
 
-void FunctionCallBracketNode::streamTo(SourceStream &s) const
+void NewExprNode::streamTo(SourceStream& s) const
 {
-  s << base << "[" << subscript << "]" << args;
+    s << "new " << PrecMember << expr << args;
 }
 
-void FunctionCallParenBracketNode::streamTo(SourceStream &s) const
+void FunctionCallValueNode::streamTo(SourceStream& s) const
 {
-  s << "(" << base << "[" << subscript << "])" << args;
+    s << PrecCall << expr << args;
 }
 
-void FunctionCallDotNode::streamTo(SourceStream &s) const
+void FunctionCallResolveNode::streamTo(SourceStream& s) const
 {
-  s << SourceStream::DotExpr << base << "." << ident << args;
+    s << ident << args;
 }
 
-void FunctionCallParenDotNode::streamTo(SourceStream &s) const
+void FunctionCallBracketNode::streamTo(SourceStream& s) const
 {
-  s << "(" << SourceStream::DotExpr << base << "." << ident << ")" << args;
+    s << PrecCall << base << "[" << subscript << "]" << args;
 }
 
-void PostfixResolveNode::streamTo(SourceStream &s) const
+void FunctionCallDotNode::streamTo(SourceStream& s) const
 {
-  s << m_ident;
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
+    s << DotExpr << PrecCall << base << "." << ident << args;
 }
 
-void PostfixBracketNode::streamTo(SourceStream &s) const
+void PostfixResolveNode::streamTo(SourceStream& s) const
 {
-  s << m_base << "[" << m_subscript << "]";
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
+    s << m_ident;
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
 }
 
-void PostfixDotNode::streamTo(SourceStream &s) const
+void PostfixBracketNode::streamTo(SourceStream& s) const
 {
-  s << SourceStream::DotExpr << m_base << "." << m_ident;
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
+    s << PrecCall << m_base << "[" << m_subscript << "]";
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
 }
 
-void PostfixErrorNode::streamTo(SourceStream& s) const
+void PostfixDotNode::streamTo(SourceStream& s) const
 {
-  s << m_expr;
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
+    s << DotExpr << PrecCall << m_base << "." << m_ident;
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
 }
 
-void DeleteResolveNode::streamTo(SourceStream &s) const
+void PostfixErrorNode::streamTo(SourceStream& s) const
 {
-  s << "delete " << m_ident;
+    s << PrecLeftHandSide << m_expr;
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
 }
 
-void DeleteBracketNode::streamTo(SourceStream &s) const
+void DeleteResolveNode::streamTo(SourceStream& s) const
 {
-  s << "delete " << m_base << "[" << m_subscript << "]";
+    s << "delete " << m_ident;
 }
 
-void DeleteDotNode::streamTo(SourceStream &s) const
+void DeleteBracketNode::streamTo(SourceStream& s) const
 {
-  s << "delete " << SourceStream::DotExpr << m_base << "." << m_ident;
+    s << "delete " << PrecCall << m_base << "[" << m_subscript << "]";
 }
 
-void DeleteValueNode::streamTo(SourceStream &s) const
+void DeleteDotNode::streamTo(SourceStream& s) const
 {
-  s << "delete " << m_expr;
+    s << "delete " << DotExpr << PrecCall << m_base << "." << m_ident;
 }
 
-void VoidNode::streamTo(SourceStream &s) const
+void DeleteValueNode::streamTo(SourceStream& s) const
 {
-  s << "void " << expr;
+    s << "delete " << PrecUnary << m_expr;
 }
 
-void TypeOfValueNode::streamTo(SourceStream &s) const
+void VoidNode::streamTo(SourceStream& s) const
 {
-  s << "typeof " << m_expr;
+    s << "void " << PrecUnary << expr;
 }
 
-void TypeOfResolveNode::streamTo(SourceStream &s) const
+void TypeOfValueNode::streamTo(SourceStream& s) const
 {
-  s << "typeof " << m_ident;
+    s << "typeof " << PrecUnary << m_expr;
 }
 
-void PrefixResolveNode::streamTo(SourceStream &s) const
+void TypeOfResolveNode::streamTo(SourceStream& s) const
 {
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
-  s << m_ident;
+    s << "typeof " << m_ident;
 }
 
-void PrefixBracketNode::streamTo(SourceStream &s) const
+void PrefixResolveNode::streamTo(SourceStream& s) const
 {
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
-  s << m_base << "[" << m_subscript << "]";
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
+    s << m_ident;
 }
 
-void PrefixDotNode::streamTo(SourceStream &s) const
+void PrefixBracketNode::streamTo(SourceStream& s) const
 {
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
-  s << SourceStream::DotExpr << m_base << "." << m_ident;
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
+    s << PrecCall << m_base << "[" << m_subscript << "]";
 }
 
-void PrefixErrorNode::streamTo(SourceStream& s) const
+void PrefixDotNode::streamTo(SourceStream& s) const
 {
-  if (m_oper == OpPlusPlus)
-    s << "++";
-  else
-    s << "--";
-  s << m_expr;
+    if (m_oper == OpPlusPlus)
+        s << "++";
+    else
+        s << "--";
+    s << DotExpr << PrecCall << m_base << "." << m_ident;
 }
 
-void UnaryPlusNode::streamTo(SourceStream &s) const
+void PrefixErrorNode::streamTo(SourceStream& s) const
 {
-  s << "+ " << expr;
+    if (m_oper == OpPlusPlus)
+        s << "++" << PrecUnary << m_expr;
+    else
+        s << "--" << PrecUnary << m_expr;
 }
 
-void NegateNode::streamTo(SourceStream &s) const
+void UnaryPlusNode::streamTo(SourceStream& s) const
 {
-  s << "- " << expr;
+    s << "+ " << PrecUnary << expr;
 }
 
-void BitwiseNotNode::streamTo(SourceStream &s) const
+void NegateNode::streamTo(SourceStream& s) const
 {
-  s << "~" << expr;
+    s << "- " << PrecUnary << expr;
 }
 
-void LogicalNotNode::streamTo(SourceStream &s) const
+void BitwiseNotNode::streamTo(SourceStream& s) const
 {
-  s << "!" << expr;
+    s << "~" << PrecUnary << expr;
 }
 
-void MultNode::streamTo(SourceStream &s) const
+void LogicalNotNode::streamTo(SourceStream& s) const
 {
-    s << term1 << " * " << term2;
+    s << "!" << PrecUnary << expr;
 }
 
-void DivNode::streamTo(SourceStream &s) const
+void MultNode::streamTo(SourceStream& s) const
 {
-    s << term1 << " / " << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "*", term1, term2);
 }
 
-void ModNode::streamTo(SourceStream &s) const
+void DivNode::streamTo(SourceStream& s) const
 {
-    s << term1 << " % " << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "/", term1, term2);
 }
 
-void AddNode::streamTo(SourceStream &s) const
+void ModNode::streamTo(SourceStream& s) const
 {
-    s << term1 << " + " << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "%", term1, term2);
 }
 
-void SubNode::streamTo(SourceStream &s) const
+void AddNode::streamTo(SourceStream& s) const
 {
-    s << term1 << " - " << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "+", term1, term2);
 }
 
-void LeftShiftNode::streamTo(SourceStream &s) const
+void SubNode::streamTo(SourceStream& s) const
 {
-  s << term1 << "<<" << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "-", term1, term2);
 }
 
-void RightShiftNode::streamTo(SourceStream &s) const
+void LeftShiftNode::streamTo(SourceStream& s) const
 {
-  s << term1 << ">>" << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "<<", term1, term2);
 }
 
-void UnsignedRightShiftNode::streamTo(SourceStream &s) const
+void RightShiftNode::streamTo(SourceStream& s) const
 {
-  s << term1 << ">>>" << term2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), ">>", term1, term2);
 }
 
-void LessNode::streamTo(SourceStream &s) const
+void UnsignedRightShiftNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " < " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), ">>>", term1, term2);
 }
 
-void GreaterNode::streamTo(SourceStream &s) const
+void LessNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " > " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "<", expr1, expr2);
 }
 
-void LessEqNode::streamTo(SourceStream &s) const
+void GreaterNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " <= " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), ">", expr1, expr2);
 }
 
-void GreaterEqNode::streamTo(SourceStream &s) const
+void LessEqNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " >= " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "<=", expr1, expr2);
 }
 
-void InstanceOfNode::streamTo(SourceStream &s) const
+void GreaterEqNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " instanceof " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), ">=", expr1, expr2);
 }
 
-void InNode::streamTo(SourceStream &s) const
+void InstanceOfNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " in " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "instanceof", expr1, expr2);
 }
 
-void EqualNode::streamTo(SourceStream &s) const
+void InNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " == " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "in", expr1, expr2);
 }
 
-void NotEqualNode::streamTo(SourceStream &s) const
+void EqualNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " == " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "==", expr1, expr2);
 }
 
-void StrictEqualNode::streamTo(SourceStream &s) const
+void NotEqualNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " === " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "!=", expr1, expr2);
 }
 
-void NotStrictEqualNode::streamTo(SourceStream &s) const
+void StrictEqualNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " !== " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "===", expr1, expr2);
 }
 
-void BitAndNode::streamTo(SourceStream &s) const
+void NotStrictEqualNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " & " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "!==", expr1, expr2);
 }
 
-void BitXOrNode::streamTo(SourceStream &s) const
+void BitAndNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " ^ " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "&", expr1, expr2);
 }
 
-void BitOrNode::streamTo(SourceStream &s) const
+void BitXOrNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " | " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "^", expr1, expr2);
 }
 
-void LogicalAndNode::streamTo(SourceStream &s) const
+void BitOrNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " && " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "|", expr1, expr2);
 }
 
-void LogicalOrNode::streamTo(SourceStream &s) const
+void LogicalAndNode::streamTo(SourceStream& s) const
 {
-  s << expr1 << " || " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "&&", expr1, expr2);
 }
 
-void ConditionalNode::streamTo(SourceStream &s) const
+void LogicalOrNode::streamTo(SourceStream& s) const
 {
-  s << logical << " ? " << expr1 << " : " << expr2;
+    streamLeftAssociativeBinaryOperator(s, precedence(), "||", expr1, expr2);
 }
 
-static void streamAssignmentOperatorTo(SourceStream &s, Operator oper)
+void ConditionalNode::streamTo(SourceStream& s) const
 {
-  const char *opStr;
-  switch (oper) {
-  case OpEqual:
-    opStr = " = ";
-    break;
-  case OpMultEq:
-    opStr = " *= ";
-    break;
-  case OpDivEq:
-    opStr = " /= ";
-    break;
-  case OpPlusEq:
-    opStr = " += ";
-    break;
-  case OpMinusEq:
-    opStr = " -= ";
-    break;
-  case OpLShift:
-    opStr = " <<= ";
-    break;
-  case OpRShift:
-    opStr = " >>= ";
-    break;
-  case OpURShift:
-    opStr = " >>>= ";
-    break;
-  case OpAndEq:
-    opStr = " &= ";
-    break;
-  case OpXOrEq:
-    opStr = " ^= ";
-    break;
-  case OpOrEq:
-    opStr = " |= ";
-    break;
-  case OpModEq:
-    opStr = " %= ";
-    break;
-  default:
-    opStr = " ?= ";
-  }
-  s << opStr;
+    s << PrecLogicalOr << logical
+        << " ? " << PrecAssignment << expr1
+        << " : " << PrecAssignment << expr2;
 }
 
-void AssignResolveNode::streamTo(SourceStream &s) const
+void AssignResolveNode::streamTo(SourceStreams) const
 {
-  s << m_ident;
-  streamAssignmentOperatorTo(s, m_oper);
-  s << m_right;
+    s << m_ident << ' ' << operatorString(m_oper) << ' ' << PrecAssignment << m_right;
 }
 
-void AssignBracketNode::streamTo(SourceStream &s) const
+void AssignBracketNode::streamTo(SourceStreams) const
 {
-  s << m_base << "[" << m_subscript << "]";
-  streamAssignmentOperatorTo(s, m_oper);
-  s << m_right;
+    s << PrecCall << m_base << '[' << m_subscript << "] "
+        << operatorString(m_oper) << ' ' << PrecAssignment << m_right;
 }
 
-void AssignDotNode::streamTo(SourceStream &s) const
+void AssignDotNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::DotExpr << m_base << "." << m_ident;
-  streamAssignmentOperatorTo(s, m_oper);
-  s << m_right;
+    s << DotExpr << PrecCall << m_base << "." << m_ident << ' '
+        << operatorString(m_oper) << ' ' << PrecAssignment << m_right;
 }
 
 void AssignErrorNode::streamTo(SourceStream& s) const
 {
-  s << m_left;
-  streamAssignmentOperatorTo(s, m_oper);
-  s << m_right;
+    s << PrecLeftHandSide << m_left << ' '
+        << operatorString(m_oper) << ' ' << PrecAssignment << m_right;
 }
 
-void CommaNode::streamTo(SourceStream &s) const
+void CommaNode::streamTo(SourceStreams) const
 {
-  s << expr1 << ", " << expr2;
+    s << PrecAssignment << expr1 << ", " << PrecAssignment << expr2;
 }
 
-void AssignExprNode::streamTo(SourceStream &s) const
+void AssignExprNode::streamTo(SourceStreams) const
 {
-  s << " = " << expr;
+    s << " = " << PrecAssignment << expr;
 }
 
-void VarDeclNode::streamTo(SourceStream &s) const
+void VarDeclNode::streamTo(SourceStreams) const
 {
-  s << ident << init;
+    s << ident << init;
 }
 
-void VarDeclListNode::streamTo(SourceStream &s) const
+void VarDeclListNode::streamTo(SourceStreams) const
 {
-  s << "var " << var;
-  for (VarDeclListNode *n = next.get(); n; n = n->next.get())
-    s << ", " << n->var;
+    s << "var " << var;
+    for (VarDeclListNode* n = next.get(); n; n = n->next.get())
+        s << ", " << n->var;
 }
 
-void VarStatementNode::streamTo(SourceStream &s) const
+void VarStatementNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << next << ";";
+    s << Endl << next << ';';
 }
 
-void BlockNode::streamTo(SourceStream &s) const
+void BlockNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "{" << SourceStream::Indent
-    << source << SourceStream::Unindent << SourceStream::Endl << "}";
+    s << Endl << "{" << Indent << source << Unindent << Endl << "}";
 }
 
-void EmptyStatementNode::streamTo(SourceStream &s) const
+void EmptyStatementNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << ";";
+    s << Endl << ';';
 }
 
-void ExprStatementNode::streamTo(SourceStream &s) const
+void ExprStatementNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << expr << ";";
+    s << Endl << expr << ';';
 }
 
-void IfNode::streamTo(SourceStream &s) const
+void IfNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "if (" << expr << ")" << SourceStream::Indent
-    << statement1 << SourceStream::Unindent;
-  if (statement2)
-    s << SourceStream::Endl << "else" << SourceStream::Indent
-      << statement2 << SourceStream::Unindent;
+    s << Endl << "if (" << expr << ')' << Indent << statement1 << Unindent;
+    if (statement2)
+        s << Endl << "else" << Indent << statement2 << Unindent;
 }
 
-void DoWhileNode::streamTo(SourceStream &s) const
+void DoWhileNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "do " << SourceStream::Indent
-    << statement << SourceStream::Unindent << SourceStream::Endl
-    << "while (" << expr << ");";
+    s << Endl << "do " << Indent << statement << Unindent << Endl
+        << "while (" << expr << ");";
 }
 
-void WhileNode::streamTo(SourceStream &s) const
+void WhileNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "while (" << expr << ")" << SourceStream::Indent
-    << statement << SourceStream::Unindent;
+  s << Endl << "while (" << expr << ')' << Indent << statement << Unindent;
 }
 
-void ForNode::streamTo(SourceStream &s) const
+void ForNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "for ("
-    << expr1
-    << "; " << expr2
-    << "; " << expr3
-    << ")" << SourceStream::Indent << statement << SourceStream::Unindent;
+    s << Endl << "for ("
+        << expr1
+        << "; " << expr2
+        << "; " << expr3
+        << ')' << Indent << statement << Unindent;
 }
 
-void ForInNode::streamTo(SourceStream &s) const
+void ForInNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "for (";
-  if (varDecl)
-    s << "var " << varDecl;
-  else
-    s << lexpr;
+    s << Endl << "for (";
+    if (varDecl)
+        s << "var " << varDecl;
+    else
+        s << PrecLeftHandSide << lexpr;
 
-  s << " in " << expr << ")" << SourceStream::Indent
-    << statement << SourceStream::Unindent;
+    s << " in " << expr << ')' << Indent << statement << Unindent;
 }
 
-void ContinueNode::streamTo(SourceStream &s) const
+void ContinueNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "continue";
-  if (!ident.isNull())
-    s << " " << ident;
-  s << ";";
+    s << Endl << "continue";
+    if (!ident.isNull())
+        s << ' ' << ident;
+    s << ';';
 }
 
-void BreakNode::streamTo(SourceStream &s) const
+void BreakNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "break";
-  if (!ident.isNull())
-    s << " " << ident;
-  s << ";";
+    s << Endl << "break";
+    if (!ident.isNull())
+        s << ' ' << ident;
+    s << ';';
 }
 
-void ReturnNode::streamTo(SourceStream &s) const
+void ReturnNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "return";
-  if (value)
-    s << " " << value;
-  s << ";";
+    s << Endl << "return";
+    if (value)
+        s << ' ' << value;
+    s << ';';
 }
 
-void WithNode::streamTo(SourceStream &s) const
+void WithNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "with (" << expr << ") "
-    << statement;
+    s << Endl << "with (" << expr << ") " << statement;
 }
 
-void CaseClauseNode::streamTo(SourceStream &s) const
+void CaseClauseNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl;
-  if (expr)
-    s << "case " << expr;
-  else
-    s << "default";
-  s << ":" << SourceStream::Indent;
-  if (source)
-    s << source;
-  s << SourceStream::Unindent;
+    s << Endl;
+    if (expr)
+        s << "case " << expr;
+    else
+        s << "default";
+    s << ":" << Indent << source << Unindent;
 }
 
-void ClauseListNode::streamTo(SourceStream &s) const
+void ClauseListNode::streamTo(SourceStreams) const
 {
-  for (const ClauseListNode *n = this; n; n = n->getNext())
-    s << n->getClause();
+    for (const ClauseListNode* n = this; n; n = n->getNext())
+        s << n->getClause();
 }
 
-void CaseBlockNode::streamTo(SourceStream &s) const
+void CaseBlockNode::streamTo(SourceStreams) const
 {
-  for (const ClauseListNode *n = list1.get(); n; n = n->getNext())
-    s << n->getClause();
-  if (def)
+    for (const ClauseListNode* n = list1.get(); n; n = n->getNext())
+        s << n->getClause();
     s << def;
-  for (const ClauseListNode *n = list2.get(); n; n = n->getNext())
-    s << n->getClause();
+    for (const ClauseListNode* n = list2.get(); n; n = n->getNext())
+        s << n->getClause();
 }
 
-void SwitchNode::streamTo(SourceStream &s) const
+void SwitchNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "switch (" << expr << ") {"
-    << SourceStream::Indent << block << SourceStream::Unindent
-    << SourceStream::Endl << "}";
+    s << Endl << "switch (" << expr << ") {"
+        << Indent << block << Unindent
+        << Endl << "}";
 }
 
-void LabelNode::streamTo(SourceStream &s) const
+void LabelNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << label << ":" << SourceStream::Indent
-    << statement << SourceStream::Unindent;
+    s << Endl << label << ":" << Indent << statement << Unindent;
 }
 
-void ThrowNode::streamTo(SourceStream &s) const
+void ThrowNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "throw " << expr << ";";
+    s << Endl << "throw " << expr << ';';
 }
 
-void TryNode::streamTo(SourceStream &s) const
+void TryNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "try " << tryBlock;
-  if (catchBlock)
-    s << SourceStream::Endl << "catch (" << exceptionIdent << ")" << catchBlock;
-  if (finallyBlock)
-    s << SourceStream::Endl << "finally " << finallyBlock;
+    s << Endl << "try " << tryBlock;
+    if (catchBlock)
+        s << Endl << "catch (" << exceptionIdent << ')' << catchBlock;
+    if (finallyBlock)
+        s << Endl << "finally " << finallyBlock;
 }
 
-void ParameterNode::streamTo(SourceStream &s) const
+void ParameterNode::streamTo(SourceStreams) const
 {
-  s << id;
-  for (ParameterNode *n = next.get(); n; n = n->next.get())
-    s << ", " << n->id;
+    s << id;
+    for (ParameterNode* n = next.get(); n; n = n->next.get())
+        s << ", " << n->id;
 }
 
-void FuncDeclNode::streamTo(SourceStream &s) const
+void FuncDeclNode::streamTo(SourceStreams) const
 {
-  s << SourceStream::Endl << "function " << ident << "(" << param << ")" << body;
+    s << Endl << "function " << ident << '(' << param << ')' << body;
 }
 
-void FuncExprNode::streamTo(SourceStream &s) const
+void FuncExprNode::streamTo(SourceStreams) const
 {
-  s << "function " << ident << "(" << param << ")" << body;
+    s << "function " << ident << '(' << param << ')' << body;
 }
 
-void SourceElementsNode::streamTo(SourceStream &s) const
+void SourceElementsNode::streamTo(SourceStreams) const
 {
-  for (const SourceElementsNode *n = this; n; n = n->next.get())
-    s << n->node;
+    for (const SourceElementsNode* n = this; n; n = n->next.get())
+        s << n->node;
+}
+
 }
index 5f8e312a7828875c587a0abf2c94b6f571b44712..94cd446482792013e6ff1d346133c0a2400da64d 100644 (file)
@@ -1,17 +1,31 @@
+2007-10-28  Darin Adler  <darin@apple.com>
+
+        Reviewed by Maciej.
+
+        - test for http://bugs.webkit.org/show_bug.cgi?id=15735
+          remove GroupNode to simplify AST and possibly get a modest speedup
+
+        One test is a start at testing that parentheses are added when needed.
+        The other test checks some aspects of object literals, since I changed
+        the way the property names is handled in those. More tests are needed.
+
+        * fast/js/function-toString-object-literals-expected.txt: Added.
+        * fast/js/function-toString-object-literals.html: Added.
+        * fast/js/function-toString-parentheses-expected.txt: Added.
+        * fast/js/function-toString-parentheses.html: Added.
+        * fast/js/resources/function-toString-object-literals.js: Added.
+        * fast/js/resources/function-toString-parentheses.js: Added.
+
 2007-10-28  Adam Roben  <aroben@apple.com>
 
         Re-enable a bunch of passing tests on Windows
 
-        Reviewed by NOBODY.
-
         * platform/win/Skipped:
 
 2007-10-27  Adam Roben  <aroben@apple.com>
 
         Updated Windows Skipped file for currently failing tests
 
-        Reviewed by NOBODY.
-
         * platform/win/Skipped:
 
 2007-10-27  Adam Roben  <aroben@apple.com>
@@ -80,8 +94,6 @@
 
         Add some more failing tests to the Windows Skipped file
 
-        Reviewed by NOBODY.
-
         * platform/win/Skipped:
 
 2007-10-27  Dan Bernstein  <mitz@apple.com>
diff --git a/LayoutTests/fast/js/function-toString-object-literals-expected.txt b/LayoutTests/fast/js/function-toString-object-literals-expected.txt
new file mode 100644 (file)
index 0000000..f0fd145
--- /dev/null
@@ -0,0 +1,28 @@
+This test checks that object literals are serialized properly. It's needed in part because JavaScriptCore converts numeric property names to string and back.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS compileAndSerialize('a = { 1: null }') is 'a = { 1: null }'
+PASS compileAndSerialize('a = { 0: null }') is 'a = { 0: null }'
+PASS compileAndSerialize('a = { 1.0: null }') is 'a = { 1: null }'
+PASS compileAndSerialize('a = { "1.0": null }') is 'a = { "1.0": null }'
+PASS compileAndSerialize('a = { 1e-500: null }') is 'a = { 0: null }'
+PASS compileAndSerialize('a = { 1e-300: null }') is 'a = { 1e-300: null }'
+PASS compileAndSerialize('a = { 1e300: null }') is 'a = { 1e+300: null }'
+PASS compileAndSerialize('a = { 1e500: null }') is 'a = { "Infinity": null }'
+PASS compileAndSerialize('a = { NaN: null }') is 'a = { "NaN": null }'
+PASS compileAndSerialize('a = { Infinity: null }') is 'a = { "Infinity": null }'
+PASS compileAndSerialize('a = { "1": null }') is 'a = { 1: null }'
+PASS compileAndSerialize('a = { "1hi": null }') is 'a = { "1hi": null }'
+PASS compileAndSerialize('a = { "\'": null }') is 'a = { "\'": null }'
+PASS compileAndSerialize('a = { "\\"": null }') is 'a = { "\\"": null }'
+PASS compileAndSerialize('a = { --1: null }') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('a = { -NaN: null }') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('a = { -0: null }') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('a = { -0.0: null }') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('a = { -Infinity: null }') threw exception SyntaxError: Parse error.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/function-toString-object-literals.html b/LayoutTests/fast/js/function-toString-object-literals.html
new file mode 100644 (file)
index 0000000..4be6a90
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="resources/js-test-style.css">
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/function-toString-object-literals.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/function-toString-parentheses-expected.txt b/LayoutTests/fast/js/function-toString-parentheses-expected.txt
new file mode 100644 (file)
index 0000000..d697f53
--- /dev/null
@@ -0,0 +1,408 @@
+This test checks that parentheses are preserved when significant, and not added where inappropriate. We need this test because the JavaScriptCore parser removes all parentheses and the serializer then adds them back.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS compileAndSerialize('a * b * c') is 'a * b * c'
+PASS compileAndSerialize('(a * b) * c') is 'a * b * c'
+PASS compileAndSerialize('a * (b * c)') is 'a * (b * c)'
+PASS compileAndSerialize('a * b + c') is 'a * b + c'
+PASS compileAndSerialize('(a * b) + c') is 'a * b + c'
+PASS compileAndSerialize('a * (b + c)') is 'a * (b + c)'
+PASS compileAndSerialize('a * b - c') is 'a * b - c'
+PASS compileAndSerialize('(a * b) - c') is 'a * b - c'
+PASS compileAndSerialize('a * (b - c)') is 'a * (b - c)'
+PASS compileAndSerialize('a / b / c') is 'a / b / c'
+PASS compileAndSerialize('(a / b) / c') is 'a / b / c'
+PASS compileAndSerialize('a / (b / c)') is 'a / (b / c)'
+PASS compileAndSerialize('a * b / c') is 'a * b / c'
+PASS compileAndSerialize('(a * b) / c') is 'a * b / c'
+PASS compileAndSerialize('a * (b / c)') is 'a * (b / c)'
+PASS compileAndSerialize('a / b + c') is 'a / b + c'
+PASS compileAndSerialize('(a / b) + c') is 'a / b + c'
+PASS compileAndSerialize('a / (b + c)') is 'a / (b + c)'
+PASS compileAndSerialize('a % b % c') is 'a % b % c'
+PASS compileAndSerialize('(a % b) % c') is 'a % b % c'
+PASS compileAndSerialize('a % (b % c)') is 'a % (b % c)'
+PASS compileAndSerialize('a * b % c') is 'a * b % c'
+PASS compileAndSerialize('(a * b) % c') is 'a * b % c'
+PASS compileAndSerialize('a * (b % c)') is 'a * (b % c)'
+PASS compileAndSerialize('a % b + c') is 'a % b + c'
+PASS compileAndSerialize('(a % b) + c') is 'a % b + c'
+PASS compileAndSerialize('a % (b + c)') is 'a % (b + c)'
+PASS compileAndSerialize('a + b + c') is 'a + b + c'
+PASS compileAndSerialize('(a + b) + c') is 'a + b + c'
+PASS compileAndSerialize('a + (b + c)') is 'a + (b + c)'
+PASS compileAndSerialize('a + b << c') is 'a + b << c'
+PASS compileAndSerialize('(a + b) << c') is 'a + b << c'
+PASS compileAndSerialize('a + (b << c)') is 'a + (b << c)'
+PASS compileAndSerialize('a + b >> c') is 'a + b >> c'
+PASS compileAndSerialize('(a + b) >> c') is 'a + b >> c'
+PASS compileAndSerialize('a + (b >> c)') is 'a + (b >> c)'
+PASS compileAndSerialize('a + b >>> c') is 'a + b >>> c'
+PASS compileAndSerialize('(a + b) >>> c') is 'a + b >>> c'
+PASS compileAndSerialize('a + (b >>> c)') is 'a + (b >>> c)'
+PASS compileAndSerialize('a - b - c') is 'a - b - c'
+PASS compileAndSerialize('(a - b) - c') is 'a - b - c'
+PASS compileAndSerialize('a - (b - c)') is 'a - (b - c)'
+PASS compileAndSerialize('a + b - c') is 'a + b - c'
+PASS compileAndSerialize('(a + b) - c') is 'a + b - c'
+PASS compileAndSerialize('a + (b - c)') is 'a + (b - c)'
+PASS compileAndSerialize('a - b << c') is 'a - b << c'
+PASS compileAndSerialize('(a - b) << c') is 'a - b << c'
+PASS compileAndSerialize('a - (b << c)') is 'a - (b << c)'
+PASS compileAndSerialize('a << b << c') is 'a << b << c'
+PASS compileAndSerialize('(a << b) << c') is 'a << b << c'
+PASS compileAndSerialize('a << (b << c)') is 'a << (b << c)'
+PASS compileAndSerialize('a << b < c') is 'a << b < c'
+PASS compileAndSerialize('(a << b) < c') is 'a << b < c'
+PASS compileAndSerialize('a << (b < c)') is 'a << (b < c)'
+PASS compileAndSerialize('a << b > c') is 'a << b > c'
+PASS compileAndSerialize('(a << b) > c') is 'a << b > c'
+PASS compileAndSerialize('a << (b > c)') is 'a << (b > c)'
+PASS compileAndSerialize('a << b <= c') is 'a << b <= c'
+PASS compileAndSerialize('(a << b) <= c') is 'a << b <= c'
+PASS compileAndSerialize('a << (b <= c)') is 'a << (b <= c)'
+PASS compileAndSerialize('a << b >= c') is 'a << b >= c'
+PASS compileAndSerialize('(a << b) >= c') is 'a << b >= c'
+PASS compileAndSerialize('a << (b >= c)') is 'a << (b >= c)'
+PASS compileAndSerialize('a << b instanceof c') is 'a << b instanceof c'
+PASS compileAndSerialize('(a << b) instanceof c') is 'a << b instanceof c'
+PASS compileAndSerialize('a << (b instanceof c)') is 'a << (b instanceof c)'
+PASS compileAndSerialize('a << b in c') is 'a << b in c'
+PASS compileAndSerialize('(a << b) in c') is 'a << b in c'
+PASS compileAndSerialize('a << (b in c)') is 'a << (b in c)'
+PASS compileAndSerialize('a >> b >> c') is 'a >> b >> c'
+PASS compileAndSerialize('(a >> b) >> c') is 'a >> b >> c'
+PASS compileAndSerialize('a >> (b >> c)') is 'a >> (b >> c)'
+PASS compileAndSerialize('a << b >> c') is 'a << b >> c'
+PASS compileAndSerialize('(a << b) >> c') is 'a << b >> c'
+PASS compileAndSerialize('a << (b >> c)') is 'a << (b >> c)'
+PASS compileAndSerialize('a >> b < c') is 'a >> b < c'
+PASS compileAndSerialize('(a >> b) < c') is 'a >> b < c'
+PASS compileAndSerialize('a >> (b < c)') is 'a >> (b < c)'
+PASS compileAndSerialize('a >>> b >>> c') is 'a >>> b >>> c'
+PASS compileAndSerialize('(a >>> b) >>> c') is 'a >>> b >>> c'
+PASS compileAndSerialize('a >>> (b >>> c)') is 'a >>> (b >>> c)'
+PASS compileAndSerialize('a << b >>> c') is 'a << b >>> c'
+PASS compileAndSerialize('(a << b) >>> c') is 'a << b >>> c'
+PASS compileAndSerialize('a << (b >>> c)') is 'a << (b >>> c)'
+PASS compileAndSerialize('a >>> b < c') is 'a >>> b < c'
+PASS compileAndSerialize('(a >>> b) < c') is 'a >>> b < c'
+PASS compileAndSerialize('a >>> (b < c)') is 'a >>> (b < c)'
+PASS compileAndSerialize('a < b < c') is 'a < b < c'
+PASS compileAndSerialize('(a < b) < c') is 'a < b < c'
+PASS compileAndSerialize('a < (b < c)') is 'a < (b < c)'
+PASS compileAndSerialize('a < b == c') is 'a < b == c'
+PASS compileAndSerialize('(a < b) == c') is 'a < b == c'
+PASS compileAndSerialize('a < (b == c)') is 'a < (b == c)'
+PASS compileAndSerialize('a < b != c') is 'a < b != c'
+PASS compileAndSerialize('(a < b) != c') is 'a < b != c'
+PASS compileAndSerialize('a < (b != c)') is 'a < (b != c)'
+PASS compileAndSerialize('a < b === c') is 'a < b === c'
+PASS compileAndSerialize('(a < b) === c') is 'a < b === c'
+PASS compileAndSerialize('a < (b === c)') is 'a < (b === c)'
+PASS compileAndSerialize('a < b !== c') is 'a < b !== c'
+PASS compileAndSerialize('(a < b) !== c') is 'a < b !== c'
+PASS compileAndSerialize('a < (b !== c)') is 'a < (b !== c)'
+PASS compileAndSerialize('a > b > c') is 'a > b > c'
+PASS compileAndSerialize('(a > b) > c') is 'a > b > c'
+PASS compileAndSerialize('a > (b > c)') is 'a > (b > c)'
+PASS compileAndSerialize('a < b > c') is 'a < b > c'
+PASS compileAndSerialize('(a < b) > c') is 'a < b > c'
+PASS compileAndSerialize('a < (b > c)') is 'a < (b > c)'
+PASS compileAndSerialize('a > b == c') is 'a > b == c'
+PASS compileAndSerialize('(a > b) == c') is 'a > b == c'
+PASS compileAndSerialize('a > (b == c)') is 'a > (b == c)'
+PASS compileAndSerialize('a <= b <= c') is 'a <= b <= c'
+PASS compileAndSerialize('(a <= b) <= c') is 'a <= b <= c'
+PASS compileAndSerialize('a <= (b <= c)') is 'a <= (b <= c)'
+PASS compileAndSerialize('a < b <= c') is 'a < b <= c'
+PASS compileAndSerialize('(a < b) <= c') is 'a < b <= c'
+PASS compileAndSerialize('a < (b <= c)') is 'a < (b <= c)'
+PASS compileAndSerialize('a <= b == c') is 'a <= b == c'
+PASS compileAndSerialize('(a <= b) == c') is 'a <= b == c'
+PASS compileAndSerialize('a <= (b == c)') is 'a <= (b == c)'
+PASS compileAndSerialize('a >= b >= c') is 'a >= b >= c'
+PASS compileAndSerialize('(a >= b) >= c') is 'a >= b >= c'
+PASS compileAndSerialize('a >= (b >= c)') is 'a >= (b >= c)'
+PASS compileAndSerialize('a < b >= c') is 'a < b >= c'
+PASS compileAndSerialize('(a < b) >= c') is 'a < b >= c'
+PASS compileAndSerialize('a < (b >= c)') is 'a < (b >= c)'
+PASS compileAndSerialize('a >= b == c') is 'a >= b == c'
+PASS compileAndSerialize('(a >= b) == c') is 'a >= b == c'
+PASS compileAndSerialize('a >= (b == c)') is 'a >= (b == c)'
+PASS compileAndSerialize('a instanceof b instanceof c') is 'a instanceof b instanceof c'
+PASS compileAndSerialize('(a instanceof b) instanceof c') is 'a instanceof b instanceof c'
+PASS compileAndSerialize('a instanceof (b instanceof c)') is 'a instanceof (b instanceof c)'
+PASS compileAndSerialize('a < b instanceof c') is 'a < b instanceof c'
+PASS compileAndSerialize('(a < b) instanceof c') is 'a < b instanceof c'
+PASS compileAndSerialize('a < (b instanceof c)') is 'a < (b instanceof c)'
+PASS compileAndSerialize('a instanceof b == c') is 'a instanceof b == c'
+PASS compileAndSerialize('(a instanceof b) == c') is 'a instanceof b == c'
+PASS compileAndSerialize('a instanceof (b == c)') is 'a instanceof (b == c)'
+PASS compileAndSerialize('a in b in c') is 'a in b in c'
+PASS compileAndSerialize('(a in b) in c') is 'a in b in c'
+PASS compileAndSerialize('a in (b in c)') is 'a in (b in c)'
+PASS compileAndSerialize('a < b in c') is 'a < b in c'
+PASS compileAndSerialize('(a < b) in c') is 'a < b in c'
+PASS compileAndSerialize('a < (b in c)') is 'a < (b in c)'
+PASS compileAndSerialize('a in b == c') is 'a in b == c'
+PASS compileAndSerialize('(a in b) == c') is 'a in b == c'
+PASS compileAndSerialize('a in (b == c)') is 'a in (b == c)'
+PASS compileAndSerialize('a == b == c') is 'a == b == c'
+PASS compileAndSerialize('(a == b) == c') is 'a == b == c'
+PASS compileAndSerialize('a == (b == c)') is 'a == (b == c)'
+PASS compileAndSerialize('a == b & c') is 'a == b & c'
+PASS compileAndSerialize('(a == b) & c') is 'a == b & c'
+PASS compileAndSerialize('a == (b & c)') is 'a == (b & c)'
+PASS compileAndSerialize('a != b != c') is 'a != b != c'
+PASS compileAndSerialize('(a != b) != c') is 'a != b != c'
+PASS compileAndSerialize('a != (b != c)') is 'a != (b != c)'
+PASS compileAndSerialize('a == b != c') is 'a == b != c'
+PASS compileAndSerialize('(a == b) != c') is 'a == b != c'
+PASS compileAndSerialize('a == (b != c)') is 'a == (b != c)'
+PASS compileAndSerialize('a != b & c') is 'a != b & c'
+PASS compileAndSerialize('(a != b) & c') is 'a != b & c'
+PASS compileAndSerialize('a != (b & c)') is 'a != (b & c)'
+PASS compileAndSerialize('a === b === c') is 'a === b === c'
+PASS compileAndSerialize('(a === b) === c') is 'a === b === c'
+PASS compileAndSerialize('a === (b === c)') is 'a === (b === c)'
+PASS compileAndSerialize('a == b === c') is 'a == b === c'
+PASS compileAndSerialize('(a == b) === c') is 'a == b === c'
+PASS compileAndSerialize('a == (b === c)') is 'a == (b === c)'
+PASS compileAndSerialize('a === b & c') is 'a === b & c'
+PASS compileAndSerialize('(a === b) & c') is 'a === b & c'
+PASS compileAndSerialize('a === (b & c)') is 'a === (b & c)'
+PASS compileAndSerialize('a !== b !== c') is 'a !== b !== c'
+PASS compileAndSerialize('(a !== b) !== c') is 'a !== b !== c'
+PASS compileAndSerialize('a !== (b !== c)') is 'a !== (b !== c)'
+PASS compileAndSerialize('a == b !== c') is 'a == b !== c'
+PASS compileAndSerialize('(a == b) !== c') is 'a == b !== c'
+PASS compileAndSerialize('a == (b !== c)') is 'a == (b !== c)'
+PASS compileAndSerialize('a !== b & c') is 'a !== b & c'
+PASS compileAndSerialize('(a !== b) & c') is 'a !== b & c'
+PASS compileAndSerialize('a !== (b & c)') is 'a !== (b & c)'
+PASS compileAndSerialize('a & b & c') is 'a & b & c'
+PASS compileAndSerialize('(a & b) & c') is 'a & b & c'
+PASS compileAndSerialize('a & (b & c)') is 'a & (b & c)'
+PASS compileAndSerialize('a & b ^ c') is 'a & b ^ c'
+PASS compileAndSerialize('(a & b) ^ c') is 'a & b ^ c'
+PASS compileAndSerialize('a & (b ^ c)') is 'a & (b ^ c)'
+PASS compileAndSerialize('a ^ b ^ c') is 'a ^ b ^ c'
+PASS compileAndSerialize('(a ^ b) ^ c') is 'a ^ b ^ c'
+PASS compileAndSerialize('a ^ (b ^ c)') is 'a ^ (b ^ c)'
+PASS compileAndSerialize('a ^ b | c') is 'a ^ b | c'
+PASS compileAndSerialize('(a ^ b) | c') is 'a ^ b | c'
+PASS compileAndSerialize('a ^ (b | c)') is 'a ^ (b | c)'
+PASS compileAndSerialize('a | b | c') is 'a | b | c'
+PASS compileAndSerialize('(a | b) | c') is 'a | b | c'
+PASS compileAndSerialize('a | (b | c)') is 'a | (b | c)'
+PASS compileAndSerialize('a | b && c') is 'a | b && c'
+PASS compileAndSerialize('(a | b) && c') is 'a | b && c'
+PASS compileAndSerialize('a | (b && c)') is 'a | (b && c)'
+PASS compileAndSerialize('a && b && c') is 'a && b && c'
+PASS compileAndSerialize('(a && b) && c') is 'a && b && c'
+PASS compileAndSerialize('a && (b && c)') is 'a && (b && c)'
+PASS compileAndSerialize('a && b || c') is 'a && b || c'
+PASS compileAndSerialize('(a && b) || c') is 'a && b || c'
+PASS compileAndSerialize('a && (b || c)') is 'a && (b || c)'
+PASS compileAndSerialize('a || b || c') is 'a || b || c'
+PASS compileAndSerialize('(a || b) || c') is 'a || b || c'
+PASS compileAndSerialize('a || (b || c)') is 'a || (b || c)'
+PASS compileAndSerialize('a = b = c') is 'a = b = c'
+PASS compileAndSerialize('(a = b) = c') is '(a = b) = c'
+PASS compileAndSerialize('a = (b = c)') is 'a = b = c'
+PASS compileAndSerialize('a = b + c') is 'a = b + c'
+PASS compileAndSerialize('(a = b) + c') is '(a = b) + c'
+PASS compileAndSerialize('a = (b + c)') is 'a = b + c'
+PASS compileAndSerialize('a + b = c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) = c') is '(a + b) = c'
+PASS compileAndSerialize('a + (b = c)') is 'a + (b = c)'
+PASS compileAndSerialize('a *= b *= c') is 'a *= b *= c'
+PASS compileAndSerialize('(a *= b) *= c') is '(a *= b) *= c'
+PASS compileAndSerialize('a *= (b *= c)') is 'a *= b *= c'
+PASS compileAndSerialize('a = b *= c') is 'a = b *= c'
+PASS compileAndSerialize('(a = b) *= c') is '(a = b) *= c'
+PASS compileAndSerialize('a = (b *= c)') is 'a = b *= c'
+PASS compileAndSerialize('a *= b + c') is 'a *= b + c'
+PASS compileAndSerialize('(a *= b) + c') is '(a *= b) + c'
+PASS compileAndSerialize('a *= (b + c)') is 'a *= b + c'
+PASS compileAndSerialize('a + b *= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) *= c') is '(a + b) *= c'
+PASS compileAndSerialize('a + (b *= c)') is 'a + (b *= c)'
+PASS compileAndSerialize('a /= b /= c') is 'a /= b /= c'
+PASS compileAndSerialize('(a /= b) /= c') is '(a /= b) /= c'
+PASS compileAndSerialize('a /= (b /= c)') is 'a /= b /= c'
+PASS compileAndSerialize('a = b /= c') is 'a = b /= c'
+PASS compileAndSerialize('(a = b) /= c') is '(a = b) /= c'
+PASS compileAndSerialize('a = (b /= c)') is 'a = b /= c'
+PASS compileAndSerialize('a /= b + c') is 'a /= b + c'
+PASS compileAndSerialize('(a /= b) + c') is '(a /= b) + c'
+PASS compileAndSerialize('a /= (b + c)') is 'a /= b + c'
+PASS compileAndSerialize('a + b /= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) /= c') is '(a + b) /= c'
+PASS compileAndSerialize('a + (b /= c)') is 'a + (b /= c)'
+PASS compileAndSerialize('a %= b %= c') is 'a %= b %= c'
+PASS compileAndSerialize('(a %= b) %= c') is '(a %= b) %= c'
+PASS compileAndSerialize('a %= (b %= c)') is 'a %= b %= c'
+PASS compileAndSerialize('a = b %= c') is 'a = b %= c'
+PASS compileAndSerialize('(a = b) %= c') is '(a = b) %= c'
+PASS compileAndSerialize('a = (b %= c)') is 'a = b %= c'
+PASS compileAndSerialize('a %= b + c') is 'a %= b + c'
+PASS compileAndSerialize('(a %= b) + c') is '(a %= b) + c'
+PASS compileAndSerialize('a %= (b + c)') is 'a %= b + c'
+PASS compileAndSerialize('a + b %= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) %= c') is '(a + b) %= c'
+PASS compileAndSerialize('a + (b %= c)') is 'a + (b %= c)'
+PASS compileAndSerialize('a += b += c') is 'a += b += c'
+PASS compileAndSerialize('(a += b) += c') is '(a += b) += c'
+PASS compileAndSerialize('a += (b += c)') is 'a += b += c'
+PASS compileAndSerialize('a = b += c') is 'a = b += c'
+PASS compileAndSerialize('(a = b) += c') is '(a = b) += c'
+PASS compileAndSerialize('a = (b += c)') is 'a = b += c'
+PASS compileAndSerialize('a += b + c') is 'a += b + c'
+PASS compileAndSerialize('(a += b) + c') is '(a += b) + c'
+PASS compileAndSerialize('a += (b + c)') is 'a += b + c'
+PASS compileAndSerialize('a + b += c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) += c') is '(a + b) += c'
+PASS compileAndSerialize('a + (b += c)') is 'a + (b += c)'
+PASS compileAndSerialize('a -= b -= c') is 'a -= b -= c'
+PASS compileAndSerialize('(a -= b) -= c') is '(a -= b) -= c'
+PASS compileAndSerialize('a -= (b -= c)') is 'a -= b -= c'
+PASS compileAndSerialize('a = b -= c') is 'a = b -= c'
+PASS compileAndSerialize('(a = b) -= c') is '(a = b) -= c'
+PASS compileAndSerialize('a = (b -= c)') is 'a = b -= c'
+PASS compileAndSerialize('a -= b + c') is 'a -= b + c'
+PASS compileAndSerialize('(a -= b) + c') is '(a -= b) + c'
+PASS compileAndSerialize('a -= (b + c)') is 'a -= b + c'
+PASS compileAndSerialize('a + b -= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) -= c') is '(a + b) -= c'
+PASS compileAndSerialize('a + (b -= c)') is 'a + (b -= c)'
+PASS compileAndSerialize('a <<= b <<= c') is 'a <<= b <<= c'
+PASS compileAndSerialize('(a <<= b) <<= c') is '(a <<= b) <<= c'
+PASS compileAndSerialize('a <<= (b <<= c)') is 'a <<= b <<= c'
+PASS compileAndSerialize('a = b <<= c') is 'a = b <<= c'
+PASS compileAndSerialize('(a = b) <<= c') is '(a = b) <<= c'
+PASS compileAndSerialize('a = (b <<= c)') is 'a = b <<= c'
+PASS compileAndSerialize('a <<= b + c') is 'a <<= b + c'
+PASS compileAndSerialize('(a <<= b) + c') is '(a <<= b) + c'
+PASS compileAndSerialize('a <<= (b + c)') is 'a <<= b + c'
+PASS compileAndSerialize('a + b <<= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) <<= c') is '(a + b) <<= c'
+PASS compileAndSerialize('a + (b <<= c)') is 'a + (b <<= c)'
+PASS compileAndSerialize('a >>= b >>= c') is 'a >>= b >>= c'
+PASS compileAndSerialize('(a >>= b) >>= c') is '(a >>= b) >>= c'
+PASS compileAndSerialize('a >>= (b >>= c)') is 'a >>= b >>= c'
+PASS compileAndSerialize('a = b >>= c') is 'a = b >>= c'
+PASS compileAndSerialize('(a = b) >>= c') is '(a = b) >>= c'
+PASS compileAndSerialize('a = (b >>= c)') is 'a = b >>= c'
+PASS compileAndSerialize('a >>= b + c') is 'a >>= b + c'
+PASS compileAndSerialize('(a >>= b) + c') is '(a >>= b) + c'
+PASS compileAndSerialize('a >>= (b + c)') is 'a >>= b + c'
+PASS compileAndSerialize('a + b >>= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) >>= c') is '(a + b) >>= c'
+PASS compileAndSerialize('a + (b >>= c)') is 'a + (b >>= c)'
+PASS compileAndSerialize('a >>>= b >>>= c') is 'a >>>= b >>>= c'
+PASS compileAndSerialize('(a >>>= b) >>>= c') is '(a >>>= b) >>>= c'
+PASS compileAndSerialize('a >>>= (b >>>= c)') is 'a >>>= b >>>= c'
+PASS compileAndSerialize('a = b >>>= c') is 'a = b >>>= c'
+PASS compileAndSerialize('(a = b) >>>= c') is '(a = b) >>>= c'
+PASS compileAndSerialize('a = (b >>>= c)') is 'a = b >>>= c'
+PASS compileAndSerialize('a >>>= b + c') is 'a >>>= b + c'
+PASS compileAndSerialize('(a >>>= b) + c') is '(a >>>= b) + c'
+PASS compileAndSerialize('a >>>= (b + c)') is 'a >>>= b + c'
+PASS compileAndSerialize('a + b >>>= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) >>>= c') is '(a + b) >>>= c'
+PASS compileAndSerialize('a + (b >>>= c)') is 'a + (b >>>= c)'
+PASS compileAndSerialize('a &= b &= c') is 'a &= b &= c'
+PASS compileAndSerialize('(a &= b) &= c') is '(a &= b) &= c'
+PASS compileAndSerialize('a &= (b &= c)') is 'a &= b &= c'
+PASS compileAndSerialize('a = b &= c') is 'a = b &= c'
+PASS compileAndSerialize('(a = b) &= c') is '(a = b) &= c'
+PASS compileAndSerialize('a = (b &= c)') is 'a = b &= c'
+PASS compileAndSerialize('a &= b + c') is 'a &= b + c'
+PASS compileAndSerialize('(a &= b) + c') is '(a &= b) + c'
+PASS compileAndSerialize('a &= (b + c)') is 'a &= b + c'
+PASS compileAndSerialize('a + b &= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) &= c') is '(a + b) &= c'
+PASS compileAndSerialize('a + (b &= c)') is 'a + (b &= c)'
+PASS compileAndSerialize('a ^= b ^= c') is 'a ^= b ^= c'
+PASS compileAndSerialize('(a ^= b) ^= c') is '(a ^= b) ^= c'
+PASS compileAndSerialize('a ^= (b ^= c)') is 'a ^= b ^= c'
+PASS compileAndSerialize('a = b ^= c') is 'a = b ^= c'
+PASS compileAndSerialize('(a = b) ^= c') is '(a = b) ^= c'
+PASS compileAndSerialize('a = (b ^= c)') is 'a = b ^= c'
+PASS compileAndSerialize('a ^= b + c') is 'a ^= b + c'
+PASS compileAndSerialize('(a ^= b) + c') is '(a ^= b) + c'
+PASS compileAndSerialize('a ^= (b + c)') is 'a ^= b + c'
+PASS compileAndSerialize('a + b ^= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) ^= c') is '(a + b) ^= c'
+PASS compileAndSerialize('a + (b ^= c)') is 'a + (b ^= c)'
+PASS compileAndSerialize('a |= b |= c') is 'a |= b |= c'
+PASS compileAndSerialize('(a |= b) |= c') is '(a |= b) |= c'
+PASS compileAndSerialize('a |= (b |= c)') is 'a |= b |= c'
+PASS compileAndSerialize('a = b |= c') is 'a = b |= c'
+PASS compileAndSerialize('(a = b) |= c') is '(a = b) |= c'
+PASS compileAndSerialize('a = (b |= c)') is 'a = b |= c'
+PASS compileAndSerialize('a |= b + c') is 'a |= b + c'
+PASS compileAndSerialize('(a |= b) + c') is '(a |= b) + c'
+PASS compileAndSerialize('a |= (b + c)') is 'a |= b + c'
+PASS compileAndSerialize('a + b |= c') threw exception SyntaxError: Parse error.
+PASS compileAndSerialize('(a + b) |= c') is '(a + b) |= c'
+PASS compileAndSerialize('a + (b |= c)') is 'a + (b |= c)'
+PASS compileAndSerialize('delete a + b') is 'delete a + b'
+PASS compileAndSerialize('(delete a) + b') is 'delete a + b'
+PASS compileAndSerialize('delete (a + b)') is 'delete (a + b)'
+PASS compileAndSerialize('!delete a') is '!delete a'
+PASS compileAndSerialize('!(delete a)') is '!delete a'
+PASS compileAndSerialize('void a + b') is 'void a + b'
+PASS compileAndSerialize('(void a) + b') is 'void a + b'
+PASS compileAndSerialize('void (a + b)') is 'void (a + b)'
+PASS compileAndSerialize('!void a') is '!void a'
+PASS compileAndSerialize('!(void a)') is '!void a'
+PASS compileAndSerialize('typeof a + b') is 'typeof a + b'
+PASS compileAndSerialize('(typeof a) + b') is 'typeof a + b'
+PASS compileAndSerialize('typeof (a + b)') is 'typeof (a + b)'
+PASS compileAndSerialize('!typeof a') is '!typeof a'
+PASS compileAndSerialize('!(typeof a)') is '!typeof a'
+PASS compileAndSerialize('++a + b') is '++a + b'
+PASS compileAndSerialize('(++a) + b') is '++a + b'
+PASS compileAndSerialize('++(a + b)') is '++(a + b)'
+PASS compileAndSerialize('!++a') is '!++a'
+PASS compileAndSerialize('!(++a)') is '!++a'
+PASS compileAndSerialize('--a + b') is '--a + b'
+PASS compileAndSerialize('(--a) + b') is '--a + b'
+PASS compileAndSerialize('--(a + b)') is '--(a + b)'
+PASS compileAndSerialize('!--a') is '!--a'
+PASS compileAndSerialize('!(--a)') is '!--a'
+PASS compileAndSerialize('+ a + b') is '+ a + b'
+PASS compileAndSerialize('(+ a) + b') is '+ a + b'
+PASS compileAndSerialize('+ (a + b)') is '+ (a + b)'
+PASS compileAndSerialize('!+ a') is '!+ a'
+PASS compileAndSerialize('!(+ a)') is '!+ a'
+PASS compileAndSerialize('- a + b') is '- a + b'
+PASS compileAndSerialize('(- a) + b') is '- a + b'
+PASS compileAndSerialize('- (a + b)') is '- (a + b)'
+PASS compileAndSerialize('!- a') is '!- a'
+PASS compileAndSerialize('!(- a)') is '!- a'
+PASS compileAndSerialize('~a + b') is '~a + b'
+PASS compileAndSerialize('(~a) + b') is '~a + b'
+PASS compileAndSerialize('~(a + b)') is '~(a + b)'
+PASS compileAndSerialize('!~a') is '!~a'
+PASS compileAndSerialize('!(~a)') is '!~a'
+PASS compileAndSerialize('!a + b') is '!a + b'
+PASS compileAndSerialize('(!a) + b') is '!a + b'
+PASS compileAndSerialize('!(a + b)') is '!(a + b)'
+PASS compileAndSerialize('!!a') is '!!a'
+PASS compileAndSerialize('!(!a)') is '!!a'
+PASS compileAndSerialize('!a++') is '!a++'
+PASS compileAndSerialize('!(a++)') is '!a++'
+PASS compileAndSerialize('(!a)++') is '(!a)++'
+PASS compileAndSerialize('!a--') is '!a--'
+PASS compileAndSerialize('!(a--)') is '!a--'
+PASS compileAndSerialize('(!a)--') is '(!a)--'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/function-toString-parentheses.html b/LayoutTests/fast/js/function-toString-parentheses.html
new file mode 100644 (file)
index 0000000..d8ea3f9
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="resources/js-test-style.css">
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/function-toString-parentheses.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/resources/function-toString-object-literals.js b/LayoutTests/fast/js/resources/function-toString-object-literals.js
new file mode 100644 (file)
index 0000000..877fec7
--- /dev/null
@@ -0,0 +1,39 @@
+description(
+"This test checks that object literals are serialized properly. " +
+"It's needed in part because JavaScriptCore converts numeric property names to string and back."
+);
+
+function compileAndSerialize(expression)
+{
+    var f = eval("(function () { return " + expression + "; })");
+    var serializedString = f.toString();
+    serializedString = serializedString.replace(/[ \t\r\n]+/g, " ");
+    serializedString = serializedString.replace("function () { return ", "");
+    serializedString = serializedString.replace("; }", "");
+    return serializedString;
+}
+
+shouldBe("compileAndSerialize('a = { 1: null }')", "'a = { 1: null }'");
+shouldBe("compileAndSerialize('a = { 0: null }')", "'a = { 0: null }'");
+shouldBe("compileAndSerialize('a = { 1.0: null }')", "'a = { 1: null }'");
+shouldBe("compileAndSerialize('a = { \"1.0\": null }')", "'a = { \"1.0\": null }'");
+shouldBe("compileAndSerialize('a = { 1e-500: null }')", "'a = { 0: null }'");
+shouldBe("compileAndSerialize('a = { 1e-300: null }')", "'a = { 1e-300: null }'");
+shouldBe("compileAndSerialize('a = { 1e300: null }')", "'a = { 1e+300: null }'");
+shouldBe("compileAndSerialize('a = { 1e500: null }')", "'a = { \"Infinity\": null }'");
+
+shouldBe("compileAndSerialize('a = { NaN: null }')", "'a = { \"NaN\": null }'");
+shouldBe("compileAndSerialize('a = { Infinity: null }')", "'a = { \"Infinity\": null }'");
+
+shouldBe("compileAndSerialize('a = { \"1\": null }')", "'a = { 1: null }'");
+shouldBe("compileAndSerialize('a = { \"1hi\": null }')", "'a = { \"1hi\": null }'");
+shouldBe("compileAndSerialize('a = { \"\\\'\": null }')", "'a = { \"\\\'\": null }'");
+shouldBe("compileAndSerialize('a = { \"\\\\\"\": null }')", "'a = { \"\\\\\"\": null }'");
+
+shouldThrow("compileAndSerialize('a = { --1: null }')");
+shouldThrow("compileAndSerialize('a = { -NaN: null }')");
+shouldThrow("compileAndSerialize('a = { -0: null }')");
+shouldThrow("compileAndSerialize('a = { -0.0: null }')");
+shouldThrow("compileAndSerialize('a = { -Infinity: null }')");
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/js/resources/function-toString-parentheses.js b/LayoutTests/fast/js/resources/function-toString-parentheses.js
new file mode 100644 (file)
index 0000000..e01c2fe
--- /dev/null
@@ -0,0 +1,132 @@
+description(
+"This test checks that parentheses are preserved when significant, and not added where inappropriate. " +
+"We need this test because the JavaScriptCore parser removes all parentheses and the serializer then adds them back."
+);
+
+function compileAndSerialize(expression)
+{
+    var f = eval("(function () { return " + expression + "; })");
+    var serializedString = f.toString();
+    serializedString = serializedString.replace(/[ \t\r\n]+/g, " ");
+    serializedString = serializedString.replace("function () { return ", "");
+    serializedString = serializedString.replace("; }", "");
+    return serializedString;
+}
+
+var removesExtraParentheses = compileAndSerialize("(a + b) + c") == "a + b + c";
+
+function testLeftAssociativeSame(opA, opB)
+{
+    shouldBe("compileAndSerialize('a " + opA + " b " + opB + " c')",
+        "'a " + opA + " b " + opB + " c'");
+    shouldBe("compileAndSerialize('(a " + opA + " b) " + opB + " c')",
+        removesExtraParentheses
+            ? "'a " + opA + " b " + opB + " c'"
+            : "'(a " + opA + " b) " + opB + " c'"
+    );
+    shouldBe("compileAndSerialize('a " + opA + " (b " + opB + " c)')",
+        "'a " + opA + " (b " + opB + " c)'");
+}
+
+function testRightAssociativeSame(opA, opB)
+{
+    shouldBe("compileAndSerialize('a " + opA + " b " + opB + " c')",
+        "'a " + opA + " b " + opB + " c'");
+    shouldBe("compileAndSerialize('(a " + opA + " b) " + opB + " c')",
+        "'(a " + opA + " b) " + opB + " c'");
+    shouldBe("compileAndSerialize('a " + opA + " (b " + opB + " c)')",
+        removesExtraParentheses
+            ? "'a " + opA + " b " + opB + " c'"
+            : "'a " + opA + " (b " + opB + " c)'"
+    );
+}
+
+function testHigherFirst(opHigher, opLower)
+{
+    shouldBe("compileAndSerialize('a " + opHigher + " b " + opLower + " c')",
+        "'a " + opHigher + " b " + opLower + " c'");
+    shouldBe("compileAndSerialize('(a " + opHigher + " b) " + opLower + " c')",
+        removesExtraParentheses
+            ? "'a " + opHigher + " b " + opLower + " c'"
+            : "'a " + opHigher + " (b " + opLower + " c)'"
+    );
+    shouldBe("compileAndSerialize('a " + opHigher + " (b " + opLower + " c)')",
+        "'a " + opHigher + " (b " + opLower + " c)'");
+}
+
+function testLowerFirst(opLower, opHigher)
+{
+    shouldBe("compileAndSerialize('a " + opLower + " b " + opHigher + " c')",
+        "'a " + opLower + " b " + opHigher + " c'");
+    shouldBe("compileAndSerialize('(a " + opLower + " b) " + opHigher + " c')",
+        "'(a " + opLower + " b) " + opHigher + " c'");
+    shouldBe("compileAndSerialize('a " + opLower + " (b " + opHigher + " c)')",
+        removesExtraParentheses
+            ? "'a " + opLower + " b " + opHigher + " c'"
+            : "'a " + opLower + " (b " + opHigher + " c)'"
+    );
+}
+
+var binaryOperators = [
+    [ "*", "/", "%" ], [ "+", "-" ],
+    [ "<<", ">>", ">>>" ],
+    [ "<", ">", "<=", ">=", "instanceof", "in" ],
+    [ "==", "!=", "===", "!==" ],
+    [ "&" ], [ "^" ], [ "|" ],
+    [ "&&" ], [ "||" ]
+];
+
+for (i = 0; i < binaryOperators.length; ++i) {
+    var ops = binaryOperators[i];
+    for (j = 0; j < ops.length; ++j) {
+        var op = ops[j];
+        testLeftAssociativeSame(op, op);
+        if (j != 0)
+            testLeftAssociativeSame(ops[0], op);
+        if (i < binaryOperators.length - 1) {
+            var nextOps = binaryOperators[i + 1];
+            if (j == 0)
+                for (k = 0; k < nextOps.length; ++k)
+                    testHigherFirst(op, nextOps[k]);
+            else
+                testHigherFirst(op, nextOps[0]);
+        }
+    }
+}
+
+var assignmentOperators = [ "=", "*=", "/=" , "%=", "+=", "-=", "<<=", ">>=", ">>>=", "&=", "^=", "|=" ];
+
+for (i = 0; i < assignmentOperators.length; ++i) {
+    var op = assignmentOperators[i];
+    testRightAssociativeSame(op, op);
+    if (i != 0)
+        testRightAssociativeSame("=", op);
+    testLowerFirst(op, "+");
+    shouldThrow("compileAndSerialize('a + b " + op + " c')");
+    shouldBe("compileAndSerialize('(a + b) " + op + " c')",
+        "'(a + b) " + op + " c'");
+    shouldBe("compileAndSerialize('a + (b " + op + " c)')",
+        "'a + (b " + op + " c)'");
+}
+
+var prefixOperators = [ "delete", "void", "typeof", "++", "--", "+", "-", "~", "!" ];
+var prefixOperatorSpace = [ " ", " ", " ", "", "", " ", " ", "", "" ];
+
+for (i = 0; i < prefixOperators.length; ++i) {
+    var op = prefixOperators[i] + prefixOperatorSpace[i];
+    shouldBe("compileAndSerialize('" + op + "a + b')", "'" + op + "a + b'");
+    shouldBe("compileAndSerialize('(" + op + "a) + b')", "'" + op + "a + b'");
+    shouldBe("compileAndSerialize('" + op + "(a + b)')", "'" + op + "(a + b)'");
+    shouldBe("compileAndSerialize('!" + op + "a')", "'!" + op + "a'");
+    shouldBe("compileAndSerialize('!(" + op + "a)')", "'!" + op + "a'");
+}
+
+shouldBe("compileAndSerialize('!a++')", "'!a++'");
+shouldBe("compileAndSerialize('!(a++)')", "'!a++'");
+shouldBe("compileAndSerialize('(!a)++')", "'(!a)++'");
+shouldBe("compileAndSerialize('!a--')", "'!a--'");
+shouldBe("compileAndSerialize('!(a--)')", "'!a--'");
+shouldBe("compileAndSerialize('(!a)--')", "'(!a)--'");
+
+
+var successfullyParsed = true;