| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2019 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the tools applications of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
| 21 | ** included in the packaging of this file. Please review the following | 
| 22 | ** information to ensure the GNU General Public License requirements will | 
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 24 | ** | 
| 25 | ** $QT_END_LICENSE$ | 
| 26 | ** | 
| 27 | ****************************************************************************/ | 
| 28 |  | 
| 29 | #include "dumpastvisitor.h" | 
| 30 |  | 
| 31 | #include <QtQml/private/qqmljslexer_p.h> | 
| 32 |  | 
| 33 | DumpAstVisitor::(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *) | 
| 34 |     : m_engine(engine), m_comment(comment) | 
| 35 | { | 
| 36 |     // Add all completely orphaned comments | 
| 37 |     m_result += getOrphanedComments(node: nullptr); | 
| 38 |  | 
| 39 |     m_scope_properties.push(t: ScopeProperties {}); | 
| 40 |  | 
| 41 |     rootNode->accept(visitor: this); | 
| 42 |  | 
| 43 |     // We need to get rid of one new-line so our output doesn't append an empty line | 
| 44 |     m_result.chop(n: 1); | 
| 45 |  | 
| 46 |     // Remove trailing whitespace | 
| 47 |     QStringList lines = m_result.split(sep: "\n" ); | 
| 48 |     for (QString& line : lines) { | 
| 49 |         while (line.endsWith(s: " " )) | 
| 50 |             line.chop(n: 1); | 
| 51 |     } | 
| 52 |  | 
| 53 |     m_result = lines.join(sep: "\n" ); | 
| 54 | } | 
| 55 |  | 
| 56 | bool DumpAstVisitor::preVisit(Node *el) | 
| 57 | { | 
| 58 |     UiObjectMember *m = el->uiObjectMemberCast(); | 
| 59 |     if (m != 0) | 
| 60 |         Node::accept(node: m->annotations, visitor: this); | 
| 61 |     return true; | 
| 62 | } | 
| 63 |  | 
| 64 | static QString parseUiQualifiedId(UiQualifiedId *id) | 
| 65 | { | 
| 66 |     QString name = id->name.toString(); | 
| 67 |     for (auto *item = id->next; item != nullptr; item = item->next) { | 
| 68 |         name += "."  + item->name; | 
| 69 |     } | 
| 70 |  | 
| 71 |     return name; | 
| 72 | } | 
| 73 |  | 
| 74 | static QString operatorToString(int op) | 
| 75 | { | 
| 76 |     switch (op) | 
| 77 |     { | 
| 78 |     case QSOperator::Add: return "+" ; | 
| 79 |     case QSOperator::And: return "&&" ; | 
| 80 |     case QSOperator::InplaceAnd: return "&=" ; | 
| 81 |     case QSOperator::Assign: return "=" ; | 
| 82 |     case QSOperator::BitAnd: return "&" ; | 
| 83 |     case QSOperator::BitOr: return "|" ; | 
| 84 |     case QSOperator::BitXor: return "^" ; | 
| 85 |     case QSOperator::InplaceSub: return "-=" ; | 
| 86 |     case QSOperator::Div: return "/" ; | 
| 87 |     case QSOperator::InplaceDiv: return "/=" ; | 
| 88 |     case QSOperator::Equal: return "==" ; | 
| 89 |     case QSOperator::Exp: return "**" ; | 
| 90 |     case QSOperator::InplaceExp: return "**=" ; | 
| 91 |     case QSOperator::Ge: return ">=" ; | 
| 92 |     case QSOperator::Gt: return ">" ; | 
| 93 |     case QSOperator::In: return "in" ; | 
| 94 |     case QSOperator::InplaceAdd: return "+=" ; | 
| 95 |     case QSOperator::InstanceOf: return "instanceof" ; | 
| 96 |     case QSOperator::Le: return "<=" ; | 
| 97 |     case QSOperator::LShift: return "<<" ; | 
| 98 |     case QSOperator::InplaceLeftShift: return "<<=" ; | 
| 99 |     case QSOperator::Lt: return "<" ; | 
| 100 |     case QSOperator::Mod: return "%" ; | 
| 101 |     case QSOperator::InplaceMod: return "%=" ; | 
| 102 |     case QSOperator::Mul: return "*" ; | 
| 103 |     case QSOperator::InplaceMul: return "*=" ; | 
| 104 |     case QSOperator::NotEqual: return "!=" ; | 
| 105 |     case QSOperator::Or: return "||" ; | 
| 106 |     case QSOperator::InplaceOr: return "|=" ; | 
| 107 |     case QSOperator::RShift: return ">>" ; | 
| 108 |     case QSOperator::InplaceRightShift: return ">>=" ; | 
| 109 |     case QSOperator::StrictEqual: return "===" ; | 
| 110 |     case QSOperator::StrictNotEqual: return "!==" ; | 
| 111 |     case QSOperator::Sub: return "-" ; | 
| 112 |     case QSOperator::URShift: return ">>>" ; | 
| 113 |     case QSOperator::InplaceURightShift: return ">>>=" ; | 
| 114 |     case QSOperator::InplaceXor: return "^=" ; | 
| 115 |     case QSOperator::As: return "as" ; | 
| 116 |     case QSOperator::Coalesce: return "??" ; | 
| 117 |     case QSOperator::Invalid: | 
| 118 |     default: | 
| 119 |         return "INVALID" ; | 
| 120 |     } | 
| 121 | } | 
| 122 |  | 
| 123 | QString DumpAstVisitor::(const Comment &) const | 
| 124 | { | 
| 125 |     QString result; | 
| 126 |  | 
| 127 |     bool  = comment.isMultiline() && !comment.isSyntheticMultiline(); | 
| 128 |  | 
| 129 |     if (useMultilineComment) | 
| 130 |         result += "/*" ; | 
| 131 |     else | 
| 132 |         result += "//" ; | 
| 133 |  | 
| 134 |     result += comment.m_text; | 
| 135 |  | 
| 136 |     if (comment.isSyntheticMultiline()) | 
| 137 |         result = result.replace(before: "\n" ,after: "\n"  + formatLine(line: "//" , newline: false)); | 
| 138 |  | 
| 139 |     if (comment.m_location == Comment::Location::Back_Inline) | 
| 140 |         result.prepend(s: " " ); | 
| 141 |  | 
| 142 |     if (useMultilineComment) | 
| 143 |         result += "*/" ; | 
| 144 |  | 
| 145 |     return result; | 
| 146 | } | 
| 147 |  | 
| 148 | QString DumpAstVisitor::(Node *node, Comment::Location location) const | 
| 149 | { | 
| 150 |     const auto&  = m_comment->attachedComments(); | 
| 151 |     if (!comments.contains(akey: node)) | 
| 152 |         return "" ; | 
| 153 |  | 
| 154 |     auto  = comments[node]; | 
| 155 |  | 
| 156 |     if (comment.m_location != location) | 
| 157 |         return "" ; | 
| 158 |  | 
| 159 |     return formatComment(comment); | 
| 160 | } | 
| 161 |  | 
| 162 | QString DumpAstVisitor::(SourceLocation srcLocation, | 
| 163 |                                            Comment::Location location) const { | 
| 164 |     const auto&  = m_comment->listComments(); | 
| 165 |  | 
| 166 |     if (!comments.contains(akey: srcLocation.begin())) | 
| 167 |         return "" ; | 
| 168 |  | 
| 169 |     auto  = comments[srcLocation.begin()]; | 
| 170 |  | 
| 171 |     if (comment.m_location != location) | 
| 172 |         return "" ; | 
| 173 |  | 
| 174 |     return formatComment(comment); | 
| 175 | } | 
| 176 |  | 
| 177 | QString DumpAstVisitor::(Node *node) const { | 
| 178 |     const auto& orphans = m_comment->orphanComments()[node]; | 
| 179 |  | 
| 180 |     if (orphans.size() == 0) | 
| 181 |         return "" ; | 
| 182 |  | 
| 183 |     QString result = "" ; | 
| 184 |  | 
| 185 |     for (const Comment& orphan : orphans) { | 
| 186 |         result += formatLine(line: formatComment(comment: orphan)); | 
| 187 |     } | 
| 188 |  | 
| 189 |     result += "\n" ; | 
| 190 |  | 
| 191 |     return result; | 
| 192 | } | 
| 193 |  | 
| 194 | QString DumpAstVisitor::parseArgumentList(ArgumentList *list) | 
| 195 | { | 
| 196 |     QString result = "" ; | 
| 197 |  | 
| 198 |     for (auto *item = list; item != nullptr; item = item->next) | 
| 199 |         result += parseExpression(expression: item->expression) + (item->next != nullptr ? ", "  : "" ); | 
| 200 |  | 
| 201 |     return result; | 
| 202 | } | 
| 203 |  | 
| 204 | QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) { | 
| 205 |     QString result = "" ; | 
| 206 |  | 
| 207 |     for (auto *item = list; item != nullptr; item = item->next) | 
| 208 |         result += parseUiQualifiedId(id: item->type) + " "  + item->name + (item->next != nullptr ? ", "  : "" ); | 
| 209 |  | 
| 210 |     return result; | 
| 211 | } | 
| 212 |  | 
| 213 | QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope) | 
| 214 | { | 
| 215 |     switch (element->type) | 
| 216 |     { | 
| 217 |     case PatternElement::Literal: | 
| 218 |         return parseExpression(expression: element->initializer); | 
| 219 |     case PatternElement::Binding: { | 
| 220 |         QString result = "" ; | 
| 221 |         QString expr = parseExpression(expression: element->initializer); | 
| 222 |  | 
| 223 |         if (scope) { | 
| 224 |             switch (element->scope) { | 
| 225 |                 case VariableScope::NoScope: | 
| 226 |                 break; | 
| 227 |             case VariableScope::Let: | 
| 228 |                 result = "let " ; | 
| 229 |                 break; | 
| 230 |             case VariableScope::Const: | 
| 231 |                 result = "const " ; | 
| 232 |                 break; | 
| 233 |             case VariableScope::Var: | 
| 234 |                 result = "var " ; | 
| 235 |                 break; | 
| 236 |             } | 
| 237 |         } | 
| 238 |  | 
| 239 |         if (element->bindingIdentifier.isEmpty()) | 
| 240 |             result += parseExpression(expression: element->bindingTarget); | 
| 241 |         else | 
| 242 |             result += element->bindingIdentifier.toString(); | 
| 243 |  | 
| 244 |         if (element->typeAnnotation != nullptr) | 
| 245 |             result += ": "  + parseType(type: element->typeAnnotation->type); | 
| 246 |  | 
| 247 |         if (!expr.isEmpty()) | 
| 248 |             result += " = " +expr; | 
| 249 |  | 
| 250 |         return result; | 
| 251 |     } | 
| 252 |     default: | 
| 253 |         m_error = true; | 
| 254 |         return "pe_unknown" ; | 
| 255 |     } | 
| 256 | } | 
| 257 |  | 
| 258 | QString escapeString(QString string) | 
| 259 | { | 
| 260 |     // Handle escape sequences | 
| 261 |     string = string.replace(before: "\r" , after: "\\r" ).replace(before: "\n" , after: "\\n" ).replace(before: "\t" , after: "\\t" ) | 
| 262 |             .replace(before: "\b" ,after: "\\b" ).replace(before: "\v" , after: "\\v" ).replace(before: "\f" , after: "\\f" ); | 
| 263 |  | 
| 264 |     // Escape backslash | 
| 265 |     string = string.replace(before: "\\" , after: "\\\\" ); | 
| 266 |  | 
| 267 |     // Escape " | 
| 268 |     string = string.replace(before: "\"" , after: "\\\"" ); | 
| 269 |  | 
| 270 |     return "\""  + string + "\"" ; | 
| 271 | } | 
| 272 |  | 
| 273 | QString DumpAstVisitor::parsePatternElementList(PatternElementList *list) | 
| 274 | { | 
| 275 |     QString result = "" ; | 
| 276 |  | 
| 277 |     for (auto *item = list; item != nullptr; item = item->next) | 
| 278 |         result += parsePatternElement(element: item->element) + (item->next != nullptr ? ", "  : "" ); | 
| 279 |  | 
| 280 |     return result; | 
| 281 | } | 
| 282 |  | 
| 283 | QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list) | 
| 284 | { | 
| 285 |     QString result = "" ; | 
| 286 |  | 
| 287 |     for (auto *item = list; item != nullptr; item = item->next) | 
| 288 |         result += parsePatternElement(element: item->element) + (item->next != nullptr ? ", "  : "" ); | 
| 289 |  | 
| 290 |     return result; | 
| 291 | } | 
| 292 |  | 
| 293 | QString DumpAstVisitor::parsePatternProperty(PatternProperty *property) | 
| 294 | { | 
| 295 |     switch (property->type) { | 
| 296 |     case PatternElement::Getter: | 
| 297 |         return "get " +parseFunctionExpression(expression: cast<FunctionExpression *>(ast: property->initializer), omitFunction: true); | 
| 298 |     case PatternElement::Setter: | 
| 299 |         return "set " +parseFunctionExpression(expression: cast<FunctionExpression *>(ast: property->initializer), omitFunction: true); | 
| 300 |     default: | 
| 301 |         if (property->name->kind == Node::Kind_ComputedPropertyName) { | 
| 302 |             return "[" +parseExpression(expression: cast<ComputedPropertyName *>(ast: property->name)->expression)+"]: " +parsePatternElement(element: property, scope: false); | 
| 303 |         } else { | 
| 304 |             return escapeString(string: property->name->asString())+": " +parsePatternElement(element: property, scope: false); | 
| 305 |         } | 
| 306 |     } | 
| 307 | } | 
| 308 |  | 
| 309 | QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list) | 
| 310 | { | 
| 311 |     QString result = "" ; | 
| 312 |  | 
| 313 |     for (auto *item = list; item != nullptr; item = item->next) { | 
| 314 |         result += formatLine(line: parsePatternProperty(property: item->property) + (item->next != nullptr ? ","  : "" )); | 
| 315 |     } | 
| 316 |  | 
| 317 |     return result; | 
| 318 | } | 
| 319 |  | 
| 320 | QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction) | 
| 321 | { | 
| 322 |     m_indentLevel++; | 
| 323 |     QString result; | 
| 324 |  | 
| 325 |     if (!functExpr->isArrowFunction) { | 
| 326 |         result += omitFunction ? ""  : "function" ; | 
| 327 |  | 
| 328 |         if (functExpr->isGenerator) | 
| 329 |             result += "*" ; | 
| 330 |  | 
| 331 |         if (!functExpr->name.isEmpty()) | 
| 332 |             result += (omitFunction ? ""  : " " ) + functExpr->name; | 
| 333 |  | 
| 334 |         result += "(" +parseFormalParameterList(list: functExpr->formals)+")" ; | 
| 335 |  | 
| 336 |         if (functExpr->typeAnnotation != nullptr) | 
| 337 |             result += " : "  + parseType(type: functExpr->typeAnnotation->type); | 
| 338 |  | 
| 339 |         result += " {\n"  + parseStatementList(list: functExpr->body); | 
| 340 |     } else { | 
| 341 |          result += "(" +parseFormalParameterList(list: functExpr->formals)+")" ; | 
| 342 |  | 
| 343 |          if (functExpr->typeAnnotation != nullptr) | 
| 344 |              result += " : "  + parseType(type: functExpr->typeAnnotation->type); | 
| 345 |  | 
| 346 |          result += " => {\n"  + parseStatementList(list: functExpr->body); | 
| 347 |     } | 
| 348 |  | 
| 349 |     m_indentLevel--; | 
| 350 |  | 
| 351 |     result += formatLine(line: "}" , newline: false); | 
| 352 |  | 
| 353 |     return result; | 
| 354 |  | 
| 355 | } | 
| 356 |  | 
| 357 | QString DumpAstVisitor::parseType(Type *type) { | 
| 358 |     QString result = parseUiQualifiedId(id: type->typeId); | 
| 359 |  | 
| 360 |     if (type->typeArguments != nullptr) { | 
| 361 |         TypeArgumentList *list = cast<TypeArgumentList *>(ast: type->typeArguments); | 
| 362 |  | 
| 363 |         result += "<" ; | 
| 364 |  | 
| 365 |         for (auto *item = list; item != nullptr; item = item->next) { | 
| 366 |             result += parseType(type: item->typeId) + (item->next != nullptr ? ", "  : "" ); | 
| 367 |         } | 
| 368 |  | 
| 369 |         result += ">" ; | 
| 370 |     } | 
| 371 |  | 
| 372 |     return result; | 
| 373 | } | 
| 374 |  | 
| 375 | QString DumpAstVisitor::parseExpression(ExpressionNode *expression) | 
| 376 | { | 
| 377 |     if (expression == nullptr) | 
| 378 |         return "" ; | 
| 379 |  | 
| 380 |     switch (expression->kind) | 
| 381 |     { | 
| 382 |     case Node::Kind_ArrayPattern: | 
| 383 |         return "[" +parsePatternElementList(list: cast<ArrayPattern *>(ast: expression)->elements)+"]" ; | 
| 384 |     case Node::Kind_IdentifierExpression: | 
| 385 |         return cast<IdentifierExpression*>(ast: expression)->name.toString(); | 
| 386 |     case Node::Kind_FieldMemberExpression: { | 
| 387 |         auto *fieldMemberExpr = cast<FieldMemberExpression *>(ast: expression); | 
| 388 |         QString result = parseExpression(expression: fieldMemberExpr->base); | 
| 389 |  | 
| 390 |         // If we're operating on a numeric literal, always put it in braces | 
| 391 |         if (fieldMemberExpr->base->kind == Node::Kind_NumericLiteral) | 
| 392 |             result = "("  + result + ")" ; | 
| 393 |  | 
| 394 |         result += "."  + fieldMemberExpr->name.toString(); | 
| 395 |  | 
| 396 |         return result; | 
| 397 |     } | 
| 398 |     case Node::Kind_ArrayMemberExpression: { | 
| 399 |         auto *arrayMemberExpr = cast<ArrayMemberExpression *>(ast: expression); | 
| 400 |         return parseExpression(expression: arrayMemberExpr->base) | 
| 401 |                 + "["  + parseExpression(expression: arrayMemberExpr->expression) + "]" ; | 
| 402 |     } | 
| 403 |     case Node::Kind_NestedExpression: | 
| 404 |         return "(" +parseExpression(expression: cast<NestedExpression *>(ast: expression)->expression)+")" ; | 
| 405 |     case Node::Kind_TrueLiteral: | 
| 406 |         return "true" ; | 
| 407 |     case Node::Kind_FalseLiteral: | 
| 408 |         return "false" ; | 
| 409 |     case Node::Kind_FunctionExpression: | 
| 410 |     { | 
| 411 |         auto *functExpr = cast<FunctionExpression *>(ast: expression); | 
| 412 |         return parseFunctionExpression(functExpr); | 
| 413 |     } | 
| 414 |     case Node::Kind_NullExpression: | 
| 415 |         return "null" ; | 
| 416 |     case Node::Kind_ThisExpression: | 
| 417 |         return "this" ; | 
| 418 |     case Node::Kind_PostIncrementExpression: | 
| 419 |         return parseExpression(expression: cast<PostIncrementExpression *>(ast: expression)->base)+"++" ; | 
| 420 |     case Node::Kind_PreIncrementExpression: | 
| 421 |         return "++" +parseExpression(expression: cast<PreIncrementExpression *>(ast: expression)->expression); | 
| 422 |     case Node::Kind_PostDecrementExpression: | 
| 423 |         return parseExpression(expression: cast<PostDecrementExpression *>(ast: expression)->base)+"--" ; | 
| 424 |     case Node::Kind_PreDecrementExpression: | 
| 425 |         return "--" +parseExpression(expression: cast<PreDecrementExpression *>(ast: expression)->expression); | 
| 426 |     case Node::Kind_NumericLiteral: | 
| 427 |         return QString::number(cast<NumericLiteral *>(ast: expression)->value); | 
| 428 |     case Node::Kind_TemplateLiteral: { | 
| 429 |         auto firstSrcLoc = cast<TemplateLiteral *>(ast: expression)->firstSourceLocation(); | 
| 430 |         auto lastSrcLoc = cast<TemplateLiteral *>(ast: expression)->lastSourceLocation(); | 
| 431 |         return m_engine->code().mid(position: static_cast<int>(firstSrcLoc.begin()), | 
| 432 |                                     n: static_cast<int>(lastSrcLoc.end() - firstSrcLoc.begin())); | 
| 433 |     } | 
| 434 |     case Node::Kind_StringLiteral: { | 
| 435 |         auto srcLoc = cast<StringLiteral *>(ast: expression)->firstSourceLocation(); | 
| 436 |         return m_engine->code().mid(position: static_cast<int>(srcLoc.begin()), | 
| 437 |                                     n: static_cast<int>(srcLoc.end() - srcLoc.begin())); | 
| 438 |     } | 
| 439 |     case Node::Kind_BinaryExpression: { | 
| 440 |         auto *binExpr = expression->binaryExpressionCast(); | 
| 441 |         return parseExpression(expression: binExpr->left) + " "  + operatorToString(op: binExpr->op) | 
| 442 |                 + " "  + parseExpression(expression: binExpr->right); | 
| 443 |     } | 
| 444 |     case Node::Kind_CallExpression: { | 
| 445 |         auto *callExpr = cast<CallExpression *>(ast: expression); | 
| 446 |  | 
| 447 |         return parseExpression(expression: callExpr->base) + "("  + parseArgumentList(list: callExpr->arguments) + ")" ; | 
| 448 |     } | 
| 449 |     case Node::Kind_NewExpression: | 
| 450 |         return "new " +parseExpression(expression: cast<NewExpression *>(ast: expression)->expression); | 
| 451 |     case Node::Kind_NewMemberExpression: { | 
| 452 |         auto *newMemberExpression = cast<NewMemberExpression *>(ast: expression); | 
| 453 |         return "new " +parseExpression(expression: newMemberExpression->base) | 
| 454 |                 + "("  +parseArgumentList(list: newMemberExpression->arguments)+")" ; | 
| 455 |     } | 
| 456 |     case Node::Kind_DeleteExpression: | 
| 457 |         return "delete "  + parseExpression(expression: cast<DeleteExpression *>(ast: expression)->expression); | 
| 458 |     case Node::Kind_VoidExpression: | 
| 459 |         return "void "  + parseExpression(expression: cast<VoidExpression *>(ast: expression)->expression); | 
| 460 |     case Node::Kind_TypeOfExpression: | 
| 461 |         return "typeof "  + parseExpression(expression: cast<TypeOfExpression *>(ast: expression)->expression); | 
| 462 |     case Node::Kind_UnaryPlusExpression: | 
| 463 |         return "+"  + parseExpression(expression: cast<UnaryPlusExpression *>(ast: expression)->expression); | 
| 464 |     case Node::Kind_UnaryMinusExpression: | 
| 465 |         return "-"  + parseExpression(expression: cast<UnaryMinusExpression *>(ast: expression)->expression); | 
| 466 |     case Node::Kind_NotExpression: | 
| 467 |         return "!"  + parseExpression(expression: cast<NotExpression *>(ast: expression)->expression); | 
| 468 |     case Node::Kind_TildeExpression: | 
| 469 |         return "~"  + parseExpression(expression: cast<TildeExpression *>(ast: expression)->expression); | 
| 470 |     case Node::Kind_ConditionalExpression: { | 
| 471 |         auto *condExpr = cast<ConditionalExpression *>(ast: expression); | 
| 472 |  | 
| 473 |         QString result = "" ; | 
| 474 |  | 
| 475 |         result += parseExpression(expression: condExpr->expression) + " ? " ; | 
| 476 |         result += parseExpression(expression: condExpr->ok) + " : " ; | 
| 477 |         result += parseExpression(expression: condExpr->ko); | 
| 478 |  | 
| 479 |         return result; | 
| 480 |     } | 
| 481 |     case Node::Kind_YieldExpression: { | 
| 482 |         auto *yieldExpr = cast<YieldExpression*>(ast: expression); | 
| 483 |  | 
| 484 |         QString result = "yield" ; | 
| 485 |  | 
| 486 |         if (yieldExpr->isYieldStar) | 
| 487 |             result += "*" ; | 
| 488 |  | 
| 489 |         if (yieldExpr->expression) | 
| 490 |             result += " "  + parseExpression(expression: yieldExpr->expression); | 
| 491 |  | 
| 492 |         return result; | 
| 493 |     } | 
| 494 |     case Node::Kind_ObjectPattern: { | 
| 495 |         auto *objectPattern = cast<ObjectPattern*>(ast: expression); | 
| 496 |         QString result = "{\n" ; | 
| 497 |  | 
| 498 |         m_indentLevel++; | 
| 499 |         result += parsePatternPropertyList(list: objectPattern->properties); | 
| 500 |         m_indentLevel--; | 
| 501 |  | 
| 502 |         result += formatLine(line: "}" , newline: false); | 
| 503 |  | 
| 504 |         return result; | 
| 505 |     } | 
| 506 |     case Node::Kind_Expression: { | 
| 507 |         auto* expr = cast<Expression*>(ast: expression); | 
| 508 |         return parseExpression(expression: expr->left)+", " +parseExpression(expression: expr->right); | 
| 509 |     } | 
| 510 |     case Node::Kind_Type: { | 
| 511 |         auto* type = reinterpret_cast<Type*>(expression); | 
| 512 |         return parseType(type); | 
| 513 |     } | 
| 514 |     case Node::Kind_RegExpLiteral: { | 
| 515 |         auto* regexpLiteral = cast<RegExpLiteral*>(ast: expression); | 
| 516 |         QString result = "/" +regexpLiteral->pattern+"/" ; | 
| 517 |  | 
| 518 |         if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Unicode) | 
| 519 |             result += "u" ; | 
| 520 |         if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Global) | 
| 521 |             result += "g" ; | 
| 522 |         if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Multiline) | 
| 523 |             result += "m" ; | 
| 524 |         if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Sticky) | 
| 525 |             result += "y" ; | 
| 526 |         if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_IgnoreCase) | 
| 527 |             result += "i" ; | 
| 528 |  | 
| 529 |         return result; | 
| 530 |     } | 
| 531 |     default: | 
| 532 |         m_error = true; | 
| 533 |         return "unknown_expression_" +QString::number(expression->kind); | 
| 534 |     } | 
| 535 | } | 
| 536 |  | 
| 537 | QString DumpAstVisitor::parseVariableDeclarationList(VariableDeclarationList *list) | 
| 538 | { | 
| 539 |     QString result = "" ; | 
| 540 |  | 
| 541 |     for (auto *item = list; item != nullptr; item = item->next) { | 
| 542 |         result += parsePatternElement(element: item->declaration, scope: (item == list)) | 
| 543 |                 + (item->next != nullptr ? ", "  : "" ); | 
| 544 |     } | 
| 545 |  | 
| 546 |     return result; | 
| 547 | } | 
| 548 |  | 
| 549 | QString DumpAstVisitor::parseCaseBlock(CaseBlock *block) | 
| 550 | { | 
| 551 |     QString result = "{\n" ; | 
| 552 |  | 
| 553 |     for (auto *item = block->clauses; item != nullptr; item = item->next) { | 
| 554 |         result += formatLine(line: "case " +parseExpression(expression: item->clause->expression)+":" ); | 
| 555 |         m_indentLevel++; | 
| 556 |         result += parseStatementList(list: item->clause->statements); | 
| 557 |         m_indentLevel--; | 
| 558 |     } | 
| 559 |  | 
| 560 |     if (block->defaultClause) { | 
| 561 |         result += formatLine(line: "default:" ); | 
| 562 |         m_indentLevel++; | 
| 563 |         result += parseStatementList(list: block->defaultClause->statements); | 
| 564 |         m_indentLevel--; | 
| 565 |     } | 
| 566 |  | 
| 567 |     result += formatLine(line: "}" , newline: false); | 
| 568 |  | 
| 569 |     return result; | 
| 570 | } | 
| 571 |  | 
| 572 | QString DumpAstVisitor::parseExportSpecifier(ExportSpecifier *specifier) | 
| 573 | { | 
| 574 |     QString result = specifier->identifier.toString(); | 
| 575 |  | 
| 576 |     if (!specifier->exportedIdentifier.isEmpty()) | 
| 577 |         result += " as "  + specifier->exportedIdentifier; | 
| 578 |  | 
| 579 |     return result; | 
| 580 | } | 
| 581 |  | 
| 582 | QString DumpAstVisitor::parseExportsList(ExportsList *list) | 
| 583 | { | 
| 584 |     QString result = "" ; | 
| 585 |  | 
| 586 |     for (auto *item = list; item != nullptr; item = item->next) { | 
| 587 |         result += formatLine(line: parseExportSpecifier(specifier: item->exportSpecifier) | 
| 588 |                              + (item->next != nullptr ? ","  : "" )); | 
| 589 |     } | 
| 590 |  | 
| 591 |     return result; | 
| 592 | } | 
| 593 |  | 
| 594 | bool needsSemicolon(int kind) | 
| 595 | { | 
| 596 |     switch (kind) { | 
| 597 |     case Node::Kind_ForStatement: | 
| 598 |     case Node::Kind_ForEachStatement: | 
| 599 |     case Node::Kind_IfStatement: | 
| 600 |     case Node::Kind_SwitchStatement: | 
| 601 |     case Node::Kind_WhileStatement: | 
| 602 |     case Node::Kind_DoWhileStatement: | 
| 603 |     case Node::Kind_TryStatement: | 
| 604 |     case Node::Kind_WithStatement: | 
| 605 |         return false; | 
| 606 |     default: | 
| 607 |         return true; | 
| 608 |     } | 
| 609 | } | 
| 610 |  | 
| 611 | QString DumpAstVisitor::parseBlock(Block *block, bool hasNext, bool allowBraceless) | 
| 612 | { | 
| 613 |     bool hasOneLine = | 
| 614 |             (block->statements != nullptr && block->statements->next == nullptr) && allowBraceless; | 
| 615 |  | 
| 616 |     QString result = hasOneLine ? "\n"  : "{\n" ; | 
| 617 |     m_indentLevel++; | 
| 618 |     result += parseStatementList(list: block->statements); | 
| 619 |     m_indentLevel--; | 
| 620 |  | 
| 621 |     if (hasNext) | 
| 622 |         result += formatLine(line: hasOneLine ? ""  : "} " , newline: false); | 
| 623 |  | 
| 624 |     if (!hasNext && !hasOneLine) | 
| 625 |         result += formatLine(line: "}" , newline: false); | 
| 626 |  | 
| 627 |     if (block->statements) { | 
| 628 |         m_blockNeededBraces |= !needsSemicolon(kind: block->statements->statement->kind) | 
| 629 |                 || (block->statements->next != nullptr); | 
| 630 |     } else { | 
| 631 |         m_blockNeededBraces = true; | 
| 632 |     } | 
| 633 |  | 
| 634 |     return result; | 
| 635 | } | 
| 636 |  | 
| 637 | QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, | 
| 638 |                                        bool blockAllowBraceless) | 
| 639 | { | 
| 640 |     if (statement == nullptr) | 
| 641 |         return "" ; | 
| 642 |  | 
| 643 |     switch (statement->kind) | 
| 644 |     { | 
| 645 |     case Node::Kind_EmptyStatement: | 
| 646 |         return "" ; | 
| 647 |     case Node::Kind_ExpressionStatement: | 
| 648 |         return parseExpression(expression: cast<ExpressionStatement *>(ast: statement)->expression); | 
| 649 |     case Node::Kind_VariableStatement: | 
| 650 |         return parseVariableDeclarationList(list: cast<VariableStatement *>(ast: statement)->declarations); | 
| 651 |     case Node::Kind_ReturnStatement: | 
| 652 |         return "return " +parseExpression(expression: cast<ReturnStatement *>(ast: statement)->expression); | 
| 653 |     case Node::Kind_ContinueStatement: | 
| 654 |         return "continue" ; | 
| 655 |     case Node::Kind_BreakStatement: | 
| 656 |         return "break" ; | 
| 657 |     case Node::Kind_SwitchStatement: { | 
| 658 |         auto *switchStatement = cast<SwitchStatement *>(ast: statement); | 
| 659 |  | 
| 660 |         QString result = "switch (" +parseExpression(expression: switchStatement->expression)+") " ; | 
| 661 |  | 
| 662 |         result += parseCaseBlock(block: switchStatement->block); | 
| 663 |  | 
| 664 |         return result; | 
| 665 |     } | 
| 666 |     case Node::Kind_IfStatement: { | 
| 667 |         auto *ifStatement = cast<IfStatement *>(ast: statement); | 
| 668 |  | 
| 669 |         m_blockNeededBraces = !blockAllowBraceless; | 
| 670 |  | 
| 671 |         QString ifFalse = parseStatement(statement: ifStatement->ko, blockHasNext: false, blockAllowBraceless: true); | 
| 672 |         QString ifTrue = parseStatement(statement: ifStatement->ok, blockHasNext: !ifFalse.isEmpty(), blockAllowBraceless: true); | 
| 673 |  | 
| 674 |         bool ifTrueBlock = ifStatement->ok->kind == Node::Kind_Block; | 
| 675 |         bool ifFalseBlock = ifStatement->ko | 
| 676 |                 ? (ifStatement->ko->kind == Node::Kind_Block || ifStatement->ko->kind == Node::Kind_IfStatement) | 
| 677 |                 : false; | 
| 678 |  | 
| 679 |         if (m_blockNeededBraces) { | 
| 680 |             ifFalse = parseStatement(statement: ifStatement->ko, blockHasNext: false, blockAllowBraceless: false); | 
| 681 |             ifTrue = parseStatement(statement: ifStatement->ok, blockHasNext: !ifFalse.isEmpty(), blockAllowBraceless: false); | 
| 682 |         } | 
| 683 |  | 
| 684 |         if (ifStatement->ok->kind != Node::Kind_Block) | 
| 685 |             ifTrue += ";" ; | 
| 686 |  | 
| 687 |         if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) | 
| 688 |             ifFalse += ";" ; | 
| 689 |  | 
| 690 |         QString result = "if ("  + parseExpression(expression: ifStatement->expression) + ")" ; | 
| 691 |  | 
| 692 |         if (m_blockNeededBraces) { | 
| 693 |             if (ifStatement->ok->kind != Node::Kind_Block) { | 
| 694 |                 QString result = "{\n" ; | 
| 695 |                 m_indentLevel++; | 
| 696 |                 result += formatLine(line: ifTrue); | 
| 697 |                 m_indentLevel--; | 
| 698 |                 result += formatLine(line: "} " , newline: false); | 
| 699 |                 ifTrue = result; | 
| 700 |                 ifTrueBlock = true; | 
| 701 |             } | 
| 702 |  | 
| 703 |             if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) { | 
| 704 |                 QString result = "{\n" ; | 
| 705 |                 m_indentLevel++; | 
| 706 |                 result += formatLine(line: ifFalse); | 
| 707 |                 m_indentLevel--; | 
| 708 |                 result += formatLine(line: "} " , newline: false); | 
| 709 |                 ifFalse = result; | 
| 710 |                 ifFalseBlock = true; | 
| 711 |             } | 
| 712 |         } | 
| 713 |  | 
| 714 |         if (ifTrueBlock) { | 
| 715 |             result += " "  + ifTrue; | 
| 716 |         } else { | 
| 717 |             result += "\n" ; | 
| 718 |             m_indentLevel++; | 
| 719 |             result += formatLine(line: ifTrue); | 
| 720 |             m_indentLevel--; | 
| 721 |         } | 
| 722 |  | 
| 723 |         if (!ifFalse.isEmpty()) | 
| 724 |         { | 
| 725 |             if (ifTrueBlock) | 
| 726 |                 result += "else" ; | 
| 727 |             else | 
| 728 |                 result += formatLine(line: "else" , newline: false); | 
| 729 |  | 
| 730 |             if (ifFalseBlock) { | 
| 731 |                 // Blocks generate an extra newline that we don't want here. | 
| 732 |                 if (!m_blockNeededBraces && ifFalse.endsWith(s: QLatin1String("\n" ))) | 
| 733 |                     ifFalse.chop(n: 1); | 
| 734 |  | 
| 735 |                 result += " "  + ifFalse; | 
| 736 |             } else { | 
| 737 |                 result += "\n" ; | 
| 738 |                 m_indentLevel++; | 
| 739 |                 result += formatLine(line: ifFalse, newline: false); | 
| 740 |                 m_indentLevel--; | 
| 741 |             } | 
| 742 |         } | 
| 743 |  | 
| 744 |         return result; | 
| 745 |     } | 
| 746 |     case Node::Kind_ForStatement: { | 
| 747 |         auto *forStatement = cast<ForStatement *>(ast: statement); | 
| 748 |  | 
| 749 |         QString expr = parseExpression(expression: forStatement->expression); | 
| 750 |         QString result = "for (" ; | 
| 751 |  | 
| 752 |         result += parseVariableDeclarationList(list: forStatement->declarations); | 
| 753 |  | 
| 754 |         result += "; " ; | 
| 755 |  | 
| 756 |         result += parseExpression(expression: forStatement->condition) + "; " ; | 
| 757 |         result += parseExpression(expression: forStatement->expression)+")" ; | 
| 758 |  | 
| 759 |         const QString statement = parseStatement(statement: forStatement->statement); | 
| 760 |  | 
| 761 |         if (!statement.isEmpty()) | 
| 762 |             result += " " +statement; | 
| 763 |         else | 
| 764 |             result += ";" ; | 
| 765 |  | 
| 766 |         return result; | 
| 767 |     } | 
| 768 |     case Node::Kind_ForEachStatement: { | 
| 769 |         auto *forEachStatement = cast<ForEachStatement *>(ast: statement); | 
| 770 |  | 
| 771 |         QString result = "for (" ; | 
| 772 |  | 
| 773 |         PatternElement *patternElement = cast<PatternElement *>(ast: forEachStatement->lhs); | 
| 774 |  | 
| 775 |         if (patternElement != nullptr) | 
| 776 |             result += parsePatternElement(element: patternElement); | 
| 777 |         else | 
| 778 |             result += parseExpression(expression: forEachStatement->lhs->expressionCast()); | 
| 779 |  | 
| 780 |         switch (forEachStatement->type) | 
| 781 |         { | 
| 782 |         case ForEachType::In: | 
| 783 |             result += " in " ; | 
| 784 |             break; | 
| 785 |         case ForEachType::Of: | 
| 786 |             result += " of " ; | 
| 787 |             break; | 
| 788 |         } | 
| 789 |  | 
| 790 |         result += parseExpression(expression: forEachStatement->expression) + ")" ; | 
| 791 |  | 
| 792 |         const QString statement = parseStatement(statement: forEachStatement->statement); | 
| 793 |  | 
| 794 |         if (!statement.isEmpty()) | 
| 795 |             result += " " +statement; | 
| 796 |         else | 
| 797 |             result += ";" ; | 
| 798 |  | 
| 799 |         return result; | 
| 800 |     } | 
| 801 |     case Node::Kind_WhileStatement: { | 
| 802 |         auto *whileStatement = cast<WhileStatement *>(ast: statement); | 
| 803 |  | 
| 804 |         m_blockNeededBraces = false; | 
| 805 |  | 
| 806 |         auto statement = parseStatement(statement: whileStatement->statement, blockHasNext: false, blockAllowBraceless: true); | 
| 807 |  | 
| 808 |         QString result = "while (" +parseExpression(expression: whileStatement->expression) + ")" ; | 
| 809 |  | 
| 810 |         if (!statement.isEmpty()) | 
| 811 |             result += (m_blockNeededBraces ? " "  : "" ) + statement; | 
| 812 |         else | 
| 813 |             result += ";" ; | 
| 814 |  | 
| 815 |         return result; | 
| 816 |     } | 
| 817 |     case Node::Kind_DoWhileStatement: { | 
| 818 |         auto *doWhileStatement = cast<DoWhileStatement *>(ast: statement); | 
| 819 |         return "do "  + parseBlock(block: cast<Block *>(ast: doWhileStatement->statement), hasNext: true, allowBraceless: false) | 
| 820 |                 + "while ("  + parseExpression(expression: doWhileStatement->expression) + ")" ; | 
| 821 |     } | 
| 822 |     case Node::Kind_TryStatement: { | 
| 823 |         auto *tryStatement = cast<TryStatement *>(ast: statement); | 
| 824 |  | 
| 825 |         Catch *catchExpr = tryStatement->catchExpression; | 
| 826 |         Finally *finallyExpr = tryStatement->finallyExpression; | 
| 827 |  | 
| 828 |         QString result; | 
| 829 |  | 
| 830 |         result += "try "  + parseBlock(block: cast<Block *>(ast: tryStatement->statement), hasNext: true, allowBraceless: false); | 
| 831 |  | 
| 832 |         result += "catch ("  + parsePatternElement(element: catchExpr->patternElement, scope: false) + ") "  | 
| 833 |                 + parseBlock(block: cast<Block *>(ast: catchExpr->statement), hasNext: finallyExpr, allowBraceless: false); | 
| 834 |  | 
| 835 |         if (finallyExpr) { | 
| 836 |             result += "finally "  + parseBlock(block: cast<Block *>(ast: tryStatement->statement), hasNext: false, allowBraceless: false); | 
| 837 |         } | 
| 838 |  | 
| 839 |         return result; | 
| 840 |     } | 
| 841 |     case Node::Kind_Block: { | 
| 842 |         return parseBlock(block: cast<Block *>(ast: statement), hasNext: blockHasNext, allowBraceless: blockAllowBraceless); | 
| 843 |     } | 
| 844 |     case Node::Kind_ThrowStatement: | 
| 845 |         return "throw " +parseExpression(expression: cast<ThrowStatement *>(ast: statement)->expression); | 
| 846 |     case Node::Kind_LabelledStatement: { | 
| 847 |         auto *labelledStatement = cast<LabelledStatement *>(ast: statement); | 
| 848 |         QString result = labelledStatement->label+":\n" ; | 
| 849 |         result += formatLine(line: parseStatement(statement: labelledStatement->statement), newline: false); | 
| 850 |  | 
| 851 |         return result; | 
| 852 |     } | 
| 853 |     case Node::Kind_WithStatement: { | 
| 854 |         auto *withStatement = cast<WithStatement *>(ast: statement); | 
| 855 |         return "with ("  + parseExpression(expression: withStatement->expression) + ") "  | 
| 856 |                 + parseStatement(statement: withStatement->statement); | 
| 857 |     } | 
| 858 |     case Node::Kind_DebuggerStatement: { | 
| 859 |         return "debugger" ; | 
| 860 |     } | 
| 861 |     case Node::Kind_ExportDeclaration: | 
| 862 |         m_error = true; | 
| 863 |         return "export_decl_unsupported" ; | 
| 864 |     case Node::Kind_ImportDeclaration: | 
| 865 |         m_error = true; | 
| 866 |         return "import_decl_unsupported" ; | 
| 867 |     default: | 
| 868 |         m_error = true; | 
| 869 |         return "unknown_statement_" +QString::number(statement->kind); | 
| 870 |     } | 
| 871 | } | 
| 872 |  | 
| 873 | QString DumpAstVisitor::parseStatementList(StatementList *list) | 
| 874 | { | 
| 875 |     QString result = "" ; | 
| 876 |  | 
| 877 |     if (list == nullptr) | 
| 878 |         return "" ; | 
| 879 |  | 
| 880 |     result += getOrphanedComments(node: list); | 
| 881 |  | 
| 882 |     for (auto *item = list; item != nullptr; item = item->next) { | 
| 883 |         QString statement = parseStatement(statement: item->statement->statementCast(), blockHasNext: false, blockAllowBraceless: true); | 
| 884 |         if (statement.isEmpty()) | 
| 885 |             continue; | 
| 886 |  | 
| 887 |         QString  = getComment(node: item->statement, location: Comment::Location::Front); | 
| 888 |         QString  = getComment(node: item->statement, location: Comment::Location::Back_Inline); | 
| 889 |  | 
| 890 |         if (!commentFront.isEmpty()) | 
| 891 |             result += formatLine(line: commentFront); | 
| 892 |  | 
| 893 |         result += formatLine(line: statement + (needsSemicolon(kind: item->statement->kind) ? ";"  : "" ) | 
| 894 |                              + commentBackInline); | 
| 895 |     } | 
| 896 |  | 
| 897 |     return result; | 
| 898 | } | 
| 899 |  | 
| 900 | bool DumpAstVisitor::visit(UiPublicMember *node) { | 
| 901 |  | 
| 902 |     QString  = getComment(node, location: Comment::Location::Front); | 
| 903 |     QString  = getComment(node, location: Comment::Location::Back_Inline); | 
| 904 |  | 
| 905 |     switch (node->type) | 
| 906 |     { | 
| 907 |     case UiPublicMember::Signal: | 
| 908 |         if (scope().m_firstSignal) { | 
| 909 |             if (scope().m_firstOfAll) | 
| 910 |                 scope().m_firstOfAll = false; | 
| 911 |             else | 
| 912 |                 addNewLine(); | 
| 913 |  | 
| 914 |             scope().m_firstSignal = false; | 
| 915 |         } | 
| 916 |  | 
| 917 |         addLine(line: commentFront); | 
| 918 |         addLine(line: "signal " +node->name.toString()+"(" +parseUiParameterList(list: node->parameters) + ")"  | 
| 919 |                 + commentBackInline); | 
| 920 |         break; | 
| 921 |     case UiPublicMember::Property: { | 
| 922 |         if (scope().m_firstProperty) { | 
| 923 |             if (scope().m_firstOfAll) | 
| 924 |                 scope().m_firstOfAll = false; | 
| 925 |             else | 
| 926 |                 addNewLine(); | 
| 927 |  | 
| 928 |             scope().m_firstProperty = false; | 
| 929 |         } | 
| 930 |  | 
| 931 |         const bool is_required = node->requiredToken.isValid(); | 
| 932 |         const bool is_default = node->defaultToken.isValid(); | 
| 933 |         const bool is_readonly = node->readonlyToken.isValid(); | 
| 934 |         const bool has_type_modifier = node->typeModifierToken.isValid(); | 
| 935 |  | 
| 936 |         QString prefix = "" ; | 
| 937 |         QString statement = parseStatement(statement: node->statement); | 
| 938 |  | 
| 939 |         if (!statement.isEmpty()) | 
| 940 |             statement.prepend(s: ": " ); | 
| 941 |  | 
| 942 |         if (is_required) | 
| 943 |             prefix += "required " ; | 
| 944 |  | 
| 945 |         if (is_default) | 
| 946 |             prefix += "default " ; | 
| 947 |  | 
| 948 |         if (is_readonly) | 
| 949 |             prefix += "readonly " ; | 
| 950 |  | 
| 951 |         QString member_type = parseUiQualifiedId(id: node->memberType); | 
| 952 |  | 
| 953 |         if (has_type_modifier) | 
| 954 |             member_type = node->typeModifier + "<"  + member_type + ">" ; | 
| 955 |  | 
| 956 |         addLine(line: commentFront); | 
| 957 |         if (is_readonly && statement.isEmpty() | 
| 958 |                 && scope().m_bindings.contains(akey: node->name.toString())) { | 
| 959 |             m_result += formatLine(line: prefix + "property "  + member_type + " " , newline: false); | 
| 960 |  | 
| 961 |             scope().m_pendingBinding = true; | 
| 962 |         } else { | 
| 963 |             addLine(line: prefix + "property "  + member_type + " "  | 
| 964 |                     + node->name+statement + commentBackInline); | 
| 965 |         } | 
| 966 |         break; | 
| 967 |     } | 
| 968 |     } | 
| 969 |  | 
| 970 |     return true; | 
| 971 | } | 
| 972 |  | 
| 973 | QString DumpAstVisitor::generateIndent() const { | 
| 974 |     constexpr int IDENT_WIDTH = 4; | 
| 975 |  | 
| 976 |     QString indent = "" ; | 
| 977 |     for (int i = 0; i < IDENT_WIDTH*m_indentLevel; i++) | 
| 978 |         indent += " " ; | 
| 979 |  | 
| 980 |     return indent; | 
| 981 | } | 
| 982 |  | 
| 983 | QString DumpAstVisitor::formatLine(QString line, bool newline) const { | 
| 984 |     QString result = generateIndent() + line; | 
| 985 |     if (newline) | 
| 986 |         result += "\n" ; | 
| 987 |  | 
| 988 |     return result; | 
| 989 | } | 
| 990 |  | 
| 991 | void DumpAstVisitor::addNewLine(bool always) { | 
| 992 |     if (!always && m_result.endsWith(s: "\n\n" )) | 
| 993 |         return; | 
| 994 |  | 
| 995 |     m_result += "\n" ; | 
| 996 | } | 
| 997 |  | 
| 998 | void DumpAstVisitor::addLine(QString line) { | 
| 999 |     // addLine does not support empty lines, use addNewLine(true) for that | 
| 1000 |     if (line.isEmpty()) | 
| 1001 |         return; | 
| 1002 |  | 
| 1003 |     m_result += formatLine(line); | 
| 1004 | } | 
| 1005 |  | 
| 1006 | QHash<QString, UiObjectMember*> findBindings(UiObjectMemberList *list) { | 
| 1007 |     QHash<QString, UiObjectMember*> bindings; | 
| 1008 |  | 
| 1009 |     // This relies on RestructureASTVisitor having run beforehand | 
| 1010 |  | 
| 1011 |     for (auto *item = list; item != nullptr; item = item->next) { | 
| 1012 |         switch (item->member->kind) { | 
| 1013 |         case Node::Kind_UiPublicMember: { | 
| 1014 |             UiPublicMember *member = cast<UiPublicMember *>(ast: item->member); | 
| 1015 |  | 
| 1016 |             if (member->type != UiPublicMember::Property) | 
| 1017 |                 continue; | 
| 1018 |  | 
| 1019 |             bindings[member->name.toString()] = nullptr; | 
| 1020 |  | 
| 1021 |             break; | 
| 1022 |         } | 
| 1023 |         case Node::Kind_UiObjectBinding: { | 
| 1024 |             UiObjectBinding *binding = cast<UiObjectBinding *>(ast: item->member); | 
| 1025 |  | 
| 1026 |             const QString name = parseUiQualifiedId(id: binding->qualifiedId); | 
| 1027 |  | 
| 1028 |             if (bindings.contains(akey: name)) | 
| 1029 |                 bindings[name] = binding; | 
| 1030 |  | 
| 1031 |             break; | 
| 1032 |         } | 
| 1033 |         case Node::Kind_UiArrayBinding: { | 
| 1034 |             UiArrayBinding *binding = cast<UiArrayBinding *>(ast: item->member); | 
| 1035 |  | 
| 1036 |             const QString name = parseUiQualifiedId(id: binding->qualifiedId); | 
| 1037 |  | 
| 1038 |             if (bindings.contains(akey: name)) | 
| 1039 |                 bindings[name] = binding; | 
| 1040 |  | 
| 1041 |             break; | 
| 1042 |         } | 
| 1043 |         case Node::Kind_UiScriptBinding: | 
| 1044 |             // We can ignore UiScriptBindings since those are actually properly attached to the property | 
| 1045 |             break; | 
| 1046 |         } | 
| 1047 |     } | 
| 1048 |  | 
| 1049 |     return bindings; | 
| 1050 | } | 
| 1051 |  | 
| 1052 | bool DumpAstVisitor::visit(UiInlineComponent *node) | 
| 1053 | { | 
| 1054 |     m_component_name = node->name.toString(); | 
| 1055 |     return true; | 
| 1056 | } | 
| 1057 |  | 
| 1058 | bool DumpAstVisitor::visit(UiObjectDefinition *node) { | 
| 1059 |     if (scope().m_firstObject) { | 
| 1060 |         if (scope().m_firstOfAll) | 
| 1061 |             scope().m_firstOfAll = false; | 
| 1062 |         else | 
| 1063 |             addNewLine(); | 
| 1064 |  | 
| 1065 |         scope().m_firstObject = false; | 
| 1066 |     } | 
| 1067 |  | 
| 1068 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1069 |     addLine(line: getComment(node, location: Comment::Location::Front_Inline)); | 
| 1070 |  | 
| 1071 |     QString component = "" ; | 
| 1072 |  | 
| 1073 |     if (!m_component_name.isEmpty()) { | 
| 1074 |         component = "component " +m_component_name+": " ; | 
| 1075 |         m_component_name = "" ; | 
| 1076 |     } | 
| 1077 |  | 
| 1078 |     addLine(line: component + parseUiQualifiedId(id: node->qualifiedTypeNameId) + " {" ); | 
| 1079 |  | 
| 1080 |     m_indentLevel++; | 
| 1081 |  | 
| 1082 |     ScopeProperties props; | 
| 1083 |     props.m_bindings = findBindings(list: node->initializer->members); | 
| 1084 |     m_scope_properties.push(t: props); | 
| 1085 |  | 
| 1086 |     m_result += getOrphanedComments(node); | 
| 1087 |  | 
| 1088 |     return true; | 
| 1089 | } | 
| 1090 |  | 
| 1091 | void DumpAstVisitor::endVisit(UiObjectDefinition *node) { | 
| 1092 |     m_indentLevel--; | 
| 1093 |  | 
| 1094 |     m_scope_properties.pop(); | 
| 1095 |  | 
| 1096 |     bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node; | 
| 1097 |  | 
| 1098 |     addLine(line: need_comma ? "},"  : "}" ); | 
| 1099 |     addLine(line: getComment(node, location: Comment::Location::Back)); | 
| 1100 |     if (!scope().m_inArrayBinding) | 
| 1101 |         addNewLine(); | 
| 1102 | } | 
| 1103 |  | 
| 1104 | bool DumpAstVisitor::visit(UiEnumDeclaration *node)  { | 
| 1105 |  | 
| 1106 |     addNewLine(); | 
| 1107 |  | 
| 1108 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1109 |     addLine(line: "enum "  + node->name + " {" ); | 
| 1110 |     m_indentLevel++; | 
| 1111 |     m_result += getOrphanedComments(node); | 
| 1112 |  | 
| 1113 |     return true; | 
| 1114 | } | 
| 1115 |  | 
| 1116 | void DumpAstVisitor::endVisit(UiEnumDeclaration *) { | 
| 1117 |     m_indentLevel--; | 
| 1118 |     addLine(line: "}" ); | 
| 1119 |  | 
| 1120 |     addNewLine(); | 
| 1121 | } | 
| 1122 |  | 
| 1123 | bool DumpAstVisitor::visit(UiEnumMemberList *node) { | 
| 1124 |     for (auto *members = node; members != nullptr; members = members->next) { | 
| 1125 |  | 
| 1126 |         addLine(line: getListItemComment(srcLocation: members->memberToken, location: Comment::Location::Front)); | 
| 1127 |  | 
| 1128 |         QString line = members->member.toString(); | 
| 1129 |  | 
| 1130 |         if (members->valueToken.isValid()) | 
| 1131 |             line += " = " +QString::number(members->value); | 
| 1132 |  | 
| 1133 |         if (members->next != nullptr) | 
| 1134 |             line += "," ; | 
| 1135 |  | 
| 1136 |         line += getListItemComment(srcLocation: members->memberToken, location: Comment::Location::Back_Inline); | 
| 1137 |  | 
| 1138 |         addLine(line); | 
| 1139 |     } | 
| 1140 |  | 
| 1141 |     return true; | 
| 1142 | } | 
| 1143 |  | 
| 1144 | bool DumpAstVisitor::visit(UiScriptBinding *node) { | 
| 1145 |     if (scope().m_firstBinding) { | 
| 1146 |         if (scope().m_firstOfAll) | 
| 1147 |             scope().m_firstOfAll = false; | 
| 1148 |         else | 
| 1149 |             addNewLine(); | 
| 1150 |  | 
| 1151 |         if (parseUiQualifiedId(id: node->qualifiedId) != "id" ) | 
| 1152 |             scope().m_firstBinding = false; | 
| 1153 |     } | 
| 1154 |  | 
| 1155 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1156 |  | 
| 1157 |     bool multiline = !needsSemicolon(kind: node->statement->kind); | 
| 1158 |  | 
| 1159 |     if (multiline) { | 
| 1160 |         m_indentLevel++; | 
| 1161 |     } | 
| 1162 |  | 
| 1163 |     QString statement = parseStatement(statement: node->statement); | 
| 1164 |  | 
| 1165 |     if (multiline) { | 
| 1166 |         statement = "{\n"  + formatLine(line: statement); | 
| 1167 |         m_indentLevel--; | 
| 1168 |         statement += formatLine(line: "}" , newline: false); | 
| 1169 |     } | 
| 1170 |  | 
| 1171 |     QString result = parseUiQualifiedId(id: node->qualifiedId) + ":" ; | 
| 1172 |  | 
| 1173 |     if (!statement.isEmpty()) | 
| 1174 |         result += " " +statement; | 
| 1175 |     else | 
| 1176 |         result += ";" ; | 
| 1177 |  | 
| 1178 |     result += getComment(node, location: Comment::Location::Back_Inline); | 
| 1179 |  | 
| 1180 |     addLine(line: result); | 
| 1181 |  | 
| 1182 |     return true; | 
| 1183 | } | 
| 1184 |  | 
| 1185 | bool DumpAstVisitor::visit(UiArrayBinding *node) { | 
| 1186 |     if (!scope().m_pendingBinding && scope().m_firstBinding) { | 
| 1187 |         if (scope().m_firstOfAll) | 
| 1188 |             scope().m_firstOfAll = false; | 
| 1189 |         else | 
| 1190 |             addNewLine(); | 
| 1191 |  | 
| 1192 |         scope().m_firstBinding = false; | 
| 1193 |     } | 
| 1194 |  | 
| 1195 |     if (scope().m_pendingBinding) { | 
| 1196 |         m_result += parseUiQualifiedId(id: node->qualifiedId)+ ": [\n" ; | 
| 1197 |         scope().m_pendingBinding = false; | 
| 1198 |     } else { | 
| 1199 |         addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1200 |         addLine(line: parseUiQualifiedId(id: node->qualifiedId)+ ": [" ); | 
| 1201 |     } | 
| 1202 |  | 
| 1203 |     m_indentLevel++; | 
| 1204 |  | 
| 1205 |     ScopeProperties props; | 
| 1206 |     props.m_inArrayBinding = true; | 
| 1207 |  | 
| 1208 |     for (auto *item = node->members; item != nullptr; item = item->next) { | 
| 1209 |         if (item->next == nullptr) | 
| 1210 |             props.m_lastInArrayBinding = item->member; | 
| 1211 |     } | 
| 1212 |  | 
| 1213 |     m_scope_properties.push(t: props); | 
| 1214 |  | 
| 1215 |     m_result += getOrphanedComments(node); | 
| 1216 |  | 
| 1217 |     return true; | 
| 1218 | } | 
| 1219 |  | 
| 1220 | void DumpAstVisitor::endVisit(UiArrayBinding *) { | 
| 1221 |     m_indentLevel--; | 
| 1222 |     m_scope_properties.pop(); | 
| 1223 |     addLine(line: "]" ); | 
| 1224 | } | 
| 1225 |  | 
| 1226 | bool DumpAstVisitor::visit(FunctionDeclaration *node) { | 
| 1227 |     if (scope().m_firstFunction) { | 
| 1228 |         if (scope().m_firstOfAll) | 
| 1229 |             scope().m_firstOfAll = false; | 
| 1230 |         else | 
| 1231 |             addNewLine(); | 
| 1232 |  | 
| 1233 |         scope().m_firstFunction = false; | 
| 1234 |     } | 
| 1235 |  | 
| 1236 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1237 |  | 
| 1238 |     QString head = "function" ; | 
| 1239 |  | 
| 1240 |     if (node->isGenerator) | 
| 1241 |         head += "*" ; | 
| 1242 |  | 
| 1243 |     head += " " +node->name+"(" +parseFormalParameterList(list: node->formals)+")" ; | 
| 1244 |  | 
| 1245 |     if (node->typeAnnotation != nullptr) | 
| 1246 |         head += " : "  + parseType(type: node->typeAnnotation->type); | 
| 1247 |  | 
| 1248 |     head += " {" ; | 
| 1249 |  | 
| 1250 |     addLine(line: head); | 
| 1251 |     m_indentLevel++; | 
| 1252 |  | 
| 1253 |     return true; | 
| 1254 | } | 
| 1255 |  | 
| 1256 | void DumpAstVisitor::endVisit(FunctionDeclaration *node) | 
| 1257 | { | 
| 1258 |     m_result += parseStatementList(list: node->body); | 
| 1259 |     m_indentLevel--; | 
| 1260 |     addLine(line: "}" ); | 
| 1261 |     addNewLine(); | 
| 1262 | } | 
| 1263 |  | 
| 1264 | bool DumpAstVisitor::visit(UiObjectBinding *node) { | 
| 1265 |     if (!scope().m_pendingBinding && scope().m_firstObject) { | 
| 1266 |         if (scope().m_firstOfAll) | 
| 1267 |             scope().m_firstOfAll = false; | 
| 1268 |         else | 
| 1269 |             addNewLine(); | 
| 1270 |  | 
| 1271 |         scope().m_firstObject = false; | 
| 1272 |     } | 
| 1273 |  | 
| 1274 |     QString name = parseUiQualifiedId(id: node->qualifiedTypeNameId); | 
| 1275 |  | 
| 1276 |     QString result = name; | 
| 1277 |  | 
| 1278 |     ScopeProperties props; | 
| 1279 |     props.m_bindings = findBindings(list: node->initializer->members); | 
| 1280 |     m_scope_properties.push(t: props); | 
| 1281 |  | 
| 1282 |     if (node->hasOnToken) | 
| 1283 |         result += " on " +parseUiQualifiedId(id: node->qualifiedId); | 
| 1284 |     else | 
| 1285 |         result.prepend(s: parseUiQualifiedId(id: node->qualifiedId) + ": " ); | 
| 1286 |  | 
| 1287 |     if (scope().m_pendingBinding) { | 
| 1288 |         m_result += result + " {\n" ; | 
| 1289 |  | 
| 1290 |         scope().m_pendingBinding = false; | 
| 1291 |     } else { | 
| 1292 |         addNewLine(); | 
| 1293 |         addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1294 |         addLine(line: getComment(node, location: Comment::Location::Front_Inline)); | 
| 1295 |         addLine(line: result + " {" ); | 
| 1296 |     } | 
| 1297 |  | 
| 1298 |     m_indentLevel++; | 
| 1299 |  | 
| 1300 |     return true; | 
| 1301 | } | 
| 1302 |  | 
| 1303 | void DumpAstVisitor::endVisit(UiObjectBinding *node) { | 
| 1304 |     m_indentLevel--; | 
| 1305 |     m_scope_properties.pop(); | 
| 1306 |  | 
| 1307 |     addLine(line: "}" ); | 
| 1308 |     addLine(line: getComment(node, location: Comment::Location::Back)); | 
| 1309 |  | 
| 1310 |     addNewLine(); | 
| 1311 | } | 
| 1312 |  | 
| 1313 | bool DumpAstVisitor::visit(UiImport *node) { | 
| 1314 |     scope().m_firstOfAll = false; | 
| 1315 |  | 
| 1316 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1317 |  | 
| 1318 |     QString result = "import " ; | 
| 1319 |  | 
| 1320 |     if (!node->fileName.isEmpty()) | 
| 1321 |         result += escapeString(string: node->fileName.toString()); | 
| 1322 |     else | 
| 1323 |         result += parseUiQualifiedId(id: node->importUri); | 
| 1324 |  | 
| 1325 |     if (node->version) { | 
| 1326 |         result += " "  + QString::number(node->version->majorVersion) + "."  | 
| 1327 |                 + QString::number(node->version->minorVersion); | 
| 1328 |     } | 
| 1329 |  | 
| 1330 |     if (node->asToken.isValid()) { | 
| 1331 |         result +=" as "  + node->importId; | 
| 1332 |     } | 
| 1333 |  | 
| 1334 |     result += getComment(node, location: Comment::Location::Back_Inline); | 
| 1335 |  | 
| 1336 |     addLine(line: result); | 
| 1337 |  | 
| 1338 |     return true; | 
| 1339 | } | 
| 1340 |  | 
| 1341 | bool DumpAstVisitor::visit(UiPragma *node) { | 
| 1342 |     scope().m_firstOfAll = false; | 
| 1343 |  | 
| 1344 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1345 |     QString result = "pragma " + node->name; | 
| 1346 |     result += getComment(node, location: Comment::Location::Back_Inline); | 
| 1347 |  | 
| 1348 |     addLine(line: result); | 
| 1349 |  | 
| 1350 |     return true; | 
| 1351 | } | 
| 1352 |  | 
| 1353 | bool DumpAstVisitor::visit(UiAnnotation *node) | 
| 1354 | { | 
| 1355 |     if (scope().m_firstObject) { | 
| 1356 |         if (scope().m_firstOfAll) | 
| 1357 |             scope().m_firstOfAll = false; | 
| 1358 |         else | 
| 1359 |             addNewLine(); | 
| 1360 |  | 
| 1361 |         scope().m_firstObject = false; | 
| 1362 |     } | 
| 1363 |  | 
| 1364 |     addLine(line: getComment(node, location: Comment::Location::Front)); | 
| 1365 |     addLine(line: QLatin1String("@" ) + parseUiQualifiedId(id: node->qualifiedTypeNameId) + " {" ); | 
| 1366 |  | 
| 1367 |     m_indentLevel++; | 
| 1368 |  | 
| 1369 |     ScopeProperties props; | 
| 1370 |     props.m_bindings = findBindings(list: node->initializer->members); | 
| 1371 |     m_scope_properties.push(t: props); | 
| 1372 |  | 
| 1373 |     m_result += getOrphanedComments(node); | 
| 1374 |  | 
| 1375 |     return true; | 
| 1376 | } | 
| 1377 |  | 
| 1378 | void DumpAstVisitor::endVisit(UiAnnotation *node) { | 
| 1379 |     m_indentLevel--; | 
| 1380 |  | 
| 1381 |     m_scope_properties.pop(); | 
| 1382 |  | 
| 1383 |     addLine(line: "}" ); | 
| 1384 |     addLine(line: getComment(node, location: Comment::Location::Back)); | 
| 1385 | } | 
| 1386 |  |