Changeset View
Changeset View
Standalone View
Standalone View
src/qmljsc/codegeneration/codegeneratorforqml.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. | ||||
3 | * | ||||
4 | * Copyright (C) 2015 Jan Marker <jan@jangmarker.de> | ||||
5 | * | ||||
6 | * This program is free software: you can redistribute it and/or modify | ||||
7 | * it under the terms of the GNU General Public License as published by | ||||
8 | * the Free Software Foundation, either version 3 of the License, or | ||||
9 | * (at your option) any later version. | ||||
10 | * | ||||
11 | * This program is distributed in the hope that it will be useful, | ||||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
14 | * GNU General Public License for more details. | ||||
15 | * | ||||
16 | * You should have received a copy of the GNU General Public License | ||||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
18 | * | ||||
19 | */ | ||||
20 | | ||||
21 | #include "codegeneratorforqml.h" | ||||
22 | #include "../utils/error.h" | ||||
23 | #include "../ir/file.h" | ||||
24 | #include "../ir/module.h" | ||||
25 | #include "purejavascriptgenerator.h" | ||||
26 | | ||||
27 | using namespace QmlJSc; | ||||
28 | | ||||
29 | static const char16_t fileTemplate[] = u"{$class:\"Component\",$children:[%1],$imports:[%2]}"; | ||||
30 | static const char16_t importTemplate[] = u"[\"qmlimport\",\"%1\",%2.%3,\"%4\",true]"; | ||||
31 | static const char16_t objectSpecTemplate[] = u"{__proto__:QMLMetaElement.prototype,$class:\"%1\"%2}"; //FIXME: Don't use non-standard __proto__ | ||||
32 | static const char16_t propertyTemplate[] = u"%1: new QMLPropertyDefinition(\"%2\")"; | ||||
33 | static const char16_t methodTemplate[] = u"%1: new QMLMethod(\"%2\")"; | ||||
34 | static const char16_t signalTemplate[] = u"%1: new QMLSignalDefinition([%2])"; | ||||
35 | static const char16_t signalParameterTemplate[] = u"{name:\"%1\",type:\"%2\"},"; | ||||
36 | static const char16_t signalHandlerTemplate[] = u"on%1: new QmlBinding(\"%2\")"; | ||||
37 | static const char16_t jsValueAssignmentTemplate[] = u"%1: %2"; | ||||
38 | static const char16_t objectAssignmentTemplate[] = u"%1: %2"; | ||||
39 | static const char16_t objectListAssignmentTemplate[] = u"%1: [%2]"; | ||||
40 | static const char16_t bindingTemplate[] = u"%1: new QmlBinding(\"%2\")"; | ||||
41 | | ||||
42 | CodeGeneratorForQml::CodeGeneratorForQml() | ||||
43 | : IR::Visitor() | ||||
44 | , m_jsGenerator(new PureJavaScriptGenerator) | ||||
45 | { | ||||
46 | } | ||||
47 | | ||||
48 | QmlJSc::CodeGeneratorForQml::~CodeGeneratorForQml() | ||||
49 | { | ||||
50 | } | ||||
51 | | ||||
52 | QString CodeGeneratorForQml::getGeneratedCode() | ||||
53 | { | ||||
54 | if (m_outputStack.size() == 0) { | ||||
55 | return QString(); | ||||
56 | } | ||||
57 | if (m_outputStack.size() != 1) { | ||||
58 | const QString errorDescription = QStringLiteral("Stack was not reduced correctly, top element is %1").arg(m_outputStack.top()); | ||||
59 | Error *stackError = new Error(Error::InternalError, errorDescription); | ||||
60 | throw stackError; | ||||
61 | } | ||||
62 | return m_outputStack.pop(); | ||||
63 | } | ||||
64 | | ||||
65 | void CodeGeneratorForQml::endVisit(IR::File *file) | ||||
66 | { | ||||
67 | QString rootObjCode = m_outputStack.pop(); | ||||
68 | QString importsCode; | ||||
69 | if (file->importedModules().size()) { | ||||
70 | for (const IR::Import &data: file->importedModules()) { | ||||
71 | importsCode += m_outputStack.pop() + ','; | ||||
72 | } | ||||
73 | importsCode.remove(-1, 1); // remove last comma | ||||
74 | } | ||||
75 | | ||||
76 | m_outputStack.push(QString::fromUtf16(fileTemplate).arg(rootObjCode, importsCode)); | ||||
77 | } | ||||
78 | | ||||
79 | void QmlJSc::CodeGeneratorForQml::endVisit(IR::Import* import) | ||||
80 | { | ||||
81 | m_outputStack.push(QString::fromUtf16(importTemplate).arg(import->module()->name()) | ||||
82 | .arg(import->module()->versionMajor()) | ||||
83 | .arg(import->module()->versionMinor()) | ||||
84 | .arg(import->localPrefix())); | ||||
85 | } | ||||
86 | | ||||
87 | void CodeGeneratorForQml::endVisit(IR::ObjectSpec *object) | ||||
88 | { | ||||
89 | QString code; | ||||
90 | int listSize = object->ownPropertiesSize() | ||||
91 | + object->ownMethodsSize() | ||||
92 | + object->ownSignalsSize() | ||||
93 | + object->valueAssignments().size() | ||||
94 | + object->signalHandlers().size(); | ||||
95 | QStack<QString>::iterator positionOnStack = m_outputStack.end(); | ||||
96 | positionOnStack -= listSize; | ||||
97 | for (; positionOnStack != m_outputStack.end(); positionOnStack++) { | ||||
98 | code += ',' + *positionOnStack; // We know QStack is actually just a QVector, | ||||
99 | // so we can just access random items in constant time | ||||
100 | } | ||||
101 | m_outputStack.erase(m_outputStack.end() - listSize, m_outputStack.end()); | ||||
102 | | ||||
103 | m_outputStack.push(QString::fromUtf16(objectSpecTemplate).arg(object->super()->name(), code)); | ||||
104 | } | ||||
105 | | ||||
106 | void CodeGeneratorForQml::endVisit(IR::Property* property) | ||||
107 | { | ||||
108 | m_outputStack.push(QString::fromUtf16(propertyTemplate) | ||||
109 | .arg(property->name, property->type->name())); | ||||
110 | } | ||||
111 | | ||||
112 | void CodeGeneratorForQml::endVisit(IR::Method* method) | ||||
113 | { | ||||
114 | if (method->body) { | ||||
115 | Q_ASSERT(method->body->elements); | ||||
116 | method->body->elements->accept(m_jsGenerator); | ||||
117 | } | ||||
118 | m_outputStack.push(QString::fromUtf16(methodTemplate) | ||||
119 | .arg(method->name, method->body ? m_jsGenerator->getGeneratedCode() : QString())); | ||||
120 | } | ||||
121 | | ||||
122 | void CodeGeneratorForQml::endVisit(IR::Signal* signal) | ||||
123 | { | ||||
124 | QString parameters; | ||||
125 | if (!signal->parameters.isEmpty()) { | ||||
126 | for (const IR::Parameter ¶meter : signal->parameters) { | ||||
127 | parameters += QString::fromUtf16(signalParameterTemplate) | ||||
128 | .arg(parameter.name, parameter.type->name()); | ||||
129 | } | ||||
130 | parameters.remove(-1, 1); // remove last comma | ||||
131 | } | ||||
132 | m_outputStack.push(QString::fromUtf16(signalTemplate) | ||||
133 | .arg(signal->name, parameters)); | ||||
134 | } | ||||
135 | | ||||
136 | void CodeGeneratorForQml::endVisit(IR::ValueAssignment* valueAssignment) | ||||
137 | { | ||||
138 | //FIXME: We should split ValueAssignment into subclasses. This is not how the | ||||
139 | // visitor pattern is supposed to look like. | ||||
140 | switch (valueAssignment->assignmentType()) { | ||||
141 | case IR::ValueAssignment::JsAssignment: | ||||
142 | valueAssignment->jsValue()->accept(m_jsGenerator); | ||||
143 | m_outputStack.push(QString::fromUtf16(jsValueAssignmentTemplate) | ||||
144 | .arg(valueAssignment->property()->name, m_jsGenerator->getGeneratedCode())); | ||||
145 | break; | ||||
146 | case IR::ValueAssignment::ObjectAssignment: | ||||
147 | m_outputStack.push(QString::fromUtf16(objectAssignmentTemplate) | ||||
148 | .arg(valueAssignment->property()->name, m_outputStack.pop())); | ||||
149 | break; | ||||
150 | case IR::ValueAssignment::ObjectList: | ||||
151 | { | ||||
152 | QString listCode; | ||||
153 | int listSize = valueAssignment->objectList()->size(); | ||||
154 | if (listSize) { | ||||
155 | QStack<QString>::iterator positionOnStack = m_outputStack.end(); | ||||
156 | positionOnStack -= listSize; | ||||
157 | for (; positionOnStack != m_outputStack.end(); positionOnStack++) { | ||||
158 | listCode += *positionOnStack + ','; // We know QStack is actually just a QVector, | ||||
159 | // so we can just access random items in constant time | ||||
160 | } | ||||
161 | listCode.remove(-1, 1); | ||||
162 | } | ||||
163 | m_outputStack.erase(m_outputStack.end() - listSize, m_outputStack.end()); | ||||
164 | m_outputStack.push(QString::fromUtf16(objectListAssignmentTemplate) | ||||
165 | .arg(valueAssignment->property()->name, listCode)); | ||||
166 | break; | ||||
167 | } | ||||
168 | case IR::ValueAssignment::Binding: | ||||
169 | valueAssignment->bindingCode()->accept(m_jsGenerator); | ||||
170 | m_outputStack.push(QString::fromUtf16(bindingTemplate) | ||||
171 | .arg(valueAssignment->property()->name, m_jsGenerator->getGeneratedCode())); | ||||
172 | break; | ||||
173 | default: | ||||
174 | throw new Error(Error::InternalError, | ||||
175 | QStringLiteral("We have an invalid value assignment here. This should not happen.")); | ||||
176 | } | ||||
177 | } | ||||
178 | | ||||
179 | void CodeGeneratorForQml::endVisit(IR::SignalHandler* signalHandler) | ||||
180 | { | ||||
181 | QString signalName = signalHandler->signal()->name; | ||||
182 | *signalName.begin() = signalName.begin()->toUpper(); | ||||
183 | signalHandler->handlerCode()->accept(m_jsGenerator); | ||||
184 | m_outputStack.push(QString::fromUtf16(signalHandlerTemplate).arg(signalName, m_jsGenerator->getGeneratedCode())); | ||||
185 | } |