• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 30213925900

26 Jul 2026 06:03PM UTC coverage: 15.298% (-0.01%) from 15.311%
30213925900

push

github

jcelerier
3rdparty: bump libossia for the network-context poll fix

Brings in ossia/libossia#913: SDL joystick init no longer requires haptic
support (which the Emscripten SDL2 port does not have), and
network_context::poll() restarts the io_context, without which the
WebAssembly polling path stops after its first tick.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019oPj1zRcxSQX7FNni7EHM6

30400 of 198713 relevant lines covered (15.3%)

997.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

27.85
/src/plugins/score-plugin-js/JS/JSProcessModel.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3

4
#include "JSProcessModel.hpp"
5

6
#include <State/Expression.hpp>
7

8
#include <Process/Dataflow/Port.hpp>
9
#include <Process/PresetHelpers.hpp>
10

11
#include <JS/ApplicationPlugin.hpp>
12
#include <JS/Commands/EditScript.hpp>
13
#include <JS/Executor/ExecutionHelpers.hpp>
14
#include <JS/JSProcessMetadata.hpp>
15
#include <JS/Qml/QmlObjects.hpp>
16
#include <Library/LibrarySettings.hpp>
17

18
#include <score/application/GUIApplicationContext.hpp>
19
#include <score/command/Dispatchers/MultiOngoingCommandDispatcher.hpp>
20
#include <score/command/Dispatchers/SingleOngoingCommandDispatcher.hpp>
21
#include <score/document/DocumentInterface.hpp>
22
#include <score/model/Identifier.hpp>
23
#include <score/serialization/VisitorCommon.hpp>
24
#include <score/tools/DeleteAll.hpp>
25
#include <score/tools/File.hpp>
26

27
#include <core/document/Document.hpp>
28

29
#include <QDebug>
30
#include <QDir>
31
#include <QFileInfo>
32
#include <QFileSystemWatcher>
33
#include <QQmlComponent>
34
#include <QQuickItem>
35
#include <QQuickWindow>
36
#include <QStandardPaths>
37

38
#include <wobjectimpl.h>
39

40
#include <vector>
41
W_OBJECT_IMPL(JS::ProcessModel)
42✔
42
namespace JS
43
{
44
static constexpr const char* default_js_program =
45
    R"_(import Score
46
import QtQuick
47
// This is a minimal example script that showcases the available API.
48
// View the complete documentation at
49
// https://ossia.io/score-docs/processes/javascript.html
50
Script {
51
  ValueInlet { id: in1; objectName: "Value In" }
52
  ValueOutlet { id: out1; objectName: "Value Out" }
53
  FloatSlider { id: sl; min: 10; max: 100; objectName: "Control" }
54

55
  // Called on every tick
56
  tick: function(token, state)
57
  {
58
    if (typeof in1.value !== 'undefined')
59
    {
60
      console.log(in1.value);
61
      out1.value = in1.value * mx + sl.value * my;
62
    }
63
  }
64
})_";
65
static constexpr const char* default_js_ui =
66
    R"_()_";
67

68
ProcessModel::ProcessModel(
2✔
69
    const TimeVal& duration, const QString& data, const Id<Process::ProcessModel>& id,
70
    QObject* parent)
71
    : Process::ProcessModel{
2✔
72
        duration, id, Metadata<ObjectKey_k, ProcessModel>::get(), parent}
2✔
73
{
2✔
74
  if(data.isEmpty())
2✔
75
  {
76
    (void)setProgram({default_js_program, default_js_ui});
2✔
77
  }
2✔
78
  else
79
  {
80
    if(!data.endsWith(".qml")) {
×
81
      (void)setProgram({data, {}});
×
82
    }
×
83
    else {
84
      auto path = data;
×
85
      QFile f{path};
×
86
      m_root = path;
×
87
      QString exec_data;
×
88
      if(f.open(QIODevice::ReadOnly))
×
89
        exec_data = f.readAll();
×
90

91
      path.resize(data.size() - 3);
×
92
      path.append("ui.qml");
×
93
      QString ui_data;
×
94
      if(QFile ui_f{path}; ui_f.open(QIODevice::ReadOnly)) {
×
95
        ui_data = ui_f.readAll();
×
96
      }
×
97
      (void)setProgram({exec_data, ui_data});
×
98
    }
×
99
  }
100

101
  metadata().setInstanceName(*this);
2✔
102
}
×
103

