Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
script_engine.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <string>
5#include <string_view>
6#include <functional>
7#include <optional>
8#include <memory>
9#include <choc/javascript/choc_javascript.h>
10#include <atomic>
11#include <cstdint>
12
13namespace pulp::view {
14
15// JS scripting engine for plugin UIs.
16// Wraps a JsEngine backend (QuickJS, JSC, or V8).
17// Not thread-safe — all calls must happen on the same thread.
18//
19// This class preserves the original ScriptEngine API for backward compatibility
20// with WidgetBridge and all existing callers. Internally it delegates to the
21// selected JsEngine backend.
23public:
24 // Create with the build default engine.
26
27 // Create with a specific engine type
29
31
33 ScriptEngine& operator=(ScriptEngine&&) noexcept;
34
35 ScriptEngine(const ScriptEngine&) = delete;
36 ScriptEngine& operator=(const ScriptEngine&) = delete;
37
38 // Evaluate JS code synchronously, returns the result as a choc::value::Value
39 // Throws on parse/runtime errors
40 choc::value::Value evaluate(const std::string& code);
41 // Evaluate a whole script (see JsEngine::evaluate_script): a large script
42 // seen before in this process reuses its compiled bytecode.
43 choc::value::Value evaluate_script(const std::string& code);
44
46 std::string evaluate_bounded_json(const std::string& code,
47 std::size_t max_bytes);
48
49 // Run JS code asynchronously (for module-style code with imports)
50 void run_module(const std::string& code,
51 ModuleResolver resolver,
52 ModuleCompletionHandler completion = {});
53
54 // Register a C++ function callable from JS.
55 // Accepts both the new NativeFunction signature and CHOC's ArgumentList signature.
56 void register_function(const std::string& name, NativeFunction fn);
57 void register_function(const std::string& name, choc::javascript::Context::NativeFunction fn);
58 void register_promise_function(const std::string& name, NativePromiseFunction fn);
59
60 // Internal WidgetBridge instrumentation seam. Registered functions retain
61 // this state because their engine may outlive the owning bridge.
62 void
63 set_bridge_call_counter(const std::shared_ptr<std::atomic<std::uint64_t>>& counter) noexcept {
64 bridge_call_counter_ = counter;
65 }
66 std::shared_ptr<std::atomic<std::uint64_t>> bridge_call_counter() const noexcept {
67 return bridge_call_counter_;
68 }
69
70 // Register a native-backed global object with snapshot properties and
71 // native methods.
72 void register_host_object(const std::string& name, HostObjectDescriptor descriptor);
73
74 // Invoke a global JS function by name with no arguments
75 choc::value::Value invoke(std::string_view name);
76
77 // Invoke a global JS function with arguments
78 template<typename... Args>
79 choc::value::Value invoke(std::string_view name, Args&&... args);
80
81 // Set a log callback for console.log/warn/error from JS
83
84 // Pump any engine-specific message loop / microtask queue.
86
87 // Cap the realm's heap in bytes (0 = unlimited); false when the active
88 // backend cannot enforce one. Every backend starts at
89 // kDefaultJsMemoryLimitBytes where it can enforce a cap at all.
90 bool set_memory_limit(std::size_t bytes);
91
92 // Whether the active backend can cooperatively abort a running evaluation
93 // from another thread (QuickJS can; see JsEngine::request_interrupt).
94 bool supports_interrupt() const;
95
96 // Ask the active backend to abort its in-flight evaluation at the next
97 // interrupt check. Thread-safe on supporting backends; no-op otherwise.
99
100 // [engine thread] Consume a late interrupt after evaluation quiesces.
101 // Returns true when the request was still pending rather than consumed by
102 // the evaluation.
104
105 // Check if the engine is valid
106 explicit operator bool() const;
107
108 // Query which engine backend is active
110
111 // Runtime / provider identity (forwarded to the active backend).
112 std::string runtime_version() const;
113 std::string provider_kind() const;
114 std::string provider_path() const;
115 std::string expected_runtime_version() const;
116
117 // Access the underlying engine (for advanced use / testing)
119 const JsEngine& engine() const;
120
121 // Weak observation token for deferred callbacks which borrow this object.
122 // It tracks this ScriptEngine object's storage and deliberately does not
123 // move with the backend.
124 std::weak_ptr<const void> liveness_token() const noexcept { return alive_; }
125
126private:
127 std::unique_ptr<JsEngine> engine_;
128 std::shared_ptr<const void> alive_ = std::make_shared<const char>('\0');
129 std::shared_ptr<std::atomic<std::uint64_t>> bridge_call_counter_;
130
131 // For QuickJS backward compatibility: WidgetBridge uses CHOC's Context directly
132 // for the stack size hack and pimpl access. We keep a reference to the CHOC
133 // context when the backend is QuickJS.
134 choc::javascript::Context* choc_context_ = nullptr;
135
136 void init_choc_compat();
137};
138
139// ── Template implementations ────────────────────────────────────────────────
140
141template<typename... Args>
142choc::value::Value ScriptEngine::invoke(std::string_view name, Args&&... args) {
143 // Convert variadic args to choc::value::Value array
144 choc::value::Value arg_values[] = { choc::value::Value(std::forward<Args>(args))... };
145 return engine_->invoke(name, arg_values, sizeof...(args));
146}
147
148} // namespace pulp::view
Definition js_engine.hpp:84
void register_promise_function(const std::string &name, NativePromiseFunction fn)
std::string expected_runtime_version() const
ScriptEngine(JsEngineType engine_type)
void set_log_callback(LogCallback callback)
ScriptEngine(ScriptEngine &&) noexcept
std::string runtime_version() const
std::string provider_path() const
void register_function(const std::string &name, choc::javascript::Context::NativeFunction fn)
choc::value::Value evaluate(const std::string &code)
std::shared_ptr< std::atomic< std::uint64_t > > bridge_call_counter() const noexcept
Definition script_engine.hpp:66
void register_function(const std::string &name, NativeFunction fn)
void register_host_object(const std::string &name, HostObjectDescriptor descriptor)
std::weak_ptr< const void > liveness_token() const noexcept
Definition script_engine.hpp:124
choc::value::Value evaluate_script(const std::string &code)
const JsEngine & engine() const
void set_bridge_call_counter(const std::shared_ptr< std::atomic< std::uint64_t > > &counter) noexcept
Definition script_engine.hpp:63
void run_module(const std::string &code, ModuleResolver resolver, ModuleCompletionHandler completion={})
std::string provider_kind() const
bool set_memory_limit(std::size_t bytes)
choc::value::Value invoke(std::string_view name)
std::string evaluate_bounded_json(const std::string &code, std::size_t max_bytes)
bool supports_bounded_json_evaluation() const
JsEngineType engine_type() const
bool supports_interrupt() const
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
JsEngineType
Definition js_engine.hpp:59
std::function< choc::value::Value(const choc::value::Value *args, size_t num_args)> NativeFunction
Definition js_engine.hpp:28
std::function< void(const std::string &error, const choc::value::Value &result)> ModuleCompletionHandler
Definition js_engine.hpp:22
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
NativeFunction NativePromiseFunction
Definition js_engine.hpp:29
std::function< std::optional< std::string >(std::string_view path)> ModuleResolver
Definition js_engine.hpp:21
std::function< void(std::string_view level, std::string_view message)> LogCallback
Definition js_engine.hpp:25
STL namespace.
Definition js_engine.hpp:45