10#include <mach-o/dyld.h>
11#elif defined(__linux__)
15#define INSTANCED_FFI(fn) [this](const std::vector<Value> &args, VM *vm) { return this->fn(args, vm); }
17#define VM_PRINT(str) vm->setRegister(VM::r0, str); vm->operation(OpCode::PRINT_R, VM::r0);
47 std::vector<Phasor::Value> cpp_elements;
49 cpp_elements.reserve(c_value.
as.
a.
count);
50 for (
size_t i = 0; i < c_value.
as.
a.
count; ++i) {
70 std::vector<std::unique_ptr<
char[]>>& string_arena,
71 std::vector<std::unique_ptr<
PhasorValue[]>>& array_arena)
85 const auto& str = cpp_value.
asString();
86 auto c_str = std::make_unique<char[]>(str.length() + 1);
87 std::copy(str.begin(), str.end(), c_str.get());
88 c_str[str.length()] =
'\0';
90 string_arena.push_back(std::move(c_str));
95 const auto& cpp_array = *cpp_value.
asArray();
96 size_t count = cpp_array.size();
101 auto c_array = std::make_unique<PhasorValue[]>(count);
102 for (
size_t i = 0; i < count; ++i) {
103 c_array[i] =
to_c_value(cpp_array[i], string_arena, array_arena);
107 array_arena.push_back(std::move(c_array));
130 std::vector<std::unique_ptr<char[]>> string_arena;
131 std::vector<std::unique_ptr<PhasorValue[]>> array_arena;
133 std::vector<PhasorValue> c_args;
134 c_args.reserve(args.size());
135 for (
const auto& arg : args)
137 c_args.push_back(
to_c_value(arg, string_arena, array_arena));
140 PhasorValue c_result = c_func(
reinterpret_cast<PhasorVM*
>(vm), (
int)c_args.size(), c_args.data());
172 HMODULE lib = LoadLibraryA(library.string().c_str());
174 std::stringstream ss;
175 ss <<
"FFI Error: Failed to load library " << library.string() <<
". Code: " << GetLastError();
176 throw std::runtime_error(ss.str());
179 auto entry_point = (PluginEntryFunc)GetProcAddress(lib,
"phasor_plugin_entry");
181 void *lib = dlopen(library.string().c_str(), RTLD_NOW);
183 std::stringstream ss;
184 ss <<
"FFI Error: Failed to load library " << library.string() <<
". Error: " << dlerror();
185 throw std::runtime_error(ss.str());
188 auto entry_point = (PluginEntryFunc)dlsym(lib,
"phasor_plugin_entry");
193 throw std::runtime_error(std::string(
"FFI Error: Could not find entry point 'phasor_plugin_entry' in " + library.string()));
205 entry_point(&api,
reinterpret_cast<PhasorVM*
>(vm));
221 if (folder.empty())
return {};
223 std::vector<std::string> plugins;
224 std::filesystem::path exeDir;
225 std::vector<std::filesystem::path> foldersToScan;
229 GetModuleFileNameA(
nullptr, path, MAX_PATH);
230 exeDir = std::filesystem::path(path).parent_path();
231#elif defined(__APPLE__)
233 uint32_t size =
sizeof(path);
234 if (_NSGetExecutablePath(path, &size) == 0)
235 exeDir = std::filesystem::path(path).parent_path();
237 exeDir = std::filesystem::current_path();
240 ssize_t count = readlink(
"/proc/self/exe", path,
sizeof(path));
242 exeDir = std::filesystem::path(std::string(path, count)).parent_path();
244 exeDir = std::filesystem::current_path();
247 foldersToScan.push_back(exeDir / folder);
248 if (!std::filesystem::equivalent(exeDir, std::filesystem::current_path())) {
249 foldersToScan.push_back(std::filesystem::current_path() / folder);
252 for (
auto &folderPath : foldersToScan)
254 if (!std::filesystem::exists(folderPath) || !std::filesystem::is_directory(folderPath))
257 for (
auto &p : std::filesystem::directory_iterator(folderPath))
259 if (!p.is_regular_file())
continue;
260 auto ext = p.path().extension().string();
262 if (ext ==
".dll" || ext ==
".phsp")
263#elif defined(__APPLE__)
264 if (ext ==
".dylib" || ext ==
".phsp")
266 if (ext ==
".so" || ext ==
".phsp")
268 plugins.push_back(p.path().string());
282 FreeLibrary(plugin.handle);
284 dlclose(plugin.handle);
294 for (
const auto &pluginPath : plugins)
300 catch (
const std::runtime_error &e)
302 std::cerr << e.what() << std::endl;
314 if (args.size() != 1)
316 throw std::runtime_error(
"load_plugin expects exactly 1 argument: the plugin path.");
318 std::filesystem::path pluginPath = args[0].
asString();
319 if (!std::filesystem::exists(pluginPath))
321 throw std::runtime_error(
"Plugin file does not exist: " + pluginPath.string());
PhasorValue(* PhasorNativeFunction)(PhasorVM *vm, int argc, const PhasorValue *argv)
static PhasorValue phasor_make_array(const PhasorValue *elements, size_t count)
static PhasorValue phasor_make_float(double f)
static PhasorValue phasor_make_string(const char *s)
static PhasorValue phasor_make_null()
static PhasorValue phasor_make_bool(bool b)
static PhasorValue phasor_make_int(int64_t i)
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.
void registerNativeFunction(const std::string &name, NativeFunction fn)
Register a native function.
A value in the Phasor VM.
double asFloat() const
Get the value as a double.
std::string asString() const
Get the value as a string.
std::shared_ptr< ArrayInstance > asArray()
Get the value as an array.
int64_t asInt() const
Get the value as an integer.
ValueType getType() const
Get the type of the value.
bool asBool() const
Get the value as a boolean.
static Value createArray(std::vector< Value > elements={})
#define INSTANCED_FFI(fn)
The Phasor Programming Language and Runtime.
static Phasor::Value from_c_value(const PhasorValue &c_value)
Converts a C-style FFI value to a C++ VM value.
static Phasor::Value c_native_func_wrapper(PhasorNativeFunction c_func, Phasor::VM *vm, const std::vector< Phasor::Value > &args)
The "trampoline" that wraps a C function from a plugin.
static PhasorValue to_c_value(const Phasor::Value &cpp_value, std::vector< std::unique_ptr< char[]> > &string_arena, std::vector< std::unique_ptr< PhasorValue[]> > &array_arena)
Converts a C++ VM value to a C-style FFI value.
static void register_native_c_func(PhasorVM *vm, const char *name, PhasorNativeFunction func)
The concrete implementation of the PhasorRegisterFunction API call.
Represents a loaded plugin.
PhasorRegisterFunction register_function
struct PhasorValue::@257106260015165034006071221344325333366100147341::@324103060207145140324203224012174236216253240107 a
const PhasorValue * elements
union PhasorValue::@257106260015165034006071221344325333366100147341 as