/* * Copyright (C) 2002 Harri Porten (porten@kde.org) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "nodes.h" #include #include #include using namespace WTF; using namespace Unicode; namespace JSC { // 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_atStartOfStatement(true) , 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<<(EndlType); SourceStream& operator<<(IndentType); SourceStream& operator<<(UnindentType); SourceStream& operator<<(DotExprType); SourceStream& operator<<(Precedence); SourceStream& operator<<(const Node*); template SourceStream& operator<<(const RefPtr& n) { return *this << n.get(); } private: UString m_string; UString m_spacesForIndentation; bool m_numberNeedsParens; bool m_atStartOfStatement; Precedence m_precedence; }; // -------- static UString escapeStringForPrettyPrinting(const UString& s) { UString escapedString; for (int i = 0; i < s.size(); i++) { UChar c = s.data()[i]; 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; } } } 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); } // -------- SourceStream& SourceStream::operator<<(char c) { m_numberNeedsParens = false; m_atStartOfStatement = false; // use unsigned char to zero-extend instead of sign-extend UChar ch(static_cast(c)); m_string.append(ch); return *this; } SourceStream& SourceStream::operator<<(const char* s) { m_numberNeedsParens = false; m_atStartOfStatement = false; m_string += s; return *this; } SourceStream& SourceStream::operator<<(double value) { bool needParens = m_numberNeedsParens; m_numberNeedsParens = false; m_atStartOfStatement = false; if (needParens) m_string.append('('); m_string += UString::from(value); if (needParens) m_string.append(')'); return *this; } SourceStream& SourceStream::operator<<(const UString& s) { m_numberNeedsParens = false; m_atStartOfStatement = false; m_string += s; return *this; } SourceStream& SourceStream::operator<<(const Identifier& s) { m_numberNeedsParens = false; m_atStartOfStatement = false; m_string += s.ustring(); return *this; } SourceStream& SourceStream::operator<<(const Node* n) { bool needParens = (m_precedence != PrecExpression && n->precedence() > m_precedence) || (m_atStartOfStatement && n->needsParensIfLeftmost()); m_precedence = PrecExpression; if (!n) return *this; if (needParens) { m_numberNeedsParens = false; m_string.append('('); } n->streamTo(*this); if (needParens) m_string.append(')'); return *this; } SourceStream& SourceStream::operator<<(EndlType) { m_numberNeedsParens = false; m_atStartOfStatement = true; m_string.append('\n'); m_string.append(m_spacesForIndentation); return *this; } SourceStream& SourceStream::operator<<(IndentType) { m_numberNeedsParens = false; m_atStartOfStatement = false; m_spacesForIndentation += " "; return *this; } SourceStream& SourceStream::operator<<(UnindentType) { m_numberNeedsParens = false; m_atStartOfStatement = false; m_spacesForIndentation = m_spacesForIndentation.substr(0, m_spacesForIndentation.size() - 2); return *this; } inline SourceStream& SourceStream::operator<<(DotExprType) { m_numberNeedsParens = true; return *this; } inline SourceStream& SourceStream::operator<<(Precedence precedence) { m_precedence = precedence; return *this; } static void streamLeftAssociativeBinaryOperator(SourceStream& s, Precedence precedence, const char* operatorString, const Node* left, const Node* right) { s << precedence << left << ' ' << operatorString << ' ' << static_cast(precedence - 1) << right; } template static inline void streamLeftAssociativeBinaryOperator(SourceStream& s, Precedence p, const char* o, const RefPtr& l, const RefPtr& r) { streamLeftAssociativeBinaryOperator(s, p, o, l.get(), r.get()); } static inline void bracketNodeStreamTo(SourceStream& s, const RefPtr& base, const RefPtr& subscript) { s << PrecCall << base.get() << "[" << subscript.get() << "]"; } static inline void dotNodeStreamTo(SourceStream& s, const RefPtr& base, const Identifier& ident) { s << DotExpr << PrecCall << base.get() << "." << ident; } // -------- UString Node::toString() const { SourceStream stream; streamTo(stream); return stream.toString(); } // -------- void NullNode::streamTo(SourceStream& s) const { s << "null"; } void BooleanNode::streamTo(SourceStream& s) const { s << (m_value ? "true" : "false"); } void NumberNode::streamTo(SourceStream& s) const { s << value(); } void StringNode::streamTo(SourceStream& s) const { s << '"' << escapeStringForPrettyPrinting(m_value.ustring()) << '"'; } void RegExpNode::streamTo(SourceStream& s) const { s << '/' << m_pattern << '/' << m_flags; } void ThisNode::streamTo(SourceStream& s) const { s << "this"; } void ResolveNode::streamTo(SourceStream& s) const { s << m_ident; } void ElementNode::streamTo(SourceStream& s) const { for (const ElementNode* n = this; n; n = n->m_next.get()) { for (int i = 0; i < n->m_elision; i++) s << ','; s << PrecAssignment << n->m_node; if (n->m_next) s << ','; } } void ArrayNode::streamTo(SourceStream& s) const { s << '[' << m_element; for (int i = 0; i < m_elision; i++) s << ','; // Parser consumes one elision comma if there's array elements // present in the expression. if (m_optional && m_element) s << ','; s << ']'; } void ObjectLiteralNode::streamTo(SourceStream& s) const { if (m_list) s << "{ " << m_list << " }"; else s << "{ }"; } void PropertyListNode::streamTo(SourceStream& s) const { s << m_node; for (const PropertyListNode* n = m_next.get(); n; n = n->m_next.get()) s << ", " << n->m_node; } void PropertyNode::streamTo(SourceStream& s) const { switch (m_type) { case Constant: { UString propertyName = name().ustring(); if (isParserRoundTripNumber(propertyName)) s << propertyName; else s << '"' << escapeStringForPrettyPrinting(propertyName) << '"'; s << ": " << PrecAssignment << m_assign; break; } case Getter: case Setter: { const FuncExprNode* func = static_cast(m_assign.get()); if (m_type == Getter) s << "get "; else s << "set "; s << escapeStringForPrettyPrinting(name().ustring()) << "(" << func->m_parameter << ')' << func->m_body; break; } } } void BracketAccessorNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); } void DotAccessorNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); } void ArgumentListNode::streamTo(SourceStream& s) const { s << PrecAssignment << m_expr; for (ArgumentListNode* n = m_next.get(); n; n = n->m_next.get()) s << ", " << PrecAssignment << n->m_expr; } void ArgumentsNode::streamTo(SourceStream& s) const { s << '(' << m_listNode << ')'; } void NewExprNode::streamTo(SourceStream& s) const { s << "new " << PrecMember << m_expr << m_args; } void EvalFunctionCallNode::streamTo(SourceStream& s) const { s << "eval" << m_args; } void FunctionCallValueNode::streamTo(SourceStream& s) const { s << PrecCall << m_expr << m_args; } void FunctionCallResolveNode::streamTo(SourceStream& s) const { s << m_ident << m_args; } void FunctionCallBracketNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); s << m_args; } void FunctionCallDotNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); s << m_args; } void PostfixResolveNode::streamTo(SourceStream& s) const { s << m_ident << operatorString(m_operator); } void PostfixBracketNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); s << operatorString(m_operator); } void PostfixDotNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); s << operatorString(m_operator); } void PostfixErrorNode::streamTo(SourceStream& s) const { s << PrecLeftHandSide << m_expr; if (m_operator == OpPlusPlus) s << "++"; else s << "--"; } void DeleteResolveNode::streamTo(SourceStream& s) const { s << "delete " << m_ident; } void DeleteBracketNode::streamTo(SourceStream& s) const { s << "delete "; bracketNodeStreamTo(s, m_base, m_subscript); } void DeleteDotNode::streamTo(SourceStream& s) const { s << "delete "; dotNodeStreamTo(s, m_base, m_ident); } void DeleteValueNode::streamTo(SourceStream& s) const { s << "delete " << PrecUnary << m_expr; } void VoidNode::streamTo(SourceStream& s) const { s << "void " << PrecUnary << m_expr; } void TypeOfValueNode::streamTo(SourceStream& s) const { s << "typeof " << PrecUnary << m_expr; } void TypeOfResolveNode::streamTo(SourceStream& s) const { s << "typeof " << m_ident; } void PrefixResolveNode::streamTo(SourceStream& s) const { s << operatorString(m_operator) << m_ident; } void PrefixBracketNode::streamTo(SourceStream& s) const { s << operatorString(m_operator); bracketNodeStreamTo(s, m_base, m_subscript); } void PrefixDotNode::streamTo(SourceStream& s) const { s << operatorString(m_operator); dotNodeStreamTo(s, m_base, m_ident); } void PrefixErrorNode::streamTo(SourceStream& s) const { if (m_operator == OpPlusPlus) s << "++" << PrecUnary << m_expr; else s << "--" << PrecUnary << m_expr; } void UnaryPlusNode::streamTo(SourceStream& s) const { s << "+ " << PrecUnary << m_expr; } void NegateNode::streamTo(SourceStream& s) const { s << "- " << PrecUnary << m_expr; } void BitwiseNotNode::streamTo(SourceStream& s) const { s << "~" << PrecUnary << m_expr; } void LogicalNotNode::streamTo(SourceStream& s) const { s << "!" << PrecUnary << m_expr; } void MultNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "*", m_expr1, m_expr2); } void DivNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "/", m_expr1, m_expr2); } void ModNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "%", m_expr1, m_expr2); } void AddNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "+", m_expr1, m_expr2); } void SubNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "-", m_expr1, m_expr2); } void LeftShiftNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "<<", m_expr1, m_expr2); } void RightShiftNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), ">>", m_expr1, m_expr2); } void UnsignedRightShiftNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), ">>>", m_expr1, m_expr2); } void LessNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "<", m_expr1, m_expr2); } void GreaterNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), ">", m_expr1, m_expr2); } void LessEqNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "<=", m_expr1, m_expr2); } void GreaterEqNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), ">=", m_expr1, m_expr2); } void InstanceOfNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "instanceof", m_expr1, m_expr2); } void InNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "in", m_expr1, m_expr2); } void EqualNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "==", m_expr1, m_expr2); } void NotEqualNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "!=", m_expr1, m_expr2); } void StrictEqualNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "===", m_expr1, m_expr2); } void NotStrictEqualNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "!==", m_expr1, m_expr2); } void BitAndNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "&", m_expr1, m_expr2); } void BitXOrNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "^", m_expr1, m_expr2); } void BitOrNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), "|", m_expr1, m_expr2); } void LogicalOpNode::streamTo(SourceStream& s) const { streamLeftAssociativeBinaryOperator(s, precedence(), (m_operator == OpLogicalAnd) ? "&&" : "||", m_expr1, m_expr2); } void ConditionalNode::streamTo(SourceStream& s) const { s << PrecLogicalOr << m_logical << " ? " << PrecAssignment << m_expr1 << " : " << PrecAssignment << m_expr2; } void ReadModifyResolveNode::streamTo(SourceStream& s) const { s << m_ident << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; } void AssignResolveNode::streamTo(SourceStream& s) const { s << m_ident << " = " << PrecAssignment << m_right; } void ReadModifyBracketNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); s << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; } void AssignBracketNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); s << " = " << PrecAssignment << m_right; } void ReadModifyDotNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); s << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; } void AssignDotNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); s << " = " << PrecAssignment << m_right; } void AssignErrorNode::streamTo(SourceStream& s) const { s << PrecLeftHandSide << m_left << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; } void CommaNode::streamTo(SourceStream& s) const { s << PrecAssignment << m_expr1 << ", " << PrecAssignment << m_expr2; } void ConstDeclNode::streamTo(SourceStream& s) const { s << m_ident; if (m_init) s << " = " << PrecAssignment << m_init; for (ConstDeclNode* n = m_next.get(); n; n = n->m_next.get()) { s << ", " << n->m_ident; if (n->m_init) s << " = " << PrecAssignment << n->m_init; } } void ConstStatementNode::streamTo(SourceStream& s) const { s << Endl << "const " << m_next << ';'; } static inline void statementListStreamTo(const Vector >& nodes, SourceStream& s) { for (Vector >::const_iterator ptr = nodes.begin(); ptr != nodes.end(); ptr++) s << *ptr; } void BlockNode::streamTo(SourceStream& s) const { s << Endl << "{" << Indent; statementListStreamTo(m_children, s); s << Unindent << Endl << "}"; } void ScopeNode::streamTo(SourceStream& s) const { s << Endl << "{" << Indent; bool printedVar = false; for (size_t i = 0; i < m_varStack.size(); ++i) { if (m_varStack[i].second == 0) { if (!printedVar) { s << Endl << "var "; printedVar = true; } else s << ", "; s << m_varStack[i].first; } } if (printedVar) s << ';'; statementListStreamTo(m_children, s); s << Unindent << Endl << "}"; } void EmptyStatementNode::streamTo(SourceStream& s) const { s << Endl << ';'; } void DebuggerStatementNode::streamTo(SourceStream& s) const { s << Endl << "debugger;"; } void ExprStatementNode::streamTo(SourceStream& s) const { s << Endl << m_expr << ';'; } void VarStatementNode::streamTo(SourceStream& s) const { s << Endl << "var " << m_expr << ';'; } void IfNode::streamTo(SourceStream& s) const { s << Endl << "if (" << m_condition << ')' << Indent << m_ifBlock << Unindent; } void IfElseNode::streamTo(SourceStream& s) const { IfNode::streamTo(s); s << Endl << "else" << Indent << m_elseBlock << Unindent; } void DoWhileNode::streamTo(SourceStream& s) const { s << Endl << "do " << Indent << m_statement << Unindent << Endl << "while (" << m_expr << ");"; } void WhileNode::streamTo(SourceStream& s) const { s << Endl << "while (" << m_expr << ')' << Indent << m_statement << Unindent; } void ForNode::streamTo(SourceStream& s) const { s << Endl << "for (" << (m_expr1WasVarDecl ? "var " : "") << m_expr1 << "; " << m_expr2 << "; " << m_expr3 << ')' << Indent << m_statement << Unindent; } void ForInNode::streamTo(SourceStream& s) const { s << Endl << "for ("; if (m_identIsVarDecl) { s << "var "; if (m_init) s << m_init; else s << PrecLeftHandSide << m_lexpr; } else s << PrecLeftHandSide << m_lexpr; s << " in " << m_expr << ')' << Indent << m_statement << Unindent; } void ContinueNode::streamTo(SourceStream& s) const { s << Endl << "continue"; if (!m_ident.isNull()) s << ' ' << m_ident; s << ';'; } void BreakNode::streamTo(SourceStream& s) const { s << Endl << "break"; if (!m_ident.isNull()) s << ' ' << m_ident; s << ';'; } void ReturnNode::streamTo(SourceStream& s) const { s << Endl << "return"; if (m_value) s << ' ' << m_value; s << ';'; } void WithNode::streamTo(SourceStream& s) const { s << Endl << "with (" << m_expr << ") " << m_statement; } void CaseClauseNode::streamTo(SourceStream& s) const { s << Endl; if (m_expr) s << "case " << m_expr; else s << "default"; s << ":" << Indent; statementListStreamTo(m_children, s); s << Unindent; } void ClauseListNode::streamTo(SourceStream& s) const { for (const ClauseListNode* n = this; n; n = n->getNext()) s << n->getClause(); } void CaseBlockNode::streamTo(SourceStream& s) const { for (const ClauseListNode* n = m_list1.get(); n; n = n->getNext()) s << n->getClause(); s << m_defaultClause; for (const ClauseListNode* n = m_list2.get(); n; n = n->getNext()) s << n->getClause(); } void SwitchNode::streamTo(SourceStream& s) const { s << Endl << "switch (" << m_expr << ") {" << Indent << m_block << Unindent << Endl << "}"; } void LabelNode::streamTo(SourceStream& s) const { s << Endl << m_name << ":" << Indent << m_statement << Unindent; } void ThrowNode::streamTo(SourceStream& s) const { s << Endl << "throw " << m_expr << ';'; } void TryNode::streamTo(SourceStream& s) const { s << Endl << "try " << m_tryBlock; if (m_catchBlock) s << Endl << "catch (" << m_exceptionIdent << ')' << m_catchBlock; if (m_finallyBlock) s << Endl << "finally " << m_finallyBlock; } void ParameterNode::streamTo(SourceStream& s) const { s << m_ident; for (ParameterNode* n = m_next.get(); n; n = n->m_next.get()) s << ", " << n->m_ident; } void FuncDeclNode::streamTo(SourceStream& s) const { s << Endl << "function " << m_ident << '(' << m_parameter << ')' << m_body; } void FuncExprNode::streamTo(SourceStream& s) const { s << "function " << m_ident << '(' << m_parameter << ')' << m_body; } } // namespace JSC