104
Process::ProcessFlags ProcessModel::flags() const noexcept
7✔
105
{
106
  auto flags = Metadata<Process::ProcessFlags_k, JS::ProcessModel>::get();
7✔
107
  if(m_ui_component)
7✔
108
    flags |= Process::ExternalUIAvailable; // FIXME set in every relevant process
×
109
  return flags;
7✔
110
}
111

112
ProcessModel::~ProcessModel()
8✔
113
{
4✔
114
  if(this->externalUI)
4✔
115
  {
116
    this->externalUI->close();
×
117
    this->externalUI = nullptr;
×
118
  }
×
119
}
8✔
120

121
QString ProcessModel::rootPath() const noexcept
4✔
122
{
123
  if(!m_root.isEmpty())
4✔
124
  {
125
    return m_root;
×
126
  }
127
  else
128
  {
129
    static const auto& lib = score::AppContext().settings<Library::Settings::Model>();
4✔
130

131
    return lib.getDefaultLibraryPath() + QDir::separator() + "Scripts"
8✔
132
           + QDir::separator() + "include" + QDir::separator() + "Script/Script.qml";
4✔
133
  }
134
}
4✔
135

136
bool ProcessModel::validate(const std::vector<QString>& script) const noexcept
×
137
{
138
  if(script.empty())
×
139
    return false;
×
140
  if(script[0].isEmpty())
×
141
    return false;
×
142

143
  const auto trimmed = script[0].trimmed();
×
144
  const QByteArray data = trimmed.toUtf8();
×
145

146
  auto path = score::locateFilePath(trimmed, score::IDocument::documentContext(*this));
×
147

148
  if(QFileInfo::exists(path))
×
149
  {
150
    return (bool)m_cache.getExecution(*this, path.toUtf8(), true);
×
151
  }
152
  else
153
  {
154
    if(!data.startsWith("import"))
×
155
      return false;
×
156
    return (bool)m_cache.getExecution(*this, data, false);
×
157
  }
158
}
×
159

160

161
QString ProcessModel::effect() const noexcept
2✔
162
{
163
  return m_qmlData;
2✔
164
}
165

