LCOV - code coverage report
Current view: top level - muse/framework/global/api/internal - apiregister.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 2.4 % 168 4
Test Date: 2026-11-05 04:07:02

            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 : }
        

Generated by: LCOV version 2.0-1