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

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