166
QQuickItem* ProcessModel::createItemForUI(const score::DocumentContext& ctx) const noexcept
×
167
{
168
  if(!m_ui_component)
×
169
    return nullptr;
×
170
  auto& dummyEngine =  ctx.app
×
171
                          .guiApplicationPlugin<JS::ApplicationPlugin>()
×
172
                          .m_scriptProcessUIEngine;
×
173

174
  auto obj = m_ui_component->beginCreate(dummyEngine.rootContext());
×
175

176
  if(!obj)
×
177
    return nullptr;
×
178
  auto script = qobject_cast<ScriptUI*>(obj);
×
179
  auto self = const_cast<JS::ProcessModel*>(this);
×
180
  if(script) {
×
181
    script->setProcess(self);
×
182
  }
×
183
  m_ui_component->completeCreate();
×
184

185
  if(!script) {
×
186
    delete obj;
×
187
    return nullptr;
×
188
  }
189

190
  if(const auto& on_exec = script->executionEvent(); on_exec.isCallable())
×
191
  {
192
    connect(this, &JS::ProcessModel::executionToUi,
×
193
            script, [&dummyEngine, on_exec] (const QVariant& v) {
×
194
      on_exec.call({dummyEngine.toScriptValue(v)});
×
195
    });
×
196
  }
×
197

198
  connect(script, &ScriptUI::executionSend, this, [self](const QJSValue& v) {
×
199
    self->uiToExecution(v.toVariant());
×
200
  });
×
201

202
  struct StateUpdater
203
  {
204
    const score::DocumentContext& ctx;
205
    JS::ProcessModel& self;
206
    std::unique_ptr<MultiOngoingCommandDispatcher> disp;
207
    int count = 0;
208

209
    void beginUpdateState(const QString& name)
×
210
    {
211
      if(count > 0) {
×
212
        count++;
×
213
      }
×
214
      else {
215
        disp = std::make_unique<MultiOngoingCommandDispatcher>(ctx.commandStack);
×
216
        count = 1;
×
217
      }
218
    }
×
219

220
    void endUpdateState()
×
221
    {
222
      if(!disp)
×
223
        return;
×
224
      count--;
×
225
      if(count > 0)
×
226
        return;
×
227

228
      disp->commit<JS::UpdateStateMacro>();
×
229
      disp.reset();
×
230
      count = 0;
×
231
    }
×
232

233
    void updateState(const QString& k, const QJSValue& v)
×
234
    {
235
      beginUpdateState("Update");
×
236

237
      disp->submit<JS::UpdateStateElement>(self, k, ossia::qt::value_from_js(v));
×
238

239
      endUpdateState();
×
240
    }
×
241

242
    void cancelUpdateState()
×
243
    {
244
      if(!disp)
×
245
        return;
×
246

247
      disp->rollback();
×
248
      disp.reset();
×
249
      count = 0;
×
250
    }
×
251

252
    void clearState()
×
253
    {
254
      beginUpdateState("Clear");
×
255

256
      disp->submit<JS::ReplaceState>(self, JS::JSState{});
×
257

258
      endUpdateState();
×
259
    }
×
260

261
    void replaceState(const QJSValue& v)
×
262
    {
263
      beginUpdateState("Replace");
×
264

265
      JS::JSState cur;
×
266
      auto var = v.toVariant().toMap();
×
267
      for(auto it = var.constBegin(); it != var.constEnd(); ++it) {
×
268
        if(it.value().isValid()) {
×
269
          cur.insert_or_assign(it.key(), ossia::qt::qt_to_ossia{}(it.value()));
×
270
        }
×
271
      }
×
272
      disp->submit<JS::ReplaceState>(self, std::move(cur));
×
273

274
      endUpdateState();
×
275
    }
×
276
  };
277
  auto updater = std::make_shared<StateUpdater>(StateUpdater{ctx, *self});
×
278

279
  connect(script, &ScriptUI::beginUpdateState,
×
280
          this, [updater] (const QString& name) {
×
281
    updater->beginUpdateState(name);
×
282
  });
×
283
  connect(script, &ScriptUI::updateState,
×
284
          this, [updater] (const QString& name, const QJSValue& v) {
×
285
    updater->updateState(name, v);
×
286
  });
×
287
  connect(script, &ScriptUI::endUpdateState,
×
288
          this, [updater] () {
×
289
    updater->endUpdateState();
×
290
  });
×
291
  connect(script, &ScriptUI::cancelUpdateState,
×
292
          this, [updater] () {
×
293
    updater->cancelUpdateState();
×
294
  });
×
295
  connect(script, &ScriptUI::clearState,
×
296
          this, [updater] () {
×
297
    updater->clearState();
×
298
  });
×
299
  connect(script, &ScriptUI::replaceState,
×
300
          this, [updater] (const QJSValue& v) {
×
301
    updater->replaceState(v);
×
302
  });
×
303

304
  if(const auto& on_stateUpdated = script->stateUpdated(); on_stateUpdated.isCallable())
×
305
  {
306
    connect(
×
307
        this, &JS::ProcessModel::stateElementChanged, script,
×
308
        [on_stateUpdated, &dummyEngine](const QString& k, const ossia::value& v) {
×
309
      if(v.valid())
×
310
      {
311
        if(auto res = v.apply(ossia::qt::ossia_to_qvariant{}); res.isValid())
×
312
          on_stateUpdated.call({k, dummyEngine.toScriptValue(res)});
×
313
        else
314
          on_stateUpdated.call({k, QJSValue{}});
×
315
      }
×
316
      else
317
        on_stateUpdated.call({k, QJSValue{}});
×
318
    }, Qt::QueuedConnection);
×
319
  }
×
320

321
  if(const auto& on_load = script->loadState(); on_load.isCallable())
×
322
  {
323
    QVariantMap vm;
×
324
    for(auto& [k, v]: this->m_state) {
×
325
      if(auto res = v.apply(ossia::qt::ossia_to_qvariant{}); res.isValid())
×
326
        vm[k] = std::move(res);
×
327
    }
328
    on_load.call({dummyEngine.toScriptValue(vm)});
×
329
  }
×
330

331
  return script;
×
332
}
×
333

