12#include <mach-o/dyld.h>
13#elif defined(__linux__)
17#define INSTANCED_FFI(fn) [this](const std::vector<Value> &args, VM *vm) { return this->fn(args, vm); }
28 vm_->log(std::format(
"FFI::{}(\"{}\")\n", __func__, library.string()));
34 HMODULE lib = LoadLibraryA(library.string().c_str());
38 ss <<
"FFI Error: Failed to load library " << library.string() <<
". Code: " << GetLastError();
39 throw std::runtime_error(ss.str());
42 auto entry_point = (PluginEntryFunc)GetProcAddress(lib,
"phasor_plugin_entry");
44 void *lib = dlopen(library.string().c_str(), RTLD_NOW);
48 ss <<
"FFI Error: Failed to load library " << library.string() <<
". Error: " << dlerror();
49 throw std::runtime_error(ss.str());
52 auto entry_point = (PluginEntryFunc)dlsym(lib,
"phasor_plugin_entry");
57 throw std::runtime_error(
58 std::string(
"FFI Error: Could not find entry point 'phasor_plugin_entry' in " + library.string()));
70 entry_point(&api,
reinterpret_cast<PhasorVM *
>(vm));
72 plugins_.push_back(
Plugin{.handle = lib, .path = library.string(), .shutdown =
nullptr});
79 vm_->log(std::format(
"FFI::{}(\"{}\")\n", __func__, pluginPath.string()));
91 vm_->log(std::format(
"FFI::{}(\"{}\")\n", __func__, folder.string()));
97 std::vector<std::string> plugins;
98 std::filesystem::path exeDir;
99 std::vector<std::filesystem::path> foldersToScan;
101 if (folder.is_absolute())
103 foldersToScan.push_back(folder);
109 GetModuleFileNameA(
nullptr, path, MAX_PATH);
110 exeDir = std::filesystem::path(path).parent_path();
111#elif defined(__APPLE__)
113 uint32_t size =
sizeof(path);
114 if (_NSGetExecutablePath(path, &size) == 0)
115 exeDir = std::filesystem::path(path).parent_path();
117 exeDir = std::filesystem::current_path();
118#elif defined(__linux__)
120 ssize_t count = readlink(
"/proc/self/exe", path,
sizeof(path));
122 exeDir = std::filesystem::path(std::string(path, count)).parent_path();
124 exeDir = std::filesystem::current_path();
126 exeDir = std::filesystem::current_path();
129 foldersToScan.push_back(exeDir / folder);
130 if (!std::filesystem::equivalent(exeDir, std::filesystem::current_path()))
131 foldersToScan.push_back(std::filesystem::current_path() / folder);
134 for (
auto &folderPath : foldersToScan)
136 if (!std::filesystem::exists(folderPath) || !std::filesystem::is_directory(folderPath))
139 for (
auto &p : std::filesystem::directory_iterator(folderPath))
141 if (!p.is_regular_file())
143 auto ext = p.path().extension().string();
145 if (ext ==
".dll" || ext ==
".phsp")
146#elif defined(__APPLE__)
147 if (ext ==
".dylib" || ext ==
".phsp")
149 if (ext ==
".so" || ext ==
".phsp")
151 plugins.push_back(p.path().string());
163 vm_->log(std::format(
"FFI::{}()\n", __func__));
169 vm_->log(std::format(
"FFI::{}(): \"{}\"\n", __func__, plugin.path));
173 FreeLibrary(plugin.handle);
175 dlclose(plugin.handle);
184 vm_->log(std::format(
"FFI::{}(): created {:#x}\n", __func__, (uintptr_t)
this));
189 for (
const auto &pluginPath : plugins)
195 catch (
const std::runtime_error &e)
197 std::cerr << e.what() << std::endl;
206 vm_->log(std::format(
"FFI::{}(): deconstructed {:#x}\n", __func__, (uintptr_t)
this));
213 if (args.size() != 1)
215 throw std::runtime_error(
"load_plugin expects exactly 1 argument: the plugin path.");
217 std::filesystem::path pluginPath = args[0].
asString();
218 if (!std::filesystem::exists(pluginPath))
220 throw std::runtime_error(
"Plugin file does not exist: " + pluginPath.string());
struct PhasorVM PhasorVM
Phasor Virtual Machine pointer.
void unloadAll()
Unloads all currently loaded plugins and clears internal state.
std::vector< Plugin > plugins_
Loaded plugins.
std::filesystem::path pluginFolder_
Plugin search folder.
~FFI()
Destructor. Unloads all loaded plugins.
std::vector< std::string > scanPlugins(const std::filesystem::path &folder)
Scans a folder for plugin libraries.
Value native_add_plugin(const std::vector< Value > &args, VM *vm)
Native function to load a plugin at runtime.
bool loadPlugin(const std::filesystem::path &library, VM *vm)
Loads a single plugin from a library file.
bool addPlugin(const std::filesystem::path &pluginPath)
Adds a single plugin from the specified path.
VM * vm_
Pointer to the Phasor VM.
FFI(const std::filesystem::path &pluginFolder, VM *vm)
Constructs the FFI manager and loads plugins.
A value in the Phasor VM.
std::string asString() const
Get the value as a string.
#define INSTANCED_FFI(fn)
The Phasor Programming Language and Runtime.
void register_native_c_func(PhasorVM *vm, const char *name, PhasorNativeFunction func)
The concrete implementation of the PhasorRegisterFunction API call.
Represents a loaded plugin.
The collection of API functions that the Phasor host provides to the plugin.
PhasorRegisterFunction register_function
Registers a native C function with the given name.