| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtQml module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 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 Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include "qv4compilerscanfunctions_p.h" | 
| 41 |  | 
| 42 | #include <QtCore/QCoreApplication> | 
| 43 | #include <QtCore/QStringList> | 
| 44 | #include <QtCore/QSet> | 
| 45 | #include <QtCore/QBuffer> | 
| 46 | #include <QtCore/QBitArray> | 
| 47 | #include <QtCore/QStack> | 
| 48 | #include <private/qqmljsast_p.h> | 
| 49 | #include <private/qv4compilercontext_p.h> | 
| 50 | #include <private/qv4codegen_p.h> | 
| 51 |  | 
| 52 | QT_USE_NAMESPACE | 
| 53 | using namespace QV4; | 
| 54 | using namespace QV4::Compiler; | 
| 55 | using namespace QQmlJS; | 
| 56 | using namespace QQmlJS::AST; | 
| 57 |  | 
| 58 | static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation) | 
| 59 | { | 
| 60 |     CompiledData::Location target; | 
| 61 |     target.line = astLocation.startLine; | 
| 62 |     target.column = astLocation.startColumn; | 
| 63 |     return target; | 
| 64 | } | 
| 65 |  | 
| 66 |  | 
| 67 | ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType) | 
| 68 |     : QQmlJS::AST::Visitor(cg->recursionDepth()) | 
| 69 |     , _cg(cg) | 
| 70 |     , _sourceCode(sourceCode) | 
| 71 |     , _context(nullptr) | 
| 72 |     , _allowFuncDecls(true) | 
| 73 |     , defaultProgramType(defaultProgramType) | 
| 74 | { | 
| 75 | } | 
| 76 |  | 
| 77 | void ScanFunctions::operator()(Node *node) | 
| 78 | { | 
| 79 |     if (node) | 
| 80 |         node->accept(visitor: this); | 
| 81 |  | 
| 82 |     calcEscapingVariables(); | 
| 83 | } | 
| 84 |  | 
| 85 | void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode) | 
| 86 | { | 
| 87 |     enterEnvironment(node: astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode" )); | 
| 88 | } | 
| 89 |  | 
| 90 | void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name) | 
| 91 | { | 
| 92 |     Context *c = _cg->_module->contextMap.value(akey: node); | 
| 93 |     if (!c) | 
| 94 |         c = _cg->_module->newContext(node, parent: _context, compilationMode); | 
| 95 |     if (!c->isStrict) | 
| 96 |         c->isStrict = _cg->_strictMode; | 
| 97 |     c->name = name; | 
| 98 |     _contextStack.append(t: c); | 
| 99 |     _context = c; | 
| 100 | } | 
| 101 |  | 
| 102 | void ScanFunctions::leaveEnvironment() | 
| 103 | { | 
| 104 |     _contextStack.pop(); | 
| 105 |     _context = _contextStack.isEmpty() ? nullptr : _contextStack.top(); | 
| 106 | } | 
| 107 |  | 
| 108 | void ScanFunctions::checkDirectivePrologue(StatementList *ast) | 
| 109 | { | 
| 110 |     for (StatementList *it = ast; it; it = it->next) { | 
| 111 |         if (ExpressionStatement *expr = cast<ExpressionStatement *>(ast: it->statement)) { | 
| 112 |             if (StringLiteral *strLit = cast<StringLiteral *>(ast: expr->expression)) { | 
| 113 |                 // Use the source code, because the StringLiteral's | 
| 114 |                 // value might have escape sequences in it, which is not | 
| 115 |                 // allowed. | 
| 116 |                 if (strLit->literalToken.length < 2) | 
| 117 |                     continue; | 
| 118 |                 QStringRef str = _sourceCode.midRef(position: strLit->literalToken.offset + 1, n: strLit->literalToken.length - 2); | 
| 119 |                 if (str == QLatin1String("use strict" )) { | 
| 120 |                     _context->isStrict = true; | 
| 121 |                 } else { | 
| 122 |                     // TODO: give a warning. | 
| 123 |                 } | 
| 124 |                 continue; | 
| 125 |             } | 
| 126 |         } | 
| 127 |  | 
| 128 |         break; | 
| 129 |     } | 
| 130 | } | 
| 131 |  | 
| 132 | void ScanFunctions::checkName(const QStringRef &name, const QQmlJS::SourceLocation &loc) | 
| 133 | { | 
| 134 |     if (_context->isStrict) { | 
| 135 |         if (name == QLatin1String("implements" ) | 
| 136 |                 || name == QLatin1String("interface" ) | 
| 137 |                 || name == QLatin1String("let" ) | 
| 138 |                 || name == QLatin1String("package" ) | 
| 139 |                 || name == QLatin1String("private" ) | 
| 140 |                 || name == QLatin1String("protected" ) | 
| 141 |                 || name == QLatin1String("public" ) | 
| 142 |                 || name == QLatin1String("static" ) | 
| 143 |                 || name == QLatin1String("yield" )) { | 
| 144 |             _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word" )); | 
| 145 |         } | 
| 146 |     } | 
| 147 | } | 
| 148 |  | 
| 149 | bool ScanFunctions::visit(Program *ast) | 
| 150 | { | 
| 151 |     enterEnvironment(node: ast, compilationMode: defaultProgramType, QStringLiteral("%ProgramCode" )); | 
| 152 |     checkDirectivePrologue(ast: ast->statements); | 
| 153 |     return true; | 
| 154 | } | 
| 155 |  | 
| 156 | void ScanFunctions::endVisit(Program *) | 
| 157 | { | 
| 158 |     leaveEnvironment(); | 
| 159 | } | 
| 160 |  | 
| 161 | bool ScanFunctions::visit(ESModule *ast) | 
| 162 | { | 
| 163 |     enterEnvironment(node: ast, compilationMode: defaultProgramType, QStringLiteral("%ModuleCode" )); | 
| 164 |     _context->isStrict = true; | 
| 165 |     return true; | 
| 166 | } | 
| 167 |  | 
| 168 | void ScanFunctions::endVisit(ESModule *) | 
| 169 | { | 
| 170 |     leaveEnvironment(); | 
| 171 | } | 
| 172 |  | 
| 173 | bool ScanFunctions::visit(ExportDeclaration *declaration) | 
| 174 | { | 
| 175 |     QString module; | 
| 176 |     if (declaration->fromClause) { | 
| 177 |         module = declaration->fromClause->moduleSpecifier.toString(); | 
| 178 |         if (!module.isEmpty()) | 
| 179 |             _context->moduleRequests << module; | 
| 180 |     } | 
| 181 |  | 
| 182 |     QString localNameForDefaultExport = QStringLiteral("*default*" ); | 
| 183 |  | 
| 184 |     if (declaration->exportAll) { | 
| 185 |         Compiler::ExportEntry entry; | 
| 186 |         entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); | 
| 187 |         entry.importName = QStringLiteral("*" ); | 
| 188 |         entry.location = location(astLocation: declaration->firstSourceLocation()); | 
| 189 |         _context->exportEntries << entry; | 
| 190 |     } else if (declaration->exportClause) { | 
| 191 |         for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) { | 
| 192 |             ExportSpecifier *spec = it->exportSpecifier; | 
| 193 |             Compiler::ExportEntry entry; | 
| 194 |             if (module.isEmpty()) | 
| 195 |                 entry.localName = spec->identifier.toString(); | 
| 196 |             else | 
| 197 |                 entry.importName = spec->identifier.toString(); | 
| 198 |  | 
| 199 |             entry.moduleRequest = module; | 
| 200 |             entry.exportName = spec->exportedIdentifier.toString(); | 
| 201 |             entry.location = location(astLocation: it->firstSourceLocation()); | 
| 202 |  | 
| 203 |             _context->exportEntries << entry; | 
| 204 |         } | 
| 205 |     } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(ast: declaration->variableStatementOrDeclaration)) { | 
| 206 |         BoundNames boundNames; | 
| 207 |         for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) { | 
| 208 |             if (!it->declaration) | 
| 209 |                 continue; | 
| 210 |             it->declaration->boundNames(names: &boundNames); | 
| 211 |         } | 
| 212 |         for (const auto &name: boundNames) { | 
| 213 |             Compiler::ExportEntry entry; | 
| 214 |             entry.localName = name.id; | 
| 215 |             entry.exportName = name.id; | 
| 216 |             entry.location = location(astLocation: vstmt->firstSourceLocation()); | 
| 217 |             _context->exportEntries << entry; | 
| 218 |         } | 
| 219 |     } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(ast: declaration->variableStatementOrDeclaration)) { | 
| 220 |         QString name = classDecl->name.toString(); | 
| 221 |         if (!name.isEmpty()) { | 
| 222 |             Compiler::ExportEntry entry; | 
| 223 |             entry.localName = name; | 
| 224 |             entry.exportName = name; | 
| 225 |             entry.location = location(astLocation: classDecl->firstSourceLocation()); | 
| 226 |             _context->exportEntries << entry; | 
| 227 |             if (declaration->exportDefault) | 
| 228 |                 localNameForDefaultExport = entry.localName; | 
| 229 |         } | 
| 230 |     } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) { | 
| 231 |         QString functionName; | 
| 232 |  | 
| 233 |         // Only function definitions for which we enter their name into the local environment | 
| 234 |         // can result in exports. Nested expressions such as (function foo() {}) are not accessible | 
| 235 |         // as locals and can only be exported as default exports (further down). | 
| 236 |         auto ast = declaration->variableStatementOrDeclaration; | 
| 237 |         if (AST::cast<AST::ExpressionStatement*>(ast) || AST::cast<AST::FunctionDeclaration*>(ast)) | 
| 238 |             functionName = fdef->name.toString(); | 
| 239 |  | 
| 240 |         if (!functionName.isEmpty()) { | 
| 241 |             Compiler::ExportEntry entry; | 
| 242 |             entry.localName = functionName; | 
| 243 |             entry.exportName = functionName; | 
| 244 |             entry.location = location(astLocation: fdef->firstSourceLocation()); | 
| 245 |             _context->exportEntries << entry; | 
| 246 |             if (declaration->exportDefault) | 
| 247 |                 localNameForDefaultExport = entry.localName; | 
| 248 |         } | 
| 249 |     } | 
| 250 |  | 
| 251 |     if (declaration->exportDefault) { | 
| 252 |         Compiler::ExportEntry entry; | 
| 253 |         entry.localName = localNameForDefaultExport; | 
| 254 |         _context->localNameForDefaultExport = localNameForDefaultExport; | 
| 255 |         entry.exportName = QStringLiteral("default" ); | 
| 256 |         entry.location = location(astLocation: declaration->firstSourceLocation()); | 
| 257 |         _context->exportEntries << entry; | 
| 258 |     } | 
| 259 |  | 
| 260 |     return true; // scan through potential assignment expression code, etc. | 
| 261 | } | 
| 262 |  | 
| 263 | bool ScanFunctions::visit(ImportDeclaration *declaration) | 
| 264 | { | 
| 265 |     QString module; | 
| 266 |     if (declaration->fromClause) { | 
| 267 |         module = declaration->fromClause->moduleSpecifier.toString(); | 
| 268 |         if (!module.isEmpty()) | 
| 269 |             _context->moduleRequests << module; | 
| 270 |     } | 
| 271 |  | 
| 272 |     if (!declaration->moduleSpecifier.isEmpty()) | 
| 273 |         _context->moduleRequests << declaration->moduleSpecifier.toString(); | 
| 274 |  | 
| 275 |     if (ImportClause *import = declaration->importClause) { | 
| 276 |         if (!import->importedDefaultBinding.isEmpty()) { | 
| 277 |             Compiler::ImportEntry entry; | 
| 278 |             entry.moduleRequest = module; | 
| 279 |             entry.importName = QStringLiteral("default" ); | 
| 280 |             entry.localName = import->importedDefaultBinding.toString(); | 
| 281 |             entry.location = location(astLocation: declaration->firstSourceLocation()); | 
| 282 |             _context->importEntries << entry; | 
| 283 |         } | 
| 284 |  | 
| 285 |         if (import->nameSpaceImport) { | 
| 286 |             Compiler::ImportEntry entry; | 
| 287 |             entry.moduleRequest = module; | 
| 288 |             entry.importName = QStringLiteral("*" ); | 
| 289 |             entry.localName = import->nameSpaceImport->importedBinding.toString(); | 
| 290 |             entry.location = location(astLocation: declaration->firstSourceLocation()); | 
| 291 |             _context->importEntries << entry; | 
| 292 |         } | 
| 293 |  | 
| 294 |         if (import->namedImports) { | 
| 295 |             for (ImportsList *it = import->namedImports->importsList; it; it = it->next) { | 
| 296 |                 Compiler::ImportEntry entry; | 
| 297 |                 entry.moduleRequest = module; | 
| 298 |                 entry.localName = it->importSpecifier->importedBinding.toString(); | 
| 299 |                 if (!it->importSpecifier->identifier.isEmpty()) | 
| 300 |                     entry.importName = it->importSpecifier->identifier.toString(); | 
| 301 |                 else | 
| 302 |                     entry.importName = entry.localName; | 
| 303 |                 entry.location = location(astLocation: declaration->firstSourceLocation()); | 
| 304 |                 _context->importEntries << entry; | 
| 305 |             } | 
| 306 |         } | 
| 307 |     } | 
| 308 |     return false; | 
| 309 | } | 
| 310 |  | 
| 311 | bool ScanFunctions::visit(CallExpression *ast) | 
| 312 | { | 
| 313 |     if (!_context->hasDirectEval) { | 
| 314 |         if (IdentifierExpression *id = cast<IdentifierExpression *>(ast: ast->base)) { | 
| 315 |             if (id->name == QLatin1String("eval" )) { | 
| 316 |                 if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) | 
| 317 |                     _context->usesArgumentsObject = Context::ArgumentsObjectUsed; | 
| 318 |                 _context->hasDirectEval = true; | 
| 319 |             } | 
| 320 |         } | 
| 321 |     } | 
| 322 |     return true; | 
| 323 | } | 
| 324 |  | 
| 325 | bool ScanFunctions::visit(PatternElement *ast) | 
| 326 | { | 
| 327 |     if (!ast->isVariableDeclaration()) | 
| 328 |         return true; | 
| 329 |  | 
| 330 |     BoundNames names; | 
| 331 |     ast->boundNames(names: &names); | 
| 332 |  | 
| 333 |     QQmlJS::SourceLocation lastInitializerLocation = ast->lastSourceLocation(); | 
| 334 |     if (_context->lastBlockInitializerLocation.isValid()) | 
| 335 |         lastInitializerLocation = _context->lastBlockInitializerLocation; | 
| 336 |  | 
| 337 |     for (const auto &name : qAsConst(t&: names)) { | 
| 338 |         if (_context->isStrict && (name.id == QLatin1String("eval" ) || name.id == QLatin1String("arguments" ))) | 
| 339 |             _cg->throwSyntaxError(loc: ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode" )); | 
| 340 |         checkName(name: QStringRef(&name.id), loc: ast->identifierToken); | 
| 341 |         if (name.id == QLatin1String("arguments" )) | 
| 342 |             _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; | 
| 343 |         if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) { | 
| 344 |             _cg->throwSyntaxError(loc: ast->identifierToken, QStringLiteral("Missing initializer in const declaration" )); | 
| 345 |             return false; | 
| 346 |         } | 
| 347 |         if (!_context->addLocalVar(name: name.id, contextType: ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, scope: ast->scope, | 
| 348 |                                    /*function*/nullptr, endOfInitializer: lastInitializerLocation)) { | 
| 349 |             _cg->throwSyntaxError(loc: ast->identifierToken, QStringLiteral("Identifier %1 has already been declared" ).arg(a: name.id)); | 
| 350 |             return false; | 
| 351 |         } | 
| 352 |     } | 
| 353 |     return true; | 
| 354 | } | 
| 355 |  | 
| 356 | bool ScanFunctions::visit(IdentifierExpression *ast) | 
| 357 | { | 
| 358 |     checkName(name: ast->name, loc: ast->identifierToken); | 
| 359 |     if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments" )) | 
| 360 |         _context->usesArgumentsObject = Context::ArgumentsObjectUsed; | 
| 361 |     _context->addUsedVariable(name: ast->name.toString()); | 
| 362 |     return true; | 
| 363 | } | 
| 364 |  | 
| 365 | bool ScanFunctions::visit(ExpressionStatement *ast) | 
| 366 | { | 
| 367 |     if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast: ast->expression)) { | 
| 368 |         if (!_allowFuncDecls) | 
| 369 |             _cg->throwSyntaxError(loc: expr->functionToken, QStringLiteral("conditional function or closure declaration" )); | 
| 370 |  | 
| 371 |         if (!enterFunction(ast: expr, /*enterName*/ true)) | 
| 372 |             return false; | 
| 373 |         Node::accept(node: expr->formals, visitor: this); | 
| 374 |         Node::accept(node: expr->body, visitor: this); | 
| 375 |         leaveEnvironment(); | 
| 376 |         return false; | 
| 377 |     } else { | 
| 378 |         SourceLocation firstToken = ast->firstSourceLocation(); | 
| 379 |         if (_sourceCode.midRef(position: firstToken.offset, n: firstToken.length) == QLatin1String("function" )) { | 
| 380 |             _cg->throwSyntaxError(loc: firstToken, QStringLiteral("unexpected token" )); | 
| 381 |         } | 
| 382 |     } | 
| 383 |     return true; | 
| 384 | } | 
| 385 |  | 
| 386 | bool ScanFunctions::visit(FunctionExpression *ast) | 
| 387 | { | 
| 388 |     return enterFunction(ast, /*enterName*/ false); | 
| 389 | } | 
| 390 |  | 
| 391 | bool ScanFunctions::visit(ClassExpression *ast) | 
| 392 | { | 
| 393 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%Class" )); | 
| 394 |     _context->isStrict = true; | 
| 395 |     _context->hasNestedFunctions = true; | 
| 396 |     if (!ast->name.isEmpty()) | 
| 397 |         _context->addLocalVar(name: ast->name.toString(), contextType: Context::VariableDefinition, scope: AST::VariableScope::Const); | 
| 398 |     return true; | 
| 399 | } | 
| 400 |  | 
| 401 | void ScanFunctions::endVisit(ClassExpression *) | 
| 402 | { | 
| 403 |     leaveEnvironment(); | 
| 404 | } | 
| 405 |  | 
| 406 | bool ScanFunctions::visit(ClassDeclaration *ast) | 
| 407 | { | 
| 408 |     if (!ast->name.isEmpty()) | 
| 409 |         _context->addLocalVar(name: ast->name.toString(), contextType: Context::VariableDeclaration, scope: AST::VariableScope::Let); | 
| 410 |  | 
| 411 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%Class" )); | 
| 412 |     _context->isStrict = true; | 
| 413 |     _context->hasNestedFunctions = true; | 
| 414 |     if (!ast->name.isEmpty()) | 
| 415 |         _context->addLocalVar(name: ast->name.toString(), contextType: Context::VariableDefinition, scope: AST::VariableScope::Const); | 
| 416 |     return true; | 
| 417 | } | 
| 418 |  | 
| 419 | void ScanFunctions::endVisit(ClassDeclaration *) | 
| 420 | { | 
| 421 |     leaveEnvironment(); | 
| 422 | } | 
| 423 |  | 
| 424 | bool ScanFunctions::visit(TemplateLiteral *ast) | 
| 425 | { | 
| 426 |     while (ast) { | 
| 427 |         if (ast->expression) | 
| 428 |             Node::accept(node: ast->expression, visitor: this); | 
| 429 |         ast = ast->next; | 
| 430 |     } | 
| 431 |     return true; | 
| 432 |  | 
| 433 | } | 
| 434 |  | 
| 435 | bool ScanFunctions::visit(SuperLiteral *) | 
| 436 | { | 
| 437 |     Context *c = _context; | 
| 438 |     bool needContext = false; | 
| 439 |     while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) { | 
| 440 |         needContext |= c->isArrowFunction; | 
| 441 |         c = c->parent; | 
| 442 |     } | 
| 443 |  | 
| 444 |     c->requiresExecutionContext |= needContext; | 
| 445 |  | 
| 446 |     return false; | 
| 447 | } | 
| 448 |  | 
| 449 | bool ScanFunctions::visit(FieldMemberExpression *ast) | 
| 450 | { | 
| 451 |     if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast: ast->base)) { | 
| 452 |         if (id->name == QLatin1String("new" )) { | 
| 453 |             // new.target | 
| 454 |             if (ast->name != QLatin1String("target" )) { | 
| 455 |                 _cg->throwSyntaxError(loc: ast->identifierToken, detail: QLatin1String("Expected 'target' after 'new.'." )); | 
| 456 |                 return false; | 
| 457 |             } | 
| 458 |             Context *c = _context; | 
| 459 |             bool needContext = false; | 
| 460 |             while (c->contextType == ContextType::Block || c->isArrowFunction) { | 
| 461 |                 needContext |= c->isArrowFunction; | 
| 462 |                 c = c->parent; | 
| 463 |             } | 
| 464 |             c->requiresExecutionContext |= needContext; | 
| 465 |             c->innerFunctionAccessesNewTarget |= needContext; | 
| 466 |  | 
| 467 |             return false; | 
| 468 |         } | 
| 469 |     } | 
| 470 |  | 
| 471 |     return true; | 
| 472 | } | 
| 473 |  | 
| 474 | bool ScanFunctions::visit(ArrayPattern *ast) | 
| 475 | { | 
| 476 |     for (PatternElementList *it = ast->elements; it; it = it->next) | 
| 477 |         Node::accept(node: it->element, visitor: this); | 
| 478 |  | 
| 479 |     return false; | 
| 480 | } | 
| 481 |  | 
| 482 | bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) | 
| 483 | { | 
| 484 |     if (_context->isStrict && (ast->name == QLatin1String("eval" ) || ast->name == QLatin1String("arguments" ))) | 
| 485 |         _cg->throwSyntaxError(loc: ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode" )); | 
| 486 |     return enterFunction(ast, name: ast->name.toString(), formals: ast->formals, body: ast->body, enterName); | 
| 487 | } | 
| 488 |  | 
| 489 | void ScanFunctions::endVisit(FunctionExpression *) | 
| 490 | { | 
| 491 |     leaveEnvironment(); | 
| 492 | } | 
| 493 |  | 
| 494 | bool ScanFunctions::visit(ObjectPattern *ast) | 
| 495 | { | 
| 496 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); | 
| 497 |     Node::accept(node: ast->properties, visitor: this); | 
| 498 |     return false; | 
| 499 | } | 
| 500 |  | 
| 501 | bool ScanFunctions::visit(PatternProperty *ast) | 
| 502 | { | 
| 503 |     Q_UNUSED(ast); | 
| 504 |     // ### Shouldn't be required anymore | 
| 505 | //    if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) { | 
| 506 | //        TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); | 
| 507 | //        return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false); | 
| 508 | //    } | 
| 509 |     return true; | 
| 510 | } | 
| 511 |  | 
| 512 | void ScanFunctions::endVisit(PatternProperty *) | 
| 513 | { | 
| 514 |     // ### | 
| 515 | //    if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) | 
| 516 | //        leaveEnvironment(); | 
| 517 | } | 
| 518 |  | 
| 519 | bool ScanFunctions::visit(FunctionDeclaration *ast) | 
| 520 | { | 
| 521 |     return enterFunction(ast, /*enterName*/ true); | 
| 522 | } | 
| 523 |  | 
| 524 | void ScanFunctions::endVisit(FunctionDeclaration *) | 
| 525 | { | 
| 526 |     leaveEnvironment(); | 
| 527 | } | 
| 528 |  | 
| 529 | bool ScanFunctions::visit(DoWhileStatement *ast) { | 
| 530 |     { | 
| 531 |         TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); | 
| 532 |         Node::accept(node: ast->statement, visitor: this); | 
| 533 |     } | 
| 534 |     Node::accept(node: ast->expression, visitor: this); | 
| 535 |     return false; | 
| 536 | } | 
| 537 |  | 
| 538 | bool ScanFunctions::visit(ForStatement *ast) { | 
| 539 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%For" )); | 
| 540 |     Node::accept(node: ast->initialiser, visitor: this); | 
| 541 |     Node::accept(node: ast->declarations, visitor: this); | 
| 542 |     Node::accept(node: ast->condition, visitor: this); | 
| 543 |     Node::accept(node: ast->expression, visitor: this); | 
| 544 |  | 
| 545 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); | 
| 546 |     Node::accept(node: ast->statement, visitor: this); | 
| 547 |  | 
| 548 |     return false; | 
| 549 | } | 
| 550 |  | 
| 551 | void ScanFunctions::endVisit(ForStatement *) | 
| 552 | { | 
| 553 |     leaveEnvironment(); | 
| 554 | } | 
| 555 |  | 
| 556 | bool ScanFunctions::visit(ForEachStatement *ast) { | 
| 557 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%Foreach" )); | 
| 558 |     if (ast->expression) | 
| 559 |         _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation(); | 
| 560 |     Node::accept(node: ast->lhs, visitor: this); | 
| 561 |     Node::accept(node: ast->expression, visitor: this); | 
| 562 |  | 
| 563 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); | 
| 564 |     Node::accept(node: ast->statement, visitor: this); | 
| 565 |  | 
| 566 |     return false; | 
| 567 | } | 
| 568 |  | 
| 569 | void ScanFunctions::endVisit(ForEachStatement *) | 
| 570 | { | 
| 571 |     leaveEnvironment(); | 
| 572 | } | 
| 573 |  | 
| 574 | bool ScanFunctions::visit(ThisExpression *) | 
| 575 | { | 
| 576 |     _context->usesThis = true; | 
| 577 |     return false; | 
| 578 | } | 
| 579 |  | 
| 580 | bool ScanFunctions::visit(Block *ast) | 
| 581 | { | 
| 582 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); | 
| 583 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%Block" )); | 
| 584 |     Node::accept(node: ast->statements, visitor: this); | 
| 585 |     return false; | 
| 586 | } | 
| 587 |  | 
| 588 | void ScanFunctions::endVisit(Block *) | 
| 589 | { | 
| 590 |     leaveEnvironment(); | 
| 591 | } | 
| 592 |  | 
| 593 | bool ScanFunctions::visit(CaseBlock *ast) | 
| 594 | { | 
| 595 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%CaseBlock" )); | 
| 596 |     return true; | 
| 597 | } | 
| 598 |  | 
| 599 | void ScanFunctions::endVisit(CaseBlock *) | 
| 600 | { | 
| 601 |     leaveEnvironment(); | 
| 602 | } | 
| 603 |  | 
| 604 | bool ScanFunctions::visit(Catch *ast) | 
| 605 | { | 
| 606 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); | 
| 607 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%CatchBlock" )); | 
| 608 |     _context->isCatchBlock = true; | 
| 609 |     QString caughtVar = ast->patternElement->bindingIdentifier.toString(); | 
| 610 |     if (caughtVar.isEmpty()) | 
| 611 |         caughtVar = QStringLiteral("@caught" ); | 
| 612 |     _context->addLocalVar(name: caughtVar, contextType: Context::MemberType::VariableDefinition, scope: VariableScope::Let); | 
| 613 |  | 
| 614 |     _context->caughtVariable = caughtVar; | 
| 615 |     if (_context->isStrict && | 
| 616 |         (caughtVar == QLatin1String("eval" ) || caughtVar == QLatin1String("arguments" ))) { | 
| 617 |         _cg->throwSyntaxError(loc: ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode" )); | 
| 618 |         return false; | 
| 619 |     } | 
| 620 |     Node::accept(node: ast->patternElement, visitor: this); | 
| 621 |     // skip the block statement | 
| 622 |     Node::accept(node: ast->statement->statements, visitor: this); | 
| 623 |     return false; | 
| 624 | } | 
| 625 |  | 
| 626 | void ScanFunctions::endVisit(Catch *) | 
| 627 | { | 
| 628 |     leaveEnvironment(); | 
| 629 | } | 
| 630 |  | 
| 631 | bool ScanFunctions::visit(WithStatement *ast) | 
| 632 | { | 
| 633 |     Node::accept(node: ast->expression, visitor: this); | 
| 634 |  | 
| 635 |     TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); | 
| 636 |     enterEnvironment(node: ast, compilationMode: ContextType::Block, QStringLiteral("%WithBlock" )); | 
| 637 |     _context->isWithBlock = true; | 
| 638 |  | 
| 639 |     if (_context->isStrict) { | 
| 640 |         _cg->throwSyntaxError(loc: ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode" )); | 
| 641 |         return false; | 
| 642 |     } | 
| 643 |     Node::accept(node: ast->statement, visitor: this); | 
| 644 |  | 
| 645 |     return false; | 
| 646 | } | 
| 647 |  | 
| 648 | void ScanFunctions::endVisit(WithStatement *) | 
| 649 | { | 
| 650 |     leaveEnvironment(); | 
| 651 | } | 
| 652 |  | 
| 653 | bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName) | 
| 654 | { | 
| 655 |     Context *outerContext = _context; | 
| 656 |     enterEnvironment(node: ast, compilationMode: ContextType::Function, name); | 
| 657 |  | 
| 658 |     FunctionExpression *expr = AST::cast<FunctionExpression *>(ast); | 
| 659 |     if (!expr) | 
| 660 |         expr = AST::cast<FunctionDeclaration *>(ast); | 
| 661 |     if (outerContext) { | 
| 662 |         outerContext->hasNestedFunctions = true; | 
| 663 |         // The identifier of a function expression cannot be referenced from the enclosing environment. | 
| 664 |         if (enterName) { | 
| 665 |             if (!outerContext->addLocalVar(name, contextType: Context::FunctionDefinition, scope: VariableScope::Var, function: expr)) { | 
| 666 |                 _cg->throwSyntaxError(loc: ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared" ).arg(a: name)); | 
| 667 |                 return false; | 
| 668 |             } | 
| 669 |             outerContext->addLocalVar(name, contextType: Context::FunctionDefinition, scope: VariableScope::Var, function: expr); | 
| 670 |         } | 
| 671 |         if (name == QLatin1String("arguments" )) | 
| 672 |             outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed; | 
| 673 |     } | 
| 674 |  | 
| 675 |     _context->name = name; | 
| 676 |     if (formals && formals->containsName(QStringLiteral("arguments" ))) | 
| 677 |         _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; | 
| 678 |     if (expr) { | 
| 679 |         if (expr->isArrowFunction) | 
| 680 |             _context->isArrowFunction = true; | 
| 681 |         else if (expr->isGenerator) | 
| 682 |             _context->isGenerator = true; | 
| 683 |  | 
| 684 |         if (expr->typeAnnotation) | 
| 685 |             _context->returnType = expr->typeAnnotation->type->toString(); | 
| 686 |     } | 
| 687 |  | 
| 688 |  | 
| 689 |     if (!enterName && (!name.isEmpty() && (!formals || !formals->containsName(name)))) | 
| 690 |         _context->addLocalVar(name, contextType: Context::ThisFunctionName, scope: VariableScope::Var); | 
| 691 |     _context->formals = formals; | 
| 692 |  | 
| 693 |     if (body && !_context->isStrict) | 
| 694 |         checkDirectivePrologue(ast: body); | 
| 695 |  | 
| 696 |     bool isSimpleParameterList = formals && formals->isSimpleParameterList(); | 
| 697 |  | 
| 698 |     _context->arguments = formals ? formals->formals() : BoundNames(); | 
| 699 |  | 
| 700 |     const BoundNames boundNames = formals ? formals->boundNames() : BoundNames(); | 
| 701 |     for (int i = 0; i < boundNames.size(); ++i) { | 
| 702 |         const QString &arg = boundNames.at(i).id; | 
| 703 |         if (_context->isStrict || !isSimpleParameterList) { | 
| 704 |             bool duplicate = (boundNames.indexOf(name: arg, from: i + 1) != -1); | 
| 705 |             if (duplicate) { | 
| 706 |                 _cg->throwSyntaxError(loc: formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed." ).arg(a: arg)); | 
| 707 |                 return false; | 
| 708 |             } | 
| 709 |         } | 
| 710 |         if (_context->isStrict) { | 
| 711 |             if (arg == QLatin1String("eval" ) || arg == QLatin1String("arguments" )) { | 
| 712 |                 _cg->throwSyntaxError(loc: formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode" ).arg(a: arg)); | 
| 713 |                 return false; | 
| 714 |             } | 
| 715 |         } | 
| 716 |         if (!_context->arguments.contains(name: arg)) | 
| 717 |             _context->addLocalVar(name: arg, contextType: Context::VariableDefinition, scope: VariableScope::Var); | 
| 718 |     } | 
| 719 |  | 
| 720 |     return true; | 
| 721 | } | 
| 722 |  | 
| 723 | void ScanFunctions::calcEscapingVariables() | 
| 724 | { | 
| 725 |     Module *m = _cg->_module; | 
| 726 |  | 
| 727 |     for (Context *inner : qAsConst(t&: m->contextMap)) { | 
| 728 |         if (inner->usesArgumentsObject != Context::ArgumentsObjectUsed) | 
| 729 |             continue; | 
| 730 |         if (inner->contextType != ContextType::Block && !inner->isArrowFunction) | 
| 731 |             continue; | 
| 732 |         Context *c = inner->parent; | 
| 733 |         while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) | 
| 734 |             c = c->parent; | 
| 735 |         if (c) | 
| 736 |             c->usesArgumentsObject = Context::ArgumentsObjectUsed; | 
| 737 |         inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; | 
| 738 |     } | 
| 739 |     for (Context *inner : qAsConst(t&: m->contextMap)) { | 
| 740 |         if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown) | 
| 741 |             inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; | 
| 742 |         if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) { | 
| 743 |             QString arguments = QStringLiteral("arguments" ); | 
| 744 |             inner->addLocalVar(name: arguments, contextType: Context::VariableDeclaration, scope: AST::VariableScope::Var); | 
| 745 |             if (!inner->isStrict) { | 
| 746 |                 inner->argumentsCanEscape = true; | 
| 747 |                 inner->requiresExecutionContext = true; | 
| 748 |             } | 
| 749 |         } | 
| 750 |     } | 
| 751 |  | 
| 752 |     for (Context *c : qAsConst(t&: m->contextMap)) { | 
| 753 |         if (c->contextType != ContextType::ESModule) | 
| 754 |             continue; | 
| 755 |         for (const auto &entry: c->exportEntries) { | 
| 756 |             auto mIt = c->members.constFind(akey: entry.localName); | 
| 757 |             if (mIt != c->members.constEnd()) | 
| 758 |                 mIt->canEscape = true; | 
| 759 |         } | 
| 760 |         break; | 
| 761 |     } | 
| 762 |  | 
| 763 |     for (Context *inner : qAsConst(t&: m->contextMap)) { | 
| 764 |         for (const QString &var : qAsConst(t&: inner->usedVariables)) { | 
| 765 |             Context *c = inner; | 
| 766 |             while (c) { | 
| 767 |                 Context *current = c; | 
| 768 |                 c = c->parent; | 
| 769 |                 if (current->isWithBlock || current->contextType != ContextType::Block) | 
| 770 |                     break; | 
| 771 |             } | 
| 772 |             Q_ASSERT(c != inner); | 
| 773 |             while (c) { | 
| 774 |                 Context::MemberMap::const_iterator it = c->members.constFind(akey: var); | 
| 775 |                 if (it != c->members.constEnd()) { | 
| 776 |                     if (c->parent || it->isLexicallyScoped()) { | 
| 777 |                         it->canEscape = true; | 
| 778 |                         c->requiresExecutionContext = true; | 
| 779 |                     } else if (c->contextType == ContextType::ESModule) { | 
| 780 |                         // Module instantiation provides a context, but vars used from inner | 
| 781 |                         // scopes need to be stored in its locals[]. | 
| 782 |                         it->canEscape = true; | 
| 783 |                     } | 
| 784 |                     break; | 
| 785 |                 } | 
| 786 |                 if (c->findArgument(name: var) != -1) { | 
| 787 |                     c->argumentsCanEscape = true; | 
| 788 |                     c->requiresExecutionContext = true; | 
| 789 |                     break; | 
| 790 |                 } | 
| 791 |                 c = c->parent; | 
| 792 |             } | 
| 793 |         } | 
| 794 |         if (inner->hasDirectEval) { | 
| 795 |             inner->hasDirectEval = false; | 
| 796 |             inner->innerFunctionAccessesNewTarget = true; | 
| 797 |             if (!inner->isStrict) { | 
| 798 |                 Context *c = inner; | 
| 799 |                 while (c->contextType == ContextType::Block) { | 
| 800 |                     c = c->parent; | 
| 801 |                 } | 
| 802 |                 Q_ASSERT(c); | 
| 803 |                 c->hasDirectEval = true; | 
| 804 |                 c->innerFunctionAccessesThis = true; | 
| 805 |             } | 
| 806 |             Context *c = inner; | 
| 807 |             while (c) { | 
| 808 |                 c->allVarsEscape = true; | 
| 809 |                 c = c->parent; | 
| 810 |             } | 
| 811 |         } | 
| 812 |         if (inner->usesThis) { | 
| 813 |             inner->usesThis = false; | 
| 814 |             bool innerFunctionAccessesThis = false; | 
| 815 |             Context *c = inner; | 
| 816 |             while (c->contextType == ContextType::Block || c->isArrowFunction) { | 
| 817 |                 innerFunctionAccessesThis |= c->isArrowFunction; | 
| 818 |                 c = c->parent; | 
| 819 |             } | 
| 820 |             Q_ASSERT(c); | 
| 821 |             if (!inner->isStrict) | 
| 822 |                 c->usesThis = true; | 
| 823 |             c->innerFunctionAccessesThis |= innerFunctionAccessesThis; | 
| 824 |         } | 
| 825 |     } | 
| 826 |     for (Context *c : qAsConst(t&: m->contextMap)) { | 
| 827 |         if (c->innerFunctionAccessesThis) { | 
| 828 |             // add an escaping 'this' variable | 
| 829 |             c->addLocalVar(QStringLiteral("this" ), contextType: Context::VariableDefinition, scope: VariableScope::Let); | 
| 830 |             c->requiresExecutionContext = true; | 
| 831 |             auto mIt = c->members.constFind(QStringLiteral("this" )); | 
| 832 |             Q_ASSERT(mIt != c->members.constEnd()); | 
| 833 |             mIt->canEscape = true; | 
| 834 |         } | 
| 835 |         if (c->innerFunctionAccessesNewTarget) { | 
| 836 |             // add an escaping 'new.target' variable | 
| 837 |             c->addLocalVar(QStringLiteral("new.target" ), contextType: Context::VariableDefinition, scope: VariableScope::Let); | 
| 838 |             c->requiresExecutionContext = true; | 
| 839 |             auto mIt = c->members.constFind(QStringLiteral("new.target" )); | 
| 840 |             Q_ASSERT(mIt != c->members.constEnd()); | 
| 841 |             mIt->canEscape = true; | 
| 842 |         } | 
| 843 |         if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty()) | 
| 844 |             c->allVarsEscape = false; | 
| 845 |         if (c->contextType == ContextType::Global || c->contextType == ContextType::ScriptImportedByQML || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode) | 
| 846 |             c->allVarsEscape = true; | 
| 847 |         if (c->allVarsEscape) { | 
| 848 |             if (c->parent) { | 
| 849 |                 c->requiresExecutionContext = true; | 
| 850 |                 c->argumentsCanEscape = true; | 
| 851 |             } else { | 
| 852 |                 for (const auto &m : qAsConst(t&: c->members)) { | 
| 853 |                     if (m.isLexicallyScoped()) { | 
| 854 |                         c->requiresExecutionContext = true; | 
| 855 |                         break; | 
| 856 |                     } | 
| 857 |                 } | 
| 858 |             } | 
| 859 |         } | 
| 860 |         if (c->contextType == ContextType::Block && c->isCatchBlock) { | 
| 861 |             c->requiresExecutionContext = true; | 
| 862 |             auto mIt = c->members.constFind(akey: c->caughtVariable); | 
| 863 |             Q_ASSERT(mIt != c->members.constEnd()); | 
| 864 |             mIt->canEscape = true; | 
| 865 |         } | 
| 866 |         const QLatin1String exprForOn("expression for on" ); | 
| 867 |         if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() && | 
| 868 |             c->name.startsWith(s: exprForOn) && c->name.at(i: exprForOn.size()).isUpper()) | 
| 869 |             // we don't really need this for bindings, but we do for signal handlers, and in this case, | 
| 870 |             // we don't know if the code is a signal handler or not. | 
| 871 |             c->requiresExecutionContext = true; | 
| 872 |         if (c->allVarsEscape) { | 
| 873 |             for (const auto &m : qAsConst(t&: c->members)) | 
| 874 |                 m.canEscape = true; | 
| 875 |         } | 
| 876 |     } | 
| 877 |  | 
| 878 |     static const bool showEscapingVars = qEnvironmentVariableIsSet(varName: "QV4_SHOW_ESCAPING_VARS" ); | 
| 879 |     if (showEscapingVars) { | 
| 880 |         qDebug() << "==== escaping variables ====" ; | 
| 881 |         for (Context *c : qAsConst(t&: m->contextMap)) { | 
| 882 |             qDebug() << "Context"  << c << c->name << "requiresExecutionContext"  << c->requiresExecutionContext << "isStrict"  << c->isStrict; | 
| 883 |             qDebug() << "    isArrowFunction"  << c->isArrowFunction << "innerFunctionAccessesThis"  << c->innerFunctionAccessesThis; | 
| 884 |             qDebug() << "    parent:"  << c->parent; | 
| 885 |             if (c->argumentsCanEscape) | 
| 886 |                 qDebug() << "    Arguments escape" ; | 
| 887 |             for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { | 
| 888 |                 qDebug() << "    "  << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:"  << it.value().isLexicallyScoped(); | 
| 889 |             } | 
| 890 |         } | 
| 891 |     } | 
| 892 | } | 
| 893 |  | 
| 894 | void ScanFunctions::throwRecursionDepthError() | 
| 895 | { | 
| 896 |     _cg->throwRecursionDepthError(); | 
| 897 | } | 
| 898 |  |