334
QWidget* ProcessModel::createWindowForUI(const score::DocumentContext& ctx,
×
335
                                         QWidget* parent) const noexcept
336
{
337
  m_ui_object = createItemForUI(ctx);
×
338
  if(!m_ui_object)
×
339
    return nullptr;
×
340

341
  auto win = new QQuickWindow{};
×
342
  // QWidget gets these from QWidgetPrivate::adjustFlags; a bare QQuickWindow
343
  // does not, and on platforms where Qt draws the chrome itself (wasm) that
344
  // leaves the window with no title bar, close or minimise button.
345
  win->setFlags(
×
346
      win->flags() | Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint
×
347
      | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint
×
348
      | Qt::WindowMaximizeButtonHint);
×
349
#if defined(__EMSCRIPTEN__)
350
  // Qt for wasm reports ShowIsFullScreen unconditionally, so QWindow::show()
351
  // turns into showFullScreen() for every top level: the requested size is
352
  // discarded and the full-screen state suppresses the frame. Only Qt::Dialog
353
  // and Qt::Popup opt out (QWasmIntegration::defaultWindowState).
354
  win->setFlags(win->flags() | Qt::Dialog);
355
#endif
356
  win->setWidth(640);
×
357
  win->setHeight(640);
×
358

359
  m_ui_object->setParentItem(win->contentItem());
×
360

361
  const auto cleanup_ui = [this] {
×
362
    delete m_ui_object;
×
363
    m_ui_object = nullptr;
×
364

365
    const_cast<QWidget*&>(externalUI) = nullptr;
×
366
    externalUIVisible(false);
×
367
  };
×
368

369
  auto widg = QWidget::createWindowContainer(win, parent);
×
370
  if(!widg) {
×
371
    cleanup_ui();
×
372
    return nullptr;
×
373
  }
374
  widg->setAttribute(Qt::WA_DeleteOnClose);
×
375

376
#if QT_VERSION >= QT_VERSION_CHECK(6,8,2)
377
  // Bug in older Qt 6 versions:
378
  // QtCore/qmetatype.h:842:23: error: invalid application of 'sizeof' to an incomplete type 'QQuickCloseEvent'
379
  // static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined");
380
  connect(win, &QQuickWindow::closing, this, cleanup_ui);
381
#endif
382
  connect(win, &QQuickWindow::destroyed, this, cleanup_ui);
×
383
  connect(this, &JS::ProcessModel::uiScriptOk, win, [this, win, &ctx]() mutable {
×
384
    delete m_ui_object;
×
385
    m_ui_object = nullptr;
×
386
    if(!m_ui_component)
×
387
    {
388
      win->close();
×
389
      win->deleteLater();
×
390
      const_cast<QWidget*&>(externalUI) = nullptr;
×
391
      externalUIVisible(false);
×
392
      return;
×
393
    }
394

395
    m_ui_object = createItemForUI(ctx);
×
396
    if(!m_ui_object)
×
397
    {
398
      win->close();
×
399
      win->deleteLater();
×
400
      const_cast<QWidget*&>(externalUI) = nullptr;
×
401
      externalUIVisible(false);
×
402
      return;
×
403
    }
404
    m_ui_object->setParentItem(win->contentItem());
×
405
    m_ui_object->setParent(win->contentItem());
×
406
  });
×
407
  return widg;
×
408
}
×
409

410
void ProcessModel::setExecutionScript(const QString& f)
4✔
411
{
412
  if(f == m_program.execution)
4✔
413
    return;
×
414
  m_program.execution = std::move(f);
4✔
415

416
  executionScriptChanged(m_program.execution);
4✔
417
}
4✔
418

419
void ProcessModel::setUiScript(const QString& f)
4✔
420
{
421
  if(f == m_program.ui)
4✔
422
    return;
4✔
423
  m_program.ui = std::move(f);
×
424

425
  uiScriptChanged(m_program.ui);
×
426
}
4✔
427

428
void ProcessModel::setState(const JSState &s)
2✔
429
{
430
  if(s == m_state)
2✔
431
    return;
2✔
432

433
  {
434
    const auto prev = std::move(m_state);
×
435
    for(auto& [prev_k, prev_v] : prev) {
×
436
      stateElementChanged(prev_k, prev_v);
×
437
    }
438
  }
×
439

440
  m_state = std::move(s);
×
441
  for(auto& [k, v] : m_state) {
×
442
    stateElementChanged(k, v);
×
443
  }
444

445
  stateChanged();
×
446
}
2✔
447

448
void ProcessModel::updateState(const QString &k, const ossia::value& res)
×
449
{
450
  if(auto it = m_state.find(k); it != m_state.end())
×
451
  {
452
    if(res.valid())
×
453
    {
454
      if(res != it->second)
×
455
      {
456
        // Updating a new element
457
        m_state[k] = res;
×
458
        stateElementChanged(k, res);
×
459
        stateChanged();
×
460
      }
×
461
    }
×
462
    else
463
    {
464
      // Removing an element
465
      m_state.erase(k);
×
466
      stateElementChanged(k, res);
×
467
      stateChanged();
×
468
    }
469
  }
×
470
  else
471
  {
472
    if(res.valid())
×
473
    {
474
      // Adding a new element
475
      m_state[k] = res;
×
476
      stateElementChanged(k, res);
×
477
      stateChanged();
×
478
    }
×
479
    else
480
    {
481
      // Already not there, nothing to do
482
    }
483
  }
484
}
×
485

486
[[nodiscard]] Process::ScriptChangeResult ProcessModel::setProgram(const JS::QmlSource& script)
4✔
487
{
488
  setExecutionScript(script.execution);
4✔
489
  setUiScript(script.ui);
4✔
490

491
  Process::ScriptChangeResult res;
4✔
492
  const auto trimmed = script.execution.trimmed();
4✔
493
  const QByteArray data = trimmed.toUtf8();
4✔
494

495
  auto path = score::locateFilePath(trimmed, score::IDocument::documentContext(*this));
4✔
496

497
  if(QFileInfo::exists(path))
4✔
498
  {
499
    if(res = setQmlData(path.toUtf8(), true); !res.valid)
×
500
      return res;
×
501
  }
×
502
  else
503
  {
504
    if(res = setQmlData(data, false); !res.valid)
4✔
505
      return res;
×
506
  }
507

508
  m_program = script;
4✔
509
  return res;
4✔
510
}
4✔
511

512
Process::ScriptChangeResult ProcessModel::setQmlData(const QByteArray& data, bool isFile)
4✔
513
{
514
  Process::ScriptChangeResult res;
4✔
515
  if(!isFile && !data.contains("import "))
4✔
516
    return res;
×
517

518
  // When loading inline scripts, pre-create all cache files before loading any QML.
519
  // Qt's QQmlTypeLoader caches directory listings on first access; if the UI cache file
520
  // doesn't exist yet when the execution script triggers that scan, it gets flagged
521
  // as "File name case mismatch" when loaded later.
522
  if(!isFile && !this->m_program.ui.isEmpty())
4✔
523
    ensureJSCacheFile(this->m_program.ui.toUtf8(), true);
×
524

525
  auto script = m_cache.getExecution(*this, data, isFile);
4✔
526
  if(!script)
4✔
527
    return res;
×
528

529
  m_isFile = isFile;
4✔
530
  m_qmlData = data;
4✔
531

532
  res.inlets = score::clearAndDeleteLater(m_inlets);
4✔
533
  res.outlets = score::clearAndDeleteLater(m_outlets);
4✔
534
  const bool had_ui = m_ui_component;
4✔
535
  m_ui_component = nullptr;
4✔
536
  delete m_ui_object;
4✔
537
  m_ui_object = nullptr;
4✔
538

539
  SCORE_ASSERT(m_inlets.size() == 0);
4✔
540
  SCORE_ASSERT(m_outlets.size() == 0);
4✔
541

542
  // Check inlets / outlets
543
  {
544
    auto cld_inlet = script->findChildren<Inlet*>();
4✔
545
    int i = 0;
4✔
546
    for(auto n : cld_inlet)
12✔
547
    {
548
      auto port = n->make(Id<Process::Port>(i++), this);
8✔
549
      if(const auto& name = n->objectName(); !name.isEmpty())
16✔
550
        port->setName(name);
8✔
551
      if(auto addr = State::parseAddressAccessor(n->address()))
8✔
552
        port->setAddress(std::move(*addr));
×
553
      m_inlets.push_back(port);
8✔
554
    }
555
  }
4✔
556

557
  {
558
    auto cld_outlet = script->findChildren<Outlet*>();
4✔
559
    int i = 0;
4✔
560
    for(auto n : cld_outlet)
8✔
561
    {
562
      auto port = n->make(Id<Process::Port>(i++), this);
4✔
563
      if(const auto& name = n->objectName(); !name.isEmpty())
8✔
564
        port->setName(name);
4✔
565
      if(auto addr = State::parseAddressAccessor(n->address()))
4✔
566
        port->setAddress(std::move(*addr));
×
567
      m_outlets.push_back(port);
4✔
568
    }
569
  }
4✔
570

571
  // Create ui if any
572
  if(!this->m_program.ui.isEmpty()) {
4✔
573
    m_ui_component = m_cache.getUi(*this, this->m_program.ui.toUtf8(), isFile);
×
574
  }
×
575

576
  if(m_isFile)
4✔
577
  {
578
    const auto name = QFileInfo{data}.baseName();
×
579
    metadata().setName(name);
×
580
    metadata().setLabel(name);
×
581
  }
×
582
  else if(metadata().getName().isEmpty())
4✔
583
  {
584
    metadata().setName(QStringLiteral("Script"));
2✔
585
  }
2✔
586

587
  executionScriptOk();
4✔
588
  res.valid = true;
4✔
589

590
  if(bool(m_ui_component) != had_ui)
4✔
591
    flagsChanged();
×
592

593
  if(m_ui_component)
4✔
594
  {
595
    uiScriptOk();
×
596
  }
×
597
  else if(externalUI)
4✔
598
  {
599
    externalUI->close();
×
600
    externalUI->deleteLater();
×
601
    externalUI = nullptr;
×
602
    externalUIVisible(false);
×
603
  }
×
604

605
  // inlets / outletsChanged : in ScriptEditCommand
606
  return res;
4✔
607
}
4✔
608

