add LuaJIT/InlineNode
This commit is contained in:
parent
7cfbfc6f41
commit
3a0c6f7316
@ -113,6 +113,7 @@ target_sources(nf7
|
||||
file/audio_context.cc
|
||||
file/audio_device.cc
|
||||
file/luajit_context.cc
|
||||
file/luajit_inline_node.cc
|
||||
file/luajit_node.cc
|
||||
file/luajit_obj.cc
|
||||
file/node_imm.cc
|
||||
|
@ -357,6 +357,43 @@ void PushMutableVector(lua_State* L, std::vector<uint8_t>&& v) noexcept {
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void PushNodeLambda(lua_State* L,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& callee,
|
||||
const std::weak_ptr<nf7::Node::Lambda>& caller) noexcept {
|
||||
constexpr auto kTypeName = "nf7::Node::Lambda";
|
||||
struct Data {
|
||||
std::shared_ptr<nf7::Node::Lambda> callee;
|
||||
std::weak_ptr<nf7::Node::Lambda> caller;
|
||||
};
|
||||
new (lua_newuserdata(L, sizeof(Data))) Data { .callee = callee, .caller = caller };
|
||||
|
||||
if (luaL_newmetatable(L, kTypeName)) {
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& d = *reinterpret_cast<Data*>(luaL_checkudata(L, 1, kTypeName));
|
||||
|
||||
auto caller = d.caller.lock();
|
||||
if (!caller) return luaL_error(L, "caller expired");
|
||||
|
||||
std::string n = luaL_checkstring(L, 2);
|
||||
auto v = nf7::luajit::CheckValue(L, 3);
|
||||
|
||||
auto callee = d.callee;
|
||||
callee->env().ExecSub(callee, [callee, caller, n = std::move(n), v = std::move(v)]() {
|
||||
callee->Handle(n, v, caller);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__call");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
reinterpret_cast<Data*>(luaL_checkudata(L, 1, kTypeName))->~Data();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
|
||||
std::optional<nf7::Value> ToValue(lua_State* L, int idx) noexcept {
|
||||
if (lua_isnoneornil(L, idx)) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "common/node.hh"
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
@ -19,6 +20,9 @@ void PushImmEnv(lua_State*) noexcept;
|
||||
void PushValue(lua_State*, const nf7::Value&) noexcept;
|
||||
void PushVector(lua_State*, const nf7::Value::ConstVector&) noexcept;
|
||||
void PushMutableVector(lua_State*, std::vector<uint8_t>&&) noexcept;
|
||||
void PushNodeLambda(lua_State*,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& callee,
|
||||
const std::weak_ptr<nf7::Node::Lambda>& caller) noexcept;
|
||||
|
||||
std::optional<nf7::Value> ToValue(lua_State*, int) noexcept;
|
||||
std::optional<nf7::Value::ConstVector> ToVector(lua_State*, int) noexcept;
|
||||
|
270
file/luajit_inline_node.cc
Normal file
270
file/luajit_inline_node.cc
Normal file
@ -0,0 +1,270 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include <ImNodes.h>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/gui_file.hh"
|
||||
#include "common/gui_node.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/logger_ref.hh"
|
||||
#include "common/luajit_queue.hh"
|
||||
#include "common/luajit_ref.hh"
|
||||
#include "common/luajit_thread.hh"
|
||||
#include "common/memento.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class InlineNode final : public nf7::File, public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<InlineNode> kType =
|
||||
{"LuaJIT/InlineNode", {"nf7::Node"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("Defines new Node using Lua object factory.");
|
||||
ImGui::Bullet();
|
||||
ImGui::TextUnformatted("refers nf7::luajit::Queue through linked LuaJIT/Obj");
|
||||
}
|
||||
|
||||
class Lambda;
|
||||
|
||||
struct Data {
|
||||
std::string script;
|
||||
};
|
||||
|
||||
InlineNode(nf7::Env& env, Data&& data = {}) noexcept :
|
||||
nf7::File(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kWidget),
|
||||
life_(*this),
|
||||
log_(std::make_shared<nf7::LoggerRef>()),
|
||||
mem_(std::move(data)) {
|
||||
mem_.onRestore = [this]() { Touch(); };
|
||||
mem_.onCommit = [this]() { Touch(); };
|
||||
}
|
||||
|
||||
InlineNode(nf7::Env& env, nf7::Deserializer& ar) : InlineNode(env) {
|
||||
ar(data().script);
|
||||
}
|
||||
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||
ar(data().script);
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
return std::make_unique<InlineNode>(env, Data {data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
|
||||
|
||||
std::span<const std::string> GetInputs() const noexcept override {
|
||||
static const std::vector<std::string> kInputs = {"in"};
|
||||
return kInputs;
|
||||
}
|
||||
std::span<const std::string> GetOutputs() const noexcept override {
|
||||
static const std::vector<std::string> kOutputs = {"out"};
|
||||
return kOutputs;
|
||||
}
|
||||
|
||||
void Handle(const Event&) noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateNode(nf7::Node::Editor&) noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<InlineNode> life_;
|
||||
|
||||
std::shared_ptr<std::mutex> mtx_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
const Data& data() const noexcept { return mem_.data(); }
|
||||
Data& data() noexcept { return mem_.data(); }
|
||||
};
|
||||
|
||||
class InlineNode::Lambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<InlineNode::Lambda> {
|
||||
public:
|
||||
Lambda(InlineNode& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent), file_(f.life_), log_(f.log_) {
|
||||
}
|
||||
|
||||
void Handle(std::string_view k, const nf7::Value& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override
|
||||
try {
|
||||
file_.EnforceAlive();
|
||||
|
||||
auto ljq = file_->
|
||||
ResolveUpwardOrThrow("_luajit").
|
||||
interfaceOrThrow<nf7::luajit::Queue>().self();
|
||||
|
||||
std::optional<std::string> scr;
|
||||
|
||||
auto& mem = file_->mem_;
|
||||
if (last_ != std::exchange(last_, mem.Save()->id())) {
|
||||
scr = mem.last().script;
|
||||
}
|
||||
|
||||
auto self = shared_from_this();
|
||||
auto th = std::make_shared<nf7::luajit::Thread>(
|
||||
self, ljq,
|
||||
[self, ljq](auto& th, auto L) { self->HandleThread(ljq, th, L); });
|
||||
th->Install(log_);
|
||||
th_.emplace_back(th);
|
||||
|
||||
auto p = std::make_pair(std::string {k}, std::move(v));
|
||||
ljq->Push(self, [self, this, ljq, caller, th, scr = std::move(scr), p = std::move(p)](auto L) {
|
||||
auto thL = th->Init(L);
|
||||
|
||||
// push function
|
||||
if (scr) {
|
||||
luaL_loadstring(thL, scr->c_str());
|
||||
lua_pushvalue(thL, -1);
|
||||
func_.emplace(self, ljq, luaL_ref(thL, LUA_REGISTRYINDEX));
|
||||
} else {
|
||||
lua_rawgeti(thL, LUA_REGISTRYINDEX, func_->index());
|
||||
}
|
||||
|
||||
// push args
|
||||
lua_pushstring(thL, p.first.c_str()); // key
|
||||
nf7::luajit::PushValue(thL, p.second); // value
|
||||
nf7::luajit::PushNodeLambda(thL, caller, self); // caller
|
||||
|
||||
// push ctx table
|
||||
if (ctxtable_ && ctxtable_->ljq() != ljq) {
|
||||
ctxtable_ = std::nullopt;
|
||||
log_->Warn("LuaJIT queue changed, ctxtable is cleared");
|
||||
}
|
||||
if (ctxtable_) {
|
||||
lua_rawgeti(thL, LUA_REGISTRYINDEX, ctxtable_->index());
|
||||
} else {
|
||||
lua_createtable(thL, 0, 0);
|
||||
lua_pushvalue(thL, -1);
|
||||
ctxtable_.emplace(self, ljq, luaL_ref(thL, LUA_REGISTRYINDEX));
|
||||
}
|
||||
|
||||
// start function
|
||||
th->Resume(thL, 4);
|
||||
});
|
||||
|
||||
} catch (nf7::LifeExpiredException&) {
|
||||
} catch (nf7::Exception& e) {
|
||||
log_->Error(e.msg());
|
||||
}
|
||||
void Abort() noexcept override {
|
||||
for (auto& wth : th_) {
|
||||
if (auto th = wth.lock()) {
|
||||
th->Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// synchronized with filesystem
|
||||
nf7::Life<InlineNode>::Ref file_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::optional<nf7::Memento::Tag::Id> last_;
|
||||
|
||||
std::vector<std::weak_ptr<nf7::luajit::Thread>> th_;
|
||||
|
||||
// used on luajit thread
|
||||
std::optional<nf7::luajit::Ref> func_;
|
||||
std::optional<nf7::luajit::Ref> ctxtable_;
|
||||
|
||||
|
||||
void HandleThread(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
nf7::luajit::Thread& th, lua_State* L) noexcept {
|
||||
switch (th.state()) {
|
||||
case nf7::luajit::Thread::kFinished:
|
||||
return;
|
||||
|
||||
case nf7::luajit::Thread::kPaused:
|
||||
log_->Warn("unexpected yield");
|
||||
ljq->Push(shared_from_this(),
|
||||
[th = th.shared_from_this(), L](auto) { th->Resume(L, 0); });
|
||||
return;
|
||||
|
||||
default:
|
||||
log_->Warn("luajit execution error: "s+lua_tostring(L, -1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> InlineNode::CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
|
||||
return std::make_shared<Lambda>(*this, parent);
|
||||
}
|
||||
|
||||
void InlineNode::Handle(const Event& ev) noexcept {
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
log_->SetUp(*this);
|
||||
return;
|
||||
|
||||
case Event::kRemove:
|
||||
log_->TearDown();
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
void InlineNode::UpdateMenu() noexcept {
|
||||
}
|
||||
void InlineNode::UpdateNode(nf7::Node::Editor&) noexcept {
|
||||
ImGui::TextUnformatted("LuaJIT/InlineNode");
|
||||
|
||||
if (ImNodes::BeginInputSlot("in", 1)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
nf7::gui::NodeSocket();
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::InputTextMultiline("##script", &data().script);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
mem_.Commit();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImNodes::BeginOutputSlot("out", 1)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
nf7::gui::NodeSocket();
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
}
|
||||
void InlineNode::UpdateWidget() noexcept {
|
||||
ImGui::TextUnformatted("LuaJIT/InlineNode");
|
||||
ImGui::InputTextMultiline("script", &data().script);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
mem_.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
@ -202,7 +202,7 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
lua_rawgeti(thL, LUA_REGISTRYINDEX, handler->index());
|
||||
lua_pushstring(thL, p.first.c_str());
|
||||
nf7::luajit::PushValue(thL, p.second);
|
||||
PushCaller(thL, caller);
|
||||
nf7::luajit::PushNodeLambda(thL, caller, self);
|
||||
PushContextTable(ljq, thL);
|
||||
th->Resume(thL, 4);
|
||||
});
|
||||
@ -228,41 +228,6 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
}
|
||||
}
|
||||
|
||||
void PushCaller(lua_State* L, const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept {
|
||||
constexpr auto kTypeName = "nf7::File/LuaJIT/Node::Owner";
|
||||
struct D final {
|
||||
std::weak_ptr<Lambda> self;
|
||||
std::shared_ptr<nf7::Node::Lambda> caller;
|
||||
};
|
||||
new (lua_newuserdata(L, sizeof(D))) D { .self = weak_from_this(), .caller = caller };
|
||||
|
||||
if (luaL_newmetatable(L, kTypeName)) {
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& d = *reinterpret_cast<D*>(luaL_checkudata(L, 1, kTypeName));
|
||||
|
||||
auto self = d.self.lock();
|
||||
if (!self) return luaL_error(L, "context expired");
|
||||
|
||||
std::string n = luaL_checkstring(L, 2);
|
||||
auto v = nf7::luajit::CheckValue(L, 3);
|
||||
|
||||
auto caller = d.caller;
|
||||
caller->env().ExecSub(self, [self, caller, n, v]() mutable {
|
||||
caller->Handle(n, std::move(v), self);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__call");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
reinterpret_cast<D*>(luaL_checkudata(L, 1, kTypeName))->~D();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void PushContextTable(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
lua_State* L) noexcept {
|
||||
if (ctxtable_ && ctxtable_->ljq() != ljq) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user