diff --git a/src/qmljsc/CMakeLists.txt b/src/qmljsc/CMakeLists.txt --- a/src/qmljsc/CMakeLists.txt +++ b/src/qmljsc/CMakeLists.txt @@ -10,13 +10,15 @@ 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 ) diff --git a/src/qmljsc/ir/builtintypes.h b/src/qmljsc/ir/builtintypes.h new file mode 100644 --- /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/builtintypes.cpp b/src/qmljsc/ir/builtintypes.cpp new file mode 100644 --- /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/typesystem.h b/src/qmljsc/ir/typesystem.h --- a/src/qmljsc/ir/typesystem.h +++ b/src/qmljsc/ir/typesystem.h @@ -52,12 +52,13 @@ 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(); diff --git a/src/qmljsc/ir/typesystem.cpp b/src/qmljsc/ir/typesystem.cpp --- a/src/qmljsc/ir/typesystem.cpp +++ b/src/qmljsc/ir/typesystem.cpp @@ -24,13 +24,15 @@ 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) { } diff --git a/src/qmljsc/moduleloading/javascriptmoduleloader.cpp b/src/qmljsc/moduleloading/javascriptmoduleloader.cpp --- a/src/qmljsc/moduleloading/javascriptmoduleloader.cpp +++ b/src/qmljsc/moduleloading/javascriptmoduleloader.cpp @@ -23,6 +23,7 @@ #include "moduleloading.h" #include "ir/module.h" #include "ir/typesystem.h" +#include "ir/builtintypes.h" #include "compiler.h" #include "error.h" @@ -411,11 +412,14 @@ } -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). + QString name = nameRef.toString(); + t = m_module->typeFromJSName(name); + if (!t) { + t = IR::BuiltinTypes::typeFromJSName(name); + } return t; } diff --git a/src/qmljsc/moduleloading/qtqmlmoduleloader.h b/src/qmljsc/moduleloading/qtqmlmoduleloader.h new file mode 100644 --- /dev/null +++ b/src/qmljsc/moduleloading/qtqmlmoduleloader.h @@ -0,0 +1,54 @@ +/* + * 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 creates the QtQml module, including all the basic types that exist + * in QML and ECMAScript. Especially as there's no module for the ECMAScript + * API, we have an extra loader class for it. + */ +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/src/qmljsc/moduleloading/qtqmlmoduleloader.cpp b/src/qmljsc/moduleloading/qtqmlmoduleloader.cpp new file mode 100644 --- /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/tests/auto/data/imports/TestModule.0.1.js b/tests/auto/data/imports/TestModule.0.1.js --- a/tests/auto/data/imports/TestModule.0.1.js +++ b/tests/auto/data/imports/TestModule.0.1.js @@ -49,6 +49,7 @@ } __engine.registerModule({ + Oven: Oven, Pastry: Pastry, Cake: Cake, Pizza: Pizza diff --git a/tests/auto/qmljsc/testir.cpp b/tests/auto/qmljsc/testir.cpp --- a/tests/auto/qmljsc/testir.cpp +++ b/tests/auto/qmljsc/testir.cpp @@ -28,6 +28,7 @@ #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 @@ -47,6 +48,7 @@ void testBasics(); void testAdd(); void testVisitorAPI(); + void testBuiltinTypes(); private: Type city; @@ -353,6 +355,46 @@ 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 diff --git a/tests/auto/qmljsc/testmodules.cpp b/tests/auto/qmljsc/testmodules.cpp --- a/tests/auto/qmljsc/testmodules.cpp +++ b/tests/auto/qmljsc/testmodules.cpp @@ -31,14 +31,18 @@ #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(); @@ -48,14 +52,19 @@ 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(); @@ -123,16 +132,100 @@ 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"); @@ -149,11 +242,17 @@ 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");