609
Script* ProcessModel::currentExecutionObject() const noexcept
×
610
{
611
  if(auto cache = m_cache.tryGet(m_qmlData, m_isFile))
×
612
    return cache->object.get();
×
613
  return nullptr;
×
614
}
×
615

616
bool ProcessModel::isGpu() const noexcept
×
617
{
618
#if defined(SCORE_HAS_GPU_JS)
619
  if(auto script = currentExecutionObject())
620
  {
621
    return
622
        script->findChild<JS::TextureInlet*>() != nullptr
623
           || script->findChild<JS::TextureOutlet*>() != nullptr
624
           // || script->findChild<JS::BufferInlet*>() != nullptr
625
           // || script->findChild<JS::BufferOutlet*>() != nullptr
626
        ;
627
  }
628
#endif
629
  return false;
×
630
}
631

632
ComponentCache::ComponentCache() { }
4✔
633
ComponentCache::~ComponentCache() { }
4✔
634

635
const ComponentCache::Cache* ComponentCache::tryGet(const QByteArray& str, bool isFile) const noexcept
4✔
636
{
637
  QByteArray content;
4✔
638
  QFile f;
4✔
639
  if(isFile)
4✔
640
  {
641
    f.setFileName(str);
×
642
    if(f.open(QIODevice::ReadOnly))
×
643
      content = score::mapAsByteArray(f);
×
644
    else
645
      return nullptr;
×
646
  }
×
647
  else
648
  {
649
    content = str;
4✔
650
  }
651

652
  if(auto it = ossia::find_if(m_map, [&](const auto& k) { return k.key == content; });
4✔
653
     it != m_map.end())
4✔
654
  {
655
    return &*it;
×
656
  }
657
  return nullptr;
4✔
658
}
4✔
659

660
Script* ComponentCache::getExecution(
4✔
661
    const ProcessModel& process, const QByteArray& str, bool isFile) noexcept
