diff --git a/src/qmljsc/CMakeLists.txt b/src/qmljsc/CMakeLists.txt index d9db311..064de46 100644 --- a/src/qmljsc/CMakeLists.txt +++ b/src/qmljsc/CMakeLists.txt @@ -1,26 +1,27 @@ find_package(Qt5 COMPONENTS Core Qml) set(libqmljsc_srcs compiler.cpp error.cpp compilerpipeline.cpp compilerpass.cpp + moduleloader.cpp ir/ir.cpp ir/module.cpp ir/file.cpp compilerpasses/parserpass.cpp compilerpasses/prettygeneratorpass.cpp ) add_library(libqmljsc SHARED ${libqmljsc_srcs}) qt5_use_modules(libqmljsc Core Qml) include_directories(${Qt5Qml_PRIVATE_INCLUDE_DIRS}) set(qmljsc_srcs main.cpp ) add_executable(qmljsc ${qmljsc_srcs}) target_link_libraries(qmljsc libqmljsc) diff --git a/src/qmljsc/ir/file.cpp b/src/qmljsc/ir/file.cpp index 6fe0a58..c3b1149 100644 --- a/src/qmljsc/ir/file.cpp +++ b/src/qmljsc/ir/file.cpp @@ -1,147 +1,148 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "file.h" #include "module.h" #include "../error.h" #include "../compiler.h" using namespace QmlJSc; using namespace QmlJSc::IR; ShortSymbolName::ShortSymbolName(char first) : QString(first) {} ShortSymbolName::ShortSymbolName(QString first) : QString(first) {} ShortSymbolName &ShortSymbolName::operator++() { Q_ASSERT(!isEmpty()); iterator i = end(); i--; char borrow = 1; while (i != begin()) { if ((*i) == '9') { *i = 'A'; borrow = 0; break; } else if ((*i) == 'Z') { *i = 'a'; borrow = 0; break; } else if ((*i) == 'z') { // We need to add a borrow of 1 to the previous digit *i = '0'; i--; borrow = 1; continue; } else { *i = i->toLatin1() + 1; borrow = 0; break; } } if (borrow == 1) { if (*i <= 'Z') { // the first letter is a capital one, so it should remain so. if (*i == 'Z') { // We need to prepend a new digit *i = '0'; prepend('A'); } else { *i = i->toLatin1() + 1; } } else { // the first letter is a small one, so it should remain so. if (*i == 'z') { // We need to prepend a new digit *i = '0'; prepend('a'); } else { *i = i->toLatin1() + 1; } } } return *this; } +bool File::ModuleData::operator==(const File::ModuleData& other) const +{ + return module == other.module; +} + File::File() : m_prefix('A' - 1) // we want the prefix to be 'A' after the first preincrement { } File::~File() { } -void File::addImport(ImportDescription import) +void File::addModule(Module *module) { - switch (import.kind) { - case ImportDescription::Kind_ModuleImport: - if (m_modules.contains(import)) - return; - - m_modules.insert(import, { - ModuleLoader::loadModule(import, compiler), - ++m_prefix - }); - break; - } + if (m_modules.contains({ module, QString() })) + return; + + m_modules.append({ + module, + ++m_prefix + }); } Type *File::type(const QString &typeName) { const ModuleData *data = moduleForType(typeName); if (data && data->module) return data->module->type(typeName); return 0; } QString File::fullyQualifiedName(const QString &typeName) { return QStringLiteral("%1.%2").arg(moduleForType(typeName)->localPrefix, typeName); } -const File::ModuleData *File::moduleForType(const QString &typeName) +const File::ModuleData *File::moduleForType(const QString &typeName) const { Type *foundType = 0; const ModuleData *moduleData = 0; - for (auto i = m_modules.constBegin(); i != m_modules.constEnd(); i++) { - if (Type * type = i->module->type(typeName)) { + foreach (const ModuleData &data, m_modules) { + if (Type * type = data.module->type(typeName)) { if (foundType) { throw Error( QmlJSc::Error::SymbolLookupError, QString("Ambitious type name. Type %1 was defined by module %2 and %3.") - .arg(typeName, moduleData->module->name(), i->module->name()) + .arg(typeName, moduleData->module->name(), data.module->name()) ); return 0; } else { foundType = type; - moduleData = &(*i); + moduleData = &data; } } } return moduleData; } diff --git a/src/qmljsc/ir/file.h b/src/qmljsc/ir/file.h index f86bd51..2c95176 100644 --- a/src/qmljsc/ir/file.h +++ b/src/qmljsc/ir/file.h @@ -1,119 +1,118 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FILE_H #define FILE_H #include "ir.h" #include #include #include #include #include namespace QmlJSc { namespace IR { struct ImportDescription { enum Kind { Kind_ModuleImport, Kind_FileImport, Kind_DirectoryImport }; Kind kind; QString name; int versionMajor; int versionMinor; inline bool operator==(const ImportDescription& other) const { return name == other.name && versionMajor == other.versionMajor && versionMinor == other.versionMinor; } }; class Module; struct Type; inline uint qHash(const ImportDescription &key, uint seed) { return qHash(key.name, seed) ^ key.versionMajor ^ key.versionMinor; } typedef QList ImportDescriptions; /** * This class represents a minified symbol name like "a", "b", "aa", "A8",... * * Initialize it with the first character you want to use as symbol name MINUS * ONE, as you normally will use the preincrement operator before accessing it. * If you won't, feel free to initialize it with the first symbol name directly, * of course. * * To get the next valid symbol name, use the preincrement operator. * * This is a subclass of QString, so you can just use it as a string. */ class ShortSymbolName : public QString { public: ShortSymbolName(char first); ShortSymbolName(QString first); ShortSymbolName &operator++(); }; class File { struct ModuleData { Module *module; QString localPrefix; + + bool operator==(const ModuleData &other) const; }; public: explicit File(); virtual ~File(); - void addImport(ImportDescription import); + void addModule(Module *module); Type* type(const QString &typeName); QString fullyQualifiedName(const QString &typeName); Component *rootObject(); void setRootObject(Component *root); -private slots: - void doLoadModule(ImportDescription import); - private: - const ModuleData *moduleForType(const QString &typeName); + const ModuleData *moduleForType(const QString &typeName) const; - QHash m_modules; + QVector m_modules; ShortSymbolName m_prefix; Component *m_rootObject; }; } // namespace IR } // namespace QMLJSc Q_DECLARE_METATYPE(QmlJSc::IR::ImportDescription); #endif // FILE_H diff --git a/src/qmljsc/ir/module.cpp b/src/qmljsc/ir/module.cpp index 8bf5716..0c4952e 100644 --- a/src/qmljsc/ir/module.cpp +++ b/src/qmljsc/ir/module.cpp @@ -1,244 +1,71 @@ /* * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Own #include "module.h" #include "../compiler.h" #include "../error.h" // Qt #include #include #include #include // Qt private #include #include #include using namespace QmlJSc; using namespace QmlJSc::IR; using namespace QQmlJS::AST; -QHash ModuleLoader::s_loadedModules; - Module::Module(ImportDescription import, QObject *parent) : m_import(import) , m_status(Loading) { } -Module *ModuleLoader::loadModule(ImportDescription import, QObject *parent) -{ - Q_ASSERT(import.kind == ImportDescription::Kind_ModuleImport); - if (Module *module = s_loadedModules.value(import)) { - return module; - } - - Module *module = new Module(import, parent); - s_loadedModules.insert(import, module); - ModuleLoader *loader = new ModuleLoader(module); // will delete itself when done - - QThreadPool::globalInstance()->start(loader); - - return module; -} - - -ModuleLoader::ModuleLoader(Module *module) - : QRunnable() - , Visitor() - , m_module(module) - , m_functionDepth(0) -{ -} - -ModuleLoader::~ModuleLoader() -{ -} - -void ModuleLoader::run() -{ - try { - doLoad(); - } catch (const Error &e) { - m_module->m_status = Module::ErrorState; - m_module->m_waitCondition.wakeAll(); - } -} - -void ModuleLoader::doLoad() -{ - // === Load Module File === - QFile moduleFile; - const QString moduleFileName = QStringLiteral("%1.%2.%3.js").arg(m_module->m_import.name) - .arg(m_module->m_import.versionMajor) - .arg(m_module->m_import.versionMinor); - - // For now we only support local files. - const QStringList &includePaths = compiler->includePaths(); - foreach (QString includePath, includePaths) { - QDir includeDir(includePath); - if (includeDir.exists(moduleFileName)) { - moduleFile.setFileName(includeDir.absoluteFilePath(moduleFileName)); - break; - } - } - - if (!moduleFile.exists()) { - throw Error(Error::ModuleImportError, QStringLiteral("Could not find file %1 in path.").arg(moduleFileName)); - } - - // Read file - moduleFile.open(QFile::ReadOnly); - QTextStream modueFileStream(&moduleFile); - QString moduleSource = modueFileStream.readAll(); - - // === Parse file === - - // parsing happens in three steps: Calling the QQmlJS-parser to parse the - // file and return an AST, then using the visit functions of this class to - // collect the data we need and put it into m_parseData and third calling - // finalizeParse() to evaluate the parse data and transform it to actual - // type information. - - QQmlJS::Engine* engine = new QQmlJS::Engine(); - QQmlJS::Lexer* lexer = new QQmlJS::Lexer(engine); - - lexer->setCode(moduleSource, 1, true); - - QQmlJS::Parser* parser = new QQmlJS::Parser(engine); - - - bool successfullyParsed = parser->parseProgram(); - if (!successfullyParsed) { - Error *err = new Error(Error::ParseError, parser->errorMessage()); - err->setColumn(parser->errorColumnNumber()); - err->setLine(parser->errorLineNumber()); - throw Error(Error::ModuleImportError, - QStringLiteral("Error while processing module %1 %2.%3") - .arg(m_module->m_import.name) - .arg(m_module->m_import.versionMajor) - .arg(m_module->m_import.versionMinor), - err); - } - - parser->rootNode()->accept(this); - - finalizeParse(); - - m_module->m_status = Module::Successful; - m_module->m_waitCondition.wakeAll(); // Wake up threads, that wait to access this module. -} - -bool ModuleLoader::visit(QQmlJS::AST::FunctionDeclaration* func) -{ - m_functionDepth++; - if (m_functionDepth == 2) - m_currentFunction = func->name; - return true; -} -void ModuleLoader::endVisit(QQmlJS::AST::FunctionDeclaration* func) -{ - m_functionDepth--; - if (m_functionDepth == 1) - m_currentFunction.clear(); -} - -bool ModuleLoader::visit(QQmlJS::AST::FunctionExpression* func) -{ - m_functionDepth++; - return true; -} -void ModuleLoader::endVisit(QQmlJS::AST::FunctionExpression* func) -{ - m_functionDepth--; -} - -bool ModuleLoader::visit(QQmlJS::AST::ReturnStatement *returnStatement) -{ - if (m_functionDepth != 1) - return false; - - ObjectLiteral *returnLiteral = cast(returnStatement->expression); - if (!returnLiteral) - return false; - - PropertyAssignmentList *assignment = returnLiteral->properties; - - while(assignment) { - PropertyNameAndValue *nameAndValue = cast(assignment->assignment); - - if (!nameAndValue) { - qWarning() << "Ignoring invalid type specification."; - continue; - } - - IdentifierExpression *functionId = cast(nameAndValue->value); - if (!functionId) { - qWarning() << "Can't recognize function identifier. Please use a simple identifier as value on type object."; - continue; - } - - m_typesToFunctionsMap.insert(nameAndValue->name->asString(), functionId->name); - assignment = assignment->next; - } - - return true; -} - -void ModuleLoader::finalizeParse() -{ - for (auto i = m_typesToFunctionsMap.constBegin(); i != m_typesToFunctionsMap.constEnd(); i++) { - // the typesToFunctionsMap contains the most basic information, namely - // which types exist. The key in this hash is the name of the type, the - // value is the name of the function that defines it. - Type *type = new Type(); - type->setName(i.key()); - m_module->m_types.insert(i.key(), type); - } -} - Module::Status Module::status() { return m_status; } const QString &Module::name() { return m_import.name; } Type *Module::type(QString name) { waitForLoaded(); return m_types.value(name); } void Module::waitForLoaded() { if (m_status != Loading) return; m_loadMutex.lock(); m_waitCondition.wait(&m_loadMutex); m_loadMutex.unlock(); } diff --git a/src/qmljsc/ir/module.h b/src/qmljsc/ir/module.h index 1748ffd..3138e6f 100644 --- a/src/qmljsc/ir/module.h +++ b/src/qmljsc/ir/module.h @@ -1,102 +1,69 @@ /* * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ -#ifndef MODULELOADER_H -#define MODULELOADER_H - -//Qt -#include - -// private Qt -#include -#include +#ifndef MODULE_H +#define MODULE_H #include "file.h" namespace QmlJSc { + +class ModuleLoader; + namespace IR { /** * This class provides API representing a Qml.js module and allows to learn * about the modules API. * * Therefore it parses the javascript source of the module and analyses it for * class definitions, properties, methods, etc. */ class Module { public: enum Status { Loading, Successful, ErrorState }; explicit Module(ImportDescription import, QObject *parent = 0); Status status(); void waitForLoaded(); Type *type(QString name); const QString &name(); private: QHash m_types; ImportDescription m_import; Status m_status; QWaitCondition m_waitCondition; QMutex m_loadMutex; - friend class ModuleLoader; -}; - -class ModuleLoader : public QRunnable, public QQmlJS::AST::Visitor -{ -public: - static Module *loadModule(ImportDescription import, QObject *parent = 0); - - void run() override; - -private: - ModuleLoader(Module *module); - virtual ~ModuleLoader(); - - void doLoad(); - bool visit(QQmlJS::AST::FunctionExpression*) override; - void endVisit(QQmlJS::AST::FunctionExpression*) override; - bool visit(QQmlJS::AST::FunctionDeclaration*) override; - void endVisit(QQmlJS::AST::FunctionDeclaration*) override; - bool visit(QQmlJS::AST::ReturnStatement *returnStatement) override; - - void finalizeParse(); - - static QHash s_loadedModules; - - Module *m_module; - int m_functionDepth; - QStringRef m_currentFunction; - QHash> m_functionProperties; - QHash m_typesToFunctionsMap; + friend class QmlJSc::ModuleLoader; }; } // namespace IR } // namespace QMLJSc -#endif // MODULELOADER_H +#endif // MODULE_H diff --git a/src/qmljsc/ir/module.cpp b/src/qmljsc/moduleloader.cpp similarity index 84% copy from src/qmljsc/ir/module.cpp copy to src/qmljsc/moduleloader.cpp index 8bf5716..5a41a0f 100644 --- a/src/qmljsc/ir/module.cpp +++ b/src/qmljsc/moduleloader.cpp @@ -1,244 +1,211 @@ /* * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Own -#include "module.h" -#include "../compiler.h" -#include "../error.h" +#include "moduleloader.h" +#include "ir/module.h" +#include "compiler.h" +#include "error.h" // Qt #include #include #include #include // Qt private #include #include #include using namespace QmlJSc; -using namespace QmlJSc::IR; using namespace QQmlJS::AST; -QHash ModuleLoader::s_loadedModules; +QHash ModuleLoader::s_loadedModules; -Module::Module(ImportDescription import, QObject *parent) - : m_import(import) - , m_status(Loading) +IR::Module *ModuleLoader::loadModule(IR::ImportDescription import) { -} - -Module *ModuleLoader::loadModule(ImportDescription import, QObject *parent) -{ - Q_ASSERT(import.kind == ImportDescription::Kind_ModuleImport); - if (Module *module = s_loadedModules.value(import)) { + Q_ASSERT(import.kind == IR::ImportDescription::Kind_ModuleImport); + if (IR::Module *module = s_loadedModules.value(import)) { return module; } - Module *module = new Module(import, parent); + IR::Module *module = new IR::Module(import, compiler); s_loadedModules.insert(import, module); ModuleLoader *loader = new ModuleLoader(module); // will delete itself when done QThreadPool::globalInstance()->start(loader); return module; } -ModuleLoader::ModuleLoader(Module *module) +ModuleLoader::ModuleLoader(IR::Module *module) : QRunnable() , Visitor() , m_module(module) , m_functionDepth(0) { } ModuleLoader::~ModuleLoader() { } void ModuleLoader::run() { try { doLoad(); } catch (const Error &e) { - m_module->m_status = Module::ErrorState; + m_module->m_status = IR::Module::ErrorState; m_module->m_waitCondition.wakeAll(); } } void ModuleLoader::doLoad() { // === Load Module File === QFile moduleFile; const QString moduleFileName = QStringLiteral("%1.%2.%3.js").arg(m_module->m_import.name) .arg(m_module->m_import.versionMajor) .arg(m_module->m_import.versionMinor); // For now we only support local files. const QStringList &includePaths = compiler->includePaths(); foreach (QString includePath, includePaths) { QDir includeDir(includePath); if (includeDir.exists(moduleFileName)) { moduleFile.setFileName(includeDir.absoluteFilePath(moduleFileName)); break; } } if (!moduleFile.exists()) { throw Error(Error::ModuleImportError, QStringLiteral("Could not find file %1 in path.").arg(moduleFileName)); } // Read file moduleFile.open(QFile::ReadOnly); QTextStream modueFileStream(&moduleFile); QString moduleSource = modueFileStream.readAll(); // === Parse file === // parsing happens in three steps: Calling the QQmlJS-parser to parse the // file and return an AST, then using the visit functions of this class to // collect the data we need and put it into m_parseData and third calling // finalizeParse() to evaluate the parse data and transform it to actual // type information. QQmlJS::Engine* engine = new QQmlJS::Engine(); QQmlJS::Lexer* lexer = new QQmlJS::Lexer(engine); lexer->setCode(moduleSource, 1, true); QQmlJS::Parser* parser = new QQmlJS::Parser(engine); bool successfullyParsed = parser->parseProgram(); if (!successfullyParsed) { Error *err = new Error(Error::ParseError, parser->errorMessage()); err->setColumn(parser->errorColumnNumber()); err->setLine(parser->errorLineNumber()); throw Error(Error::ModuleImportError, QStringLiteral("Error while processing module %1 %2.%3") .arg(m_module->m_import.name) .arg(m_module->m_import.versionMajor) .arg(m_module->m_import.versionMinor), err); } parser->rootNode()->accept(this); finalizeParse(); - m_module->m_status = Module::Successful; + m_module->m_status = IR::Module::Successful; m_module->m_waitCondition.wakeAll(); // Wake up threads, that wait to access this module. } bool ModuleLoader::visit(QQmlJS::AST::FunctionDeclaration* func) { m_functionDepth++; if (m_functionDepth == 2) m_currentFunction = func->name; return true; } void ModuleLoader::endVisit(QQmlJS::AST::FunctionDeclaration* func) { m_functionDepth--; if (m_functionDepth == 1) m_currentFunction.clear(); } bool ModuleLoader::visit(QQmlJS::AST::FunctionExpression* func) { m_functionDepth++; return true; } void ModuleLoader::endVisit(QQmlJS::AST::FunctionExpression* func) { m_functionDepth--; } bool ModuleLoader::visit(QQmlJS::AST::ReturnStatement *returnStatement) { if (m_functionDepth != 1) return false; ObjectLiteral *returnLiteral = cast(returnStatement->expression); if (!returnLiteral) return false; PropertyAssignmentList *assignment = returnLiteral->properties; while(assignment) { PropertyNameAndValue *nameAndValue = cast(assignment->assignment); if (!nameAndValue) { qWarning() << "Ignoring invalid type specification."; continue; } IdentifierExpression *functionId = cast(nameAndValue->value); if (!functionId) { qWarning() << "Can't recognize function identifier. Please use a simple identifier as value on type object."; continue; } m_typesToFunctionsMap.insert(nameAndValue->name->asString(), functionId->name); assignment = assignment->next; } return true; } void ModuleLoader::finalizeParse() { for (auto i = m_typesToFunctionsMap.constBegin(); i != m_typesToFunctionsMap.constEnd(); i++) { // the typesToFunctionsMap contains the most basic information, namely // which types exist. The key in this hash is the name of the type, the // value is the name of the function that defines it. - Type *type = new Type(); + IR::Type *type = new IR::Type(); type->setName(i.key()); m_module->m_types.insert(i.key(), type); } } - -Module::Status Module::status() -{ - return m_status; -} - - -const QString &Module::name() -{ - return m_import.name; -} - -Type *Module::type(QString name) -{ - waitForLoaded(); - return m_types.value(name); -} - -void Module::waitForLoaded() -{ - if (m_status != Loading) - return; - - m_loadMutex.lock(); - m_waitCondition.wait(&m_loadMutex); - m_loadMutex.unlock(); -} diff --git a/src/qmljsc/ir/module.h b/src/qmljsc/moduleloader.h similarity index 64% copy from src/qmljsc/ir/module.h copy to src/qmljsc/moduleloader.h index 1748ffd..a198e13 100644 --- a/src/qmljsc/ir/module.h +++ b/src/qmljsc/moduleloader.h @@ -1,102 +1,82 @@ /* * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MODULELOADER_H #define MODULELOADER_H //Qt #include // private Qt #include #include -#include "file.h" +#include "ir/file.h" namespace QmlJSc { -namespace IR { /** - * This class provides API representing a Qml.js module and allows to learn - * about the modules API. + * This class parses the javascript source of a module and analyses it for + * class definitions, properties, methods, etc. to create a Module object. * - * Therefore it parses the javascript source of the module and analyses it for - * class definitions, properties, methods, etc. + * It will always run asynchronously in its own thread. + * + * To load a module, use the static function ModuleLoader::loadModule(). */ -class Module -{ -public: - enum Status { - Loading, - Successful, - ErrorState - }; - - explicit Module(ImportDescription import, QObject *parent = 0); - - Status status(); - void waitForLoaded(); - - Type *type(QString name); - - const QString &name(); - -private: - QHash m_types; - ImportDescription m_import; - Status m_status; - QWaitCondition m_waitCondition; - QMutex m_loadMutex; - - friend class ModuleLoader; -}; - class ModuleLoader : public QRunnable, public QQmlJS::AST::Visitor { public: - static Module *loadModule(ImportDescription import, QObject *parent = 0); - - void run() override; + /** + * Initializes module loading. + * Creates a ModuleLoader object and runs it through QThreadPool. + * Beware, that it is an asynchronous function and returns a valid but + * uncompleted Module as soon as loading *started* (not when finished). + * + * This function is to be called from the compile-unit thread. + */ + static IR::Module *loadModule(IR::ImportDescription import); private: - ModuleLoader(Module *module); + ModuleLoader(IR::Module *module); virtual ~ModuleLoader(); + void run() override; + void doLoad(); bool visit(QQmlJS::AST::FunctionExpression*) override; void endVisit(QQmlJS::AST::FunctionExpression*) override; bool visit(QQmlJS::AST::FunctionDeclaration*) override; void endVisit(QQmlJS::AST::FunctionDeclaration*) override; bool visit(QQmlJS::AST::ReturnStatement *returnStatement) override; void finalizeParse(); - static QHash s_loadedModules; + static QHash s_loadedModules; - Module *m_module; + IR::Module *m_module; int m_functionDepth; QStringRef m_currentFunction; QHash> m_functionProperties; QHash m_typesToFunctionsMap; }; -} // namespace IR } // namespace QMLJSc #endif // MODULELOADER_H + diff --git a/tests/auto/qmljsc/testmodules.cpp b/tests/auto/qmljsc/testmodules.cpp index 06a303c..5f323aa 100644 --- a/tests/auto/qmljsc/testmodules.cpp +++ b/tests/auto/qmljsc/testmodules.cpp @@ -1,150 +1,151 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include "../../../src/qmljsc/compiler.h" +#include "../../../src/qmljsc/moduleloader.h" #include "../../../src/qmljsc/ir/file.h" class TestSymbolTable : public QObject { Q_OBJECT private slots: void loadModule(); void testShortSymbolName(); }; using namespace QmlJSc; using namespace QmlJSc::IR; void TestSymbolTable::loadModule() { Compiler c; IR::File file; const ImportDescription testImportDescription = {ImportDescription::Kind_ModuleImport, "TestModule", 0, 1}; compiler->addIncludePath(":/test/"); - file.addImport(testImportDescription); + file.addModule(ModuleLoader::loadModule(testImportDescription)); QVERIFY(file.type("Pastry")); QCOMPARE(file.fullyQualifiedName("Pastry"), QStringLiteral("A.Pastry")); QVERIFY(file.type("Cake")); QCOMPARE(file.fullyQualifiedName("Cake"), QStringLiteral("A.Cake")); QVERIFY(file.type("Pizza")); QCOMPARE(file.fullyQualifiedName("Pizza"), QStringLiteral("A.Pizza")); QVERIFY(!file.type("Printer")); } void TestSymbolTable::testShortSymbolName() { ShortSymbolName nA('A'); ShortSymbolName nB('B'); ShortSymbolName nZ('Z'); ShortSymbolName na('a'); ShortSymbolName nb('b'); ShortSymbolName nz('z'); ShortSymbolName nA0("A0"); ShortSymbolName nA1("A1"); ShortSymbolName nA9("A9"); ShortSymbolName nAA("AA"); ShortSymbolName nAB("AB"); ShortSymbolName nAZ("AZ"); ShortSymbolName nAa("Aa"); ShortSymbolName nAb("Ab"); ShortSymbolName nAz("Az"); ShortSymbolName na0("a0"); ShortSymbolName na1("a1"); ShortSymbolName na9("a9"); ShortSymbolName naA("aA"); ShortSymbolName naB("aB"); ShortSymbolName naZ("aZ"); ShortSymbolName naa("aa"); ShortSymbolName nab("ab"); ShortSymbolName naz("az"); ShortSymbolName nZ9("Z9"); ShortSymbolName nZZ("ZZ"); ShortSymbolName nZz("Zz"); ShortSymbolName nz9("z9"); ShortSymbolName nzZ("zZ"); ShortSymbolName nzz("zz"); ShortSymbolName nA5dh("A5dh"); ShortSymbolName nA5dz("A5dz"); ShortSymbolName nA5zz("A5zz"); ShortSymbolName nAzzz("Azzz"); ShortSymbolName nZzzz("Zzzz"); ShortSymbolName nzzzz("zzzz"); QCOMPARE(static_cast(++nA), QStringLiteral("B")); QCOMPARE(static_cast(++nB), QStringLiteral("C")); QCOMPARE(static_cast(++nZ), QStringLiteral("A0")); QCOMPARE(static_cast(++na), QStringLiteral("b")); QCOMPARE(static_cast(++nb), QStringLiteral("c")); QCOMPARE(static_cast(++nz), QStringLiteral("a0")); QCOMPARE(static_cast(++nA0), QStringLiteral("A1")); QCOMPARE(static_cast(++nA1), QStringLiteral("A2")); QCOMPARE(static_cast(++nA9), QStringLiteral("AA")); QCOMPARE(static_cast(++nAA), QStringLiteral("AB")); QCOMPARE(static_cast(++nAB), QStringLiteral("AC")); QCOMPARE(static_cast(++nAZ), QStringLiteral("Aa")); QCOMPARE(static_cast(++nAa), QStringLiteral("Ab")); QCOMPARE(static_cast(++nAb), QStringLiteral("Ac")); QCOMPARE(static_cast(++nAz), QStringLiteral("B0")); QCOMPARE(static_cast(++na0), QStringLiteral("a1")); QCOMPARE(static_cast(++na1), QStringLiteral("a2")); QCOMPARE(static_cast(++na9), QStringLiteral("aA")); QCOMPARE(static_cast(++naA), QStringLiteral("aB")); QCOMPARE(static_cast(++naB), QStringLiteral("aC")); QCOMPARE(static_cast(++naZ), QStringLiteral("aa")); QCOMPARE(static_cast(++naa), QStringLiteral("ab")); QCOMPARE(static_cast(++nab), QStringLiteral("ac")); QCOMPARE(static_cast(++naz), QStringLiteral("b0")); QCOMPARE(static_cast(++nZ9), QStringLiteral("ZA")); QCOMPARE(static_cast(++nZZ), QStringLiteral("Za")); QCOMPARE(static_cast(++nZz), QStringLiteral("A00")); QCOMPARE(static_cast(++nz9), QStringLiteral("zA")); QCOMPARE(static_cast(++nzZ), QStringLiteral("za")); QCOMPARE(static_cast(++nzz), QStringLiteral("a00")); QCOMPARE(static_cast(++nA5dh), QStringLiteral("A5di")); QCOMPARE(static_cast(++nA5dz), QStringLiteral("A5e0")); QCOMPARE(static_cast(++nA5zz), QStringLiteral("A600")); QCOMPARE(static_cast(++nAzzz), QStringLiteral("B000")); QCOMPARE(static_cast(++nZzzz), QStringLiteral("A0000")); QCOMPARE(static_cast(++nzzzz), QStringLiteral("a0000")); } QTEST_MAIN(TestSymbolTable) #include "testmodules.moc"