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
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)
30 , _cg(cg)
32 , _context(nullptr)
33 , _allowFuncDecls(true)
35{
36}
37
38void ScanFunctions::operator()(Node *node)
39{
40 if (node)
41 node->accept(this);
42
44}
45
46void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode)
47{
48 enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode"));
49}
50
51void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name)
52{
53 Context *c = _cg->_module->contextMap.value(node);
54 if (!c)
55 c = _cg->_module->newContext(node, _context, compilationMode);
56 if (!c->isStrict)
57 c->isStrict = _cg->_strictMode;
58 c->name = name;
59 _contextStack.append(c);
60 _context = c;
61}
62
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 *>(it->statement)) {
74 if (StringLiteral *strLit = cast<StringLiteral *>(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(strLit->literalToken.offset + 1, 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(ast, defaultProgramType, QStringLiteral("%ProgramCode"));
115 checkDirectivePrologue(ast->statements);
116 return true;
117}
118
119void ScanFunctions::endVisit(Program *)
120{
122}
123
124bool ScanFunctions::visit(ESModule *ast)
125{
126 enterEnvironment(ast, defaultProgramType, QStringLiteral("%ModuleCode"));
127 Q_ASSERT(_context);
128 _context->isStrict = true;
129 return true;
130}
131
132void ScanFunctions::endVisit(ESModule *)
133{
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(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(it->firstSourceLocation());
169
170 _context->exportEntries << entry;
171 }
172 } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(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(&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(vstmt->firstSourceLocation());
184 _context->exportEntries << entry;
185 }
186 } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(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(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(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(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(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(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(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->base)) {
284 if (id->name == QLatin1String("eval")) {
285 if (_context->usesArgumentsObject == Context::UsesArgumentsObject::Unknown)
286 _context->usesArgumentsObject = Context::UsesArgumentsObject::Used;
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);
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(names)) {
312 if (_context->isStrict && (name.id == QLatin1String("eval") || name.id == QLatin1String("arguments")))
313 _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
314 checkName(QStringView(name.id), ast->identifierToken);
315 if (name.id == QLatin1String("arguments"))
316 _context->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
317 if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) {
318 _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
319 return false;
320 }
321 if (!_context->addLocalVar(name.id, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
322 /*function*/nullptr, declarationLocation)) {
323 _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name.id));
324 return false;
325 }
326 }
327 return true;
328}
329
330bool ScanFunctions::visit(IdentifierExpression *ast)
331{
332 Q_ASSERT(_context);
333 checkName(ast->name, ast->identifierToken);
334 if (_context->usesArgumentsObject == Context::UsesArgumentsObject::Unknown && ast->name == QLatin1String("arguments"))
335 _context->usesArgumentsObject = Context::UsesArgumentsObject::Used;
336 _context->addUsedVariable(ast->name.toString());
337 return true;
338}
339
340bool ScanFunctions::visit(ExpressionStatement *ast)
341{
342 if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
343 if (!_allowFuncDecls)
344 _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
345
346 if (!enterFunction(expr, expr->identifierToken.length > 0
347 ? FunctionNameContext::Inner
348 : FunctionNameContext::None)) {
349 return false;
350 }
351 Node::accept(expr->formals, this);
352 Node::accept(expr->body, this);
354 return false;
355 } else {
356 SourceLocation firstToken = ast->firstSourceLocation();
357 if (QStringView{_sourceCode}.mid(firstToken.offset, firstToken.length) == QLatin1String("function")) {
358 _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token"));
359 }
360 }
361 return true;
362}
363
364bool ScanFunctions::visit(FunctionExpression *ast)
365{
366 return enterFunction(ast, ast->identifierToken.length > 0
367 ? FunctionNameContext::Inner
368 : FunctionNameContext::None);
369}
370
371bool ScanFunctions::visit(ClassExpression *ast)
372{
373 enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
374 Q_ASSERT(_context);
375 _context->isStrict = true;
376 _context->hasNestedFunctions = true;
377 if (!ast->name.isEmpty())
378 _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
379 return true;
380}
381
382void ScanFunctions::endVisit(ClassExpression *)
383{
385}
386
387bool ScanFunctions::visit(ClassDeclaration *ast)
388{
389 Q_ASSERT(_context);
390 if (!ast->name.isEmpty())
391 _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let);
392
393 enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
394 _context->isStrict = true;
395 _context->hasNestedFunctions = true;
396 if (!ast->name.isEmpty())
397 _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
398 return true;
399}
400
401void ScanFunctions::endVisit(ClassDeclaration *)
402{
404}
405
406bool ScanFunctions::visit(TemplateLiteral *ast)
407{
408 while (ast) {
409 if (ast->expression)
410 Node::accept(ast->expression, 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->base)) {
435 if (id->name == QLatin1String("new")) {
436 // new.target
437 if (ast->name != QLatin1String("target")) {
438 _cg->throwSyntaxError(ast->identifierToken, 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(it->element, 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(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
471 return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, nameContext);
472}
473
474void ScanFunctions::endVisit(FunctionExpression *)
475{
477}
478
479bool ScanFunctions::visit(ObjectPattern *ast)
480{
481 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
482 Node::accept(ast->properties, 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, FunctionNameContext::Outer);
507}
508
509void ScanFunctions::endVisit(FunctionDeclaration *)
510{
512}
513
514bool ScanFunctions::visit(DoWhileStatement *ast) {
515 {
516 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
517 Node::accept(ast->statement, this);
518 }
519 Node::accept(ast->expression, this);
520 return false;
521}
522
523bool ScanFunctions::visit(ForStatement *ast) {
524 enterEnvironment(ast, ContextType::Block, QStringLiteral("%For"));
525 Node::accept(ast->initialiser, this);
526 Node::accept(ast->declarations, this);
527 Node::accept(ast->condition, this);
528 Node::accept(ast->expression, this);
529
530 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
531 Node::accept(ast->statement, this);
532
533 return false;
534}
535
536void ScanFunctions::endVisit(ForStatement *)
537{
539}
540
541bool ScanFunctions::visit(ForEachStatement *ast)
542{
543 enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
544 if (ast->expression) {
545 Q_ASSERT(_context);
546 _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation();
547 }
548 Node::accept(ast->lhs, this);
549 Node::accept(ast->expression, this);
550
551 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
552 Node::accept(ast->statement, this);
553
554 return false;
555}
556
557void ScanFunctions::endVisit(ForEachStatement *)
558{
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(ast, ContextType::Block, QStringLiteral("%Block"));
573 Node::accept(ast->statements, this);
574 return false;
575}
576
578{
580}
581
582bool ScanFunctions::visit(CaseBlock *ast)
583{
584 enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock"));
585 return true;
586}
587
588void ScanFunctions::endVisit(CaseBlock *)
589{
591}
592
593bool ScanFunctions::visit(Catch *ast)
594{
595 Q_ASSERT(_context);
596 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
597 enterEnvironment(ast, 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(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let);
603
604 _context->caughtVariable = caughtVar;
605 if (_context->isStrict &&
606 (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) {
607 _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
608 return false;
609 }
610 Node::accept(ast->patternElement, this);
611 // skip the block statement
612 Node::accept(ast->statement->statements, this);
613 return false;
614}
615
617{
619}
620
621bool ScanFunctions::visit(WithStatement *ast)
622{
623 Q_ASSERT(_context);
624 Node::accept(ast->expression, this);
625
626 TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
627 enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock"));
628 _context->isWithBlock = true;
629
630 if (_context->isStrict) {
631 _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
632 return false;
633 }
634 Node::accept(ast->statement, this);
635
636 return false;
637}
638
639void ScanFunctions::endVisit(WithStatement *)
640{
642}
643
645 Node *ast, const QString &name, FormalParameterList *formals, StatementList *body,
646 FunctionNameContext nameContext)
647{
648 Context *outerContext = _context;
649 enterEnvironment(ast, 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, Context::FunctionDefinition, VariableScope::Var,
659 expr, expr->identifierToken)) {
660 _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
661 return false;
662 }
663 outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
664 }
665 if (name == QLatin1String("arguments"))
666 outerContext->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
667 }
668
669 Q_ASSERT(_context);
670 _context->name = name;
671 if (formals && formals->containsName(QStringLiteral("arguments")))
672 _context->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
673 if (expr) {
674 if (expr->isArrowFunction)
675 _context->isArrowFunction = true;
676 else if (expr->isGenerator)
677 _context->isGenerator = true;
678
679 if (expr->typeAnnotation)
680 _context->returnType = expr->typeAnnotation->type;
681 }
682
683
684 if (nameContext == FunctionNameContext::Inner
685 && (!name.isEmpty() && (!formals || !formals->containsName(name)))) {
686 _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
687 }
688 _context->formals = formals;
689
690 if (body && !_context->isStrict)
691 checkDirectivePrologue(body);
692
693 bool isSimpleParameterList = formals && formals->isSimpleParameterList();
694
695 _context->arguments = formals ? formals->formals() : BoundNames();
696
697 const BoundNames boundNames = formals ? formals->boundNames() : BoundNames();
698 for (int i = 0; i < boundNames.size(); ++i) {
699 const auto &arg = boundNames.at(i);
700 if (_context->isStrict || !isSimpleParameterList) {
701 bool duplicate = (boundNames.indexOf(arg.id, i + 1) != -1);
702 if (duplicate) {
703 _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg.id));
704 return false;
705 }
706 }
707 if (_context->isStrict) {
708 if (arg.id == QLatin1String("eval") || arg.id == QLatin1String("arguments")) {
709 _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg.id));
710 return false;
711 }
712 }
713 if (!_context->arguments.contains(arg.id)) {
714 _context->addLocalVar(arg.id, Context::VariableDefinition, VariableScope::Var, nullptr,
715 QQmlJS::SourceLocation(), arg.isInjected());
716 }
717 }
718
719 return true;
720}
721
723{
724 Module *m = _cg->_module;
725
726 for (Context *inner : std::as_const(m->contextMap)) {
727 if (inner->usesArgumentsObject != Context::UsesArgumentsObject::Used)
728 continue;
729 if (inner->contextType != ContextType::Block && !inner->isArrowFunction)
730 continue;
731 Context *c = inner->parent;
732 while (c && (c->contextType == ContextType::Block || c->isArrowFunction))
733 c = c->parent;
734 if (c)
735 c->usesArgumentsObject = Context::UsesArgumentsObject::Used;
736 inner->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
737 }
738 for (Context *inner : std::as_const(m->contextMap)) {
739 if (!inner->parent || inner->usesArgumentsObject == Context::UsesArgumentsObject::Unknown)
740 inner->usesArgumentsObject = Context::UsesArgumentsObject::NotUsed;
741 if (inner->usesArgumentsObject == Context::UsesArgumentsObject::Used) {
742 QString arguments = QStringLiteral("arguments");
743 inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
744 if (!inner->isStrict) {
745 inner->argumentsCanEscape = true;
746 inner->requiresExecutionContext = true;
747 }
748 }
749 }
750
751 for (Context *c : std::as_const(m->contextMap)) {
752 if (c->contextType != ContextType::ESModule)
753 continue;
754 for (const auto &entry: std::as_const(c->exportEntries)) {
755 auto mIt = c->members.constFind(entry.localName);
756 if (mIt != c->members.constEnd())
757 mIt->canEscape = true;
758 }
759 break;
760 }
761
762 for (Context *inner : std::as_const(m->contextMap)) {
763 for (const QString &var : std::as_const(inner->usedVariables)) {
764 Context *c = inner;
765 while (c) {
766 Context *current = c;
767 c = c->parent;
768 if (current->isWithBlock || current->contextType != ContextType::Block)
769 break;
770 }
771 Q_ASSERT(c != inner);
772 while (c) {
773 Context::MemberMap::const_iterator it = c->members.constFind(var);
774 if (it != c->members.constEnd()) {
775 if (c->parent || it->isLexicallyScoped()) {
776 it->canEscape = true;
777 c->requiresExecutionContext = true;
778 } else if (c->contextType == ContextType::ESModule) {
779 // Module instantiation provides a context, but vars used from inner
780 // scopes need to be stored in its locals[].
781 it->canEscape = true;
782 }
783 break;
784 }
785 if (c->hasArgument(var)) {
786 c->argumentsCanEscape = true;
787 c->requiresExecutionContext = true;
788 break;
789 }
790 c = c->parent;
791 }
792 }
793 if (inner->hasDirectEval) {
794 inner->hasDirectEval = false;
795 inner->innerFunctionAccessesNewTarget = true;
796 if (!inner->isStrict) {
797 Context *c = inner;
798 while (c->contextType == ContextType::Block) {
799 c = c->parent;
800 }
801 Q_ASSERT(c);
802 c->hasDirectEval = true;
803 c->innerFunctionAccessesThis = true;
804 }
805 Context *c = inner;
806 while (c) {
807 c->allVarsEscape = true;
808 c = c->parent;
809 }
810 }
811 if (inner->usesThis) {
812 inner->usesThis = false;
813 bool innerFunctionAccessesThis = false;
814 Context *c = inner;
815 while (c->contextType == ContextType::Block || c->isArrowFunction) {
816 innerFunctionAccessesThis |= c->isArrowFunction;
817 c = c->parent;
818 }
819 Q_ASSERT(c);
820 if (!inner->isStrict)
821 c->usesThis = true;
822 c->innerFunctionAccessesThis |= innerFunctionAccessesThis;
823 }
824 }
825 for (Context *c : std::as_const(m->contextMap)) {
826 if (c->innerFunctionAccessesThis) {
827 // add an escaping 'this' variable
828 c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let);
829 c->requiresExecutionContext = true;
830 auto mIt = c->members.constFind(QStringLiteral("this"));
831 Q_ASSERT(mIt != c->members.constEnd());
832 mIt->canEscape = true;
833 }
834 if (c->innerFunctionAccessesNewTarget) {
835 // add an escaping 'new.target' variable
836 c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
837 c->requiresExecutionContext = true;
838 auto mIt = c->members.constFind(QStringLiteral("new.target"));
839 Q_ASSERT(mIt != c->members.constEnd());
840 mIt->canEscape = true;
841 }
842 if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
843 c->allVarsEscape = false;
844 if (c->contextType == ContextType::Global || c->contextType == ContextType::ScriptImportedByQML || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
845 c->allVarsEscape = true;
846 if (c->allVarsEscape) {
847 if (c->parent) {
848 c->requiresExecutionContext = true;
849 c->argumentsCanEscape = true;
850 } else {
851 for (const auto &m : std::as_const(c->members)) {
852 if (m.isLexicallyScoped()) {
853 c->requiresExecutionContext = true;
854 break;
855 }
856 }
857 }
858 }
859 if (c->contextType == ContextType::Block && c->isCatchBlock) {
860 c->requiresExecutionContext = true;
861 auto mIt = c->members.constFind(c->caughtVariable);
862 Q_ASSERT(mIt != c->members.constEnd());
863 mIt->canEscape = true;
864 }
865 const QLatin1String exprForOn("expression for on");
866 if (c->contextType == ContextType::Binding && c->name.size() > exprForOn.size() &&
867 c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper())
868 // we don't really need this for bindings, but we do for signal handlers, and in this case,
869 // we don't know if the code is a signal handler or not.
870 c->requiresExecutionContext = true;
871 if (c->allVarsEscape) {
872 for (const auto &m : std::as_const(c->members))
873 m.canEscape = true;
874 }
875 }
876
877 static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS");
878 if (showEscapingVars) {
879 qDebug() << "==== escaping variables ====";
880 for (Context *c : std::as_const(m->contextMap)) {
881 qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
882 qDebug() << " isArrowFunction" << c->isArrowFunction << "innerFunctionAccessesThis" << c->innerFunctionAccessesThis;
883 qDebug() << " parent:" << c->parent;
884 if (c->argumentsCanEscape)
885 qDebug() << " Arguments escape";
886 for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
887 qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
888 }
889 }
890 }
891}
892
894{
895 _cg->throwRecursionDepthError();
896}
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:23
static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation)