/**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ **/ #include "qqmldomastdumper_p.h" #include "qqmldomerrormessage_p.h" #include #include #include #include QT_BEGIN_NAMESPACE namespace QQmlJS { namespace Dom { using namespace AST; /*! \internal \enum QQmlJS::AstDumperOption This enum type specifies the options for the AstDumper. The values can be combined with the '|' operator, and checked using the '&' operator. \value None Default dumping options \value NoLocations Does not dump SourceLocations, allowing one to compare equivalent AST generated by code formatted differently \value NoAnnotations Does not dump annotations \value DumpNode Does dump a in preVisit/postVisit */ /*! \internal \class QQmlJS::AstDumper \brief Dumps or compares AST in an xml like format, mostly for testing/debugging Initialize it with a lambda that dumps a string, and configure it with .setX methods. If \l{indent} is set to a non zero value the xml is indented by that amount, and \l{baseIndent} is the initial indent. If \l{emitNode} is true the node tag is emitted in the preVisit/postVisit. If \l{emitLocation} is true the SourceLocations are emitted. If \l{emitAnnotations} is true annotations are emitted The implementation has unnecessary roundtrips to QString, but it is supposed to be used for debugging purposes... Probably you will not use the visitor at all but rather the static method diff or the qDebug() and ostream operator << that use the visitor... \fn AstDumper::diff(AST::Node *n1, AST::Node *n2, int nContext, AstDumperOptions opt, int indent) \brief compares the two AST::Node n1 and n2 and returns a string describing their first difference If there are no differences the empty string is returned, so .isEmpty() can be use to check for no differences. \l{nContext} decides how much context is printed out. */ // no export, just a supporting class... class AstDumper: public AST::BaseVisitor { public: AstDumper(const std::function &dumper, AstDumperOptions options = AstDumperOption::None, int indent = 1, int baseIndent = 0, function_ref loc2str = &noStr) : dumper(dumper), options(options), indent(indent), baseIndent(baseIndent), loc2str(loc2str) { } private: void start(QStringView str) { dumper(QString::fromLatin1(" ").repeated(baseIndent)); dumper(u"<"); dumper(str); dumper(u">\n"); baseIndent += indent; } void stop(QStringView str) { baseIndent -= indent; dumper(QString::fromLatin1(" ").repeated(baseIndent)); dumper(u"\n"); } QString quotedString(const QString &s) { QString res(s); return QStringLiteral(u"\"") + res .replace(QLatin1String("\\"), QLatin1String("\\\\")) .replace(QLatin1String("\""), QLatin1String("\\\"")) + QLatin1String("\""); } QString quotedString(QStringView s) { return quotedString(s.toString()); } QString loc(const SourceLocation &s, bool trim = false) { QString tokenStr; if (s.length > 0) tokenStr = loc2str(s).toString() .replace(QLatin1String("\\"), QLatin1String("\\\\")) .replace(QLatin1String("\""),QLatin1String("\\\"")); if (trim) tokenStr = tokenStr.trimmed(); if (noLocations() || s == SourceLocation()) return QLatin1String("\"%1\"").arg(tokenStr); else { return QLatin1String("\"off:%1 len:%2 l:%3 c:%4 %5\"").arg(QString::number(s.offset), QString::number(s.length), QString::number(s.startLine), QString::number(s.startColumn), tokenStr); } } QString semicolonToken(const SourceLocation &s) { if (options & AstDumperOption::SloppyCompare) return QString(); return QLatin1String(" semicolonToken=") + loc(s); } QString boolStr(bool v) { return (v ? quotedString(u"true"): quotedString(u"false")); } public: bool preVisit(Node *) override { if (dumpNode()) start(u"Node"); return true; } void postVisit(Node *) override { if (dumpNode()) stop(u"Node"); } // Ui bool visit(UiProgram *) override { start(u"UiProgram"); return true; } void endVisit(AST::UiProgram *) override { stop(u"UiProgram"); } bool visit(UiHeaderItemList *) override { start(u"UiHeaderItemList"); return true; } void endVisit(AST::UiHeaderItemList *) override { stop(u"UiHeaderItemList"); } bool visit(UiPragma *el) override { start(QLatin1String("UiPragma name=%1 pragmaToken=%2%3") .arg(quotedString(el->name), loc(el->pragmaToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::UiPragma *) override { stop(u"UiPragma"); } bool visit(UiImport *el) override { start(QLatin1String("UiImport fileName=%1 importId=%2 importToken=%3 fileNameToken=%4 " "asToken=%5 importIdToken=%6%7") .arg(quotedString(el->fileName), quotedString(el->importId), loc(el->importToken), loc(el->fileNameToken), loc(el->asToken), loc(el->importIdToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::UiImport *el) override { Node::accept(el->version, this); stop(u"UiImport"); } bool visit(UiPublicMember *el) override { QString typeStr = ((el->type == UiPublicMember::Signal) ? QLatin1String("Signal") : (el->type == UiPublicMember::Property) ? QLatin1String("Property") : QLatin1String("Unexpected(%1)").arg(QString::number(el->type))); start(QLatin1String("UiPublicMember type=%1 typeModifier=%2 name=%3 isDefaultMember=%4 " "isReadonlyMember=%5 isRequired=%6 " "defaultToken=%7 readonlyToken=%8 propertyToken=%9 requiredToken=%10 " "typeModifierToken=%11 typeToken=%12 " "identifierToken=%13 colonToken=%14%15") .arg(quotedString(typeStr), quotedString(el->typeModifier), quotedString(el->name), boolStr(el->isDefaultMember()), boolStr(el->isReadonly()), boolStr(el->isRequired()), loc(el->defaultToken()), loc(el->readonlyToken()), loc(el->propertyToken()), loc(el->requiredToken()), loc(el->typeModifierToken), loc(el->typeToken), loc(el->identifierToken), loc(el->colonToken), semicolonToken(el->semicolonToken))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); Node::accept(el->memberType, this); return true; } void endVisit(AST::UiPublicMember *el) override { Node::accept(el->parameters, this); stop(u"UiPublicMember"); } bool visit(AST::UiSourceElement *el) override { start(u"UiSourceElement"); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiSourceElement *) override { stop(u"UiSourceElement"); } bool visit(AST::UiObjectDefinition *el) override { start(u"UiObjectDefinition"); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiObjectDefinition *) override { stop(u"UiObjectDefinition"); } bool visit(AST::UiObjectInitializer *el) override { start(QLatin1String("UiObjectInitializer lbraceToken=%1 rbraceToken=%2") .arg(loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::UiObjectInitializer *) override { stop(u"UiObjectInitializer"); } bool visit(AST::UiObjectBinding *el) override { start(QLatin1String("UiObjectBinding colonToken=%1 hasOnToken=%2") .arg(loc(el->colonToken), boolStr(el->hasOnToken))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiObjectBinding *) override { stop(u"UiObjectBinding"); } bool visit(AST::UiScriptBinding *el) override { start(QLatin1String("UiScriptBinding colonToken=%1") .arg(loc(el->colonToken))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiScriptBinding *) override { stop(u"UiScriptBinding"); } bool visit(AST::UiArrayBinding *el) override { start(QLatin1String("UiArrayBinding colonToken=%1 lbracketToken=%2 rbracketToken=%3") .arg(loc(el->colonToken), loc(el->lbracketToken), loc(el->rbracketToken))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiArrayBinding *) override { stop(u"UiArrayBinding"); } bool visit(AST::UiParameterList *el) override { start(QLatin1String("UiArrayBinding name=%1 commaToken=%2 propertyTypeToken=%3 identifierToken=%4 colonToken=%5") .arg(quotedString(el->name), loc(el->commaToken), loc(el->propertyTypeToken), loc(el->identifierToken), loc(el->colonToken))); Node::accept(el->type, this); return true; } void endVisit(AST::UiParameterList *el) override { stop(u"UiParameterList"); Node::accept(el->next, this); // put other args at the same level as this one... } bool visit(AST::UiObjectMemberList *) override { start(u"UiObjectMemberList"); return true; } void endVisit(AST::UiObjectMemberList *) override { stop(u"UiObjectMemberList"); } bool visit(AST::UiArrayMemberList *el) override { start(QLatin1String("UiArrayMemberList commaToken=%1") .arg(loc(el->commaToken))); return true; } void endVisit(AST::UiArrayMemberList *) override { stop(u"UiArrayMemberList"); } bool visit(AST::UiQualifiedId *el) override { start(QLatin1String("UiQualifiedId name=%1 identifierToken=%2") .arg(quotedString(el->name), loc(el->identifierToken))); Node::accept(el->next, this); return true; } void endVisit(AST::UiQualifiedId *) override { stop(u"UiQualifiedId"); } bool visit(AST::UiEnumDeclaration *el) override { start(QLatin1String("UiEnumDeclaration enumToken=%1 rbraceToken=%2 name=%3") .arg(loc(el->enumToken), loc(el->rbraceToken), quotedString(el->name))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiEnumDeclaration *) override { stop(u"UiEnumDeclaration"); } bool visit(AST::UiEnumMemberList *el) override { start(QLatin1String("UiEnumMemberList member=%1 value=%2 memberToken=%3 valueToken=%4") .arg(quotedString(el->member), quotedString(QString::number(el->value)), loc(el->memberToken), loc(el->valueToken))); return true; } void endVisit(AST::UiEnumMemberList *el) override { stop(u"UiEnumMemberList"); Node::accept(el->next, this); // put other enum members at the same level as this one... } bool visit(AST::UiVersionSpecifier *el) override { start(QLatin1String("UiVersionSpecifier majorVersion=%1 minorVersion=%2 majorToken=%3 minorToken=%4") .arg(quotedString(QString::number(el->version.majorVersion())), quotedString(QString::number(el->version.minorVersion())), loc(el->majorToken), loc(el->minorToken))); return true; } void endVisit(AST::UiVersionSpecifier *) override { stop(u"UiVersionSpecifier"); } bool visit(AST::UiInlineComponent *el) override { start(QLatin1String("UiInlineComponent name=%1 componentToken=%2") .arg(quotedString(el->name), loc(el->componentToken))); if (!noAnnotations()) // put annotations inside the node they refer to Node::accept(el->annotations, this); return true; } void endVisit(AST::UiInlineComponent *) override { stop(u"UiInlineComponent"); } bool visit(UiRequired *el) override { start(QLatin1String("UiRequired name=%1 requiredToken=%2%3") .arg(quotedString(el->name), loc(el->requiredToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(UiRequired *) override { stop(u"UiRequired"); } bool visit(UiAnnotation *) override { start(u"UiAnnotation"); return true; } void endVisit(UiAnnotation *) override { stop(u"UiAnnotation"); } bool visit(UiAnnotationList *) override { start(u"UiAnnotationList"); return true; } void endVisit(UiAnnotationList *) override { stop(u"UiAnnotationList"); } // QQmlJS bool visit(AST::TypeExpression *) override { start(u"TypeExpression"); return true; } void endVisit(AST::TypeExpression *) override { stop(u"TypeExpression"); } bool visit(AST::ThisExpression *el) override { start(QLatin1String("ThisExpression thisToken=%1") .arg(loc(el->thisToken))); return true; } void endVisit(AST::ThisExpression *) override { stop(u"ThisExpression"); } bool visit(AST::IdentifierExpression *el) override { start(QLatin1String("IdentifierExpression name=%1 identiferToken=%2") .arg(quotedString(el->name), loc(el->identifierToken))); return true; } void endVisit(AST::IdentifierExpression *) override { stop(u"IdentifierExpression"); } bool visit(AST::NullExpression *el) override { start(QLatin1String("NullExpression nullToken=%1") .arg(loc(el->nullToken))); return true; } void endVisit(AST::NullExpression *) override { stop(u"NullExpression"); } bool visit(AST::TrueLiteral *el) override { start(QLatin1String("TrueLiteral trueToken=%1") .arg(loc(el->trueToken))); return true; } void endVisit(AST::TrueLiteral *) override { stop(u"TrueLiteral"); } bool visit(AST::FalseLiteral *el) override { start(QLatin1String("FalseLiteral falseToken=%1") .arg(loc(el->falseToken))); return true; } void endVisit(AST::FalseLiteral *) override { stop(u"FalseLiteral"); } bool visit(AST::SuperLiteral *el) override { start(QLatin1String("SuperLiteral superToken=%1") .arg(loc(el->superToken))); return true; } void endVisit(AST::SuperLiteral *) override { stop(u"SuperLiteral"); } bool visit(AST::StringLiteral *el) override { start(QLatin1String("StringLiteral value=%1 literalToken=%2") .arg(quotedString(el->value), loc(el->literalToken))); return true; } void endVisit(AST::StringLiteral *) override { stop(u"StringLiteral"); } bool visit(AST::TemplateLiteral *el) override { start(QLatin1String("TemplateLiteral value=%1 rawValue=%2 literalToken=%3") .arg(quotedString(el->value), quotedString(el->rawValue), loc(el->literalToken))); Node::accept(el->expression, this); return true; } void endVisit(AST::TemplateLiteral *) override { stop(u"TemplateLiteral"); } bool visit(AST::NumericLiteral *el) override { start(QLatin1String("NumericLiteral value=%1 literalToken=%2") .arg(quotedString(QString::number(el->value)), loc(el->literalToken))); return true; } void endVisit(AST::NumericLiteral *) override { stop(u"NumericLiteral"); } bool visit(AST::RegExpLiteral *el) override { start(QLatin1String("RegExpLiteral pattern=%1 flags=%2 literalToken=%3") .arg(quotedString(el->pattern), quotedString(QString::number(el->flags, 16)), loc(el->literalToken))); return true; } void endVisit(AST::RegExpLiteral *) override { stop(u"RegExpLiteral"); } bool visit(AST::ArrayPattern *el) override { start(QLatin1String("ArrayPattern lbracketToken=%1, commaToken=%2, rbracketToken=%3 parseMode=%4") .arg(loc(el->lbracketToken),loc(el->commaToken),loc(el->rbracketToken), quotedString(QString::number(el->parseMode, 16)))); return true; } void endVisit(AST::ArrayPattern *) override { stop(u"ArrayPattern"); } bool visit(AST::ObjectPattern *el) override { start(QLatin1String("ObjectPattern lbraceToken=%1 rbraceToken=%2 parseMode=%3") .arg(loc(el->lbraceToken), loc(el->rbraceToken), quotedString(QString::number(el->parseMode, 16)))); return true; } void endVisit(AST::ObjectPattern *) override { stop(u"ObjectPattern"); } bool visit(AST::PatternElementList *) override { start(u"PatternElementList"); return true; } void endVisit(AST::PatternElementList *) override { stop(u"PatternElementList"); } bool visit(AST::PatternPropertyList *) override { start(u"PatternPropertyList"); return true; } void endVisit(AST::PatternPropertyList *) override { stop(u"PatternPropertyList"); } bool visit(AST::PatternElement *el) override { start(QLatin1String("PatternElement identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5") .arg(loc(el->identifierToken), quotedString(el->bindingIdentifier), quotedString(QString::number(el->type, 16)), quotedString(QString::number(static_cast(el->scope), 16)), boolStr(el->isForDeclaration))); return true; } void endVisit(AST::PatternElement *) override { stop(u"PatternElement"); } bool visit(AST::PatternProperty *el) override { start(QLatin1String("PatternProperty identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5 colonToken=%6") .arg(loc(el->identifierToken), quotedString(el->bindingIdentifier), quotedString(QString::number(el->type, 16)), quotedString(QString::number(static_cast(el->scope), 16)), boolStr(el->isForDeclaration), loc(el->colonToken))); return true; } void endVisit(AST::PatternProperty *) override { stop(u"PatternProperty"); } bool visit(AST::Elision *el) override { start(QLatin1String("Elision commaToken=%1") .arg(loc(el->commaToken))); return true; } void endVisit(AST::Elision *el) override { stop(u"Elision"); Node::accept(el->next, this); // emit other elisions at the same level } bool visit(AST::NestedExpression *el) override { start(QLatin1String("NestedExpression lparenToken=%1 rparenToken=%2") .arg(loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::NestedExpression *) override { stop(u"NestedExpression"); } bool visit(AST::IdentifierPropertyName *el) override { if (options & AstDumperOption::SloppyCompare) start(QLatin1String("StringLiteralOrIdentifierPropertyName id=%1") .arg(quotedString(el->id))); else start(QLatin1String("IdentifierPropertyName id=%1 propertyNameToken=%2") .arg(quotedString(el->id), loc(el->propertyNameToken))); return true; } void endVisit(AST::IdentifierPropertyName *) override { if (options & AstDumperOption::SloppyCompare) stop(u"StringLiteralOrIdentifierPropertyName"); else stop(u"IdentifierPropertyName"); } bool visit(AST::StringLiteralPropertyName *el) override { if (options & AstDumperOption::SloppyCompare) start(QLatin1String("StringLiteralOrIdentifierPropertyName id=%1") .arg(quotedString(el->id))); else start(QLatin1String("StringLiteralPropertyName id=%1 propertyNameToken=%2") .arg(quotedString(el->id), loc(el->propertyNameToken))); return true; } void endVisit(AST::StringLiteralPropertyName *) override { if (options & AstDumperOption::SloppyCompare) stop(u"StringLiteralOrIdentifierPropertyName"); else stop(u"StringLiteralPropertyName"); } bool visit(AST::NumericLiteralPropertyName *el) override { start(QLatin1String("NumericLiteralPropertyName id=%1 propertyNameToken=%2") .arg(quotedString(QString::number(el->id)),loc(el->propertyNameToken))); return true; } void endVisit(AST::NumericLiteralPropertyName *) override { stop(u"NumericLiteralPropertyName"); } bool visit(AST::ComputedPropertyName *) override { start(u"ComputedPropertyName"); return true; } void endVisit(AST::ComputedPropertyName *) override { stop(u"ComputedPropertyName"); } bool visit(AST::ArrayMemberExpression *el) override { start(QLatin1String("ArrayMemberExpression lbraketToken=%1 rbraketToken=%2") .arg(loc(el->lbracketToken), loc(el->rbracketToken))); return true; } void endVisit(AST::ArrayMemberExpression *) override { stop(u"ArrayMemberExpression"); } bool visit(AST::FieldMemberExpression *el) override { start(QLatin1String("FieldMemberExpression name=%1 dotToken=%2 identifierToken=%3") .arg(quotedString(el->name), loc(el->dotToken), loc(el->identifierToken))); return true; } void endVisit(AST::FieldMemberExpression *) override { stop(u"FieldMemberExpression"); } bool visit(AST::TaggedTemplate *) override { start(u"TaggedTemplate"); return true; } void endVisit(AST::TaggedTemplate *) override { stop(u"TaggedTemplate"); } bool visit(AST::NewMemberExpression *el) override { start(QLatin1String("NewMemberExpression newToken=%1 lparenToken=%2 rparenToken=%3") .arg(loc(el->newToken), loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::NewMemberExpression *) override { stop(u"NewMemberExpression"); } bool visit(AST::NewExpression *el) override { start(QLatin1String("NewExpression newToken=%1") .arg(loc(el->newToken))); return true; } void endVisit(AST::NewExpression *) override { stop(u"NewExpression"); } bool visit(AST::CallExpression *el) override { start(QLatin1String("CallExpression lparenToken=%1 rparenToken=%2") .arg(loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::CallExpression *) override { stop(u"CallExpression"); } bool visit(AST::ArgumentList *el) override { start(QLatin1String("ArgumentList commaToken=%1 isSpreadElement=%2") .arg(loc(el->commaToken), boolStr(el->isSpreadElement))); return true; } void endVisit(AST::ArgumentList *) override { stop(u"ArgumentList"); } bool visit(AST::PostIncrementExpression *el) override { start(QLatin1String("PostIncrementExpression incrementToken=%1") .arg(loc(el->incrementToken))); return true; } void endVisit(AST::PostIncrementExpression *) override { stop(u"PostIncrementExpression"); } bool visit(AST::PostDecrementExpression *el) override { start(QLatin1String("PostDecrementExpression decrementToken=%1") .arg(loc(el->decrementToken))); return true; } void endVisit(AST::PostDecrementExpression *) override { stop(u"PostDecrementExpression"); } bool visit(AST::DeleteExpression *el) override { start(QLatin1String("DeleteExpression deleteToken=%1") .arg(loc(el->deleteToken))); return true; } void endVisit(AST::DeleteExpression *) override { stop(u"DeleteExpression"); } bool visit(AST::VoidExpression *el) override { start(QLatin1String("VoidExpression voidToken=%1") .arg(loc(el->voidToken))); return true; } void endVisit(AST::VoidExpression *) override { stop(u"VoidExpression"); } bool visit(AST::TypeOfExpression *el) override { start(QLatin1String("TypeOfExpression typeofToken=%1") .arg(loc(el->typeofToken))); return true; } void endVisit(AST::TypeOfExpression *) override { stop(u"TypeOfExpression"); } bool visit(AST::PreIncrementExpression *el) override { start(QLatin1String("PreIncrementExpression incrementToken=%1") .arg(loc(el->incrementToken))); return true; } void endVisit(AST::PreIncrementExpression *) override { stop(u"PreIncrementExpression"); } bool visit(AST::PreDecrementExpression *el) override { start(QLatin1String("PreDecrementExpression decrementToken=%1") .arg(loc(el->decrementToken))); return true; } void endVisit(AST::PreDecrementExpression *) override { stop(u"PreDecrementExpression"); } bool visit(AST::UnaryPlusExpression *el) override { start(QLatin1String("UnaryPlusExpression plusToken=%1") .arg(loc(el->plusToken))); return true; } void endVisit(AST::UnaryPlusExpression *) override { stop(u"UnaryPlusExpression"); } bool visit(AST::UnaryMinusExpression *el) override { start(QLatin1String("UnaryMinusExpression minusToken=%1") .arg(loc(el->minusToken))); return true; } void endVisit(AST::UnaryMinusExpression *) override { stop(u"UnaryMinusExpression"); } bool visit(AST::TildeExpression *el) override { start(QLatin1String("TildeExpression tildeToken=%1") .arg(loc(el->tildeToken))); return true; } void endVisit(AST::TildeExpression *) override { stop(u"TildeExpression"); } bool visit(AST::NotExpression *el) override { start(QLatin1String("NotExpression notToken=%1") .arg(loc(el->notToken))); return true; } void endVisit(AST::NotExpression *) override { stop(u"NotExpression"); } bool visit(AST::BinaryExpression *el) override { start(QLatin1String("BinaryExpression op=%1 operatorToken=%2") .arg(quotedString(QString::number(el->op,16)), loc(el->operatorToken))); return true; } void endVisit(AST::BinaryExpression *) override { stop(u"BinaryExpression"); } bool visit(AST::ConditionalExpression *el) override { start(QLatin1String("ConditionalExpression questionToken=%1 colonToken=%2") .arg(loc(el->questionToken), loc(el->colonToken))); return true; } void endVisit(AST::ConditionalExpression *) override { stop(u"ConditionalExpression"); } bool visit(AST::Expression *el) override { start(QLatin1String("Expression commaToken=%1") .arg(loc(el->commaToken))); return true; } void endVisit(AST::Expression *) override { stop(u"Expression"); } bool visit(AST::Block *el) override { start(QLatin1String("Block lbraceToken=%1 rbraceToken=%2") .arg(loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::Block *) override { stop(u"Block"); } bool visit(AST::StatementList *) override { start(u"StatementList"); return true; } void endVisit(AST::StatementList *) override { stop(u"StatementList"); } bool visit(AST::VariableStatement *el) override { start(QLatin1String("VariableStatement declarationKindToken=%1") .arg(loc(el->declarationKindToken))); return true; } void endVisit(AST::VariableStatement *) override { stop(u"VariableStatement"); } bool visit(AST::VariableDeclarationList *el) override { start(QLatin1String("VariableDeclarationList commaToken=%1") .arg(loc(el->commaToken))); return true; } void endVisit(AST::VariableDeclarationList *) override { stop(u"VariableDeclarationList"); } bool visit(AST::EmptyStatement *el) override { start(QLatin1String("EmptyStatement%1").arg(semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::EmptyStatement *) override { stop(u"EmptyStatement"); } bool visit(AST::ExpressionStatement *el) override { if (options & AstDumperOption::SloppyCompare) start(u"ExpressionStatement"); else start(QLatin1String("ExpressionStatement%1").arg(semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::ExpressionStatement *) override { stop(u"ExpressionStatement"); } bool visit(AST::IfStatement *el) override { start(QLatin1String("IfStatement ifToken=%1 lparenToken=%2 rparenToken=%3 elseToken=%4") .arg(loc(el->ifToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->elseToken))); return true; } void endVisit(AST::IfStatement *) override { stop(u"IfStatement"); } bool visit(AST::DoWhileStatement *el) override { start(QLatin1String( "DoWhileStatement doToken=%1 whileToken=%2 lparenToken=%3 rparenToken=%4%5") .arg(loc(el->doToken), loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::DoWhileStatement *) override { stop(u"DoWhileStatement"); } bool visit(AST::WhileStatement *el) override { start(QLatin1String("WhileStatement whileToken=%1 lparenToken=%2 rparenToken=%3") .arg(loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::WhileStatement *) override { stop(u"WhileStatement"); } bool visit(AST::ForStatement *el) override { if (options & AstDumperOption::SloppyCompare) start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 rparenToken=%5") .arg(loc(el->forToken), loc(el->lparenToken), loc(el->rparenToken))); else start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 firstSemicolonToken=%3 " "secondSemicolonToken=%4 rparenToken=%5") .arg(loc(el->forToken), loc(el->lparenToken), loc(el->firstSemicolonToken), loc(el->secondSemicolonToken), loc(el->rparenToken))); return true; } void endVisit(AST::ForStatement *) override { stop(u"ForStatement"); } bool visit(AST::ForEachStatement *el) override { start(QLatin1String("ForEachStatement forToken=%1 lparenToken=%2 inOfToken=%3 rparenToken=%4 type=%5") .arg(loc(el->forToken), loc(el->lparenToken), loc(el->inOfToken), loc(el->rparenToken), quotedString(QString::number(static_cast(el->type), 16)))); return true; } void endVisit(AST::ForEachStatement *) override { stop(u"ForEachStatement"); } bool visit(AST::ContinueStatement *el) override { start(QLatin1String("ContinueStatement label=%1 continueToken=%2 identifierToken=%3%4") .arg(quotedString(el->label), loc(el->continueToken), loc(el->identifierToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::ContinueStatement *) override { stop(u"ContinueStatement"); } bool visit(AST::BreakStatement *el) override { start(QLatin1String("BreakStatement label=%1 breakToken=%2 identifierToken=%3%4") .arg(quotedString(el->label), loc(el->breakToken), loc(el->identifierToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::BreakStatement *) override { stop(u"BreakStatement"); } bool visit(AST::ReturnStatement *el) override { start(QLatin1String("ReturnStatement returnToken=%1%2") .arg(loc(el->returnToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::ReturnStatement *) override { stop(u"ReturnStatement"); } bool visit(AST::YieldExpression *el) override { start(QLatin1String("YieldExpression isYieldStar=%1 yieldToken=%2") .arg(boolStr(el->isYieldStar), loc(el->yieldToken))); return true; } void endVisit(AST::YieldExpression *) override { stop(u"YieldExpression"); } bool visit(AST::WithStatement *el) override { start(QLatin1String("WithStatement withToken=%1 lparenToken=%2 rparenToken=%3") .arg(loc(el->withToken), loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::WithStatement *) override { stop(u"WithStatement"); } bool visit(AST::SwitchStatement *el) override { start(QLatin1String("SwitchStatement switchToken=%1 lparenToken=%2 rparenToken=%3") .arg(loc(el->switchToken), loc(el->lparenToken), loc(el->rparenToken))); return true; } void endVisit(AST::SwitchStatement *) override { stop(u"SwitchStatement"); } bool visit(AST::CaseBlock *el) override { start(QLatin1String("CaseBlock lbraceToken=%1 rbraceToken=%2") .arg(loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::CaseBlock *) override { stop(u"CaseBlock"); } bool visit(AST::CaseClauses *) override { start(u"CaseClauses"); return true; } void endVisit(AST::CaseClauses *) override { stop(u"CaseClauses"); } bool visit(AST::CaseClause *el) override { start(QLatin1String("CaseClause caseToken=%1 colonToken=%2") .arg(loc(el->caseToken), loc(el->colonToken))); return true; } void endVisit(AST::CaseClause *) override { stop(u"CaseClause"); } bool visit(AST::DefaultClause *el) override { start(QLatin1String("DefaultClause defaultToken=%1 colonToken=%2") .arg(loc(el->defaultToken), loc(el->colonToken))); return true; } void endVisit(AST::DefaultClause *) override { stop(u"DefaultClause"); } bool visit(AST::LabelledStatement *el) override { start(QLatin1String("LabelledStatement label=%1 identifierToken=%2 colonToken=%3") .arg(quotedString(el->label), loc(el->identifierToken), loc(el->colonToken))); return true; } void endVisit(AST::LabelledStatement *) override { stop(u"LabelledStatement"); } bool visit(AST::ThrowStatement *el) override { start(QLatin1String("ThrowStatement throwToken=%1%2") .arg(loc(el->throwToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::ThrowStatement *) override { stop(u"ThrowStatement"); } bool visit(AST::TryStatement *el) override { start(QLatin1String("TryStatement tryToken=%1") .arg(loc(el->tryToken))); return true; } void endVisit(AST::TryStatement *) override { stop(u"TryStatement"); } bool visit(AST::Catch *el) override { start(QLatin1String("Catch catchToken=%1 lparenToken=%2 identifierToken=%3 rparenToken=%4") .arg(loc(el->catchToken), loc(el->lparenToken), loc(el->identifierToken), loc(el->rparenToken))); return true; } void endVisit(AST::Catch *) override { stop(u"Catch"); } bool visit(AST::Finally *el) override { start(QLatin1String("Finally finallyToken=%1") .arg(loc(el->finallyToken))); return true; } void endVisit(AST::Finally *) override { stop(u"Finally"); } bool visit(AST::FunctionDeclaration *el) override { start(QLatin1String("FunctionDeclaration name=%1 isArrowFunction=%2 isGenerator=%3 functionToken=%4 " "identifierToken=%5 lparenToken=%6 rparenToken=%7 lbraceToken=%8 rbraceToken=%9") .arg(quotedString(el->name), boolStr(el->isArrowFunction), boolStr(el->isGenerator), loc(el->functionToken, options & AstDumperOption::SloppyCompare), loc(el->identifierToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::FunctionDeclaration *) override { stop(u"FunctionDeclaration"); } bool visit(AST::FunctionExpression *el) override { start(QLatin1String("FunctionExpression name=%1 isArrowFunction=%2 isGenerator=%3 " "functionToken=%4 " "identifierToken=%5 lparenToken=%6 rparenToken=%7 lbraceToken=%8 " "rbraceToken=%9") .arg(quotedString(el->name), boolStr(el->isArrowFunction), boolStr(el->isGenerator), loc(el->functionToken, options & AstDumperOption::SloppyCompare), loc(el->identifierToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::FunctionExpression *) override { stop(u"FunctionExpression"); } bool visit(AST::FormalParameterList *) override { start(u"FormalParameterList"); return true; } void endVisit(AST::FormalParameterList *) override { stop(u"FormalParameterList"); } bool visit(AST::ClassExpression *el) override { start(QLatin1String("ClassExpression name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5") .arg(quotedString(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::ClassExpression *) override { stop(u"ClassExpression"); } bool visit(AST::ClassDeclaration *el) override { start(QLatin1String("ClassDeclaration name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5") .arg(quotedString(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken))); return true; } void endVisit(AST::ClassDeclaration *) override { stop(u"ClassDeclaration"); } bool visit(AST::ClassElementList *el) override { start(QLatin1String("ClassElementList isStatic=%1") .arg(boolStr(el->isStatic))); return true; } void endVisit(AST::ClassElementList *) override { stop(u"ClassElementList"); } bool visit(AST::Program *) override { start(u"Program"); return true; } void endVisit(AST::Program *) override { stop(u"Program"); } bool visit(AST::NameSpaceImport *el) override { start(QLatin1String("NameSpaceImport starToken=%1 importedBindingToken=%2 importedBinding=%3") .arg(loc(el->starToken), loc(el->importedBindingToken), quotedString(el->importedBinding))); return true; } void endVisit(AST::NameSpaceImport *) override { stop(u"NameSpaceImport"); } bool visit(AST::ImportSpecifier *el) override { start(QLatin1String("ImportSpecifier identifierToken=%1 importedBindingToken=%2 identifier=%3 importedBinding=%4") .arg(loc(el->identifierToken), loc(el->importedBindingToken), quotedString(el->identifier), quotedString(el->importedBinding))); return true; } void endVisit(AST::ImportSpecifier *) override { stop(u"ImportSpecifier"); } bool visit(AST::ImportsList *el) override { start(QLatin1String("ImportsList importSpecifierToken=%1") .arg(loc(el->importSpecifierToken))); return true; } void endVisit(AST::ImportsList *) override { stop(u"ImportsList"); } bool visit(AST::NamedImports *el) override { start(QLatin1String("NamedImports leftBraceToken=%1 rightBraceToken=%2") .arg(loc(el->leftBraceToken), loc(el->rightBraceToken))); return true; } void endVisit(AST::NamedImports *) override { stop(u"NamedImports"); } bool visit(AST::FromClause *el) override { start(QLatin1String("FromClause fromToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3") .arg(loc(el->fromToken), loc(el->moduleSpecifierToken), quotedString(el->moduleSpecifier))); return true; } void endVisit(AST::FromClause *) override { stop(u"FromClause"); } bool visit(AST::ImportClause *el) override { start(QLatin1String("ImportClause importedDefaultBindingToken=%1 importedDefaultBinding=%2") .arg(loc(el->importedDefaultBindingToken), quotedString(el->importedDefaultBinding))); return true; } void endVisit(AST::ImportClause *) override { stop(u"ImportClause"); } bool visit(AST::ImportDeclaration *el) override { start(QLatin1String("ImportDeclaration importToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3") .arg(loc(el->importToken), loc(el->moduleSpecifierToken), quotedString(el->moduleSpecifier))); return true; } void endVisit(AST::ImportDeclaration *) override { stop(u"ImportDeclaration"); } bool visit(AST::ExportSpecifier *el) override { start(QLatin1String("ExportSpecifier identifierToken=%1 exportedIdentifierToken=%2 identifier=%3 exportedIdentifier=%4") .arg(loc(el->identifierToken), loc(el->exportedIdentifierToken), quotedString(el->identifier), quotedString(el->exportedIdentifier))); return true; } void endVisit(AST::ExportSpecifier *) override { stop(u"ExportSpecifier"); } bool visit(AST::ExportsList *) override { start(u"ExportsList"); return true; } void endVisit(AST::ExportsList *) override { stop(u"ExportsList"); } bool visit(AST::ExportClause *el) override { start(QLatin1String("ExportClause leftBraceToken=%1 rightBraceToken=%2") .arg(loc(el->leftBraceToken), loc(el->rightBraceToken))); return true; } void endVisit(AST::ExportClause *) override { stop(u"ExportClause"); } bool visit(AST::ExportDeclaration *el) override { start(QLatin1String("ExportDeclaration exportToken=%1 exportDefault=%3") .arg(loc(el->exportToken), boolStr(el->exportDefault))); return true; } void endVisit(AST::ExportDeclaration *) override { stop(u"ExportDeclaration"); } bool visit(AST::ESModule *) override { start(u"ESModule"); return true; } void endVisit(AST::ESModule *) override { stop(u"ESModule"); } bool visit(AST::DebuggerStatement *el) override { start(QLatin1String("DebuggerStatement debuggerToken=%1%2") .arg(loc(el->debuggerToken), semicolonToken(el->semicolonToken))); return true; } void endVisit(AST::DebuggerStatement *) override { stop(u"DebuggerStatement"); } bool visit(AST::Type *) override { start(u"Type"); return true; } void endVisit(AST::Type *) override { stop(u"Type"); } bool visit(AST::TypeArgumentList *) override { start(u"TypeArgumentList"); return true; } void endVisit(AST::TypeArgumentList *) override { stop(u"TypeArgumentList"); } bool visit(AST::TypeAnnotation *el) override { start(QLatin1String("TypeAnnotation colonToken=%1") .arg(loc(el->colonToken))); return true; } void endVisit(AST::TypeAnnotation *) override { stop(u"TypeAnnotation"); } void throwRecursionDepthError() override { qCWarning(domLog) << "Maximum statement or expression depth exceeded in AstDumper"; } private: // attributes std::function dumper; AstDumperOptions options = AstDumperOption::None; int indent = 0; int baseIndent = 0; function_ref loc2str; bool dumpNode(){ return options & AstDumperOption::DumpNode; } bool noLocations() { return options & AstDumperOption::NoLocations; } bool noAnnotations() { return options & AstDumperOption::NoAnnotations; } }; QDebug operator<<(QDebug d, AST::Node *n) { QDebug noQuote = d.noquote().nospace(); AstDumper visitor([&noQuote](QStringView s){ noQuote << s; }); Node::accept(n, &visitor); return d; } QString lineDiff(QString s1, QString s2, int nContext) { QTextStream d1(&s1), d2(&s2); QList preLines(nContext); int nLine = 0; bool same = true; QString l1, l2; while (same && !d1.atEnd() && !d2.atEnd()) { l1=d1.readLine(); l2=d2.readLine(); if (l1 == l2) preLines[nLine++ % nContext] = l1; else same = false; } QString res; QTextStream ss(&res); if (!same || !d1.atEnd() || !d2.atEnd()) { for (int iline = qMin(nLine, nContext); iline > 0; --iline) { ss << QLatin1String(" ") << preLines[(nLine - iline) % nContext] << QLatin1String("\n"); } int iline = 0; if (!same) { ss << QLatin1String("-") << l1 << QLatin1String("\n"); ++iline; } if (same && nContext == 0) nContext = 1; for (;iline < nContext && !d1.atEnd(); iline ++) { l1 = d1.readLine(); ss << QLatin1String("-") << l1 << QLatin1String("\n"); } iline = 0; if (!same) { ss << QLatin1String("+") << l2 << QLatin1String("\n"); ++iline; } for (;iline < nContext && !d2.atEnd(); iline ++) { l2 = d2.readLine(); ss << QLatin1String("+") << l2 << QLatin1String("\n"); } } return res; } QString astNodeDiff(AST::Node *n1, AST::Node *n2, int nContext, AstDumperOptions opt, int indent, function_refloc2str1, function_refloc2str2) { QString s1, s2; QTextStream d1(&s1), d2(&s2); AstDumper visitor1=AstDumper([&d1](QStringView s){ d1 << s; }, opt, indent, 0, loc2str1); AstDumper visitor2=AstDumper([&d2](QStringView s){ d2 << s; }, opt, indent, 0, loc2str2); Node::accept(n1, &visitor1); Node::accept(n2, &visitor2); d1.flush(); d2.flush(); return lineDiff(s1, s2, nContext); } void astNodeDumper(Sink s, Node *n, AstDumperOptions opt, int indent, int baseIndent, function_refloc2str) { AstDumper visitor=AstDumper(s, opt, indent, baseIndent, loc2str); Node::accept(n, &visitor); } QString astNodeDump(Node *n, AstDumperOptions opt, int indent, int baseIndent, function_refloc2str) { return dumperToString([n, opt, indent, baseIndent, loc2str](Sink s) { astNodeDumper(s, n, opt, indent, baseIndent, loc2str); }); } } // end namespace Dom } // end namespace QQmlJS QT_END_NAMESPACE