aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp285
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h16
-rw-r--r--src/qml/jsruntime/qv4script.cpp4
-rw-r--r--src/qml/jsruntime/qv4script_p.h7
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp8
-rw-r--r--src/qml/parser/qqmljsengine_p.h5
-rw-r--r--src/qml/parser/qqmljslexer.cpp159
-rw-r--r--src/qml/parser/qqmljslexer_p.h11
-rw-r--r--src/qml/parser/qqmljsparser.cpp19
-rw-r--r--src/qml/qml/qqmltypeloader.cpp17
-rw-r--r--tests/auto/qml/qmlmin/tst_qmlmin.cpp3
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp6
-rw-r--r--tools/qmlimportscanner/main.cpp79
-rw-r--r--tools/qmlmin/main.cpp14
14 files changed, 307 insertions, 326 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 07ea2a6fff..dc2389c30e 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -216,60 +216,6 @@ static void replaceWithSpace(QString &str, int idx, int n)
*data++ = space;
}
-#define CHECK_LINE if (l.tokenStartLine() != startLine) return;
-#define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return;
-
-static const int uriTokens[] = {
- QQmlJSGrammar::T_IDENTIFIER,
- QQmlJSGrammar::T_PROPERTY,
- QQmlJSGrammar::T_SIGNAL,
- QQmlJSGrammar::T_READONLY,
- QQmlJSGrammar::T_ON,
- QQmlJSGrammar::T_BREAK,
- QQmlJSGrammar::T_CASE,
- QQmlJSGrammar::T_CATCH,
- QQmlJSGrammar::T_CONTINUE,
- QQmlJSGrammar::T_DEFAULT,
- QQmlJSGrammar::T_DELETE,
- QQmlJSGrammar::T_DO,
- QQmlJSGrammar::T_ELSE,
- QQmlJSGrammar::T_FALSE,
- QQmlJSGrammar::T_FINALLY,
- QQmlJSGrammar::T_FOR,
- QQmlJSGrammar::T_FUNCTION,
- QQmlJSGrammar::T_IF,
- QQmlJSGrammar::T_IN,
- QQmlJSGrammar::T_INSTANCEOF,
- QQmlJSGrammar::T_NEW,
- QQmlJSGrammar::T_NULL,
- QQmlJSGrammar::T_RETURN,
- QQmlJSGrammar::T_SWITCH,
- QQmlJSGrammar::T_THIS,
- QQmlJSGrammar::T_THROW,
- QQmlJSGrammar::T_TRUE,
- QQmlJSGrammar::T_TRY,
- QQmlJSGrammar::T_TYPEOF,
- QQmlJSGrammar::T_VAR,
- QQmlJSGrammar::T_VOID,
- QQmlJSGrammar::T_WHILE,
- QQmlJSGrammar::T_CONST,
- QQmlJSGrammar::T_DEBUGGER,
- QQmlJSGrammar::T_RESERVED_WORD,
- QQmlJSGrammar::T_WITH,
-
- QQmlJSGrammar::EOF_SYMBOL
-};
-static inline bool isUriToken(int token)
-{
- const int *current = uriTokens;
- while (*current != QQmlJSGrammar::EOF_SYMBOL) {
- if (*current == token)
- return true;
- ++current;
- }
- return false;
-}
-
void Document::collectTypeReferences()
{
foreach (Object *obj, objects) {
@@ -296,198 +242,6 @@ void Document::collectTypeReferences()
}
}
-void Document::extractScriptMetaData(QString &script, QQmlJS::DiagnosticMessage *error)
-{
- Q_ASSERT(error);
-
- const QString js(QLatin1String(".js"));
- const QString library(QLatin1String("library"));
-
- QQmlJS::MemoryPool *pool = jsParserEngine.pool();
-
- QQmlJS::Lexer l(0);
- l.setCode(script, 0);
-
- int token = l.lex();
-
- while (true) {
- if (token != QQmlJSGrammar::T_DOT)
- return;
-
- int startOffset = l.tokenOffset();
- int startLine = l.tokenStartLine();
- int startColumn = l.tokenStartColumn();
-
- error->loc.startLine = startLine + 1; // 0-based, adjust to be 1-based
-
- token = l.lex();
-
- CHECK_LINE;
-
- if (token == QQmlJSGrammar::T_IMPORT) {
-
- // .import <URI> <Version> as <Identifier>
- // .import <file.js> as <Identifier>
-
- token = l.lex();
-
- CHECK_LINE;
- QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
-
- if (token == QQmlJSGrammar::T_STRING_LITERAL) {
-
- QString file = l.tokenText();
-
- if (!file.endsWith(js)) {
- error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- bool invalidImport = false;
-
- token = l.lex();
-
- if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
- invalidImport = true;
- } else {
- token = l.lex();
-
- if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
- invalidImport = true;
- }
-
-
- if (invalidImport) {
- error->message = QCoreApplication::translate("QQmlParser","File import requires a qualifier");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- int endOffset = l.tokenLength() + l.tokenOffset();
-
- QString importId = script.mid(l.tokenOffset(), l.tokenLength());
-
- token = l.lex();
-
- if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- replaceWithSpace(script, startOffset, endOffset - startOffset);
-
- import->type = QV4::CompiledData::Import::ImportScript;
- import->uriIndex = registerString(file);
- import->qualifierIndex = registerString(importId);
- import->location.line = startLine;
- import->location.column = startColumn;
- imports << import;
- } else {
- // URI
- QString uri;
-
- while (true) {
- if (!isUriToken(token)) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- uri.append(l.tokenText());
-
- token = l.lex();
- CHECK_LINE;
- if (token != QQmlJSGrammar::T_DOT)
- break;
-
- uri.append(QLatin1Char('.'));
-
- token = l.lex();
- CHECK_LINE;
- }
-
- if (token != QQmlJSGrammar::T_NUMERIC_LITERAL) {
- error->message = QCoreApplication::translate("QQmlParser","Module import requires a version");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- int vmaj, vmin;
- IRBuilder::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()),
- &vmaj, &vmin);
-
- bool invalidImport = false;
-
- token = l.lex();
-
- if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
- invalidImport = true;
- } else {
- token = l.lex();
-
- if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
- invalidImport = true;
- }
-
-
- if (invalidImport) {
- error->message = QCoreApplication::translate("QQmlParser","Module import requires a qualifier");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- int endOffset = l.tokenLength() + l.tokenOffset();
-
- QString importId = script.mid(l.tokenOffset(), l.tokenLength());
-
- token = l.lex();
-
- if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier");
- error->loc.startColumn = l.tokenStartColumn();
- return;
- }
-
- replaceWithSpace(script, startOffset, endOffset - startOffset);
-
- import->type = QV4::CompiledData::Import::ImportLibrary;
- import->uriIndex = registerString(uri);
- import->majorVersion = vmaj;
- import->minorVersion = vmin;
- import->qualifierIndex = registerString(importId);
- import->location.line = startLine;
- import->location.column = startColumn;
- imports << import;
- }
- } else if (token == QQmlJSGrammar::T_PRAGMA) {
- token = l.lex();
-
- CHECK_TOKEN(T_IDENTIFIER);
- CHECK_LINE;
-
- QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
- int endOffset = l.tokenLength() + l.tokenOffset();
-
- if (pragmaValue == library) {
- unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
- replaceWithSpace(script, startOffset, endOffset - startOffset);
- } else {
- return;
- }
-
- token = l.lex();
- if (l.tokenStartLine() == startLine)
- return;
-
- } else {
- return;
- }
- }
- return;
-}
-
void Document::removeScriptPragmas(QString &script)
{
const QString pragma(QLatin1String("pragma"));
@@ -541,6 +295,45 @@ Document::Document(bool debugMode)
{
}
+ScriptDirectivesCollector::ScriptDirectivesCollector(QQmlJS::Engine *engine, QV4::Compiler::JSUnitGenerator *unitGenerator)
+ : engine(engine)
+ , jsGenerator(unitGenerator)
+ , hasPragmaLibrary(false)
+{
+}
+
+void ScriptDirectivesCollector::pragmaLibrary()
+{
+ hasPragmaLibrary = true;
+}
+
+void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString &module, int lineNumber, int column)
+{
+ QV4::CompiledData::Import *import = engine->pool()->New<QV4::CompiledData::Import>();
+ import->type = QV4::CompiledData::Import::ImportScript;
+ import->uriIndex = jsGenerator->registerString(jsfile);
+ import->qualifierIndex = jsGenerator->registerString(module);
+ import->location.line = lineNumber;
+ import->location.column = column;
+ imports << import;
+}
+
+void ScriptDirectivesCollector::importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column)
+{
+ QV4::CompiledData::Import *import = engine->pool()->New<QV4::CompiledData::Import>();
+ import->type = QV4::CompiledData::Import::ImportLibrary;
+ import->uriIndex = jsGenerator->registerString(uri);
+ int vmaj;
+ int vmin;
+ IRBuilder::extractVersion(QStringRef(&version), &vmaj, &vmin);
+ import->majorVersion = vmaj;
+ import->minorVersion = vmin;
+ import->qualifierIndex = jsGenerator->registerString(module);
+ import->location.line = lineNumber;
+ import->location.column = column;
+ imports << import;
+}
+
IRBuilder::IRBuilder(const QSet<QString> &illegalNames)
: illegalNames(illegalNames)
, _object(0)
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index cc22023f8e..6177b96878 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -41,6 +41,7 @@
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
+#include <private/qqmljslexer_p.h>
#include <QTextStream>
#include <QCoreApplication>
@@ -326,10 +327,23 @@ struct Q_QML_PRIVATE_EXPORT Document
int registerString(const QString &str) { return jsGenerator.registerString(str); }
QString stringAt(int index) const { return jsGenerator.stringForIndex(index); }
- void extractScriptMetaData(QString &script, QQmlJS::DiagnosticMessage *error);
static void removeScriptPragmas(QString &script);
};
+struct Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
+{
+ ScriptDirectivesCollector(QQmlJS::Engine *engine, QV4::Compiler::JSUnitGenerator *unitGenerator);
+
+ QQmlJS::Engine *engine;
+ QV4::Compiler::JSUnitGenerator *jsGenerator;
+ QList<const QV4::CompiledData::Import *> imports;
+ bool hasPragmaLibrary;
+
+ virtual void pragmaLibrary();
+ virtual void importFile(const QString &jsfile, const QString &module, int lineNumber, int column);
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column);
+};
+
struct Q_QML_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 9191b53cfc..4337fc1101 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -321,12 +321,14 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors)
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector)
{
using namespace QQmlJS;
using namespace QQmlJS::AST;
QQmlJS::Engine ee;
+ if (directivesCollector)
+ ee.setDirectives(directivesCollector);
QQmlJS::Lexer lexer(&ee);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
QQmlJS::Parser parser(&ee);
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index 467e8af3e5..894859ce5e 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -43,6 +43,10 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
+namespace QQmlJS {
+class Directives;
+}
+
namespace QV4 {
struct ContextStateSaver {
@@ -137,7 +141,8 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors = 0);
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source,
+ QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject);
};
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index 7ac5dbc9bb..fac2d82ff2 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -114,7 +114,7 @@ double integerFromString(const QString &str, int radix)
Engine::Engine()
- : _lexer(0)
+ : _lexer(0), _directives(0)
{ }
Engine::~Engine()
@@ -135,6 +135,12 @@ Lexer *Engine::lexer() const
void Engine::setLexer(Lexer *lexer)
{ _lexer = lexer; }
+Directives *Engine::directives() const
+{ return _directives; }
+
+void Engine::setDirectives(Directives *directives)
+{ _directives = directives; }
+
MemoryPool *Engine::pool()
{ return &_pool; }
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index 661681d19c..81b2aa88e7 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -57,6 +57,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Lexer;
+class Directives;
class MemoryPool;
class QML_PARSER_EXPORT DiagnosticMessage
@@ -84,6 +85,7 @@ public:
class QML_PARSER_EXPORT Engine
{
Lexer *_lexer;
+ Directives *_directives;
MemoryPool _pool;
QList<AST::SourceLocation> _comments;
QString _extraCode;
@@ -102,6 +104,9 @@ public:
Lexer *lexer() const;
void setLexer(Lexer *lexer);
+ Directives *directives() const;
+ void setDirectives(Directives *directives);
+
MemoryPool *pool();
inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); }
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index 2e2a4e5f8b..a69fa21c32 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -1224,12 +1224,60 @@ bool Lexer::canInsertAutomaticSemicolon(int token) const
|| _followsClosingBrace;
}
-bool Lexer::scanDirectives(Directives *directives)
+static const int uriTokens[] = {
+ QQmlJSGrammar::T_IDENTIFIER,
+ QQmlJSGrammar::T_PROPERTY,
+ QQmlJSGrammar::T_SIGNAL,
+ QQmlJSGrammar::T_READONLY,
+ QQmlJSGrammar::T_ON,
+ QQmlJSGrammar::T_BREAK,
+ QQmlJSGrammar::T_CASE,
+ QQmlJSGrammar::T_CATCH,
+ QQmlJSGrammar::T_CONTINUE,
+ QQmlJSGrammar::T_DEFAULT,
+ QQmlJSGrammar::T_DELETE,
+ QQmlJSGrammar::T_DO,
+ QQmlJSGrammar::T_ELSE,
+ QQmlJSGrammar::T_FALSE,
+ QQmlJSGrammar::T_FINALLY,
+ QQmlJSGrammar::T_FOR,
+ QQmlJSGrammar::T_FUNCTION,
+ QQmlJSGrammar::T_IF,
+ QQmlJSGrammar::T_IN,
+ QQmlJSGrammar::T_INSTANCEOF,
+ QQmlJSGrammar::T_NEW,
+ QQmlJSGrammar::T_NULL,
+ QQmlJSGrammar::T_RETURN,
+ QQmlJSGrammar::T_SWITCH,
+ QQmlJSGrammar::T_THIS,
+ QQmlJSGrammar::T_THROW,
+ QQmlJSGrammar::T_TRUE,
+ QQmlJSGrammar::T_TRY,
+ QQmlJSGrammar::T_TYPEOF,
+ QQmlJSGrammar::T_VAR,
+ QQmlJSGrammar::T_VOID,
+ QQmlJSGrammar::T_WHILE,
+ QQmlJSGrammar::T_CONST,
+ QQmlJSGrammar::T_DEBUGGER,
+ QQmlJSGrammar::T_RESERVED_WORD,
+ QQmlJSGrammar::T_WITH,
+
+ QQmlJSGrammar::EOF_SYMBOL
+};
+static inline bool isUriToken(int token)
{
- if (_qmlMode) {
- // the directives are a Javascript-only extension.
- return false;
+ const int *current = uriTokens;
+ while (*current != QQmlJSGrammar::EOF_SYMBOL) {
+ if (*current == token)
+ return true;
+ ++current;
}
+ return false;
+}
+
+bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
+{
+ Q_ASSERT(!_qmlMode);
lex(); // fetch the first token
@@ -1237,24 +1285,33 @@ bool Lexer::scanDirectives(Directives *directives)
return true;
do {
- lex(); // skip T_DOT
-
const int lineNumber = tokenStartLine();
+ const int column = tokenStartColumn();
+
+ lex(); // skip T_DOT
if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
- return false; // expected a valid QML/JS directive
+ return true; // expected a valid QML/JS directive
const QString directiveName = tokenText();
if (! (directiveName == QLatin1String("pragma") ||
- directiveName == QLatin1String("import")))
+ directiveName == QLatin1String("import"))) {
+ error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false; // not a valid directive name
+ }
// it must be a pragma or an import directive.
if (directiveName == QLatin1String("pragma")) {
// .pragma library
- if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library")))
+ if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) {
+ error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false; // expected `library
+ }
// we found a .pragma library directive
directives->pragmaLibrary();
@@ -1273,22 +1330,53 @@ bool Lexer::scanDirectives(Directives *directives)
fileImport = true;
pathOrUri = tokenText();
+ if (!pathOrUri.endsWith(QLatin1String("js"))) {
+ error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
+ return false;
+ }
+
} else if (_tokenKind == T_IDENTIFIER) {
// .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER
- pathOrUri = tokenText();
+ while (true) {
+ if (!isUriToken(_tokenKind)) {
+ error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
+ return false;
+ }
- lex(); // skip the first T_IDENTIFIER
- for (; _tokenKind == T_DOT; lex()) {
- if (lex() != T_IDENTIFIER)
+ pathOrUri.append(tokenText());
+
+ lex();
+ if (tokenStartLine() != lineNumber) {
+ error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false;
+ }
+ if (_tokenKind != QQmlJSGrammar::T_DOT)
+ break;
+
+ pathOrUri.append(QLatin1Char('.'));
- pathOrUri += QLatin1Char('.');
- pathOrUri += tokenText();
+ lex();
+ if (tokenStartLine() != lineNumber) {
+ error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
+ return false;
+ }
}
- if (_tokenKind != T_NUMERIC_LITERAL)
+ if (_tokenKind != T_NUMERIC_LITERAL) {
+ error->message = QCoreApplication::translate("QQmlParser","Module import requires a version");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false; // expected the module version number
+ }
version = tokenText();
}
@@ -1296,22 +1384,51 @@ bool Lexer::scanDirectives(Directives *directives)
//
// recognize the mandatory `as' followed by the module name
//
- if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as")))
+ if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as") && tokenStartLine() == lineNumber)) {
+ if (fileImport)
+ error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
+ else
+ error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
+ if (tokenStartLine() != lineNumber) {
+ error->loc.startLine = lineNumber;
+ error->loc.startColumn = column;
+ } else {
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
+ }
return false; // expected `as'
+ }
- if (lex() != T_IDENTIFIER)
+ if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) {
+ if (fileImport)
+ error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
+ else
+ error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false; // expected module name
+ }
const QString module = tokenText();
+ if (!module.at(0).isUpper()) {
+ error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
+ return false;
+ }
if (fileImport)
- directives->importFile(pathOrUri, module);
+ directives->importFile(pathOrUri, module, lineNumber, column);
else
- directives->importModule(pathOrUri, version, module);
+ directives->importModule(pathOrUri, version, module, lineNumber, column);
}
- if (tokenStartLine() != lineNumber)
+ if (tokenStartLine() != lineNumber) {
+ error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
+ error->loc.startLine = tokenStartLine();
+ error->loc.startColumn = tokenStartColumn();
return false; // the directives cannot span over multiple lines
+ }
// fetch the first token after the .pragma/.import directive
lex();
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 9106c94477..b5415f8777 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -55,6 +55,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Engine;
+class DiagnosticMessage;
class QML_PARSER_EXPORT Directives {
public:
@@ -64,17 +65,21 @@ public:
{
}
- virtual void importFile(const QString &jsfile, const QString &module)
+ virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
{
Q_UNUSED(jsfile);
Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
}
- virtual void importModule(const QString &uri, const QString &version, const QString &module)
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
{
Q_UNUSED(uri);
Q_UNUSED(version);
Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
}
};
@@ -146,7 +151,7 @@ public:
int lex();
bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
- bool scanDirectives(Directives *directives);
+ bool scanDirectives(Directives *directives, DiagnosticMessage *error);
int regExpFlags() const { return _patternFlags; }
QString regExpPattern() const { return _tokenText; }
diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp
index 762e60c827..35eb0e3ad5 100644
--- a/src/qml/parser/qqmljsparser.cpp
+++ b/src/qml/parser/qqmljsparser.cpp
@@ -161,7 +161,24 @@ bool Parser::parse(int startToken)
token_buffer[0].token = startToken;
first_token = &token_buffer[0];
- last_token = &token_buffer[1];
+ if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
+ Directives ignoreDirectives;
+ Directives *directives = driver->directives();
+ if (!directives)
+ directives = &ignoreDirectives;
+ DiagnosticMessage error;
+ if (!lexer->scanDirectives(directives, &error)) {
+ diagnostic_messages.append(error);
+ return false;
+ }
+ token_buffer[1].token = lexer->tokenKind();
+ token_buffer[1].dval = lexer->tokenValue();
+ token_buffer[1].loc = location(lexer);
+ token_buffer[1].spell = lexer->tokenSpell();
+ last_token = &token_buffer[2];
+ } else {
+ last_token = &token_buffer[1];
+ }
tos = -1;
program = 0;
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 927577a9e1..4b7fe9eb04 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -2584,20 +2584,10 @@ void QQmlScriptBlob::dataReceived(const Data &data)
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
QmlIR::Document irUnit(v4->debugger != 0);
- QQmlJS::DiagnosticMessage metaDataError;
- irUnit.extractScriptMetaData(source, &metaDataError);
- if (!metaDataError.message.isEmpty()) {
- QQmlError e;
- e.setUrl(finalUrl());
- e.setLine(metaDataError.loc.startLine);
- e.setColumn(metaDataError.loc.startColumn);
- e.setDescription(metaDataError.message);
- setError(e);
- return;
- }
+ QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator);
QList<QQmlError> errors;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, v4, finalUrl(), source, &errors);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, v4, finalUrl(), source, &errors, &collector);
// No need to addref on unit, it's initial refcount is 1
source.clear();
if (!errors.isEmpty()) {
@@ -2608,6 +2598,9 @@ void QQmlScriptBlob::dataReceived(const Data &data)
unit.take(new EmptyCompilationUnit);
}
irUnit.javaScriptCompilationUnit = unit;
+ irUnit.imports = collector.imports;
+ if (collector.hasPragmaLibrary)
+ irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
QmlIR::QmlUnitGenerator qmlGenerator;
QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit);
diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
index 85d1cc6836..1ff259b74c 100644
--- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp
+++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
@@ -107,9 +107,12 @@ void tst_qmlmin::initTestCase()
invalidFiles << "tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedFileQualifier.js";
+ invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedFileQualifier.2.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedImport.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedModule.js";
+ invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedFile.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedModuleQualifier.js";
+ invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedModuleQualifier.2.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedModuleVersion.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/missingFileQualifier.js";
invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/missingModuleQualifier.js";
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index cd1302e5c8..d4c39e9112 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -4107,7 +4107,7 @@ void tst_qqmlecmascript::importScripts_data()
<< testFileUrl("jsimportfail/malformedImport.qml")
<< false /* compilation should succeed */
<< QString()
- << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1:1: Syntax error"))
+ << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1:2: Syntax error"))
<< QStringList()
<< QVariantList();
@@ -4139,7 +4139,7 @@ void tst_qqmlecmascript::importScripts_data()
<< testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
<< false /* compilation should succeed */
<< QString()
- << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
+ << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:23: Invalid import qualifier"))
<< QStringList()
<< QVariantList();
@@ -4187,7 +4187,7 @@ void tst_qqmlecmascript::importScripts_data()
<< testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
<< false /* compilation should succeed */
<< QString()
- << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
+ << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:24: Invalid import qualifier"))
<< QStringList()
<< QVariantList();
}
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 6f5ec28c4d..08710d358f 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -246,6 +246,41 @@ static QVariantList findQmlImportsInQmlFile(const QString &filePath)
return findQmlImportsInQmlCode(filePath, code);
}
+struct ImportCollector : public QQmlJS::Directives
+{
+ QVariantList imports;
+
+ virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
+ {
+ QVariantMap entry;
+ entry[QLatin1String("type")] = QStringLiteral("javascript");
+ entry[QLatin1String("path")] = jsfile;
+ imports << entry;
+
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
+ {
+ QVariantMap entry;
+ if (uri.contains(QLatin1Char('/'))) {
+ entry[QLatin1String("type")] = QStringLiteral("directory");
+ entry[QLatin1String("name")] = uri;
+ } else {
+ entry[QLatin1String("type")] = QStringLiteral("module");
+ entry[QLatin1String("name")] = uri;
+ entry[QLatin1String("version")] = version;
+ }
+ imports << entry;
+
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+};
+
// Scan a single javascrupt file for import statements
QVariantList findQmlImportsInJavascriptFile(const QString &filePath)
{
@@ -256,42 +291,22 @@ QVariantList findQmlImportsInJavascriptFile(const QString &filePath)
return QVariantList();
}
- QVariantList imports;
-
QString sourceCode = QString::fromUtf8(file.readAll());
file.close();
- QmlIR::Document doc(/*debug mode*/false);
- QQmlJS::DiagnosticMessage error;
- doc.extractScriptMetaData(sourceCode, &error);
- if (!error.message.isEmpty())
- return imports;
- foreach (const QV4::CompiledData::Import *import, doc.imports) {
- QVariantMap entry;
- const QString name = doc.stringAt(import->uriIndex);
- switch (import->type) {
- case QV4::CompiledData::Import::ImportScript:
- entry[QStringLiteral("type")] = QStringLiteral("javascript");
- entry[QStringLiteral("path")] = name;
- break;
- case QV4::CompiledData::Import::ImportLibrary:
- if (name.contains(QLatin1Char('/'))) {
- entry[QStringLiteral("type")] = QStringLiteral("directory");
- entry[QStringLiteral("name")] = name;
- } else {
- entry[QStringLiteral("type")] = QStringLiteral("module");
- entry[QStringLiteral("name")] = name;
- entry[QStringLiteral("version")] = QString::number(import->majorVersion) + QLatin1Char('.') + QString::number(import->minorVersion);
- }
- break;
- default:
- Q_UNREACHABLE();
- continue;
- }
- imports << entry;
- }
+ QQmlJS::Engine ee;
+ ImportCollector collector;
+ ee.setDirectives(&collector);
+ QQmlJS::Lexer lexer(&ee);
+ lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
+ QQmlJS::Parser parser(&ee);
+ parser.parseProgram();
- return imports;
+ foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages())
+ if (m.isError())
+ return QVariantList();
+
+ return collector.imports;
}
// Scan a single qml or js file for import statements
diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp
index aa3adf053d..aa99b242a7 100644
--- a/tools/qmlmin/main.cpp
+++ b/tools/qmlmin/main.cpp
@@ -93,7 +93,7 @@ public:
_directives += QLatin1String(".pragma library\n");
}
- virtual void importFile(const QString &jsfile, const QString &module)
+ virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
{
_directives += QLatin1String(".import");
_directives += QLatin1Char('"');
@@ -102,9 +102,11 @@ public:
_directives += QLatin1String("as ");
_directives += module;
_directives += QLatin1Char('\n');
+ Q_UNUSED(line);
+ Q_UNUSED(column);
}
- virtual void importModule(const QString &uri, const QString &version, const QString &module)
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
{
_directives += QLatin1String(".import ");
_directives += uri;
@@ -113,6 +115,8 @@ public:
_directives += QLatin1String(" as ");
_directives += module;
_directives += QLatin1Char('\n');
+ Q_UNUSED(line);
+ Q_UNUSED(column);
}
protected:
@@ -262,7 +266,8 @@ bool Minify::parse(int startToken)
if (startToken == T_FEED_JS_PROGRAM) {
// parse optional pragma directive
- if (scanDirectives(this)) {
+ DiagnosticMessage error;
+ if (scanDirectives(this, &error)) {
// append the scanned directives to the minifier code.
append(directives());
@@ -433,7 +438,8 @@ bool Tokenize::parse(int startToken)
if (startToken == T_FEED_JS_PROGRAM) {
// parse optional pragma directive
- if (scanDirectives(this)) {
+ DiagnosticMessage error;
+ if (scanDirectives(this, &error)) {
// append the scanned directives as one token to
// the token stream.
_minifiedCode.append(directives());