diff options
author | Lars Knoll <lars.knoll@qt.io> | 2017-07-03 07:52:13 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2017-07-03 13:38:02 +0000 |
commit | 540cad8cef8c1e59859c57a8960467bea4be8ee8 (patch) | |
tree | 6247bdcefe456f95a8a9fcfd36ec0feea7c7f220 /src | |
parent | bac9b54dfb38767a34410fa55f8f46e64b458efb (diff) |
Move Codegen::ScanFunctions into it's own file
Change-Id: I2c6c2fa09bc58a26a350011f5d525ec9f4b89d7f
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/compiler.pri | 2 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 358 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 75 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions.cpp | 412 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions_p.h | 158 |
6 files changed, 576 insertions, 432 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 826eafe966..c5d6a17e62 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -8,6 +8,7 @@ HEADERS += \ $$PWD/qv4compilationunit_moth_p.h \ $$PWD/qv4compilercontext_p.h \ $$PWD/qv4compilercontrolflow_p.h \ + $$PWD/qv4compilerscanfunctions_p.h \ $$PWD/qv4codegen_p.h \ $$PWD/qqmlirbuilder_p.h \ $$PWD/qqmltypecompiler_p.h @@ -18,6 +19,7 @@ SOURCES += \ $$PWD/qv4compiler.cpp \ $$PWD/qv4compilationunit_moth.cpp \ $$PWD/qv4compilercontext.cpp \ + $$PWD/qv4compilerscanfunctions.cpp \ $$PWD/qv4codegen.cpp \ $$PWD/qqmlirbuilder.cpp diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index dacfe272ed..312ddd753b 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -43,6 +43,7 @@ #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> +#include <private/qv4compilerscanfunctions_p.h> #include <QCoreApplication> #include <QCryptographicHash> @@ -1586,7 +1587,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil { QVector<int> runtimeFunctionIndices(functions.size()); - ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode); + QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode); scan.enterEnvironment(0, QV4::Compiler::QmlBinding); scan.enterQmlScope(qmlRoot, QStringLiteral("context scope")); for (const CompiledFunctionOrExpression &f : functions) { diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index d2ec9be105..befebba9a3 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -55,6 +55,7 @@ #include <private/qv4compilercontrolflow_p.h> #include <private/qv4bytecodegenerator_p.h> #include <private/qv4compilationunit_moth_p.h> +#include <private/qv4compilerscanfunctions_p.h> #ifndef V4_BOOTSTRAP #include <qv4context_p.h> @@ -121,363 +122,6 @@ static inline QV4::Runtime::RuntimeMethods aluOpFunction(QSOperator::Op op) } }; - -Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) - : _cg(cg) - , _sourceCode(sourceCode) - , _context(0) - , _allowFuncDecls(true) - , defaultProgramMode(defaultProgramMode) -{ -} - -void Codegen::ScanFunctions::operator()(Node *node) -{ - if (node) - node->accept(this); -} - -void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) -{ - Context *e = _cg->_module->newContext(node, _context, compilationMode); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _contextStack.append(e); - _context = e; -} - -void Codegen::ScanFunctions::leaveEnvironment() -{ - _contextStack.pop(); - _context = _contextStack.isEmpty() ? 0 : _contextStack.top(); -} - -void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { - if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { - if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { - // Use the source code, because the StringLiteral's - // value might have escape sequences in it, which is not - // allowed. - if (strLit->literalToken.length < 2) - continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QLatin1String("use strict")) { - _context->isStrict = true; - } else { - // TODO: give a warning. - } - continue; - } - } - } - - break; - } -} - -void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) -{ - if (_context->isStrict) { - if (name == QLatin1String("implements") - || name == QLatin1String("interface") - || name == QLatin1String("let") - || name == QLatin1String("package") - || name == QLatin1String("private") - || name == QLatin1String("protected") - || name == QLatin1String("public") - || name == QLatin1String("static") - || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); - } - } -} -void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) -{ - while (parameters) { - if (parameters->name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - parameters = parameters->next; - } -} - -bool Codegen::ScanFunctions::visit(Program *ast) -{ - enterEnvironment(ast, defaultProgramMode); - checkDirectivePrologue(ast->elements); - return true; -} - -void Codegen::ScanFunctions::endVisit(Program *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(CallExpression *ast) -{ - if (! _context->hasDirectEval) { - if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { - if (id->name == QLatin1String("eval")) { - if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) - _context->usesArgumentsObject = Context::ArgumentsObjectUsed; - _context->hasDirectEval = true; - } - } - } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) -{ - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); - return true; -} - -bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) -{ - int index = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - } - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index); - return true; -} - -bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) -{ - if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); - return false; - } - QString name = ast->name.toString(); - const Context::Member *m = 0; - if (_context->memberInfo(name, &m)) { - if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); - return false; - } - } - _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope); - return true; -} - -bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) -{ - checkName(ast->name, ast->identifierToken); - if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectUsed; - return true; -} - -bool Codegen::ScanFunctions::visit(ExpressionStatement *ast) -{ - if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { - if (!_allowFuncDecls) - _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); - - enterFunction(expr, /*enterName*/ true); - Node::accept(expr->formals, this); - Node::accept(expr->body, this); - leaveEnvironment(); - return false; - } else { - SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { - _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); - } - } - return true; -} - -bool Codegen::ScanFunctions::visit(FunctionExpression *ast) -{ - enterFunction(ast, /*enterName*/ false); - return true; -} - -void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) -{ - if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); -} - -void Codegen::ScanFunctions::endVisit(FunctionExpression *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) -{ - int argc = 0; - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - QString key = it->assignment->name->asString(); - if (QV4::String::toArrayIndex(key) != UINT_MAX) - ++argc; - ++argc; - if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) - ++argc; - } - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - Node::accept(ast->properties, this); - return false; -} - -bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast) -{ - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); - return true; -} - -void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast) -{ - enterFunction(ast, /*enterName*/ true, /*isExpression */false); - return true; -} - -void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) -{ - leaveEnvironment(); -} - -bool Codegen::ScanFunctions::visit(WithStatement *ast) -{ - if (_context->isStrict) { - _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); - return false; - } - - return true; -} - -bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { - { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); - } - Node::accept(ast->expression, this); - return false; -} - -bool Codegen::ScanFunctions::visit(ForStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { - Node::accept(ast->declarations, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { - Node::accept(ast->declaration, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); - - return false; -} - -bool Codegen::ScanFunctions::visit(ThisExpression *) -{ - _context->usesThis = true; - return false; -} - -bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); - Node::accept(ast->statements, this); - return false; -} - -void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) -{ - if (_context) { - _context->hasNestedFunctions = true; - // The identifier of a function expression cannot be referenced from the enclosing environment. - if (expr) - _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); - if (name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - } - - enterEnvironment(ast, FunctionCode); - checkForArguments(formals); - - _context->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _context->formals = formals; - - if (body && !_context->isStrict) - checkDirectivePrologue(body->elements); - - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - if (_context->isStrict) { - if (_context->arguments.contains(arg)) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); - return; - } - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); - return; - } - } - _context->arguments += arg; - } -} - - Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) : _module(0) , _returnAddress(0) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index e872dae464..6341070d0e 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -497,6 +497,7 @@ public: static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); protected: + friend class ScanFunctions; friend struct ControlFlow; friend struct ControlFlowCatch; friend struct ControlFlowFinally; @@ -514,80 +515,6 @@ protected: bool _fileNameIsUrl; bool hasError; QList<QQmlJS::DiagnosticMessage> _errors; - - - class ScanFunctions: protected Visitor - { - typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; - public: - ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); - void operator()(AST::Node *node); - - void enterEnvironment(AST::Node *node, CompilationMode compilationMode); - void leaveEnvironment(); - - void enterQmlScope(AST::Node *ast, const QString &name) - { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); } - - void enterQmlFunction(AST::FunctionDeclaration *ast) - { enterFunction(ast, false, false); } - - protected: - using Visitor::visit; - using Visitor::endVisit; - - void checkDirectivePrologue(AST::SourceElements *ast); - - void checkName(const QStringRef &name, const AST::SourceLocation &loc); - void checkForArguments(AST::FormalParameterList *parameters); - - bool visit(AST::Program *ast) override; - void endVisit(AST::Program *) override; - - bool visit(AST::CallExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::ArrayLiteral *ast) override; - bool visit(AST::VariableDeclaration *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::FunctionExpression *ast) override; - - void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true); - - void endVisit(AST::FunctionExpression *) override; - - bool visit(AST::ObjectLiteral *ast) override; - - bool visit(AST::PropertyGetterSetter *ast) override; - void endVisit(AST::PropertyGetterSetter *) override; - - bool visit(AST::FunctionDeclaration *ast) override; - void endVisit(AST::FunctionDeclaration *) override; - - bool visit(AST::WithStatement *ast) override; - - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - bool visit(AST::LocalForStatement *ast) override; - bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::LocalForEachStatement *ast) override; - bool visit(AST::ThisExpression *ast) override; - - bool visit(AST::Block *ast) override; - - protected: - void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression); - - // fields: - Codegen *_cg; - const QString _sourceCode; - Context *_context; - QStack<Context *> _contextStack; - - bool _allowFuncDecls; - CompilationMode defaultProgramMode; - }; - }; #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp new file mode 100644 index 0000000000..80980cf394 --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -0,0 +1,412 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4compilerscanfunctions_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QBitArray> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <private/qqmljsast_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4string_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::Compiler; +using namespace QQmlJS::AST; + +ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) + : _cg(cg) + , _sourceCode(sourceCode) + , _context(0) + , _allowFuncDecls(true) + , defaultProgramMode(defaultProgramMode) +{ +} + +void ScanFunctions::operator()(Node *node) +{ + if (node) + node->accept(this); +} + +void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) +{ + Context *e = _cg->_module->newContext(node, _context, compilationMode); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _contextStack.append(e); + _context = e; +} + +void ScanFunctions::leaveEnvironment() +{ + _contextStack.pop(); + _context = _contextStack.isEmpty() ? 0 : _contextStack.top(); +} + +void ScanFunctions::checkDirectivePrologue(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QLatin1String("use strict")) { + _context->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } +} + +void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) +{ + if (_context->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); + } + } +} +void ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) +{ + while (parameters) { + if (parameters->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + parameters = parameters->next; + } +} + +bool ScanFunctions::visit(Program *ast) +{ + enterEnvironment(ast, defaultProgramMode); + checkDirectivePrologue(ast->elements); + return true; +} + +void ScanFunctions::endVisit(Program *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(CallExpression *ast) +{ + if (! _context->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QLatin1String("eval")) { + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + _context->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(NewMemberExpression *ast) +{ + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; +} + +bool ScanFunctions::visit(ArrayLiteral *ast) +{ + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index); + return true; +} + +bool ScanFunctions::visit(VariableDeclaration *ast) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Context::Member *m = 0; + if (_context->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope); + return true; +} + +bool ScanFunctions::visit(IdentifierExpression *ast) +{ + checkName(ast->name, ast->identifierToken); + if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectUsed; + return true; +} + +bool ScanFunctions::visit(ExpressionStatement *ast) +{ + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { + _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); + } + } + return true; +} + +bool ScanFunctions::visit(FunctionExpression *ast) +{ + enterFunction(ast, /*enterName*/ false); + return true; +} + +void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) +{ + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); +} + +void ScanFunctions::endVisit(FunctionExpression *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(ObjectLiteral *ast) +{ + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + QString key = it->assignment->name->asString(); + if (QV4::String::toArrayIndex(key) != UINT_MAX) + ++argc; + ++argc; + if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) + ++argc; + } + _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; +} + +bool ScanFunctions::visit(PropertyGetterSetter *ast) +{ + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); + return true; +} + +void ScanFunctions::endVisit(PropertyGetterSetter *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(FunctionDeclaration *ast) +{ + enterFunction(ast, /*enterName*/ true, /*isExpression */false); + return true; +} + +void ScanFunctions::endVisit(FunctionDeclaration *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(WithStatement *ast) +{ + if (_context->isStrict) { + _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); + return false; + } + + return true; +} + +bool ScanFunctions::visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; +} + +bool ScanFunctions::visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); + Node::accept(ast->statement, this); + + return false; +} + +bool ScanFunctions::visit(ThisExpression *) +{ + _context->usesThis = true; + return false; +} + +bool ScanFunctions::visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; +} + +void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) +{ + if (_context) { + _context->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); + if (name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + } + + enterEnvironment(ast, FunctionCode); + checkForArguments(formals); + + _context->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _context->formals = formals; + + if (body && !_context->isStrict) + checkDirectivePrologue(body->elements); + + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (_context->isStrict) { + if (_context->arguments.contains(arg)) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + return; + } + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { + _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); + return; + } + } + _context->arguments += arg; + } +} diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h new file mode 100644 index 0000000000..3014cbff27 --- /dev/null +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4COMPILERSCANFUNCTIONS_P_H +#define QV4COMPILERSCANFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qv4global_p.h" +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qv4util_p.h> +#include <QtCore/QStringList> +#include <QStack> + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +namespace QV4 { + +namespace Moth { +struct Instruction; +} + +namespace CompiledData { +struct CompilationUnit; +} + +namespace Compiler { + +class Codegen; + +class ScanFunctions: protected QQmlJS::AST::Visitor +{ + typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); + void operator()(AST::Node *node); + + void enterEnvironment(AST::Node *node, CompilationMode compilationMode); + void leaveEnvironment(); + + void enterQmlScope(AST::Node *ast, const QString &name) + { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); } + + void enterQmlFunction(AST::FunctionDeclaration *ast) + { enterFunction(ast, false, false); } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(AST::SourceElements *ast); + + void checkName(const QStringRef &name, const AST::SourceLocation &loc); + void checkForArguments(AST::FormalParameterList *parameters); + + bool visit(AST::Program *ast) override; + void endVisit(AST::Program *) override; + + bool visit(AST::CallExpression *ast) override; + bool visit(AST::NewMemberExpression *ast) override; + bool visit(AST::ArrayLiteral *ast) override; + bool visit(AST::VariableDeclaration *ast) override; + bool visit(AST::IdentifierExpression *ast) override; + bool visit(AST::ExpressionStatement *ast) override; + bool visit(AST::FunctionExpression *ast) override; + + void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true); + + void endVisit(AST::FunctionExpression *) override; + + bool visit(AST::ObjectLiteral *ast) override; + + bool visit(AST::PropertyGetterSetter *ast) override; + void endVisit(AST::PropertyGetterSetter *) override; + + bool visit(AST::FunctionDeclaration *ast) override; + void endVisit(AST::FunctionDeclaration *) override; + + bool visit(AST::WithStatement *ast) override; + + bool visit(AST::DoWhileStatement *ast) override; + bool visit(AST::ForStatement *ast) override; + bool visit(AST::LocalForStatement *ast) override; + bool visit(AST::ForEachStatement *ast) override; + bool visit(AST::LocalForEachStatement *ast) override; + bool visit(AST::ThisExpression *ast) override; + + bool visit(AST::Block *ast) override; + +protected: + void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression); + +// fields: + Codegen *_cg; + const QString _sourceCode; + Context *_context; + QStack<Context *> _contextStack; + + bool _allowFuncDecls; + CompilationMode defaultProgramMode; +}; + +} + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H |