diff --git a/src/qmljsc/CMakeLists.txt b/src/qmljsc/CMakeLists.txt index b9c3c3a..948f162 100644 --- a/src/qmljsc/CMakeLists.txt +++ b/src/qmljsc/CMakeLists.txt @@ -1,30 +1,32 @@ find_package(Qt5 COMPONENTS Core Qml) set(libqmljsc_srcs compiler.cpp error.cpp compilerpipeline.cpp compilerpass.cpp ir/ir.cpp ir/module.cpp ir/file.cpp compilerpasses/parserpass.cpp compilerpasses/prettygeneratorpass.cpp moduleloading/abstractmoduleloader.cpp moduleloading/javascriptmoduleloader.cpp moduleloading/moduleloading.cpp + + utils/shortsymbolname.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 cdc57ad..4c5515f 100644 --- a/src/qmljsc/ir/file.cpp +++ b/src/qmljsc/ir/file.cpp @@ -1,158 +1,98 @@ /* * 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::addModule(Module *module) { - if (m_modules.contains({ module, QString() })) + if (m_importedModules.contains({ module, QString() })) return; - m_modules.append({ + m_importedModules.append({ module, ++m_prefix }); } Type *File::type(const QString &typeName) const { 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); } Component *File::rootObject() const { return m_rootObject; } void File::setRootObject(Component *rootObject) { m_rootObject = rootObject; } const File::ModuleData *File::moduleForType(const QString &typeName) const { Type *foundType = 0; const ModuleData *moduleData = 0; - foreach (const ModuleData &data, m_modules) { + foreach (const ModuleData &data, m_importedModules) { 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.") + QString("Ambigious type name. Type %1 was defined by module %2 and %3.") .arg(typeName, moduleData->module->name(), data.module->name()) ); return 0; } else { foundType = type; moduleData = &data; } } } return moduleData; } diff --git a/src/qmljsc/ir/file.h b/src/qmljsc/ir/file.h index 9c4d35e..82996ba 100644 --- a/src/qmljsc/ir/file.h +++ b/src/qmljsc/ir/file.h @@ -1,118 +1,72 @@ /* * 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 "importdefinition.h" +#include "../utils/shortsymbolname.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 addModule(Module *module); Type* type(const QString &typeName) const; QString fullyQualifiedName(const QString &typeName); Component *rootObject() const; void setRootObject(Component *root); private: const ModuleData *moduleForType(const QString &typeName) const; - QVector m_modules; + QVector m_importedModules; 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/importdefinition.h b/src/qmljsc/ir/importdefinition.h new file mode 100644 index 0000000..218939a --- /dev/null +++ b/src/qmljsc/ir/importdefinition.h @@ -0,0 +1,59 @@ +/* + * 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 IMPORTDESCRIPTION_H +#define IMPORTDESCRIPTION_H + +#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; + } +}; + +inline uint qHash(const ImportDescription &key, uint seed) +{ + return qHash(key.name, seed) ^ key.versionMajor ^ key.versionMinor; +} + +} // namespace IR +} // namespace QmlJSc + +Q_DECLARE_METATYPE(QmlJSc::IR::ImportDescription); + +#endif // IMPORTDESCRIPTION_H + diff --git a/src/qmljsc/ir/module.cpp b/src/qmljsc/ir/module.cpp index 39c4afe..7546cbb 100644 --- a/src/qmljsc/ir/module.cpp +++ b/src/qmljsc/ir/module.cpp @@ -1,109 +1,109 @@ /* * 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 . * */ // 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; Module::Module(ImportDescription import, QObject *parent) - : m_import(import) - , m_status(Loading) + : m_importDescription(import) + , m_loadingState(Loading) { } Module::~Module() { foreach (Type *type, m_types) { delete type; } } -Module::Status Module::status() +Module::LoadingState Module::loadingState() { - return m_status; + return m_loadingState; } -void Module::setStatus(Module::Status status) +void Module::setLoadingState(Module::LoadingState status) { - Q_ASSERT_X(m_status == Loading, __FILE__, + Q_ASSERT_X(m_loadingState == Loading, __FILE__, "It's not allowed to change status after loading finished."); - m_status = status; - if (m_status == Successful || m_status == ErrorState) { - m_waitCondition.wakeAll(); + m_loadingState = status; + if (m_loadingState == Successful || m_loadingState == ErrorState) { + m_loadFinishedCondition.wakeAll(); } } const QString &Module::name() { - return m_import.name; + return m_importDescription.name; } Type *Module::type(QString name) { waitForLoaded(); return m_types.value(name); } Type *Module::typeFromJSName(QString name) { return m_jsNameToTypeHash.value(name); } void Module::addType(Type *type) { - Q_ASSERT_X(m_status == Loading, __FILE__, + Q_ASSERT_X(m_loadingState == Loading, __FILE__, "It's not allowed to add types after loading finished."); m_types.insert(type->name(), type); m_jsNameToTypeHash.insert(type->javaScriptName(), type); } void Module::waitForLoaded() { - if (m_status != Loading) + if (m_loadingState != Loading) return; m_loadMutex.lock(); - m_waitCondition.wait(&m_loadMutex); + m_loadFinishedCondition.wait(&m_loadMutex); m_loadMutex.unlock(); } ImportDescription Module::importDescription() { - return m_import; + return m_importDescription; } diff --git a/src/qmljsc/ir/module.h b/src/qmljsc/ir/module.h index 50ffc62..2ef3c25 100644 --- a/src/qmljsc/ir/module.h +++ b/src/qmljsc/ir/module.h @@ -1,90 +1,90 @@ /* * 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 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 { + enum LoadingState { Loading, Successful, ErrorState }; explicit Module(ImportDescription import, QObject *parent = 0); ~Module(); - Status status(); - void setStatus(Status status); + LoadingState loadingState(); + void setLoadingState(LoadingState status); void waitForLoaded(); /** * Returns the type object for the (QML-) name. * * If the data isn't available, yet, because the module is still loading, * this function will block until the data is available. */ Type *type(QString name); /** * Returns the type object for the name it has in JS code. * * This function won't wait for the data to be available, only use this * function if you're certain about it! */ Type *typeFromJSName(QString name); const QString &name(); void addType(Type *type); ImportDescription importDescription(); private: QHash m_types; QHash m_jsNameToTypeHash; - ImportDescription m_import; - Status m_status; - QWaitCondition m_waitCondition; + ImportDescription m_importDescription; + LoadingState m_loadingState; + QWaitCondition m_loadFinishedCondition; QMutex m_loadMutex; }; } // namespace IR } // namespace QMLJSc #endif // MODULE_H diff --git a/src/qmljsc/moduleloading/abstractmoduleloader.cpp b/src/qmljsc/moduleloading/abstractmoduleloader.cpp index 2fd7ffd..5b8b820 100644 --- a/src/qmljsc/moduleloading/abstractmoduleloader.cpp +++ b/src/qmljsc/moduleloading/abstractmoduleloader.cpp @@ -1,47 +1,47 @@ /* * 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 . * */ // Own #include "abstractmoduleloader.h" #include "ir/module.h" #include "error.h" using namespace QmlJSc; +AbstractModuleLoader::AbstractModuleLoader(IR::Module *module) + : QRunnable() + , m_module(module) +{} + void AbstractModuleLoader::run() { try { doLoad(); } catch (Error *e) { qWarning() << e->what(); - m_module->setStatus(IR::Module::ErrorState); + m_module->setLoadingState(IR::Module::ErrorState); } } IR::Module *AbstractModuleLoader::module() { return m_module; } -void AbstractModuleLoader::setModule(IR::Module* module) -{ - m_module = module; -} - diff --git a/src/qmljsc/moduleloading/abstractmoduleloader.h b/src/qmljsc/moduleloading/abstractmoduleloader.h index 10c2e65..613c70a 100644 --- a/src/qmljsc/moduleloading/abstractmoduleloader.h +++ b/src/qmljsc/moduleloading/abstractmoduleloader.h @@ -1,70 +1,68 @@ /* * 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 ABSTRACTMODULELOADER_H #define ABSTRACTMODULELOADER_H //Qt #include namespace QmlJSc { namespace IR { class Module; } /** - * This class parses the javascript source of a module and analyses it for - * class definitions, properties, methods, etc. to create a Module object. + * This class provides an abstract base class for classes that can load modules. + * Examples for base classes are the javascript-module loader and the + * qtqml-module loader. * - * It will always run asynchronously in its own thread. + * It brings the basic infrastructure to run it on it's own thread (so module + * loaders will always run asynchronously in their own thread) and to figure out + * if it's the right loader for a specific module. * - * To load a module, use the static function Modules::loadModule(). - * - * If you implement a module loader, reimplement doLoad and setModule. If an - * error occurs during loading, throw an exception. AbstractModuleLoader will - * take care of communicating it up. + * If you implement this class, you need to implement canLoad, telling if the + * class is suitable to load this module and doLoad, to actually load it. + * Use module() to get the module you're about to load and add types to it. */ class AbstractModuleLoader : public QRunnable { public: + AbstractModuleLoader(IR::Module *module); /** - * Don't reimplement this in a moduleloader. It's + * Don't reimplement this in a moduleloader. */ void run() override; virtual bool canLoad() = 0; virtual void doLoad() = 0; - /** - * Needs to be called *before* run() is called. - */ - void setModule(IR::Module *module); IR::Module *module(); private: IR::Module *m_module; }; } // namespace QMLJSc #endif // ABSTRACTMODULELOADER_H diff --git a/src/qmljsc/moduleloading/javascriptmoduleloader.cpp b/src/qmljsc/moduleloading/javascriptmoduleloader.cpp index 9a051ba..fb5ae19 100644 --- a/src/qmljsc/moduleloading/javascriptmoduleloader.cpp +++ b/src/qmljsc/moduleloading/javascriptmoduleloader.cpp @@ -1,510 +1,514 @@ /* * 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 . * */ // Own #include "javascriptmoduleloader.h" #include "moduleloading.h" #include "ir/module.h" #include "ir/ir.h" #include "compiler.h" #include "error.h" // Qt #include #include #include #include // Qt private #include #include #include #define assert(condition, token, errorMessage) \ if (!condition) { \ assertFailed(token, errorMessage); \ } using namespace QmlJSc; using namespace QQmlJS; static void assertFailed(const AST::SourceLocation &token, QString errorMessage) { // Normally you wouldn't throw by reference but by value. We need to create // the error object on the stack and throw by pointer, because we'll need it // to be available on another thread. Error *error = new Error(Error::ModuleImportError, errorMessage); error->setLine(token.startLine); error->setColumn(token.startColumn); throw error; } RegisterModuleVisitor::RegisterModuleVisitor(IR::Module* module) : QQmlJS::AST::Visitor() , m_module(module) { } bool RegisterModuleVisitor::visit(AST::CallExpression *call) { if (call->base->kind != AST::Node::Kind_FieldMemberExpression) { return true; } AST::FieldMemberExpression *base = AST::cast(call->base); if (!base || base->name != QStringLiteral("registerModule")) { return true; } AST::IdentifierExpression *probablyEngine = AST::cast(base->base); if (!probablyEngine || probablyEngine->name != QStringLiteral("__engine")) { return true; } // Ok, apparently, it's the __engine.registerModule expression, we're looking for. // If now still some assumption fails, throw an error. if (!call->arguments) { Error *error = new Error(Error::ModuleImportError, "Malformed registerModule call: No argument provided."); error->setLine(call->lparenToken.startLine); error->setColumn(call->lparenToken.startColumn); throw error; } AST::ObjectLiteral *moduleInfoLiteral = AST::cast(call->arguments->expression); assert(moduleInfoLiteral, call->lparenToken, "Malformed registerModule call: Wrong argument type provided. Expected Object Literal."); AST::PropertyAssignmentList *assignment = moduleInfoLiteral->properties; while(assignment) { AST::PropertyNameAndValue *nameAndValue = AST::cast(assignment->assignment); assert(nameAndValue, assignment->assignment->firstSourceLocation(), "Malformed registerModule call: Invalid type specification." ); AST::IdentifierExpression *functionId = AST::cast(nameAndValue->value); assert(functionId, nameAndValue->value->firstSourceLocation(), "Malformed registerModule call: Can't recognize function identifier. " "Please use a simple identifier as value on type object." ); IR::Class *c = new IR::Class; c->setName(nameAndValue->name->asString()); c->setJavaScriptName(functionId->name.toString()); m_module->addType(c); assignment = assignment->next; } return true; } TypeDefinitionVisitor::TypeDefinitionVisitor(IR::Module* module) : QQmlJS::AST::Visitor() , m_module(module) { } bool TypeDefinitionVisitor::visit(AST::FunctionExpression* func) { m_currentFunctionStack << func; return true; } void TypeDefinitionVisitor::endVisit(AST::FunctionExpression* func) { m_currentFunctionStack.removeLast(); } bool TypeDefinitionVisitor::visit(AST::FunctionDeclaration* func) { m_currentFunctionStack << func; return true; } void TypeDefinitionVisitor::endVisit(AST::FunctionDeclaration* func) { m_currentFunctionStack.removeLast(); } bool TypeDefinitionVisitor::visit(AST::BinaryExpression* expr) { // --- Might be a property, a method, a signal, a class or uninteresting. if (expr->right->kind == AST::Node::Kind_FunctionExpression) { findMethodDefinition(expr); // Each function expression is a potential class definition. Give it // a name, so we may reference it later on. QStringRef name; if (expr->left->kind == AST::Node::Kind_FieldMemberExpression) { name = AST::cast(expr->left)->name; } else if (expr->left->kind == AST::Node::Kind_FunctionExpression) { name = AST::cast(expr->left)->name; } else { return true; } AST::cast(expr->right)->name = name; } else if (expr->right->kind == AST::Node::Kind_CallExpression) { findSignalDefinition(expr); } else if (expr->right->kind == AST::Node::Kind_NewMemberExpression) { findPropertyDefinition(expr); } return true; } bool TypeDefinitionVisitor::visit(AST::VariableDeclaration *var) { AST::FunctionExpression *func = AST::cast(var->expression); if (!func) { return true; } // This is a potential class definition. Give the function a name, so we may // reference it later on. func->name = var->name; return true; } bool TypeDefinitionVisitor::visit(AST::CallExpression *call) { if (call->base->kind != AST::Node::Kind_IdentifierExpression) { return true; } AST::IdentifierExpression *maybeInheritance = AST::cast(call->base); if (!maybeInheritance || maybeInheritance->name != QStringLiteral("QW_INHERIT")) { return true; } // Apparently this is a QW_INHERIT call, we're looking for. // If now still some assumption fails, throw an error. assert(call->arguments && call->arguments->next, call->lparenToken, "Malformed QW_INHERIT call: One or no argument provided. Expected two." ); AST::IdentifierExpression *constructor = AST::cast(call->arguments->expression); AST::IdentifierExpression *baseClass = AST::cast(call->arguments->next->expression); assert(constructor && baseClass, call->lparenToken, "Malformed QW_INHERIT call: Wrong argument types provided. Expected two identifier expressions." ); IR::Type *t = m_module->typeFromJSName(constructor->name.toString()); assert(t, constructor->firstSourceLocation(), "Using a type that won't get registered."); t->setSuper(getType(baseClass->name)); return true; } void TypeDefinitionVisitor::findPropertyDefinition(AST::BinaryExpression *expr) { - if (expr->op != 3) { // 3 stands for "=" + if (expr->op != QSOperator::Assign) { return; } AST::FieldMemberExpression *lValue = AST::cast(expr->left); AST::NewMemberExpression *rValue = AST::cast(expr->right); if (!rValue || !lValue) { return; } AST::IdentifierExpression *constructor = AST::cast(rValue->base); AST::ThisExpression *maybeThis = AST::cast(lValue->base); if (!constructor || constructor->name != QStringLiteral("QWProperty") || !maybeThis) { return; } // Ok, this is a property definition IR::Type *t = m_module->typeFromJSName(m_currentFunctionStack.last()->name.toString()); assert(t, lValue->firstSourceLocation(), "Registering properties to a type that won't get registered."); IR::Property *property = t->addProperty(lValue->name.toString()); AST::ObjectLiteral *parameters = AST::cast(rValue->arguments->expression); if (!parameters) { return; } for (AST::PropertyAssignmentList *aList = parameters->properties; aList; aList = aList->next) { AST::PropertyNameAndValue *nameAndValue = AST::cast(aList->assignment); assert(nameAndValue, aList->assignment->firstSourceLocation(), "Malformed QWProperty call: Expected argument to be a name value pair." ); if (nameAndValue->name->asString() == "type") { AST::IdentifierExpression *id = AST::cast(nameAndValue->value); assert(id, nameAndValue->value->firstSourceLocation(), "Malformed QWProperty call: Expected argument 'type' to have an identifier expression as value." ); property->type = getType(id->name); continue; } if (nameAndValue->name->asString() == "typeArg") { AST::IdentifierExpression *id = AST::cast(nameAndValue->value); assert(id, nameAndValue->value->firstSourceLocation(), "Malformed QWProperty call: Expected argument 'typeArg' to have an identifier expression as value." ); property->type = getType(id->name); continue; } if (nameAndValue->name->asString() == "initialValue") { property->jsValue = nameAndValue->value; continue; } if (nameAndValue->name->asString() == "readonly") { assert(nameAndValue->value->kind == AST::Node::Kind_TrueLiteral || nameAndValue->value->kind == AST::Node::Kind_FalseLiteral, nameAndValue->colonToken, "Malformed QWProperty call: readonly may only have true or false as value." ); property->readOnly = nameAndValue->value->kind == AST::Node::Kind_TrueLiteral; continue; } if (nameAndValue->name->asString() == "constant") { assert(nameAndValue->value->kind == AST::Node::Kind_TrueLiteral || nameAndValue->value->kind == AST::Node::Kind_FalseLiteral, nameAndValue->colonToken, "Malformed QWProperty call: constant may only have true or false as value." ); property->constant = nameAndValue->value->kind == AST::Node::Kind_TrueLiteral; continue; } } } void TypeDefinitionVisitor::findMethodDefinition(AST::BinaryExpression *expr) { - if (expr->op != 3) { // 3 stands for "=" + if (expr->op != QSOperator::Assign) { return; } AST::FieldMemberExpression *lValue = AST::cast(expr->left); AST::FunctionExpression *func = AST::cast(expr->right); IR::Type *t; if (lValue->base->kind == AST::Node::Kind_ThisExpression) { t = m_module->typeFromJSName(m_currentFunctionStack.last()->name.toString()); } else { AST::FieldMemberExpression *first = AST::cast(lValue->base); if (!first || first->name != QStringLiteral("prototype")) { return; } AST::IdentifierExpression *constructor = AST::cast(first->base); if (!first) { return; } t = m_module->typeFromJSName(constructor->name.toString()); } if (!t) { return; } // Treat as a method definiton of a class. We can't ever be sure, but let's // try and discard later, if it turns out not to be one. IR::Method *method = t->addMethod(lValue->name.toString()); AST::FormalParameterList *parameter = func->formals; while (parameter) { method->parameters << parameter->name.toString(); parameter = parameter->next; } } void TypeDefinitionVisitor::findSignalDefinition(AST::BinaryExpression *expr) { - if (expr->op != 3) { // 3 stands for "=" + if (expr->op != QSOperator::Assign) { return; } AST::FieldMemberExpression *lValue = AST::cast(expr->left); AST::CallExpression *rValue = AST::cast(expr->right); if (!rValue || !lValue) { return; } AST::IdentifierExpression *maybeSignal = AST::cast(rValue->base); AST::ThisExpression *maybeThis = AST::cast(lValue->base); if (!maybeSignal || maybeSignal->name != QStringLiteral("QWSignal") || !maybeThis) { return; } // Ok, this is a signal definition IR::Type *t = m_module->typeFromJSName(m_currentFunctionStack.last()->name.toString()); assert(t, lValue->firstSourceLocation(), "Registering a signal to a type that won't get registered."); IR::Signal *signal = t->addSignal(lValue->name.toString()); AST::ArgumentList *argumentList = rValue->arguments; if (!argumentList) { return; // Ok, no arguments, so we're done here. } AST::ArrayLiteral *arrayLit = AST::cast(argumentList->expression); assert(arrayLit, argumentList->expression->firstSourceLocation(), "Malformed Signal definition:" " First argument must be an array literal."); AST::ElementList *array = arrayLit->elements; // Go through all the elements in the list, each of which is an object // literal describing one parameter while (array) { AST::ObjectLiteral *parameterObject = AST::cast(array->expression); assert(parameterObject, array->expression->firstSourceLocation(), "Malformed Signal definition: Array elements must be object literals."); AST::PropertyAssignmentList *parameterData = parameterObject->properties; IR::Type *type = 0; QString name; // Go through all properties of the object literal. Basically we're // looking for "type" and "name" while (parameterData) { AST::PropertyNameAndValue *nameAndValue = AST::cast(parameterData->assignment); assert(nameAndValue, parameterData->assignment->firstSourceLocation(), "Malformed Signal definition: The definition of a parameter " "must only contain name and value."); if (nameAndValue->name->asString() == QStringLiteral("type")) { AST::IdentifierExpression *t = AST::cast(nameAndValue->value); assert(t, nameAndValue->value->firstSourceLocation(), "Malformed Signal definition:" "The type definition of a parameter must be an identifier expression" "that refers to a type."); type = getType(t->name); } else if (nameAndValue->name->asString() == QStringLiteral("name")) { AST::StringLiteral *n = AST::cast(nameAndValue->value); assert(n, nameAndValue->value->firstSourceLocation(), "Malformed Signal definition:" "The name definition of a parameter must be a string literal."); name = n->value.toString(); } parameterData = parameterData->next; } signal->parameters.append({type, name}); array = array->next; } } IR::Type *TypeDefinitionVisitor::getType(const QStringRef& name) { IR::Type *t = 0; t = m_module->typeFromJSName(name.toString()); - // TODO: Search different locations. + // TODO: Search different locations (Task T488). return t; } -JavaScriptModuleLoader *JavaScriptModuleLoader::create() +JavaScriptModuleLoader *JavaScriptModuleLoader::create(IR::Module *module) { - return new JavaScriptModuleLoader; + return new JavaScriptModuleLoader(module); } +JavaScriptModuleLoader::JavaScriptModuleLoader(IR::Module *module) + : AbstractModuleLoader(module) +{} + bool JavaScriptModuleLoader::canLoad() { IR::Module *module = AbstractModuleLoader::module(); QString moduleFileName = QStringLiteral("%1.%2.%3.js").arg(module->importDescription().name) - .arg(module->importDescription().versionMajor) - .arg(module->importDescription().versionMinor); + .arg(module->importDescription().versionMajor) + .arg(module->importDescription().versionMinor); // For now we only support local files. const QStringList &includePaths = compiler->includePaths(); foreach (QString includePath, includePaths) { QDir includeDir(includePath); if (includeDir.exists(moduleFileName)) { m_moduleFile.setFileName(includeDir.absoluteFilePath(moduleFileName)); break; } } return m_moduleFile.exists(); } void JavaScriptModuleLoader::doLoad() { IR::Module *module = AbstractModuleLoader::module(); if (!m_moduleFile.exists()) { // We checked that already, so it should never happen. throw new Error(Error::ModuleImportError, QStringLiteral("Could not find file %1 in path.").arg(m_moduleFile.fileName())); } // Read file m_moduleFile.open(QFile::ReadOnly); QTextStream modueFileStream(&m_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 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 new Error(Error::ModuleImportError, - QStringLiteral("Error while processing module %1 %2.%3") + QStringLiteral("Error while processing module %1 %2.%3") .arg(module->importDescription().name) .arg(module->importDescription().versionMajor) .arg(module->importDescription().versionMinor), - err); + err); } AST::Program *ast = AST::cast(parser->rootNode()); try { RegisterModuleVisitor registerModuleVisitor(module); TypeDefinitionVisitor typeDefinitionVisitor(module); ast->accept(®isterModuleVisitor); ast->accept(&typeDefinitionVisitor); } catch (Error *e) { e->setFile(m_moduleFile.fileName()); throw e; } - module->setStatus(IR::Module::Successful); + module->setLoadingState(IR::Module::Successful); } diff --git a/src/qmljsc/moduleloading/javascriptmoduleloader.h b/src/qmljsc/moduleloading/javascriptmoduleloader.h index 3835ed5..61f63da 100644 --- a/src/qmljsc/moduleloading/javascriptmoduleloader.h +++ b/src/qmljsc/moduleloading/javascriptmoduleloader.h @@ -1,112 +1,113 @@ /* * 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 JAVASCRIPTMODULELOADER_H #define JAVASCRIPTMODULELOADER_H // Qt #include #include // private Qt #include #include // own #include "abstractmoduleloader.h" #include "../ir/file.h" namespace QmlJSc { namespace IR { class Class; class Module; } class RegisterModuleVisitor : public QQmlJS::AST::Visitor { public: explicit RegisterModuleVisitor(IR::Module *module); bool visit(QQmlJS::AST::CallExpression *call) override; private: IR::Module *m_module; }; class TypeDefinitionVisitor : public QQmlJS::AST::Visitor { public: explicit TypeDefinitionVisitor(IR::Module *module); 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::BinaryExpression*) override; bool visit(QQmlJS::AST::VariableDeclaration*) override; bool visit(QQmlJS::AST::CallExpression *call) override; void findPropertyDefinition(QQmlJS::AST::BinaryExpression *expr); void findMethodDefinition(QQmlJS::AST::BinaryExpression *expr); void findSignalDefinition(QQmlJS::AST::BinaryExpression *expr); /** * Looks for the type with the given name at several places and returns it, * if found or, if not, it will throw an error. * * This is used when seeing a referenced type. */ IR::Type *getType(const QStringRef &name); private: IR::Module *m_module; /** * Stack of nested functions (closures) we're in. * Each function is represented by it's AST subtree. */ QVector m_currentFunctionStack; }; /** * This class parses the javascript source of a module and analyses it for * class definitions, properties, methods, etc. to create a Module object. * * It will always run asynchronously in its own thread. * * To load a module, use the static function ModuleLoading::loadModule(). */ class JavaScriptModuleLoader : public AbstractModuleLoader { public: - static JavaScriptModuleLoader *create(); + static JavaScriptModuleLoader *create(IR::Module *module); bool canLoad(); void doLoad(); private: + JavaScriptModuleLoader(IR::Module *module); QFile m_moduleFile; }; } // namespace QMLJSc #endif // JAVASCRIPTMODULELOADER_H diff --git a/src/qmljsc/moduleloading/moduleloading.cpp b/src/qmljsc/moduleloading/moduleloading.cpp index d6c5a87..5823c6c 100644 --- a/src/qmljsc/moduleloading/moduleloading.cpp +++ b/src/qmljsc/moduleloading/moduleloading.cpp @@ -1,87 +1,86 @@ /* * 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 . * */ // Own #include "moduleloading.h" #include "abstractmoduleloader.h" #include "ir/module.h" #include "ir/ir.h" #include "compiler.h" #include "error.h" // Qt #include #include #include #include // Qt private #include #include #include #define assert(condition, token, errorMessage) \ if (!condition) { \ assertFailed(token, errorMessage); \ } using namespace QmlJSc; using namespace QQmlJS; QHash ModuleLoading::s_loadedModules; -QVector> ModuleLoading::s_moduleLoaders; +QVector ModuleLoading::s_moduleLoaderFactories; IR::Module *ModuleLoading::loadModule(IR::ImportDescription import) { Q_ASSERT(import.kind == IR::ImportDescription::Kind_ModuleImport); if (IR::Module *module = s_loadedModules.value(import)) { return module; } IR::Module *module = new IR::Module(import, compiler); s_loadedModules.insert(import, module); AbstractModuleLoader *loader = 0; - foreach (auto factory, s_moduleLoaders) { - loader = factory(); // will delete itself when done - loader->setModule(module); + foreach (ModuleLoaderFactoryFunc createLoaderFor, s_moduleLoaderFactories) { + loader = createLoaderFor(module); // will delete itself when done if (loader->canLoad()) { break; } else { delete loader; loader = 0; } } if (!loader) { throw new Error(Error::ModuleImportError, QStringLiteral("Module %1 %2.%3 was not " "found. Check if it is installed and include paths are correctly set")); } QThreadPool::globalInstance()->start(loader); return module; } -void ModuleLoading::registerModuleLoader(std::function factory) +void ModuleLoading::registerModuleLoader(ModuleLoaderFactoryFunc factory) { - s_moduleLoaders.append(factory); + s_moduleLoaderFactories.append(factory); } diff --git a/src/qmljsc/moduleloading/moduleloading.h b/src/qmljsc/moduleloading/moduleloading.h index 34cd589..68db9b5 100644 --- a/src/qmljsc/moduleloading/moduleloading.h +++ b/src/qmljsc/moduleloading/moduleloading.h @@ -1,63 +1,65 @@ /* * 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 MODULELOADING_H #define MODULELOADING_H // Qt #include // std #include // own #include "../ir/file.h" namespace QmlJSc { namespace IR { class Class; } class AbstractModuleLoader; +typedef std::function ModuleLoaderFactoryFunc; + class ModuleLoading { public: /** * Initializes module loading. * Creates a AbstractModuleLoader 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); - static void registerModuleLoader(std::function factory); + static void registerModuleLoader(ModuleLoaderFactoryFunc factory); private: static QHash s_loadedModules; - static QVector> s_moduleLoaders; + static QVector s_moduleLoaderFactories; }; } // namespace QMLJSc #endif // MODULELOADING_H diff --git a/src/qmljsc/ir/file.cpp b/src/qmljsc/utils/shortsymbolname.cpp similarity index 55% copy from src/qmljsc/ir/file.cpp copy to src/qmljsc/utils/shortsymbolname.cpp index cdc57ad..f127394 100644 --- a/src/qmljsc/ir/file.cpp +++ b/src/qmljsc/utils/shortsymbolname.cpp @@ -1,158 +1,83 @@ /* * 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" +#include "shortsymbolname.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::addModule(Module *module) -{ - if (m_modules.contains({ module, QString() })) - return; - - m_modules.append({ - module, - ++m_prefix - }); -} - -Type *File::type(const QString &typeName) const -{ - 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); -} - -Component *File::rootObject() const -{ - return m_rootObject; -} - -void File::setRootObject(Component *rootObject) -{ - m_rootObject = rootObject; -} - -const File::ModuleData *File::moduleForType(const QString &typeName) const -{ - Type *foundType = 0; - const ModuleData *moduleData = 0; - 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(), data.module->name()) - ); - return 0; - } else { - foundType = type; - moduleData = &data; - } - } - } - return moduleData; -} diff --git a/src/qmljsc/utils/shortsymbolname.h b/src/qmljsc/utils/shortsymbolname.h new file mode 100644 index 0000000..484afa6 --- /dev/null +++ b/src/qmljsc/utils/shortsymbolname.h @@ -0,0 +1,52 @@ +/* + * 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 SHORTSYMBOLNAME_H +#define SHORTSYMBOLNAME_H + +#include + +namespace QmlJSc { + +/** + * 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++(); +}; + +} // namespace QmlJSc + +#endif // SHORTSYMBOLNAME_H + diff --git a/tests/auto/qmljsc/testmodules.cpp b/tests/auto/qmljsc/testmodules.cpp index aff18e5..657e6fe 100644 --- a/tests/auto/qmljsc/testmodules.cpp +++ b/tests/auto/qmljsc/testmodules.cpp @@ -1,263 +1,261 @@ /* * 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 . * */ // Qt #include #include #include #include // Qt private #include // own #include "../../../src/qmljsc/compiler.h" #include "../../../src/qmljsc/moduleloading/moduleloading.h" #include "../../../src/qmljsc/moduleloading/javascriptmoduleloader.h" #include "../../../src/qmljsc/ir/module.h" -class TestSymbolTable : public QObject +class TestModules : public QObject { Q_OBJECT private slots: void loadMinimalModule(); -// void loadModule(); + void loadModule(); void testShortSymbolName(); }; using namespace QmlJSc; using namespace QmlJSc::IR; using namespace QQmlJS; -void TestSymbolTable::loadMinimalModule() +void TestModules::loadMinimalModule() { Compiler comp; ModuleLoading::registerModuleLoader(&JavaScriptModuleLoader::create); const ImportDescription testImportDescription = {ImportDescription::Kind_ModuleImport, "MinimalModule", 0, 1}; compiler->addIncludePath(":/test/"); IR::Module *module = ModuleLoading::loadModule(testImportDescription); module->waitForLoaded(); Type *a = module->type("A"); Type *b = module->type("B"); Type *c = module->type("C"); Type *d = module->type("D"); Type *e = module->type("E"); - QCOMPARE((int)module->status(), (int)Module::Successful); + QCOMPARE((int)module->loadingState(), (int)Module::Successful); QVERIFY(a); QVERIFY(b); QVERIFY(c); QVERIFY(d); QVERIFY(!e); QCOMPARE(a->name(), QStringLiteral("A")); QCOMPARE(b->name(), QStringLiteral("B")); QCOMPARE(c->name(), QStringLiteral("C")); QCOMPARE(d->name(), QStringLiteral("D")); QVERIFY(b->property("minimalProp")); QVERIFY(c->property("otherPropWithType")); QVERIFY(c->property("yaPropWithType")); QVERIFY(d->property("readonlyProp")); QVERIFY(d->property("otherPropWithType")); QVERIFY(!d->property("yaPropWithType")); IR::Property *readonlyProp = d->property("readonlyProp"); QVERIFY(readonlyProp); QCOMPARE(readonlyProp->type, d); QCOMPARE(readonlyProp->readOnly, true); QCOMPARE(readonlyProp->jsValue->kind, (int)AST::Node::Kind_FalseLiteral); IR::Method *prototypeMethod = b->method("prototypeMethod"); QVERIFY(prototypeMethod); QCOMPARE(prototypeMethod->name, QStringLiteral("prototypeMethod")); IR::Method *bPrototypeMethod = b->method("prototypeMethod"); QVERIFY(bPrototypeMethod); QCOMPARE(bPrototypeMethod->parameters.size(), 0); IR::Method *bPrototypeMethod2 = b->method("prototypeMethod2"); QVERIFY(bPrototypeMethod2); QCOMPARE(bPrototypeMethod2->parameters.size(), 1); QCOMPARE(bPrototypeMethod2->parameters[0], QStringLiteral("prop")); IR::Method *bConstructorMethod = b->method("constructorMethod"); QVERIFY(bConstructorMethod); QCOMPARE(bConstructorMethod->parameters.size(), 2); QCOMPARE(bConstructorMethod->parameters[0], QStringLiteral("prop1")); QCOMPARE(bConstructorMethod->parameters[1], QStringLiteral("prop2")); QVERIFY(c->method("prototypeMethod")); QVERIFY(c->method("constructorMethod")); QVERIFY(d->method("prototypeMethod")); QVERIFY(d->method("constructorMethod")); IR::Signal *someSignal = d->signal("someSignal"); QVERIFY(someSignal); QCOMPARE(someSignal->parameters.size(), 0); IR::Signal *anotherSignal = d->signal("anotherSignal"); QVERIFY(anotherSignal); QCOMPARE(anotherSignal->parameters.size(), 2); QCOMPARE(anotherSignal->parameters[0].name, QStringLiteral("arg1")); QCOMPARE(anotherSignal->parameters[0].type, a); QCOMPARE(anotherSignal->parameters[1].name, QStringLiteral("arg2")); QCOMPARE(anotherSignal->parameters[1].type, a); } -#if false -void TestSymbolTable::loadModule() +void TestModules::loadModule() { + QSKIP("This will be implemented later (Task T488)."); Compiler c; const ImportDescription testImportDescription = {ImportDescription::Kind_ModuleImport, "TestModule", 0, 1}; compiler->addIncludePath(":/test/"); IR::Module *module = ModuleLoading::loadModule(testImportDescription); module->waitForLoaded(); Type *pastry = module->type("Pastry"); Type *cake = module->type("Cake"); Type *pizza = module->type("Pizza"); Type *printer = module->type("Printer"); - QVERIFY(module->status() == Module::Successful); + QVERIFY(module->loadingState() == Module::Successful); QVERIFY(pastry); QVERIFY(cake); QVERIFY(pizza); QVERIFY(!printer); QCOMPARE(pastry->name(), QStringLiteral("Pastry")); QCOMPARE(cake->name(), QStringLiteral("Cake")); QCOMPARE(pizza->name(), QStringLiteral("Pizza")); QVERIFY(pastry->property("bakingTime")); QVERIFY(cake->property("containsRawEgg")); QVERIFY(cake->property("bakingTime")); QVERIFY(pizza->property("isCalzone")); QVERIFY(pizza->property("topping")); QVERIFY(!pizza->property("containsRawEgg")); IR::Property *baked = pizza->property("baked"); QVERIFY(baked); -// QCOMPARE(baked->type, ); QCOMPARE(baked->readOnly, true); QCOMPARE(baked->jsValue->kind, (int)AST::Node::Kind_FalseLiteral); QVERIFY(pastry->method("eat")); QVERIFY(pastry->method("bake")); QVERIFY(pizza->method("eat")); QVERIFY(pizza->method("bake")); QVERIFY(pizza->signal("bakingFinished")); } -#endif -void TestSymbolTable::testShortSymbolName() +void TestModules::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) +QTEST_MAIN(TestModules) #include "testmodules.moc"