diff --git a/src/qmljsc/CMakeLists.txt b/src/qmljsc/CMakeLists.txt index 02c0d92..cfe53ac 100644 --- a/src/qmljsc/CMakeLists.txt +++ b/src/qmljsc/CMakeLists.txt @@ -1,33 +1,35 @@ find_package(Qt5 COMPONENTS Core Qml) set(libqmljsc_srcs compiler.cpp error.cpp compilerpipeline.cpp compilerpass.cpp ir/objecttree.cpp ir/module.cpp ir/file.cpp ir/typesystem.cpp + ir/builtintypes.cpp compilerpasses/parserpass.cpp compilerpasses/prettygeneratorpass.cpp + moduleloading/moduleloading.cpp moduleloading/abstractmoduleloader.cpp moduleloading/javascriptmoduleloader.cpp - moduleloading/moduleloading.cpp + moduleloading/qtqmlmoduleloader.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/builtintypes.cpp b/src/qmljsc/ir/builtintypes.cpp new file mode 100644 index 0000000..2e2f7c2 --- /dev/null +++ b/src/qmljsc/ir/builtintypes.cpp @@ -0,0 +1,104 @@ +/* + * 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 "builtintypes.h" +#include "ir/typesystem.h" + +using namespace QmlJSc::IR; + +Type BuiltinTypes::s_builtinTypes[] = +{ + {"bool", "Boolean"}, + {"double", "QWDouble"}, + {"enum", "QWEnum"}, + {"int", "QWInt"}, + {"list", "QWList", Type::IsList}, + {"real", "QWReal"}, + {"string", "String"}, + {"url", "QWUrl"}, + {"var", "QWVar"} +}; + +Type *BuiltinTypes::type(const QString &name) +{ + for (int i = 0; i < builtinTypesCount; i++) { + if (s_builtinTypes[i].name() == name) { + return &s_builtinTypes[i]; + } + } + return 0; +} + +Type *BuiltinTypes::typeFromJSName(const QString &jsName) +{ + for (int i = 0; i < builtinTypesCount; i++) { + if (s_builtinTypes[i].javaScriptName() == jsName) { + return &s_builtinTypes[i]; + } + } + return 0; +} + +Type *BuiltinTypes::boolType() +{ + return &s_builtinTypes[0]; +} + +Type *BuiltinTypes::doubleType() +{ + return &s_builtinTypes[1]; +} + +Type *BuiltinTypes::enumType() +{ + return &s_builtinTypes[2]; +} + +Type *BuiltinTypes::intType() +{ + return &s_builtinTypes[3]; +} + +Type *BuiltinTypes::listType() +{ + return &s_builtinTypes[4]; +} + +Type *BuiltinTypes::realType() +{ + return &s_builtinTypes[5]; +} + +Type *BuiltinTypes::stringType() +{ + return &s_builtinTypes[6]; +} + +Type *BuiltinTypes::urlType() +{ + return &s_builtinTypes[7]; +} + +Type *BuiltinTypes::varType() +{ + return &s_builtinTypes[8]; +} + diff --git a/src/qmljsc/ir/builtintypes.h b/src/qmljsc/ir/builtintypes.h new file mode 100644 index 0000000..82321fd --- /dev/null +++ b/src/qmljsc/ir/builtintypes.h @@ -0,0 +1,58 @@ +/* + * 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 BUILTINTYPES_H +#define BUILTINTYPES_H + +#include + +namespace QmlJSc { + +namespace IR { + +class Type; + +class BuiltinTypes +{ +public: + static Type *type(const QString &name); + static Type *typeFromJSName(const QString &name); + static Type *boolType(); + static Type *doubleType(); + static Type *enumType(); + static Type *intType(); + static Type *listType(); + static Type *realType(); + static Type *stringType(); + static Type *urlType(); + static Type *varType(); + +private: + static bool init(); + + static const int builtinTypesCount = 9; + + static Type s_builtinTypes[builtinTypesCount]; +}; + +} // namespace IR +} // namespace QMLJSc + +#endif // BUILTINTYPES_H diff --git a/src/qmljsc/ir/typesystem.cpp b/src/qmljsc/ir/typesystem.cpp index f545dc1..996e136 100644 --- a/src/qmljsc/ir/typesystem.cpp +++ b/src/qmljsc/ir/typesystem.cpp @@ -1,175 +1,177 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * Copyright (C) 2015 Jan Marker * * 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 "typesystem.h" using namespace QmlJSc::IR; Type::Type() - : m_super(0) + : m_super(0) { } -Type::Type(Flags flags) - : m_flags(flags) - , m_super(0) +Type::Type(const QString &name, const QString &jsName, Flags flags) + : m_name(name) + , m_javaScriptName(jsName) + , m_flags(flags) + , m_super(0) { } const QString &Type::name() { return m_name; } const QString &Type::javaScriptName() { return m_javaScriptName; } Type::Flags Type::flags() { return m_flags; } void Type::setFlags(Flags flags) { m_flags = flags; } Property *Type::addProperty(const QString &name) { return &m_properties.insert(name, Property(name)).value(); } Method *Type::addMethod(const QString &name) { return &m_methods.insert(name, Method(name)).value(); } Signal *Type::addSignal(const QString &name) { return &m_signals.insert(name, Signal(name)).value(); } void Type::setName(const QString &name) { m_name = name; } void Type::setJavaScriptName(const QString &jsName) { m_javaScriptName = jsName; } Property *Type::property(const QString &name) { if (m_properties.contains(name)) { return &m_properties[name]; } if (m_super) { return m_super->property(name); } return 0; } Method *Type::method(const QString &name) { if (m_methods.contains(name)) { return &m_methods[name]; } if (m_super) { return m_super->method(name); } return 0; } Signal *Type::signal(const QString &name) { if (m_signals.contains(name)) { return &m_signals[name]; } if (m_super) { return m_super->signal(name); } return 0; } Type *Type::super() { return m_super; } void Type::setSuper(Type *superType) { m_super = superType; } Type * Type::attachedType() { return m_attachedType; } void Type::setAttachedType(Type *attachedType) { m_attachedType = attachedType; } Method::Method() : returnType(0) { } Method::Method(const QString &name) : returnType(0) , name(name) { } Method::Method(Type *returnType, QString name) : returnType(returnType) , name(name) { } Signal::Signal() { } Signal::Signal(QString name) : name(name) { } Property::Property() : type(0) { } Property::Property(const QString &name) : type(0) , name(name) { } Property::Property(Type *type, QString name) : type(type) , name(name) { } \ No newline at end of file diff --git a/src/qmljsc/ir/typesystem.h b/src/qmljsc/ir/typesystem.h index 6243287..286ea51 100644 --- a/src/qmljsc/ir/typesystem.h +++ b/src/qmljsc/ir/typesystem.h @@ -1,145 +1,146 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * Copyright (C) 2015 Jan Marker * * 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 TYPESYSTEM_H #define TYPESYSTEM_H #include #include #include namespace QQmlJS { namespace AST { class ExpressionNode; } } namespace QmlJSc { namespace IR { class Property; class Method; class Signal; /** * This class provides API representing Qml.js objects and types and allows to * learn about the type's properties, functions, etc. * * Type hereby refers to (built in) basic types, types provided by modules and * components as well as any objects defined in QML. */ class Type { public: enum Flag { None = 0, IsInstantiable = 1, - IsComponent = 2 + IsComponent = 2, + IsList = 4 }; Q_DECLARE_FLAGS(Flags, Flag); Type(); - Type(Flags flags); + Type(const QString &name, const QString &jsName = QString(), Flags flags = None); const QString &name(); const QString &javaScriptName(); Property *property(const QString &name); Method *method(const QString &name); Signal *signal(const QString &name); Property *addProperty(const QString &name); Method *addMethod(const QString &name); Signal *addSignal(const QString &name); void setName(const QString &name); void setJavaScriptName(const QString &jsName); Type *super(); void setSuper(Type *superType); void setFlags(Flags flags); Flags flags(); Type *attachedType(); void setAttachedType(Type *); protected: QString m_name; QString m_javaScriptName; Flags m_flags; QHash m_properties; QHash m_methods; QHash m_signals; Type *m_attachedType; /** * pointer to the super class or in case of objects the class of the object */ Type *m_super; friend class TestIR; }; struct Parameter { Type *type; QString name; }; class Method { public: Method(); Method(const QString &name); Method(Type *returnType, QString name); Type *returnType; QString name; QVector parameters; }; class Signal { public: Signal(); Signal(QString name); QString name; QVector parameters; }; class Property { public: Property(); Property(const QString &name); Property(Type *type, QString name); Type *type; QString name; bool readOnly :1; bool constant :1; bool dummy :6; }; } // namespace IR } // namespace QmlJSc #endif // TYPESYSTEM_H diff --git a/src/qmljsc/moduleloading/javascriptmoduleloader.cpp b/src/qmljsc/moduleloading/javascriptmoduleloader.cpp index c98be3d..47649d1 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/typesystem.h" +#include "ir/builtintypes.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::Type *c = new IR::Type; 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 != 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() == "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 != 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 != 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 *TypeDefinitionVisitor::getType(const QStringRef& nameRef) { - IR::Type *t = 0; - t = m_module->typeFromJSName(name.toString()); - // TODO: Search different locations (Task T488). + IR::Type *type = 0; + QString name = nameRef.toString(); + type = m_module->typeFromJSName(name); + if (!type) { + type = IR::BuiltinTypes::typeFromJSName(name); + } - return t; + return type; } JavaScriptModuleLoader *JavaScriptModuleLoader::create(IR::Module *module) { 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); // 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") .arg(module->importDescription().name) .arg(module->importDescription().versionMajor) .arg(module->importDescription().versionMinor), 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->setLoadingState(IR::Module::Successful); } diff --git a/src/qmljsc/moduleloading/qtqmlmoduleloader.cpp b/src/qmljsc/moduleloading/qtqmlmoduleloader.cpp new file mode 100644 index 0000000..26a494c --- /dev/null +++ b/src/qmljsc/moduleloading/qtqmlmoduleloader.cpp @@ -0,0 +1,121 @@ +/* + * 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 "qtqmlmoduleloader.h" +#include "ir/typesystem.h" +#include "ir/module.h" +#include "ir/builtintypes.h" +#include "error.h" + +using namespace QmlJSc; + +QtQmlModuleLoader *QtQmlModuleLoader::create(IR::Module *module) +{ + return new QtQmlModuleLoader(module); +} + +QtQmlModuleLoader::QtQmlModuleLoader(IR::Module *module) + : AbstractModuleLoader(module) +{} + +bool QmlJSc::QtQmlModuleLoader::canLoad() +{ + return module()->importDescription().name == "QtQml"; +} + +void QtQmlModuleLoader::doLoad() +{ + IR::Module *module = AbstractModuleLoader::module(); + + IR::Property *p = 0; + IR::Method *m = 0; + IR::Signal *s = 0; + + // === Classes === + + // Component + IR::Type *componentClass = new IR::Type("Component", "QWComponent", IR::Type::Flags(IR::Type::IsInstantiable | IR::Type::IsComponent) ); + p = componentClass->addProperty("progress"); + p->type = IR::BuiltinTypes:: realType(); + p = componentClass->addProperty("status"); + p->type = IR::BuiltinTypes:: enumType(); + p = componentClass->addProperty("url"); + p->type = IR::BuiltinTypes:: urlType(); + m = componentClass->addMethod("createObject"); + m->parameters.append("parent"); + m->parameters.append("properties"); + m = componentClass->addMethod("errorString"); + m = componentClass->addMethod("incubateObject"); + m->parameters.append("parent"); + m->parameters.append("properties"); + m->parameters.append("mode"); + + IR::Type *componentAttached = new IR::Type; + componentAttached->addSignal("completed"); + componentAttached->addSignal("destruction"); + componentClass->setAttachedType(componentAttached); + module->addType(componentClass); + + // QtObject + IR::Type *qtobjectClass = new IR::Type("QtObject", "QWObject", IR::Type::IsInstantiable); + p = qtobjectClass->addProperty("objectName"); + p->type = IR::BuiltinTypes:: stringType(); + module->addType(qtobjectClass); + + // Binding + IR::Type *bindingClass = new IR::Type("Binding", "", IR::Type::IsInstantiable); + p = bindingClass->addProperty("property"); + p->type = IR::BuiltinTypes:: stringType(); + p = bindingClass->addProperty("target"); + p->type = qtobjectClass; + p = bindingClass->addProperty("value"); + p->type = IR::BuiltinTypes:: varType(); + p = bindingClass->addProperty("when"); + p->type = IR::BuiltinTypes:: boolType(); + module->addType(bindingClass); + + // Connections + IR::Type *connectionsClass = new IR::Type("Connections", "", IR::Type::IsInstantiable); + p = connectionsClass->addProperty("ignoreUnknownSignals"); + p->type = IR::BuiltinTypes:: boolType(); + p = connectionsClass->addProperty("target"); + p->type = qtobjectClass; + module->addType(connectionsClass); + + // Timer + IR::Type *timerClass = new IR::Type("Timer", "", IR::Type::IsInstantiable); + p = timerClass->addProperty("interval"); + p->type = IR::BuiltinTypes:: intType(); + p = timerClass->addProperty("repeat"); + p->type = IR::BuiltinTypes:: boolType(); + p = timerClass->addProperty("running"); + p->type = IR::BuiltinTypes:: boolType(); + p = timerClass->addProperty("triggeredOnStart"); + p->type = IR::BuiltinTypes:: boolType(); + timerClass->addSignal("triggered"); + timerClass->addMethod("restart"); + timerClass->addMethod("start"); + timerClass->addMethod("stop"); + module->addType(timerClass); + + module->setLoadingState(IR::Module::Successful); +} + diff --git a/src/qmljsc/moduleloading/qtqmlmoduleloader.h b/src/qmljsc/moduleloading/qtqmlmoduleloader.h new file mode 100644 index 0000000..256218c --- /dev/null +++ b/src/qmljsc/moduleloading/qtqmlmoduleloader.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 QTQMLMODULELOADER_H +#define QTQMLMODULELOADER_H + +#include "abstractmoduleloader.h" + +//Qt +#include + +namespace QmlJSc { + +namespace IR { + class Module; +} + +/** + * This class is used when loading the QtQml module. + */ +class QtQmlModuleLoader : public AbstractModuleLoader +{ +public: + static QtQmlModuleLoader *create(IR::Module *module); + + bool canLoad() override; + void doLoad() override; + +private: + QtQmlModuleLoader(IR::Module *module); +}; + +} // namespace QMLJSc + +#endif // QTQMLMODULELOADER_H diff --git a/tests/auto/data/imports/TestModule.0.1.js b/tests/auto/data/imports/TestModule.0.1.js index 38b9d8c..515e1fa 100644 --- a/tests/auto/data/imports/TestModule.0.1.js +++ b/tests/auto/data/imports/TestModule.0.1.js @@ -1,56 +1,57 @@ (function() { QW_INHERIT(Pastry, QWObject); if (window.__qmljsTestModuleDefined) throw "TestModule defined twice."; window.__qmljsTestModuleDefined = true; function Oven(parent) { QWObject.call(this, parent); this.temperature = new QWProperty({ type: QWInt, initialValue: 20 }); this.content = new QWProperty({ type: QWList, typeArg: Pastry, initialValue: new QWList() }); } Pastry.prototype.bake = function() { console.log("Baking for " + this.bakingTime.get() + " minutes."); } function Pastry(parent) { QWObject.call(this, parent); this.baked = new QWProperty({ type: Boolean, readonly: true, initialValue: false }); this.bakingTime = new QWProperty({ type: QWInt }); this.bakingFinished = QWSignal(); this.eat = function() { console.log("mnompf!"); } } var Cake = function(parent) { Pastry.call(this, parent); this.containsRawEgg = new QWProperty({ type: Boolean }); } QW_INHERIT(Cake, Pastry); QW_INHERIT(Pizza, Pastry); function Pizza(parent) { Pastry.call(this, parent); this.isCalzone = new QWProperty({ type: Boolean, initialValue: false }); this.topping = new QWProperty({ type: QWVar, initialValue: ["tomato sauce", "mozzarella"] }); } __engine.registerModule({ + Oven: Oven, Pastry: Pastry, Cake: Cake, Pizza: Pizza }); })(); diff --git a/tests/auto/qmljsc/testir.cpp b/tests/auto/qmljsc/testir.cpp index 4804fe5..fdf38c2 100644 --- a/tests/auto/qmljsc/testir.cpp +++ b/tests/auto/qmljsc/testir.cpp @@ -1,362 +1,404 @@ /* * 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/ir/objecttree.h" #include "../../../src/qmljsc/ir/visitor.h" #include "../../../src/qmljsc/ir/typesystem.h" +#include "../../../src/qmljsc/ir/builtintypes.h" // Qt private #include namespace QmlJSc { namespace IR { class TestIR : public QObject { Q_OBJECT public: TestIR(); private slots: void initTestCase(); void testBasics(); void testAdd(); void testVisitorAPI(); + void testBuiltinTypes(); private: Type city; Object christiania; Object copenhagen; Type state; Type democracy; Object ottomanEmpire; Object denmark; QString christianiaName; QString copenhagenName; QString ottomanEmpireName; QString ottomanEmpireLanguage; QString denmarkName; QString denmarkLanguage; QString denmarkRParty; QStringRef christianiaNameRef; QStringRef copenhagenNameRef; QStringRef ottomanEmpireNameRef; QStringRef ottomanEmpireLangRef; QStringRef denmarkNameRef; QStringRef denmarkLangRef; QStringRef denmarkRPartyRef; QQmlJS::AST::StringLiteral christianiaNameNode; QQmlJS::AST::StringLiteral copenhagenNameNode; QQmlJS::AST::StringLiteral ottomanEmpireNameNode; QQmlJS::AST::StringLiteral ottomanEmpireLangNode; QQmlJS::AST::StringLiteral denmarkNameNode; QQmlJS::AST::StringLiteral denmarkLangNode; QQmlJS::AST::StringLiteral denmarkRPartyNode; QQmlJS::AST::NumericLiteral christianiaPostCodeNode; }; TestIR::TestIR() : QObject() , christianiaName(QStringLiteral("Fristad Christiania")) , copenhagenName(QStringLiteral("København")) , ottomanEmpireName(QStringLiteral("Osmanlı İmparatorluğu")) , ottomanEmpireLanguage(QStringLiteral("Ottoman Turkish")) , denmarkName(QStringLiteral("Danmark")) , denmarkLanguage(QStringLiteral("Danish")) , denmarkRParty(QStringLiteral("S-RV")) , christianiaNameRef(&christianiaName) , copenhagenNameRef(&copenhagenName) , ottomanEmpireNameRef(&ottomanEmpireName) , ottomanEmpireLangRef(&ottomanEmpireLanguage) , denmarkNameRef(&denmarkName) , denmarkLangRef(&denmarkLanguage) , denmarkRPartyRef(&denmarkRParty) , christianiaNameNode(christianiaNameRef) , copenhagenNameNode(copenhagenNameRef) , ottomanEmpireNameNode(ottomanEmpireNameRef) , ottomanEmpireLangNode(ottomanEmpireLangRef) , denmarkNameNode(denmarkNameRef) , denmarkLangNode(denmarkLangRef) , denmarkRPartyNode(denmarkRPartyRef) , christianiaPostCodeNode(1050) { } void TestIR::initTestCase() { city.m_name = "City"; city.m_properties = { {"name", {0,"name"}}, {"postCode", {0,"postCode"}} }; city.m_methods = { {"visit", {0, "visit"}}, }; state.m_name = "State"; state.m_properties = { {"name", {0,"name"}}, {"language", {0,"language"}}, {"capital", {&city, "capital"}} }; state.m_methods = { {"visit", {0, "visit"}}, }; Signal isOffensive("warStarted"); isOffensive.parameters.append({0, "isOffensive"}); state.m_signals = { {"warStarted", isOffensive}, }; democracy.m_name = "Democracy"; democracy.m_super = &state; democracy.m_properties = { {"reigningParty", {0,"reigningParty"}}, }; democracy.m_methods = { {"elect", {0, "elect"}}, }; democracy.m_signals = { {"lawAdopted", {"lawAdopted"}}, }; copenhagen.m_super = &city; christiania.m_super = &city; ottomanEmpire.m_name = "OttomanEmpire"; ottomanEmpire.m_super = &state; ottomanEmpire.m_methods = { {"capital", {0, "capital"}}, // Capital for year, overrides property capital. }; ottomanEmpire.m_valueAssignments = { {&state.m_properties["name"], 0, &ottomanEmpireNameNode}, {&state.m_properties["language"], 0, &ottomanEmpireLangNode} }; denmark.m_name = "Denmark"; denmark.m_super = &democracy; } void TestIR::testBasics() { QCOMPARE(city.name(), QStringLiteral("City")); QVERIFY(city.property("name")); QCOMPARE(city.property("name")->name, QStringLiteral("name")); QVERIFY(city.property("postCode")); QVERIFY(city.property("postCode") == city.property("postCode")); // Check that no copying happens QVERIFY(!city.property("capital")); QVERIFY(!city.property("visit")); QVERIFY(city.method("visit")); QCOMPARE(city.method("visit")->name, QStringLiteral("visit")); QVERIFY(city.method("visit") == city.method("visit")); // Check that no copying happens QCOMPARE(state.name(), QStringLiteral("State")); QVERIFY(state.property("name")); QCOMPARE(state.property("name")->name, QStringLiteral("name")); QVERIFY(state.property("language")); QVERIFY(state.property("capital")); QCOMPARE(state.property("capital")->type, &city); QCOMPARE(state.property("capital")->type->name(), QStringLiteral("City")); QVERIFY(!state.property("reigningParty")); QVERIFY(!state.property("postCode")); QVERIFY(state.method("visit")); QVERIFY(state.method("visit") != city.method("visit")); QVERIFY(state.signal("warStarted")); QVERIFY(state.signal("warStarted") == state.signal("warStarted")); // Check that no copying happens QCOMPARE(state.signal("warStarted")->name, QStringLiteral("warStarted")); QCOMPARE(state.signal("warStarted")->parameters[0].name, QStringLiteral("isOffensive")); QCOMPARE(democracy.name(), QStringLiteral("Democracy")); QVERIFY(democracy.property("name")); QCOMPARE(democracy.property("name")->name, QStringLiteral("name")); QVERIFY(democracy.property("language")); QVERIFY(democracy.property("capital")); QCOMPARE(democracy.property("capital")->type, &city); QCOMPARE(democracy.property("capital")->type->name(), QStringLiteral("City")); QVERIFY(democracy.property("reigningParty")); QVERIFY(!democracy.property("postCode")); QCOMPARE(ottomanEmpire.name(), QStringLiteral("OttomanEmpire")); QVERIFY(ottomanEmpire.property("name")); QVERIFY(!ottomanEmpire.property("reigningParty")); QVERIFY(!ottomanEmpire.property("postCode")); QCOMPARE(ottomanEmpire.valueAssignments().count(), 2); QCOMPARE(ottomanEmpire.valueAssignments()[0].property, &state.m_properties["name"]); QCOMPARE(reinterpret_cast( ottomanEmpire.valueAssignments()[0].jsValue)->value.toString(), QStringLiteral("Osmanlı İmparatorluğu")); } void TestIR::testAdd() { ValueAssignment *denmarkNameAssignment = denmark.addValueAssignment(); denmarkNameAssignment->property = &state.m_properties["name"]; denmarkNameAssignment->jsValue = &denmarkNameNode; ValueAssignment *denmarkLanguageAssignment = denmark.addValueAssignment(); denmarkLanguageAssignment->property = &state.m_properties["language"]; denmarkLanguageAssignment->jsValue = &denmarkLangNode; ValueAssignment *denmarkCapitalAssignment = denmark.addValueAssignment(); denmarkCapitalAssignment->property = &state.m_properties["capital"]; denmarkCapitalAssignment->objectValue = &copenhagen; ValueAssignment *denmarkReigningPartyAssignment = denmark.addValueAssignment(); denmarkReigningPartyAssignment->property = &democracy.m_properties["reigningParty"]; denmarkReigningPartyAssignment->jsValue = &denmarkRPartyNode; Property *christianiaProperty = copenhagen.addProperty("christiania"); christianiaProperty->type = &city; ValueAssignment *christianiaAssignment = copenhagen.addValueAssignment(); christianiaAssignment->objectValue = &christiania; ValueAssignment *copenhagenNameAssignment = copenhagen.addValueAssignment(); copenhagenNameAssignment->property = &city.m_properties["name"]; copenhagenNameAssignment->jsValue = &copenhagenNameNode; Method *buyWeed = christiania.addMethod("buyWeed"); Signal *policeRaid = christiania.addSignal("policeRaid"); ValueAssignment *christianiaNameAssignment = christiania.addValueAssignment(); christianiaNameAssignment->property = &city.m_properties["name"]; christianiaNameAssignment->jsValue = &christianiaNameNode; ValueAssignment *christianiaPostCodeAssignment = christiania.addValueAssignment(); christianiaPostCodeAssignment->property = &city.m_properties["postCode"]; christianiaPostCodeAssignment->jsValue = &christianiaPostCodeNode; QVERIFY(denmark.property("capital")); QVERIFY(denmark.property("reigningParty")); QVERIFY(!denmark.property("postCode")); QCOMPARE(denmark.valueAssignments().count(), 4); QVERIFY(denmark.valueAssignments()[2].property); QVERIFY(denmark.valueAssignments()[2].property == denmark.valueAssignments()[2].property); QCOMPARE(denmark.valueAssignments()[3].property, &democracy.m_properties["reigningParty"]); QCOMPARE(reinterpret_cast( denmark.valueAssignments()[3].jsValue)->value.toString(), QStringLiteral("S-RV")); QVERIFY(copenhagen.property("name")); QVERIFY(copenhagen.property("christiania")); QCOMPARE(copenhagen.valueAssignments().count(), 2); QVERIFY(christiania.method("visit")); QVERIFY(christiania.method("buyWeed")); QCOMPARE(christiania.method("buyWeed")->name, QStringLiteral("buyWeed")); QVERIFY(christiania.method("buyWeed") == buyWeed); // Check that no copying happens QVERIFY(christiania.signal("policeRaid")); QCOMPARE(christiania.signal("policeRaid")->name, QStringLiteral("policeRaid")); QVERIFY(christiania.signal("policeRaid") == policeRaid); // Check that no copying happens } class TestVisitor : public Visitor { public: TestVisitor() : Visitor() , objectsVisited(0) , propertiesVisited(0) , methodsVisited(0) , signalsVisited(0) , currentDepth(0) , valueAssignmentsVisited(0) , bindingAssignmentsVisited(0) , lastValueAssigned(0) {} virtual void visit(Object *object) { currentDepth++; objectsVisited++; } virtual void visit(ValueAssignment *valueAssignment) { currentDepth++; valueAssignmentsVisited++; lastValueAssigned = valueAssignment->jsValue; } virtual void visit(BindingAssignment *bindingAssignment) { currentDepth++; bindingAssignmentsVisited++; } virtual void endVisit(Object *object) { currentDepth--; } virtual void endVisit(ValueAssignment *valueAssignment) { currentDepth--; } virtual void endVisit(BindingAssignment *bindingAssignment) { currentDepth--; } int objectsVisited; int propertiesVisited; int methodsVisited; int signalsVisited; int valueAssignmentsVisited; int bindingAssignmentsVisited; int currentDepth; QString lastPropertyVisited; QQmlJS::AST::ExpressionNode *lastValueAssigned; }; void TestIR::testVisitorAPI() { QSKIP("Needs propertydef to be added to succeed."); TestVisitor visitor; copenhagen.accept(&visitor); QCOMPARE(visitor.currentDepth, 0); QCOMPARE(visitor.objectsVisited, 2); QCOMPARE(visitor.propertiesVisited, 1); QCOMPARE(visitor.methodsVisited, 1); QCOMPARE(visitor.signalsVisited, 1); QCOMPARE(visitor.valueAssignmentsVisited, 3); QCOMPARE(visitor.bindingAssignmentsVisited, 0); QCOMPARE(visitor.lastPropertyVisited, QStringLiteral("christiania")); QCOMPARE(visitor.lastValueAssigned->kind, (int)QQmlJS::AST::Node::Kind_StringLiteral); QQmlJS::AST::StringLiteral* lastValueAssigned = QQmlJS::AST::cast(visitor.lastValueAssigned); if (lastValueAssigned) QCOMPARE(lastValueAssigned->value.toString(), QStringLiteral("København")); } +void TestIR::testBuiltinTypes() +{ + Type *boolType = BuiltinTypes::type("bool"); + QVERIFY(boolType); + QCOMPARE(BuiltinTypes::typeFromJSName("Boolean"), boolType); + + Type *doubleType = BuiltinTypes::type("double"); + QVERIFY(doubleType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWDouble"), doubleType); + + Type *enumType = BuiltinTypes::type("enum"); + QVERIFY(enumType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWEnum"), enumType); + + Type *intType = BuiltinTypes::type("int"); + QVERIFY(intType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWInt"), intType); + + Type *listType = BuiltinTypes::type("list"); + QVERIFY(listType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWList"), listType); + QCOMPARE(listType->flags(), Type::IsList); + + Type *realType = BuiltinTypes::type("real"); + QVERIFY(realType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWReal"), realType); + + Type *stringType = BuiltinTypes::type("string"); + QVERIFY(stringType); + QCOMPARE(BuiltinTypes::typeFromJSName("String"), stringType); + + Type *urlType = BuiltinTypes::type("url"); + QVERIFY(urlType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWUrl"), urlType); + + Type *varType = BuiltinTypes::type("var"); + QVERIFY(varType); + QCOMPARE(BuiltinTypes::typeFromJSName("QWVar"), varType); +} + } // namespace IR } // namespace QMLJSc QTEST_MAIN(QmlJSc::IR::TestIR) #include "testir.moc" diff --git a/tests/auto/qmljsc/testmodules.cpp b/tests/auto/qmljsc/testmodules.cpp index 312404e..9a2cf3a 100644 --- a/tests/auto/qmljsc/testmodules.cpp +++ b/tests/auto/qmljsc/testmodules.cpp @@ -1,259 +1,358 @@ /* * 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/moduleloading/qtqmlmoduleloader.h" #include "../../../src/qmljsc/ir/module.h" +#include "../../../src/qmljsc/ir/builtintypes.h" class TestModules : public QObject { Q_OBJECT private slots: + void initTestCase(); void loadMinimalModule(); + void loadQtQml(); void loadModule(); void testShortSymbolName(); }; using namespace QmlJSc; using namespace QmlJSc::IR; using namespace QQmlJS; -void TestModules::loadMinimalModule() +void TestModules::initTestCase() { - Compiler comp; + new Compiler; + compiler->addIncludePath(":/test/"); + ModuleLoading::registerModuleLoader(&JavaScriptModuleLoader::create); + ModuleLoading::registerModuleLoader(&QtQmlModuleLoader::create); +} +void TestModules::loadMinimalModule() +{ 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->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); 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); } -void TestModules::loadModule() +void TestModules::loadQtQml() { - QSKIP("This will be implemented later (Task T488)."); - Compiler c; + const ImportDescription qtqmlImportDescription = {ImportDescription::Kind_ModuleImport, "QtQml", 1, 0}; + IR::Module *qtqmlModule = ModuleLoading::loadModule(qtqmlImportDescription); + qtqmlModule->waitForLoaded(); + + IR::Property *p = 0; + IR::Method *m = 0; + IR::Signal *s = 0; + + Type *componentClass = qtqmlModule->type("Component"); + QVERIFY(componentClass); + QCOMPARE(qtqmlModule->typeFromJSName("QWComponent"), componentClass); + QVERIFY(componentClass->flags() & IR::Type::IsComponent); + QVERIFY(p = componentClass->property("progress")); + QCOMPARE(p->type, BuiltinTypes:: realType()); + QVERIFY(p = componentClass->property("status")); + QCOMPARE(p->type, BuiltinTypes:: enumType()); + QVERIFY(p = componentClass->property("url")); + QCOMPARE(p->type, BuiltinTypes:: urlType()); + QVERIFY(!componentClass->property("heyo")); + QVERIFY(!componentClass->method("heyo")); + QVERIFY(m = componentClass->method("createObject")); + QCOMPARE(m->parameters.count(), 2); + QCOMPARE(m->parameters.at(0), QStringLiteral("parent")); + QCOMPARE(m->parameters.at(1), QStringLiteral("properties")); + QVERIFY(m = componentClass->method("errorString")); + QCOMPARE(m->parameters.count(), 0); + QVERIFY(m = componentClass->method("incubateObject")); + QCOMPARE(m->parameters.count(), 3); + QCOMPARE(m->parameters.at(0), QStringLiteral("parent")); + QCOMPARE(m->parameters.at(1), QStringLiteral("properties")); + QCOMPARE(m->parameters.at(2), QStringLiteral("mode")); + Type *componentAttached = componentClass->attachedType(); + QVERIFY(componentAttached); + QVERIFY(s = componentAttached->signal("completed")); + QCOMPARE(s->parameters.count(), 0); + QVERIFY(!componentAttached->property("completed")); + QVERIFY(!componentAttached->method("completed")); + QVERIFY(s = componentAttached->signal("destruction")); + QCOMPARE(s->parameters.count(), 0); + + Type *qtobjectClass = qtqmlModule->type("QtObject"); + QVERIFY(qtobjectClass); + QCOMPARE(qtqmlModule->typeFromJSName("QWObject"), qtobjectClass); + QVERIFY(p = qtobjectClass->property("objectName")); + QCOMPARE(p->type, BuiltinTypes:: stringType()); + + Type *bindingClass = qtqmlModule->type("Binding"); + QVERIFY(bindingClass); + QVERIFY(p = bindingClass->property("property")); + QCOMPARE(p->type, BuiltinTypes:: stringType()); + QVERIFY(p = bindingClass->property("target")); + QCOMPARE(p->type, qtobjectClass); + QVERIFY(p = bindingClass->property("value")); + QCOMPARE(p->type, BuiltinTypes:: varType()); + QVERIFY(p = bindingClass->property("when")); + QCOMPARE(p->type, BuiltinTypes:: boolType()); + + + Type *connectionsClass = qtqmlModule->type("Connections"); + QVERIFY(connectionsClass); + QVERIFY(p = connectionsClass->property("ignoreUnknownSignals")); + QCOMPARE(p->type, BuiltinTypes:: boolType()); + QVERIFY(p = connectionsClass->property("target")); + QCOMPARE(p->type, qtobjectClass); + + + Type *timerClass = qtqmlModule->type("Timer"); + QVERIFY(timerClass); + QVERIFY(p = timerClass->property("interval")); + QCOMPARE(p->type, BuiltinTypes:: intType()); + QVERIFY(p = timerClass->property("repeat")); + QCOMPARE(p->type, BuiltinTypes:: boolType()); + QVERIFY(p = timerClass->property("running")); + QCOMPARE(p->type, BuiltinTypes:: boolType()); + QVERIFY(p = timerClass->property("triggeredOnStart")); + QCOMPARE(p->type, BuiltinTypes:: boolType()); + QVERIFY(timerClass->method("restart")); + QVERIFY(timerClass->method("start")); + QVERIFY(timerClass->method("stop")); + QVERIFY(s = timerClass->signal("triggered")); + QCOMPARE(s->parameters.count(), 0); +} +void TestModules::loadModule() +{ + const ImportDescription qtqmlImportDescription = {ImportDescription::Kind_ModuleImport, "QtQml", 1, 0}; const ImportDescription testImportDescription = {ImportDescription::Kind_ModuleImport, "TestModule", 0, 1}; - compiler->addIncludePath(":/test/"); IR::Module *module = ModuleLoading::loadModule(testImportDescription); + IR::Module *qtqmlModule = ModuleLoading::loadModule(qtqmlImportDescription); module->waitForLoaded(); + qtqmlModule->waitForLoaded(); Type *pastry = module->type("Pastry"); Type *cake = module->type("Cake"); Type *pizza = module->type("Pizza"); Type *printer = module->type("Printer"); 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")); + IR::Property *p = 0; + QVERIFY(p = pastry->property("bakingTime")); + QCOMPARE(p->type, BuiltinTypes::type("int")); + QVERIFY(p = cake->property("containsRawEgg")); + QCOMPARE(p->type, BuiltinTypes::type("bool")); + QVERIFY(p = cake->property("bakingTime")); + QCOMPARE(p->type, BuiltinTypes::type("int")); + QVERIFY(p = pizza->property("isCalzone")); + QCOMPARE(p->type, BuiltinTypes::type("bool")); + QVERIFY(p = pizza->property("topping")); + QCOMPARE(p->type, BuiltinTypes::type("var")); QVERIFY(!pizza->property("containsRawEgg")); IR::Property *baked = pizza->property("baked"); QVERIFY(baked); QCOMPARE(baked->readOnly, true); QVERIFY(pastry->method("eat")); QVERIFY(pastry->method("bake")); QVERIFY(pizza->method("eat")); QVERIFY(pizza->method("bake")); QVERIFY(pizza->signal("bakingFinished")); } 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(TestModules) #include "testmodules.moc"