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

source code of qtdeclarative/src/qml/compiler/qv4compilerscanfunctions.cpp