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
646 Node *ast, const QString &name, FormalParameterList *formals, StatementList *body,
647 FunctionNameContext nameContext)
648{
649 Context *outerContext = _context;
650 enterEnvironment(ast, ContextType::Function, name);
651
652 FunctionExpression *expr = AST::cast<FunctionExpression *>(ast);
653 if (!expr)
654 expr = AST::cast<FunctionDeclaration *>(ast);
655 if (outerContext) {
656 outerContext->hasNestedFunctions = true;
657 // The identifier of a function expression cannot be referenced from the enclosing environment.
658 if (nameContext == FunctionNameContext::Outer) {
659 if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var,
660 expr, expr->identifierToken)) {
661 _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
662 return false;
663 }
664 outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
665 }
666 if (name == QLatin1String("arguments"))
667 outerContext->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
668 }
669
670 Q_ASSERT(_context);
671 _context->name = name;
672 if (formals && formals->containsName(QStringLiteral("arguments")))
673 _context->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
674 if (expr) {
675 if (expr->isArrowFunction)
676 _context->isArrowFunction = true;
677 else if (expr->isGenerator)
678 _context->isGenerator = true;
679
680 if (expr->typeAnnotation)
681 _context->returnType = expr->typeAnnotation->type;
682 }
683
684
685 if (nameContext == FunctionNameContext::Inner
686 && (!name.isEmpty() && (!formals || !formals->containsName(name)))) {
687 _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
688 }
689 _context->formals = formals;
690
691 if (body && !_context->isStrict)
692 checkDirectivePrologue(body);
693
694 bool isSimpleParameterList = formals && formals->isSimpleParameterList();
695
696 _context->arguments = formals ? formals->formals() : BoundNames();
697
698 const BoundNames boundNames = formals ? formals->boundNames() : BoundNames();
699 for (int i = 0; i < boundNames.size(); ++i) {
700 const auto &arg = boundNames.at(i);
701 if (_context->isStrict || !isSimpleParameterList) {
702 bool duplicate = (boundNames.indexOf(arg.id, i + 1) != -1);
703 if (duplicate) {
704 _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg.id));
705 return false;
706 }
707 }
708 if (_context->isStrict) {
709 if (arg.id == QLatin1String("eval") || arg.id == QLatin1String("arguments")) {
710 _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg.id));
711 return false;
712 }
713 }
714 if (!_context->arguments.contains(arg.id)) {
715 _context->addLocalVar(arg.id, Context::VariableDefinition, VariableScope::Var, nullptr,
716 QQmlJS::SourceLocation(), arg.isInjected());
717 }
718 }
719
720 return true;
721}
722
723enum class Iteration { Continue, Break };
724
726{
728#ifdef QT_NO_DEBUG
730 void operator++() {}
731 void dismiss() {}
732#else
735
736 void operator++() { numContexts++; }
738
739private:
740 Module *module = nullptr;
741 qsizetype numContexts = 0;
742#endif
743};
744
745
746template<typename Action>
747void forEachContext(Module *m, Action &&action)
748{
749 ContextCounter counter(m);
750
751 if (!m->rootContext)
752 return;
753
754 QVarLengthArray<Context *> stack {m->rootContext};
755 do {
756 Context *current = stack.back();
757 stack.pop_back();
758
759 for (Context *child : std::as_const(current->nestedContexts))
760 stack.push_back(child);
761
762 if (action(current) == Iteration::Break) {
763 counter.dismiss();
764 return;
765 }
766
767 ++counter;
768 } while (!stack.empty());
769}
770
772{
773 Module *m = _cg->_module;
774
775 forEachContext(m, [](Context *inner) {
776 if (inner->usesArgumentsObject != Context::UsesArgumentsObject::Used)
777 return Iteration::Continue;
778 if (inner->contextType != ContextType::Block && !inner->isArrowFunction)
779 return Iteration::Continue;
780 Context *c = inner->parent;
781 while (c && (c->contextType == ContextType::Block || c->isArrowFunction))
782 c = c->parent;
783 if (c)
784 c->usesArgumentsObject = Context::UsesArgumentsObject::Used;
785 inner->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
786 return Iteration::Continue;
787 });
788
789 forEachContext(m, [](Context *inner) {
790 if (!inner->parent || inner->usesArgumentsObject == Context::UsesArgumentsObject::Unknown)
791 inner->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
792 if (inner->usesArgumentsObject == Context::UsesArgumentsObject::Used) {
793 QString arguments = QStringLiteral("arguments");
794 inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
795 if (!inner->isStrict) {
796 inner->argumentsCanEscape = true;
797 inner->requiresExecutionContext = true;
798 }
799 }
800 return Iteration::Continue;
801 });
802
803 forEachContext(m, [](Context *c) {
804 if (c->contextType != ContextType::ESModule)
805 return Iteration::Continue;
806 for (const auto &entry: std::as_const(c->exportEntries)) {
807 auto mIt = c->members.constFind(entry.localName);
808 if (mIt != c->members.constEnd())
809 mIt->canEscape = true;
810 }
811 return Iteration::Break;
812 });
813
814 forEachContext(m, [](Context *inner) {
815 for (const QString &var : std::as_const(inner->usedVariables)) {
816 Context *c = inner;
817 while (c) {
818 Context *current = c;
819 c = c->parent;
820 if (current->isWithBlock || current->contextType != ContextType::Block)
821 break;
822 }
823 Q_ASSERT(c != inner);
824 while (c) {
825 Context::MemberMap::const_iterator it = c->members.constFind(var);
826 if (it != c->members.constEnd()) {
827 if (c->parent || it->isLexicallyScoped()) {
828 it->canEscape = true;
829 c->requiresExecutionContext = true;
830 } else if (c->contextType == ContextType::ESModule) {
831 // Module instantiation provides a context, but vars used from inner
832 // scopes need to be stored in its locals[].
833 it->canEscape = true;
834 }
835 break;
836 }
837 if (c->hasArgument(var)) {
838 c->argumentsCanEscape = true;
839 c->requiresExecutionContext = true;
840 break;
841 }
842 c = c->parent;
843 }
844 }
845 if (inner->hasDirectEval) {
846 inner->hasDirectEval = false;
847 inner->innerFunctionAccessesNewTarget = true;
848 if (!inner->isStrict) {
849 Context *c = inner;
850 while (c->contextType == ContextType::Block) {
851 c = c->parent;
852 }
853 Q_ASSERT(c);
854 c->hasDirectEval = true;
855 c->innerFunctionAccessesThis = true;
856 }
857 Context *c = inner;
858 while (c) {
859 c->allVarsEscape = true;
860 c = c->parent;
861 }
862 }
863 if (inner->usesThis) {
864 inner->usesThis = false;
865 bool innerFunctionAccessesThis = false;
866 Context *c = inner;
867 while (c->contextType == ContextType::Block || c->isArrowFunction) {
868 innerFunctionAccessesThis |= c->isArrowFunction;
869 c = c->parent;
870 }
871 Q_ASSERT(c);
872 if (!inner->isStrict)
873 c->usesThis = true;
874 c->innerFunctionAccessesThis |= innerFunctionAccessesThis;
875 }
876
877 return Iteration::Continue;
878 });
879
880 forEachContext(m, [m](Context *c) {
881 if (c->innerFunctionAccessesThis) {
882 // add an escaping 'this' variable
883 c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let);
884 c->requiresExecutionContext = true;
885 auto mIt = c->members.constFind(QStringLiteral("this"));
886 Q_ASSERT(mIt != c->members.constEnd());
887 mIt->canEscape = true;
888 }
889 if (c->innerFunctionAccessesNewTarget) {
890 // add an escaping 'new.target' variable
891 c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
892 c->requiresExecutionContext = true;
893 auto mIt = c->members.constFind(QStringLiteral("new.target"));
894 Q_ASSERT(mIt != c->members.constEnd());
895 mIt->canEscape = true;
896 }
897 if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
898 c->allVarsEscape = false;
899 if (c->contextType == ContextType::Global || c->contextType == ContextType::ScriptImportedByQML || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
900 c->allVarsEscape = true;
901 if (c->allVarsEscape) {
902 if (c->parent) {
903 c->requiresExecutionContext = true;
904 c->argumentsCanEscape = true;
905 } else {
906 for (const auto &m : std::as_const(c->members)) {
907 if (m.isLexicallyScoped()) {
908 c->requiresExecutionContext = true;
909 break;
910 }
911 }
912 }
913 }
914 if (c->contextType == ContextType::Block && c->isCatchBlock) {
915 c->requiresExecutionContext = true;
916 auto mIt = c->members.constFind(c->caughtVariable);
917 Q_ASSERT(mIt != c->members.constEnd());
918 mIt->canEscape = true;
919 }
920 const QLatin1String exprForOn("expression for on");
921 if (c->contextType == ContextType::Binding && c->name.size() > exprForOn.size() &&
922 c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper())
923 // we don't really need this for bindings, but we do for signal handlers, and in this case,
924 // we don't know if the code is a signal handler or not.
925 c->requiresExecutionContext = true;
926 if (c->allVarsEscape) {
927 for (const auto &m : std::as_const(c->members))
928 m.canEscape = true;
929 }
930
931 return Iteration::Continue;
932 });
933
934 static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS");
935 if (showEscapingVars) {
936 qDebug() << "==== escaping variables ====";
937 for (Context *c : std::as_const(m->contextMap)) {
938 qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
939 qDebug() << " isArrowFunction" << c->isArrowFunction << "innerFunctionAccessesThis" << c->innerFunctionAccessesThis;
940 qDebug() << " parent:" << c->parent;
941 if (c->argumentsCanEscape)
942 qDebug() << " Arguments escape";
943 for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
944 qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
945 }
946 }
947 }
948}
949
951{
952 _cg->throwRecursionDepthError();
953}
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)