662
{
663
  if(auto cache = tryGet(str, isFile))
4✔
664
    return cache->object.get();
×
665

666
  auto& dummyEngine = score::GUIAppContext()
8✔
667
                          .guiApplicationPlugin<JS::ApplicationPlugin>()
4✔
668
                          .m_scriptProcessUIEngine;
4✔
669
  std::unique_ptr<QQmlComponent> comp;
4✔
670
  if(!isFile)
4✔
671
  {
672
    comp = std::make_unique<QQmlComponent>(&dummyEngine);
4✔
673
    loadJSObjectFromString(process.rootPath(), str, *comp, false);
4✔
674
  }
4✔
675
  else
676
  {
677
    comp = std::make_unique<QQmlComponent>(&dummyEngine, QUrl::fromLocalFile(str));
×
678
  }
679

680
  const auto& errs = comp->errors();
4✔
681
  if(!errs.empty())
4✔
682
  {
683
    const auto& err = errs.first();
×
684
    qDebug() << err.line() << err.toString();
×
685
    auto str = err.toString();
×
686
    str.remove("<Unknown File>:");
×
687
    process.errorMessage(/* err.line(), */str);
×
688
    return nullptr;
×
689
  }
×
690

691
  auto obj = comp->create();
4✔
692
  auto script = qobject_cast<JS::Script*>(obj);
4✔
693
  if(script)
4✔
694
  {
695
    if(m_map.size() > 5)
4✔
696
      m_map.erase(m_map.begin());
×
697

698
    m_map.emplace_back(
8✔
699
        Cache{str, std::move(comp), std::unique_ptr<JS::Script>(script)});
4✔
700
    return script;
4✔
701
  }
702
  else
703
  {
704
    process.errorMessage(/* 0, */"The component must be of type Script");
×
705
    if(obj)
×
706
    {
707
      delete obj;
×
708
    }
×
709
    return nullptr;
×
710
  }
711
}
4✔
712

713
QQmlComponent* ComponentCache::getUi(
×
714
    const ProcessModel& process, const QByteArray& str, bool isFile) noexcept
715
{
716
  if(auto cache = tryGet(str, isFile))
×
717
    return cache->component.get();
×
718

719
  auto& dummyEngine = score::GUIAppContext()
×
720
                          .guiApplicationPlugin<JS::ApplicationPlugin>()
×
721
                          .m_scriptProcessUIEngine;
×
722

723
  std::unique_ptr<QQmlComponent> comp;
×
724
  if(!isFile)
×
725
  {
726
    comp = std::make_unique<QQmlComponent>(&dummyEngine);
×
727
    loadJSObjectFromString(process.rootPath(), str, *comp, true);
×
728
  }
×
729
  else
730
  {
731
    comp = std::make_unique<QQmlComponent>(&dummyEngine, QUrl::fromLocalFile(str));
×
732
  }
733

734
  const auto& errs = comp->errors();
×
735
  if(!errs.empty())
×
736
  {
737
    const auto& err = errs.first();
×
738
    qDebug() << err.line() << err.toString();
×
739
    auto str = err.toString();
×
740
    str.remove("<Unknown File>:");
×
741
    process.errorMessage(/* err.line(), */str);
×
742
    return nullptr;
×
743
  }
×
744

745
  auto obj = comp->beginCreate(dummyEngine.rootContext());
×
746
  if(!obj) {
×
747
    process.errorMessage(/* 0, */"Cannot create UI object");
×
748
    return nullptr;
×
749
  }
750
  auto script = qobject_cast<ScriptUI*>(obj);
×
751
  if(script) {
×
752
    script->setProcess((Process::ProcessModel*)&process);
×
753
  }
×
754
  comp->completeCreate();
×
755
  if(script)
×
756
  {
757
    if(m_map.size() > 5)
×
758
      m_map.erase(m_map.begin());
×
759

760
    m_map.emplace_back(
×
761
        Cache{str, std::move(comp), {}});
×
762
    delete script;
×
763
    return m_map.back().component.get();
×
764
  }
765
  else
766
  {
767
    process.errorMessage(/* 0, */"The component must be of type Script");
×
768
    if(obj)
×
769
      delete obj;
×
770
    return nullptr;
×
771
  }
772
}
×
773

774
void ProcessModel::loadPreset(const Process::Preset& preset)
×
775
{
776
  Process::loadScriptProcessPreset<ProcessModel::p_program>(*this, preset);
×
777
}
×
778

779
Process::Preset ProcessModel::savePreset() const noexcept
×
780
{
781
  // FIXME this should save p_program
782
  return Process::saveScriptProcessPreset(*this, this->m_qmlData);
×
783
}
784

785
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc