Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "apiregister.h"
23 :
24 : #include <QQmlEngine>
25 : #include <QMetaObject>
26 : #include <QMetaMethod>
27 :
28 : #include "log.h"
29 :
30 : using namespace muse::api;
31 :
32 : struct SingletonApiCreator : public IApiRegister::ICreator
33 : {
34 : ApiObject* obj = nullptr;
35 0 : SingletonApiCreator(ApiObject* o)
36 0 : : obj(o) {}
37 :
38 0 : ApiObject* create(IApiEngine*) override { return obj; }
39 0 : bool isNeedDelete() const override { return false; }
40 : };
41 :
42 92 : ApiRegister::~ApiRegister()
43 : {
44 46 : for (auto& e : m_apiengines) {
45 0 : delete e.apiengine;
46 : }
47 46 : for (auto& p : m_creators) {
48 0 : delete p.second.c;
49 : }
50 92 : }
51 :
52 0 : void ApiRegister::regApiCreator(const std::string& module, const std::string& api, ICreator* c)
53 : {
54 0 : ApiCreator ac;
55 : {
56 0 : auto it = m_creators.find(api);
57 0 : if (it != m_creators.end()) {
58 0 : ac = it->second;
59 : }
60 : }
61 :
62 0 : IF_ASSERT_FAILED(!ac.c) {
63 0 : LOGE() << "already registered creator for api: " << api << ", before creator will deleted";
64 0 : delete ac.c;
65 : }
66 :
67 0 : ac.module = module;
68 0 : ac.c = c;
69 0 : m_creators[api] = ac;
70 :
71 : // register for Qml
72 0 : std::string name;
73 : {
74 0 : auto pos = api.find('.');
75 0 : if (pos != std::string::npos) {
76 0 : name = api.substr(pos + 1);
77 : }
78 : }
79 :
80 0 : IF_ASSERT_FAILED(!name.empty()) {
81 0 : return;
82 : }
83 :
84 0 : qmlRegisterSingletonType(api.c_str(), 1, 0, name.c_str(), [this, api](QQmlEngine*, QJSEngine* jsengine) -> QJSValue {
85 0 : auto obj = createApi(api, makeApiEngine(jsengine));
86 0 : bool isNeedDelete = obj.second;
87 0 : QJSEngine::setObjectOwnership(obj.first, isNeedDelete ? QJSEngine::JavaScriptOwnership : QJSEngine::CppOwnership);
88 0 : return jsengine->newQObject(obj.first);
89 : });
90 0 : }
91 :
92 0 : JsApiEngine* ApiRegister::makeApiEngine(QJSEngine* jsengine)
93 : {
94 0 : auto it = std::find_if(m_apiengines.begin(), m_apiengines.end(), [jsengine](const ApiEngine& e) {
95 0 : return e.jsengine == jsengine;
96 : });
97 :
98 0 : if (it != m_apiengines.end()) {
99 0 : return it->apiengine;
100 : }
101 :
102 0 : QmlIoCContext* qmlIoc = jsengine->property("ioc_context").value<QmlIoCContext*>();
103 0 : IF_ASSERT_FAILED(qmlIoc) {
104 0 : return nullptr;
105 : }
106 :
107 0 : ApiEngine e;
108 0 : e.jsengine = jsengine;
109 0 : e.apiengine = new JsApiEngine(jsengine, qmlIoc->ctx);
110 0 : m_apiengines.push_back(e);
111 :
112 0 : QObject::connect(jsengine, &QJSEngine::destroyed, [this, jsengine]() {
113 0 : auto it = std::find_if(m_apiengines.begin(), m_apiengines.end(), [jsengine](const ApiEngine& e) {
114 0 : return e.jsengine == jsengine;
115 : });
116 :
117 0 : if (it != m_apiengines.end()) {
118 0 : delete it->apiengine;
119 0 : m_apiengines.erase(it);
120 : }
121 0 : });
122 :
123 0 : return e.apiengine;
124 0 : }
125 :
126 0 : void ApiRegister::regApiSingltone(const std::string& module, const std::string& api, ApiObject* o)
127 : {
128 0 : QJSEngine::setObjectOwnership(o, QJSEngine::CppOwnership);
129 0 : regApiCreator(module, api, new SingletonApiCreator(o));
130 0 : }
131 :
132 0 : std::pair<ApiObject*, bool /*is need delete*/> ApiRegister::createApi(const std::string& api, IApiEngine* e) const
133 : {
134 0 : auto it = m_creators.find(api);
135 0 : if (it == m_creators.end()) {
136 0 : LOGE() << "not registered creator for api: " << api;
137 0 : return { nullptr, false };
138 : }
139 0 : return { it->second.c->create(e), it->second.c->isNeedDelete() };
140 : }
141 :
142 0 : void ApiRegister::regEnum(const char* uri, const char* name, const QMetaEnum& meta, EnumType type)
143 : {
144 0 : qmlRegisterSingletonType(uri, 1, 0, name, [meta, type](QQmlEngine*, QJSEngine* jsengine) -> QJSValue {
145 0 : QJSValue enumObj = jsengine->newObject();
146 :
147 0 : for (int i = 0; i < meta.keyCount(); ++i) {
148 0 : QString key = QString::fromLatin1(meta.key(i));
149 0 : if (type == EnumType::String) {
150 0 : enumObj.setProperty(key, key);
151 : } else {
152 0 : int val = meta.value(i);
153 0 : enumObj.setProperty(key, val);
154 : }
155 0 : }
156 :
157 0 : QJSValue freezeFn = jsengine->evaluate("Object.freeze");
158 0 : return freezeFn.call({ enumObj });
159 0 : });
160 0 : }
161 :
162 0 : void ApiRegister::regGlobalEnum(const std::string& module, const QMetaEnum& meta,
163 : EnumType type,
164 : const std::string& name)
165 : {
166 0 : std::string ename = name.empty() ? std::string(meta.enumName()) : name;
167 0 : m_globalEnums.push_back({ module, ename, meta, type });
168 0 : }
169 :
170 0 : const std::vector<ApiRegister::GlobalEnum>& ApiRegister::globalEnums() const
171 : {
172 0 : return m_globalEnums;
173 : }
174 :
175 : class DumpApiEngine : public IApiEngine
176 : {
177 : public:
178 :
179 : QQmlEngine engine;
180 :
181 0 : const muse::modularity::ContextPtr& iocContext() const override
182 : {
183 0 : static muse::modularity::ContextPtr ctx;
184 0 : return ctx;
185 : }
186 :
187 0 : int apiversion() const override
188 : {
189 0 : return 2;
190 : }
191 :
192 0 : QJSValue newQObject(QObject* o) override
193 : {
194 0 : return engine.newQObject(o);
195 : }
196 :
197 0 : QJSValue newObject() override
198 : {
199 0 : return engine.newObject();
200 : }
201 :
202 0 : QJSValue newArray(size_t length) override
203 : {
204 0 : return engine.newArray(static_cast<uint>(length));
205 : }
206 :
207 0 : QJSValue newArrayBuffer(const QByteArray& data) override
208 : {
209 0 : return engine.toScriptValue(data);
210 : }
211 :
212 0 : JsPromise newPromise() override
213 : {
214 0 : return muse::api::JsPromise::make(&engine);
215 : }
216 :
217 0 : QJSValue freeze(const QJSValue& val) override
218 : {
219 0 : static QJSValue freezeFn = engine.evaluate("Object.freeze");
220 0 : return freezeFn.call({ val });
221 0 : }
222 :
223 0 : QJSValue evaluate(const QString& code) override
224 : {
225 0 : return engine.evaluate(code);
226 : }
227 : };
228 :
229 0 : ApiRegister::Dump ApiRegister::dump() const
230 : {
231 0 : Dump dump;
232 0 : DumpApiEngine engine;
233 :
234 : static const QSet<QString> qtMethods = {
235 : "destroyed",
236 : "objectName",
237 : "objectNameChanged",
238 : "deleteLater",
239 : "_q_reregisterTimers"
240 0 : };
241 :
242 0 : auto makePropertySig = [](const QMetaProperty& p) {
243 0 : Dump::Sig sig;
244 0 : sig.name = p.name();
245 0 : sig.retType = p.typeName();
246 0 : return sig;
247 0 : };
248 :
249 0 : auto makeMethodSig = [](const QMetaMethod& m) {
250 0 : int pcount = m.parameterCount();
251 0 : QList<QByteArray> ptypes = m.parameterTypes();
252 0 : QList<QByteArray> pnames = m.parameterNames();
253 0 : QMetaType retType = m.returnMetaType();
254 :
255 0 : Dump::Sig sig;
256 0 : sig.name = m.name();
257 0 : sig.retType = retType.name();
258 0 : for (int i = 0; i < pcount; ++i) {
259 0 : Dump::Arg a;
260 0 : a.type = ptypes.at(i).constData();
261 0 : a.name = pnames.at(i).constData();
262 0 : sig.args.push_back(std::move(a));
263 0 : }
264 :
265 0 : return sig;
266 0 : };
267 :
268 0 : for (const auto& p : m_creators) {
269 0 : Dump::Api api;
270 0 : api.prefix = QString::fromStdString(p.first); // like MuseInternal.Dispatcher
271 :
272 0 : ApiObject* obj = p.second.c->create(&engine);
273 0 : const QMetaObject* meta = obj->metaObject();
274 :
275 0 : for (int i = 0; i < meta->propertyCount(); ++i) {
276 0 : const QMetaProperty op = meta->property(i);
277 0 : if (qtMethods.contains(op.name())) {
278 0 : continue;
279 : }
280 0 : Dump::Method dm;
281 0 : dm.type = Dump::MethodType::Property;
282 0 : dm.sig = makePropertySig(op);
283 0 : api.methods.push_back(dm);
284 0 : }
285 :
286 0 : for (int i = 0; i < meta->methodCount(); ++i) {
287 0 : const QMetaMethod om = meta->method(i);
288 0 : QByteArray mname = om.name();
289 0 : if (qtMethods.contains(mname)) {
290 0 : continue;
291 : }
292 0 : if (mname.endsWith("_doc")) {
293 0 : continue;
294 : }
295 :
296 0 : Dump::Method dm;
297 0 : dm.type = Dump::MethodType::Method;
298 0 : dm.sig = makeMethodSig(om);
299 :
300 0 : QByteArray mname_doc = mname + "_doc()";
301 0 : int doc_idx = meta->indexOfMethod(mname_doc.constData());
302 0 : if (doc_idx != -1) {
303 0 : QMetaMethod docme = meta->method(doc_idx);
304 0 : docme.invoke(obj, Q_RETURN_ARG(QString, dm.doc));
305 : }
306 :
307 0 : api.methods.push_back(dm);
308 0 : }
309 :
310 0 : if (p.second.c->isNeedDelete()) {
311 0 : delete obj;
312 : }
313 :
314 0 : dump.apis.push_back(std::move(api));
315 0 : }
316 :
317 0 : return dump;
318 0 : }
|