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