From bd61e775937342764fb948cb850492ac6ac3995a Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 30 May 2022 22:53:24 +0530 Subject: [PATCH] extension module implemented now it's possible to import a dynamic libray (*.dll, *.so) in a pocket script. --- Makefile | 2 +- README.md | 12 +- scripts/download_premake.py | 48 ----- scripts/generate_native.py | 28 +-- scripts/leak_detect.py | 98 --------- scripts/vs2019.bat | 19 +- src/core/internal.h | 4 - src/core/public.c | 23 +- src/core/value.c | 3 + src/core/value.h | 7 + src/core/vm.c | 107 ++++++--- src/core/vm.h | 7 + src/include/pocketlang.h | 24 +++ src/libs/gen/nativeapi.h | 205 ++++++++++++++++++ src/libs/libs.h | 13 ++ src/libs/std_os.c | 99 ++++++++- src/libs/std_path.c | 18 +- tests/native/dl/README.md | 14 ++ tests/native/dl/main.pk | 9 + tests/native/dl/mylib.c | 14 ++ tests/native/dl/pknative.c | 417 ++++++++++++++++++++++++++++++++++++ tests/native/example0.c | 8 +- tests/native/script.pk | 2 + 23 files changed, 944 insertions(+), 237 deletions(-) delete mode 100644 scripts/download_premake.py delete mode 100644 scripts/leak_detect.py create mode 100644 src/libs/gen/nativeapi.h create mode 100644 tests/native/dl/README.md create mode 100644 tests/native/dl/main.pk create mode 100644 tests/native/dl/mylib.c create mode 100644 tests/native/dl/pknative.c create mode 100644 tests/native/script.pk diff --git a/Makefile b/Makefile index 882396f..edacf4b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ CC = gcc CFLAGS = -fPIC DEBUG_CFLAGS = -D DEBUG -g3 -Og RELEASE_CFLAGS = -g -O3 -LDFLAGS = -lm +LDFLAGS = -lm -ldl TARGET_EXEC = pocket BUILD_DIR = ./build/ diff --git a/README.md b/README.md index 1ebc3fb..248b293 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ except for a c99 compatible compiler. It can be compiled with the following comm #### GCC / MinGw / Clang (alias with gcc) ``` -gcc -o pocket cli/*.c src/core/*.c src/libs/*.c -Isrc/include -lm +gcc -o pocket cli/*.c src/core/*.c src/libs/*.c -Isrc/include -lm -ldl ``` #### MSVC @@ -93,10 +93,12 @@ for the MSVS installation path and setup the build environment. ### For other compiler/IDE 1. Create an empty project file / makefile. -2. Add all C files in the src directory. -3. Add all C files in the cli directory (**NOT** recursively). -4. Add `src/include` to include path. -5. Compile. +2. Add all C files in the src/core/ directory. +3. Add all C files in the src/libs/ directory. +4. Add all C files in the cli/ directory. +5. Add `src/include` to include path. +6. If \*nix link m, dl +7. Compile. Visual studio project files can be generated with the premake, see [scripts/README](https://github.com/ThakeeNathees/pocketlang/scripts/README.md) diff --git a/scripts/download_premake.py b/scripts/download_premake.py deleted file mode 100644 index 7ac29ef..0000000 --- a/scripts/download_premake.py +++ /dev/null @@ -1,48 +0,0 @@ -## -## Copyright (c) 2020-2022 Thakee Nathees -## Copyright (c) 2021-2022 Pocketlang Contributors -## Distributed Under The MIT License -## - -## A tiny script to download and place premake binary in this path. -## Primarly used to generate Visual Studio project files. Feel free -## add other build script and platforms. - -import urllib.request -import os, platform -import tempfile, zipfile, tarfile -from os.path import abspath, dirname, join - -THIS_PATH = abspath(dirname(__file__)) - -PREMAKE_PLATFORM_URL = { - "Windows": "https://github.com/premake/premake-core/releases/download/v5.0.0-beta1/premake-5.0.0-beta1-windows.zip", - #"Linux": TODO, - #"Darwin": TODO, -} - -def main(): - system = platform.system() - if system not in PREMAKE_PLATFORM_URL: - error_exit(f"Platform {system} is currently not supported.\n" +\ - "(You can modify this script to add your platform and contribute).") - - premake_url = PREMAKE_PLATFORM_URL[system] - tmpdir = tempfile.mkdtemp() - zip_path = join(tmpdir, 'premake5.zip') - - with urllib.request.urlopen(premake_url) as binary_zip: - with open(zip_path, 'wb') as f: - f.write(binary_zip.read()) - premake_zip = zipfile.ZipFile(zip_path, 'r') - premake_zip.extractall(THIS_PATH) - - print("premake5 downloaded successfully.") - -## prints an error message to stderr and exit immediately. -def error_exit(msg): - print("Error:", msg, file=sys.stderr) - sys.exit(1) - -if __name__ == "__main__": - main() diff --git a/scripts/generate_native.py b/scripts/generate_native.py index 78ea4cf..54f79fc 100644 --- a/scripts/generate_native.py +++ b/scripts/generate_native.py @@ -11,12 +11,12 @@ from os.path import (join, exists, abspath, ## the root path. ROOT_PATH = abspath(join(dirname(__file__), "../")) -NATIVE_HEADER = "pknative.gen.h" -NATIVE_SOURCE = "nativeapi.gen.h" +NATIVE_API_EXT = "pknative.c" +NATIVE_API_IMP = "nativeapi.h" POCKET_HEADER = join(ROOT_PATH, f"src/include/pocketlang.h") -TARGET_HEADER = join(ROOT_PATH, f"src/include/{NATIVE_HEADER}") -TARGET_SOURCE = join(ROOT_PATH, f"src/core/{NATIVE_SOURCE}") +TARGET_EXT = join(ROOT_PATH, f"tests/native/dl/{NATIVE_API_EXT}") +TARGET_IMP = join(ROOT_PATH, f"src/libs/gen/{NATIVE_API_IMP}") DL_IMPLEMENT = 'PK_DL_IMPLEMENT' PK_API = "pk_api" @@ -38,11 +38,7 @@ HEADER = f'''\ * Copyright (c) 2021-2022 Pocketlang Contributors * Distributed Under The MIT License */ - -#ifndef PK_AMALGAMATED -#include %s -#endif - +%s // !! THIS FILE IS GENERATED DO NOT EDIT !! ''' @@ -135,18 +131,16 @@ def generate(): api_functions = get_api_functions() ## Generate pocket native header. - with open(TARGET_HEADER, 'w') as fp: - fp.write(HEADER % '') + with open(TARGET_EXT, 'w') as fp: + fp.write(HEADER % '\n#include \n') fp.write(fn_typedefs(api_functions)) fp.write(api_typedef(api_functions)) - fp.write(f'\n#if defined({DL_IMPLEMENT})\n\n') fp.write(init_api(api_functions)) fp.write(define_functions(api_functions)) - fp.write(f'\n#endif //{DL_IMPLEMENT}\n') ## Generate native api source. - with open(TARGET_SOURCE, 'w') as fp: - fp.write(HEADER % '') + with open(TARGET_IMP, 'w') as fp: + fp.write(HEADER % '\n#ifndef PK_AMALGAMATED\n#include \n#endif\n\n') fp.write(fn_typedefs(api_functions)) fp.write(api_typedef(api_functions)) @@ -163,5 +157,5 @@ def generate(): if __name__ == "__main__": generate() - print("Generated:", relpath(TARGET_HEADER, ROOT_PATH)) - print("Generated:", relpath(TARGET_SOURCE, ROOT_PATH)) + print("Generated:", relpath(TARGET_EXT, ROOT_PATH)) + print("Generated:", relpath(TARGET_IMP, ROOT_PATH)) diff --git a/scripts/leak_detect.py b/scripts/leak_detect.py deleted file mode 100644 index bafe4b6..0000000 --- a/scripts/leak_detect.py +++ /dev/null @@ -1,98 +0,0 @@ -## -## Copyright (c) 2020-2022 Thakee Nathees -## Copyright (c) 2021-2022 Pocketlang Contributors -## Distributed Under The MIT License -## - -## A quick script to detect memory leaks, from the trace report. -## To get the trace report redefine TRACE_MEMORY as 1 at the -## pk_internal.h and compile pocketlang. - -raise "This script Need refactor after removing pkAllocString " + \ - "and adding pkRealloc" - -import sys - -def detect_leak(): - trace_log_id = "[memory trace]" - total_bytes = 0 ## Totally allocated bytes. - mem = dict() ## key = address, value = bytes. - - str_alloc_id = "[alloc string]" - strs = dict() ## string allocations. - - trace_log_file_path = sys.argv[1] - - with open(trace_log_file_path, 'r') as f: - for line in f.readlines(): - if line.startswith(str_alloc_id): - line = line[len(trace_log_id) + 1:].strip() - type, data = split_and_strip(line, ':') - if type == "alloc": - addr, bytes = split_and_strip(data, '=') - bytes = int(bytes.replace(' bytes', '')) - strs[addr] = bytes - - else: ## "dealloc" - addr = data.strip() - strs.pop(addr) - - elif line.startswith(trace_log_id): - line = line[len(trace_log_id) + 1:].strip() - type, data = split_and_strip(line, ':') - - addr, bytes = split_and_strip(data, '=') - bytes = bytes.replace(' bytes', '') - - if type == "malloc": - bytes = int(bytes) - assert(bytes >= 0); total_bytes += bytes - mem[addr] = bytes - - elif type == "free": - bytes = int(bytes) - assert(bytes <= 0); total_bytes += bytes - mem.pop(addr) - - elif type == "realloc": - oldp, newp = split_and_strip(addr, '->') - olds, news = split_and_strip(bytes, '->') - olds = int(olds); news = int(news) - total_bytes += (news - olds) - assert(mem[oldp] == olds) - mem.pop(oldp) - mem[newp] = news - - success = True - if total_bytes != 0: - print_err(f"Memory leak detected - {total_bytes} bytes were never freed.") - success = False - - if len(mem) != 0: - print_err("Memory leak detected - some addresses were never freed.") - for addr in mem: - print(f" {addr} : {mem[addr]} bytes") - success = False - - if len(strs) != 0: - print_err("Memory leak detected - string allocation(s) were never freed.") - for addr in strs: - print(f" {addr} : {strs[addr]} bytes") - success = False - - if success: - print("No leaks were detected.") - else: - sys.exit(1) - - -def split_and_strip(string, delim): - return map(lambda s: s.strip(), string.split(delim)) - - -def print_err(msg): - print("Error:", msg, file=sys.stderr) - - -if __name__ == "__main__": - detect_leak() diff --git a/scripts/vs2019.bat b/scripts/vs2019.bat index 59b973a..6242b4e 100644 --- a/scripts/vs2019.bat +++ b/scripts/vs2019.bat @@ -1 +1,18 @@ -@premake5 vs2019 +@echo off + +pushd %cd% +cd %~dp0 + +:: unzip.exe comes with git for windows. + +if not exist premake5.exe ( + echo premake5.exe not exists, downloading + echo. + curl -L https://github.com/premake/premake-core/releases/download/v5.0.0-beta1/premake-5.0.0-beta1-windows.zip --output premake5.zip + unzip premake5.zip && premake5.zip + echo. +) + +premake5 vs2019 + +popd diff --git a/src/core/internal.h b/src/core/internal.h index 9b8cf59..2cdab06 100644 --- a/src/core/internal.h +++ b/src/core/internal.h @@ -51,10 +51,6 @@ // Dump the stack values and the globals. #define DUMP_STACK 0 -// Trace memory allocations. Enable this and redirect the trace dump to a file -// and run the script leak_detect.py to check for memory leaks. -#define TRACE_MEMORY 0 - // Nan-Tagging could be disable for debugging/portability purposes. See "var.h" // header for more information on Nan-tagging. #define VAR_NAN_TAGGING 1 diff --git a/src/core/public.c b/src/core/public.c index 8d259c9..11c1b7a 100644 --- a/src/core/public.c +++ b/src/core/public.c @@ -17,7 +17,7 @@ #include "vm.h" #endif -// TODO: Document this or Find a better way. +// FIXME: Document this or Find a better way. // // Pocketlang core doesn't implement path resolving funcionality. Rather it // should be provided the host application. By default we're using an @@ -32,6 +32,13 @@ void registerLibs(PKVM* vm); void cleanupLibs(PKVM* vm); char* pathResolveImport(PKVM* vm, const char* from, const char* path); + +#ifndef PK_NO_DL + void* osLoadDL(PKVM* vm, const char* path); + PkHandle* osImportDL(PKVM* vm, void* handle); + void osUnloadDL(PKVM* vm, void* handle); +#endif + #endif #define CHECK_ARG_NULL(name) \ @@ -81,15 +88,8 @@ static char* stdinRead(PKVM* vm); static char* loadScript(PKVM* vm, const char* path); void* pkRealloc(PKVM* vm, void* ptr, size_t size) { - ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL."); -#if TRACE_MEMORY - void* newptr = vm->config.realloc_fn(ptr, size, vm->config.user_data); - printf("[pkRealloc] %p -> %p %+li bytes\n", ptr, newptr, (long) size); - return ptr; -#else return vm->config.realloc_fn(ptr, size, vm->config.user_data); -#endif } PkConfiguration pkNewConfiguration() { @@ -103,6 +103,13 @@ PkConfiguration pkNewConfiguration() { config.stdin_read = stdinRead; #ifndef PK_NO_LIBS config.resolve_path_fn = pathResolveImport; + +#ifndef PK_NO_DL + config.load_dl_fn = osLoadDL; + config.import_dl_fn = osImportDL; + config.unload_dl_fn = osUnloadDL; +#endif + #endif config.load_script_fn = loadScript; diff --git a/src/core/value.c b/src/core/value.c index 372c76c..8de673a 100644 --- a/src/core/value.c +++ b/src/core/value.c @@ -1143,6 +1143,9 @@ void freeObject(PKVM* vm, Object* self) { pkVarBufferClear(&module->globals, vm); pkUintBufferClear(&module->global_names, vm); pkVarBufferClear(&module->constants, vm); + #ifndef PK_NO_DL + if (module->handle) vmUnloadDlHandle(vm, module->handle); + #endif DEALLOCATE(vm, self, Module); return; } diff --git a/src/core/value.h b/src/core/value.h index 30f6540..c43b6b2 100644 --- a/src/core/value.h +++ b/src/core/value.h @@ -311,6 +311,13 @@ struct Module { // will be set to true. If a module doesn't have globals, We can safely set // it to true to prevent from running the above body function, if it has one. bool initialized; + +#ifndef PK_NO_DL + // If the module loaded from a native extension, this handle will reference + // to the platform dependent handle of that module and will be released + // when the module garbage collected. + void* handle; +#endif }; // A struct contain opcodes and other information of a compiled function. diff --git a/src/core/vm.c b/src/core/vm.c index a946cc5..3189ac9 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -47,39 +47,7 @@ void* vmRealloc(PKVM* vm, void* memory, size_t old_size, size_t new_size) { vm->collecting_garbage = false; } -#if TRACE_MEMORY - - void* ptr = vm->config.realloc_fn(memory, new_size, vm->config.user_data); - do { - // Deallocation of the VM itself cannot be traced. - if (memory == vm) break; - if (memory == NULL && new_size == 0) { //< Just nothing. - ASSERT(old_size == 0, OOPS); - break; - } - - if (old_size == 0 && new_size > 0) { // New allocation. - ASSERT(memory == NULL, OOPS); - printf("[memory trace] malloc : %p = %+li bytes\n", - ptr, (long) new_size); - } else if (new_size == 0) { // Free. - ASSERT(memory != NULL && old_size != 0, OOPS); - printf("[memory trace] free : %p = -%li bytes\n", - memory, (long) old_size); - } else { // Realloc. - ASSERT(old_size != 0 && new_size != 0 && memory != NULL, OOPS); - printf("[memory trace] realloc : %p -> %p = %+li -> %+li bytes\n", - memory, ptr, (long) (old_size), (long) (new_size)); - } - - } while (false); - - return ptr; - -#else return vm->config.realloc_fn(memory, new_size, vm->config.user_data); -#endif - } void vmPushTempRef(PKVM* vm, Object* obj) { @@ -368,6 +336,68 @@ PkResult vmCallFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) { return vmCallMethod(vm, VAR_UNDEFINED, fn, argc, argv, ret); } +#ifndef PK_NO_DL + +// Returns true if the path ends with ".dll" or ".so". +static bool _isPathDL(String* path) { + const char* dlext[] = { + ".so", + ".dll", + NULL, + }; + + for (const char** ext = dlext; *ext != NULL; ext++) { + const char* start = path->data + (path->length - strlen(*ext)); + if (!strncmp(start, *ext, strlen(*ext))) return true; + } + + return false; +} + +static Module* _importDL(PKVM* vm, String* resolved, String* name) { + if (vm->config.import_dl_fn == NULL) { + VM_SET_ERROR(vm, newString(vm, "Dynamic library importer not provided.")); + return NULL; + } + + ASSERT(vm->config.load_dl_fn != NULL, OOPS); + void* handle = vm->config.load_dl_fn(vm, resolved->data); + + if (handle == NULL) { + VM_SET_ERROR(vm, stringFormat(vm, "Error loading module at \"@\"", + resolved)); + return NULL; + } + + PkHandle* pkhandle = vm->config.import_dl_fn(vm, handle); + if (pkhandle == NULL) { + VM_SET_ERROR(vm, stringFormat(vm, "Error loading module at \"@\"", + resolved)); + return NULL; + } + + if (!IS_OBJ_TYPE(pkhandle->value, OBJ_MODULE)) { + VM_SET_ERROR(vm, stringFormat(vm, "Returned handle wasn't a " + "module at \"@\"", resolved)); + return NULL; + } + + Module* module = (Module*) AS_OBJ(pkhandle->value); + module->name = name; + module->path = resolved; + module->handle = handle; + vmRegisterModule(vm, module, resolved); + + pkReleaseHandle(vm, pkhandle); + return module; +} + +void vmUnloadDlHandle(PKVM* vm, void* handle) { + if (handle && vm->config.unload_dl_fn) + vm->config.unload_dl_fn(vm, handle); +} +#endif // PK_NO_DL + /*****************************************************************************/ /* VM INTERNALS */ /*****************************************************************************/ @@ -460,7 +490,14 @@ Var vmImportModule(PKVM* vm, String* from, String* path) { // The script not exists in the VM, make sure we have the script loading // api function. + #ifndef PK_NO_DL + bool isdl = _isPathDL(resolved); + if (isdl && vm->config.load_dl_fn == NULL + || vm->config.load_script_fn == NULL) { + #else if (vm->config.load_script_fn == NULL) { + #endif + VM_SET_ERROR(vm, newString(vm, "Cannot import. The hosting application " "haven't registered the module loading API")); return VAR_NULL; @@ -485,7 +522,13 @@ Var vmImportModule(PKVM* vm, String* from, String* path) { } _name->hash = utilHashString(_name->data); vmPushTempRef(vm, &_name->_super); // _name. + + #ifndef PK_NO_DL + if (isdl) module = _importDL(vm, resolved, _name); + else /* ... */ + #endif module = _importScript(vm, resolved, _name); + vmPopTempRef(vm); // _name. } vmPopTempRef(vm); // resolved. diff --git a/src/core/vm.h b/src/core/vm.h index b51cea9..261f95e 100644 --- a/src/core/vm.h +++ b/src/core/vm.h @@ -248,4 +248,11 @@ PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn, // On failure, it'll set an error and return VAR_NULL. Var vmImportModule(PKVM* vm, String* from, String* path); +#ifndef PK_NO_DL + +// Release platform dependent native extension module handle. (*.dll, *.so). +void vmUnloadDlHandle(PKVM* vm, void* handle); + +#endif + #endif // PK_VM_H diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 5efdc41..f5bd0ed 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -109,6 +109,24 @@ typedef void (*pkSignalFn) (void*); // the VM will claim the ownership of the string. typedef char* (*pkLoadScriptFn) (PKVM* vm, const char* path); +#ifndef PK_NO_DL + +// Load and return the native extension (*.dll, *.so) from the path, this will +// then used to import the module with the pkImportImportDL function. On error +// the function should return NULL and shouldn't use any error api function. +typedef void* (*pkLoadDL) (PKVM* vm, const char* path); + +// Native extension loader from the dynamic library. The handle should be vaiid +// as long as the module handle is alive. On error the function should return +// NULL and shouldn't use any error api function. +typedef PkHandle* (*pkImportDL) (PKVM* vm, void* handle); + +// Once the native module is gargage collected, the dl handle will be released +// with pkUnloadDL function. +typedef void (*pkUnloadDL) (PKVM* vm, void* handle); + +#endif // PK_NO_DL + // A function callback to resolve the import statement path. [from] path can // be either path to a script or a directory or NULL if [path] is relative to // cwd. If the path is a directory it'll always ends with a path separator @@ -189,6 +207,12 @@ struct PkConfiguration { pkResolvePathFn resolve_path_fn; pkLoadScriptFn load_script_fn; + #ifndef PK_NO_DL + pkLoadDL load_dl_fn; + pkImportDL import_dl_fn; + pkUnloadDL unload_dl_fn; + #endif + // If true stderr calls will use ansi color codes. bool use_ansi_escape; diff --git a/src/libs/gen/nativeapi.h b/src/libs/gen/nativeapi.h new file mode 100644 index 0000000..b7cb54e --- /dev/null +++ b/src/libs/gen/nativeapi.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2020-2022 Thakee Nathees + * Copyright (c) 2021-2022 Pocketlang Contributors + * Distributed Under The MIT License + */ + +#ifndef PK_AMALGAMATED +#include +#endif + + +// !! THIS FILE IS GENERATED DO NOT EDIT !! + +typedef PkConfiguration (*pkNewConfiguration_t)(); +typedef PKVM* (*pkNewVM_t)(PkConfiguration*); +typedef void (*pkFreeVM_t)(PKVM*); +typedef void (*pkSetUserData_t)(PKVM*, void*); +typedef void* (*pkGetUserData_t)(const PKVM*); +typedef void (*pkRegisterBuiltinFn_t)(PKVM*, const char*, pkNativeFn, int, const char*); +typedef void (*pkAddSearchPath_t)(PKVM*, const char*); +typedef void* (*pkRealloc_t)(PKVM*, void*, size_t); +typedef void (*pkReleaseHandle_t)(PKVM*, PkHandle*); +typedef PkHandle* (*pkNewModule_t)(PKVM*, const char*); +typedef void (*pkRegisterModule_t)(PKVM*, PkHandle*); +typedef void (*pkModuleAddFunction_t)(PKVM*, PkHandle*, const char*, pkNativeFn, int); +typedef PkHandle* (*pkNewClass_t)(PKVM*, const char*, PkHandle*, PkHandle*, pkNewInstanceFn, pkDeleteInstanceFn); +typedef void (*pkClassAddMethod_t)(PKVM*, PkHandle*, const char*, pkNativeFn, int); +typedef void (*pkModuleAddSource_t)(PKVM*, PkHandle*, const char*); +typedef PkResult (*pkRunString_t)(PKVM*, const char*); +typedef PkResult (*pkRunFile_t)(PKVM*, const char*); +typedef PkResult (*pkRunREPL_t)(PKVM*); +typedef void (*pkSetRuntimeError_t)(PKVM*, const char*); +typedef void* (*pkGetSelf_t)(const PKVM*); +typedef int (*pkGetArgc_t)(const PKVM*); +typedef bool (*pkCheckArgcRange_t)(PKVM*, int, int, int); +typedef bool (*pkValidateSlotBool_t)(PKVM*, int, bool*); +typedef bool (*pkValidateSlotNumber_t)(PKVM*, int, double*); +typedef bool (*pkValidateSlotInteger_t)(PKVM*, int, int32_t*); +typedef bool (*pkValidateSlotString_t)(PKVM*, int, const char**, uint32_t*); +typedef bool (*pkValidateSlotType_t)(PKVM*, int, PkVarType); +typedef bool (*pkValidateSlotInstanceOf_t)(PKVM*, int, int); +typedef bool (*pkIsSlotInstanceOf_t)(PKVM*, int, int, bool*); +typedef void (*pkReserveSlots_t)(PKVM*, int); +typedef int (*pkGetSlotsCount_t)(PKVM*); +typedef PkVarType (*pkGetSlotType_t)(PKVM*, int); +typedef bool (*pkGetSlotBool_t)(PKVM*, int); +typedef double (*pkGetSlotNumber_t)(PKVM*, int); +typedef const char* (*pkGetSlotString_t)(PKVM*, int, uint32_t*); +typedef PkHandle* (*pkGetSlotHandle_t)(PKVM*, int); +typedef void* (*pkGetSlotNativeInstance_t)(PKVM*, int); +typedef void (*pkSetSlotNull_t)(PKVM*, int); +typedef void (*pkSetSlotBool_t)(PKVM*, int, bool); +typedef void (*pkSetSlotNumber_t)(PKVM*, int, double); +typedef void (*pkSetSlotString_t)(PKVM*, int, const char*); +typedef void (*pkSetSlotStringLength_t)(PKVM*, int, const char*, uint32_t); +typedef void (*pkSetSlotHandle_t)(PKVM*, int, PkHandle*); +typedef void (*pkPlaceSelf_t)(PKVM*, int); +typedef void (*pkGetClass_t)(PKVM*, int, int); +typedef bool (*pkNewInstance_t)(PKVM*, int, int, int, int); +typedef void (*pkNewRange_t)(PKVM*, int, double, double); +typedef void (*pkNewList_t)(PKVM*, int); +typedef void (*pkNewMap_t)(PKVM*, int); +typedef bool (*pkListInsert_t)(PKVM*, int, int32_t, int); +typedef bool (*pkListPop_t)(PKVM*, int, int32_t, int); +typedef uint32_t (*pkListLength_t)(PKVM*, int); +typedef bool (*pkCallFunction_t)(PKVM*, int, int, int, int); +typedef bool (*pkCallMethod_t)(PKVM*, int, const char*, int, int, int); +typedef bool (*pkGetAttribute_t)(PKVM*, int, const char*, int); +typedef bool (*pkSetAttribute_t)(PKVM*, int, const char*, int); +typedef bool (*pkImportModule_t)(PKVM*, const char*, int); + +typedef struct { + pkNewConfiguration_t pkNewConfiguration_ptr; + pkNewVM_t pkNewVM_ptr; + pkFreeVM_t pkFreeVM_ptr; + pkSetUserData_t pkSetUserData_ptr; + pkGetUserData_t pkGetUserData_ptr; + pkRegisterBuiltinFn_t pkRegisterBuiltinFn_ptr; + pkAddSearchPath_t pkAddSearchPath_ptr; + pkRealloc_t pkRealloc_ptr; + pkReleaseHandle_t pkReleaseHandle_ptr; + pkNewModule_t pkNewModule_ptr; + pkRegisterModule_t pkRegisterModule_ptr; + pkModuleAddFunction_t pkModuleAddFunction_ptr; + pkNewClass_t pkNewClass_ptr; + pkClassAddMethod_t pkClassAddMethod_ptr; + pkModuleAddSource_t pkModuleAddSource_ptr; + pkRunString_t pkRunString_ptr; + pkRunFile_t pkRunFile_ptr; + pkRunREPL_t pkRunREPL_ptr; + pkSetRuntimeError_t pkSetRuntimeError_ptr; + pkGetSelf_t pkGetSelf_ptr; + pkGetArgc_t pkGetArgc_ptr; + pkCheckArgcRange_t pkCheckArgcRange_ptr; + pkValidateSlotBool_t pkValidateSlotBool_ptr; + pkValidateSlotNumber_t pkValidateSlotNumber_ptr; + pkValidateSlotInteger_t pkValidateSlotInteger_ptr; + pkValidateSlotString_t pkValidateSlotString_ptr; + pkValidateSlotType_t pkValidateSlotType_ptr; + pkValidateSlotInstanceOf_t pkValidateSlotInstanceOf_ptr; + pkIsSlotInstanceOf_t pkIsSlotInstanceOf_ptr; + pkReserveSlots_t pkReserveSlots_ptr; + pkGetSlotsCount_t pkGetSlotsCount_ptr; + pkGetSlotType_t pkGetSlotType_ptr; + pkGetSlotBool_t pkGetSlotBool_ptr; + pkGetSlotNumber_t pkGetSlotNumber_ptr; + pkGetSlotString_t pkGetSlotString_ptr; + pkGetSlotHandle_t pkGetSlotHandle_ptr; + pkGetSlotNativeInstance_t pkGetSlotNativeInstance_ptr; + pkSetSlotNull_t pkSetSlotNull_ptr; + pkSetSlotBool_t pkSetSlotBool_ptr; + pkSetSlotNumber_t pkSetSlotNumber_ptr; + pkSetSlotString_t pkSetSlotString_ptr; + pkSetSlotStringLength_t pkSetSlotStringLength_ptr; + pkSetSlotHandle_t pkSetSlotHandle_ptr; + pkPlaceSelf_t pkPlaceSelf_ptr; + pkGetClass_t pkGetClass_ptr; + pkNewInstance_t pkNewInstance_ptr; + pkNewRange_t pkNewRange_ptr; + pkNewList_t pkNewList_ptr; + pkNewMap_t pkNewMap_ptr; + pkListInsert_t pkListInsert_ptr; + pkListPop_t pkListPop_ptr; + pkListLength_t pkListLength_ptr; + pkCallFunction_t pkCallFunction_ptr; + pkCallMethod_t pkCallMethod_ptr; + pkGetAttribute_t pkGetAttribute_ptr; + pkSetAttribute_t pkSetAttribute_ptr; + pkImportModule_t pkImportModule_ptr; +} PkNativeApi; + +#define PK_API_INIT_FN_NAME "pkInitApi" +#define PK_EXPORT_FN_NAME "pkExportModule" + +typedef void (*pkInitApiFn)(PkNativeApi*); +typedef PkHandle* (*pkExportModuleFn)(PKVM*); + +#ifdef PK_DL_IMPLEMENT + +PkNativeApi pkMakeNativeAPI() { + + PkNativeApi api; + + api.pkNewConfiguration_ptr = pkNewConfiguration; + api.pkNewVM_ptr = pkNewVM; + api.pkFreeVM_ptr = pkFreeVM; + api.pkSetUserData_ptr = pkSetUserData; + api.pkGetUserData_ptr = pkGetUserData; + api.pkRegisterBuiltinFn_ptr = pkRegisterBuiltinFn; + api.pkAddSearchPath_ptr = pkAddSearchPath; + api.pkRealloc_ptr = pkRealloc; + api.pkReleaseHandle_ptr = pkReleaseHandle; + api.pkNewModule_ptr = pkNewModule; + api.pkRegisterModule_ptr = pkRegisterModule; + api.pkModuleAddFunction_ptr = pkModuleAddFunction; + api.pkNewClass_ptr = pkNewClass; + api.pkClassAddMethod_ptr = pkClassAddMethod; + api.pkModuleAddSource_ptr = pkModuleAddSource; + api.pkRunString_ptr = pkRunString; + api.pkRunFile_ptr = pkRunFile; + api.pkRunREPL_ptr = pkRunREPL; + api.pkSetRuntimeError_ptr = pkSetRuntimeError; + api.pkGetSelf_ptr = pkGetSelf; + api.pkGetArgc_ptr = pkGetArgc; + api.pkCheckArgcRange_ptr = pkCheckArgcRange; + api.pkValidateSlotBool_ptr = pkValidateSlotBool; + api.pkValidateSlotNumber_ptr = pkValidateSlotNumber; + api.pkValidateSlotInteger_ptr = pkValidateSlotInteger; + api.pkValidateSlotString_ptr = pkValidateSlotString; + api.pkValidateSlotType_ptr = pkValidateSlotType; + api.pkValidateSlotInstanceOf_ptr = pkValidateSlotInstanceOf; + api.pkIsSlotInstanceOf_ptr = pkIsSlotInstanceOf; + api.pkReserveSlots_ptr = pkReserveSlots; + api.pkGetSlotsCount_ptr = pkGetSlotsCount; + api.pkGetSlotType_ptr = pkGetSlotType; + api.pkGetSlotBool_ptr = pkGetSlotBool; + api.pkGetSlotNumber_ptr = pkGetSlotNumber; + api.pkGetSlotString_ptr = pkGetSlotString; + api.pkGetSlotHandle_ptr = pkGetSlotHandle; + api.pkGetSlotNativeInstance_ptr = pkGetSlotNativeInstance; + api.pkSetSlotNull_ptr = pkSetSlotNull; + api.pkSetSlotBool_ptr = pkSetSlotBool; + api.pkSetSlotNumber_ptr = pkSetSlotNumber; + api.pkSetSlotString_ptr = pkSetSlotString; + api.pkSetSlotStringLength_ptr = pkSetSlotStringLength; + api.pkSetSlotHandle_ptr = pkSetSlotHandle; + api.pkPlaceSelf_ptr = pkPlaceSelf; + api.pkGetClass_ptr = pkGetClass; + api.pkNewInstance_ptr = pkNewInstance; + api.pkNewRange_ptr = pkNewRange; + api.pkNewList_ptr = pkNewList; + api.pkNewMap_ptr = pkNewMap; + api.pkListInsert_ptr = pkListInsert; + api.pkListPop_ptr = pkListPop; + api.pkListLength_ptr = pkListLength; + api.pkCallFunction_ptr = pkCallFunction; + api.pkCallMethod_ptr = pkCallMethod; + api.pkGetAttribute_ptr = pkGetAttribute; + api.pkSetAttribute_ptr = pkSetAttribute; + api.pkImportModule_ptr = pkImportModule; + + return api; +} + +#endif // PK_DL_IMPLEMENT diff --git a/src/libs/libs.h b/src/libs/libs.h index 9243e42..ce92f70 100644 --- a/src/libs/libs.h +++ b/src/libs/libs.h @@ -51,6 +51,19 @@ void cleanupLibs(PKVM* vm); // inorder to use the import statements. char* pathResolveImport(PKVM * vm, const char* from, const char* path); +#ifndef PK_NO_DL + +// Loads the platform dependent dynamic library and returns the handle. +void* osLoadDL(PKVM* vm, const char* path); + +// Import the module from the dynamic library handle which was loaded from os. +PkHandle* osImportDL(PKVM* vm, void* handle); + +// Release the dynamic library. +void osUnloadDL(PKVM* vm, void* handle); + +#endif + // Write the executable's path to the buffer and return true, if it failed // it'll return false. bool osGetExeFilePath(char* buff, int size); diff --git a/src/libs/std_os.c b/src/libs/std_os.c index 16e5144..159ec87 100644 --- a/src/libs/std_os.c +++ b/src/libs/std_os.c @@ -29,21 +29,20 @@ #define _PKOS_LINUX_ #define OS_NAME "linux" -#elif defined(__unix__) - #define _PKOS_UNIX_ - #define OS_NAME "unix" - -#elif defined(_POSIX_VERSION) - #define _PKOS_POSIX_ - #define OS_NAME "posix" - #else #define _PKOS_UNKNOWN_ #define OS_NAME "" #endif -#if defined(_MSC_VER) || (defined(_WIN32) && defined(__TINYC__)) +#if defined(_PKOS_WIN_) #include +#endif + +#if defined(__linux__) && !defined(PK_NO_DL) + #include +#endif + +#if defined(_MSC_VER) || (defined(_WIN32) && defined(__TINYC__)) #include #include @@ -65,6 +64,88 @@ // See: https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html #define MAX_PATH_LEN 4096 +#ifndef PK_NO_DL + +#define PK_DL_IMPLEMENT +#include "gen/nativeapi.h" //<< AMALG_INLINE >> + +#ifdef _PKOS_WIN_ +void* osLoadDL(PKVM* vm, const char* path) { + HMODULE handle = LoadLibraryA(path); + if (handle == NULL) return NULL; + + pkInitApiFn init_fn = \ + (pkInitApiFn) GetProcAddress(handle, PK_API_INIT_FN_NAME); + + if (init_fn == NULL) { + FreeLibrary(handle); + return NULL; + } + + PkNativeApi api = pkMakeNativeAPI(); + init_fn(&api); + + return (void*) handle; +} + +PkHandle* osImportDL(PKVM* vm, void* handle) { + pkExportModuleFn export_fn = \ + (pkExportModuleFn)GetProcAddress((HMODULE) handle, PK_EXPORT_FN_NAME); + if (export_fn == NULL) return NULL; + + return export_fn(vm); +} + +void osUnloadDL(PKVM* vm, void* handle) { + FreeLibrary((HMODULE) handle); +} + +#elif defined(__linux__) + +void* osLoadDL(PKVM* vm, const char* path) { + // https://man7.org/linux/man-pages/man3/dlopen.3.html + void* handle = dlopen(path, RTLD_LAZY); + if (handle == NULL) return NULL; + + pkInitApiFn init_fn = dlsym(handle, PK_API_INIT_FN_NAME); + if (init_fn == NULL) { + if (dlclose(handle)) { /* TODO: Handle error. */ } + dlerror(); // Clear error. + return NULL; + } + + PkNativeApi api = pkMakeNativeAPI(); + init_fn(&api); + + return handle; +} + +PkHandle* osImportDL(PKVM* vm, void* handle) { + pkExportModuleFn export_fn = dlsym(handle, PK_EXPORT_FN_NAME); + if (export_fn == NULL) { + dlerror(); // Clear error. + return NULL; + } + + PkHandle* module = export_fn(vm); + dlerror(); // Clear error. + return module; +} + +void osUnloadDL(PKVM* vm, void* handle) { + dlclose(handle); +} + +#else + +void* osLoadDL(PKVM* vm, const char* path) { return NULL; } +PkHandle* osImportDL(PKVM* vm, void* handle) { return NULL; } +void osUnloadDL(PKVM* vm, void* handle) { } + +#endif + +#endif // PK_NO_DL + bool osGetExeFilePath(char* buff, int size) { #if defined(_PKOS_WIN_) diff --git a/src/libs/std_path.c b/src/libs/std_path.c index dc8d6a5..2344d71 100644 --- a/src/libs/std_path.c +++ b/src/libs/std_path.c @@ -97,6 +97,13 @@ static char* tryImportPaths(PKVM* vm, char* path, char* buff) { "", ".pk", "/_init.pk", +#ifndef PK_NO_DL + #ifdef _WIN32 + ".dll", + #else + ".so", + #endif +#endif NULL, // Sentinal to mark the array end. }; @@ -382,15 +389,10 @@ void _registerSearchPaths(PKVM* vm) { buff[length++] = last; } - buff[length] = '\0'; - - pkAddSearchPath(vm, buff); - // FIXME: the bellow code is hard coded. - if (length + strlen("libs/") < MAX_PATH_LEN) { - strcpy(buff + length, (ps == CWK_STYLE_WINDOWS) ? "libs\\" : "libs/"); - pkAddSearchPath(vm, buff); - } + ASSERT(length + strlen("libs/") < MAX_PATH_LEN, OOPS); + strcpy(buff + length, (ps == CWK_STYLE_WINDOWS) ? "libs\\" : "libs/"); + pkAddSearchPath(vm, buff); } void registerModulePath(PKVM* vm) { diff --git a/tests/native/dl/README.md b/tests/native/dl/README.md new file mode 100644 index 0000000..0a4c597 --- /dev/null +++ b/tests/native/dl/README.md @@ -0,0 +1,14 @@ +## Example on how compile a native extension. + +#### MSVC +``` +cl /LD mylib.c pknative.c /I../../../src/include/ +rm *.obj *.exp *.lib +``` + +#### GCC +``` +gcc -fPIC -c mylib.c pknative.c -I../../../src/include/ +gcc -shared -o mylib.so mylib.o pknative.o +rm *.o +``` diff --git a/tests/native/dl/main.pk b/tests/native/dl/main.pk new file mode 100644 index 0000000..319b0ea --- /dev/null +++ b/tests/native/dl/main.pk @@ -0,0 +1,9 @@ + +## Import the native extension module. +## from either mylib.so, or mylib.dll +import mylib + +if __name__ == "__main__" + ## Call the registered function. + print('mylib.hello() = ${mylib.hello()}') +end diff --git a/tests/native/dl/mylib.c b/tests/native/dl/mylib.c new file mode 100644 index 0000000..1697d03 --- /dev/null +++ b/tests/native/dl/mylib.c @@ -0,0 +1,14 @@ + +#include + +PK_EXPORT void hello(PKVM* vm) { + pkSetSlotString(vm, 0, "hello from dynamic lib."); +} + +PK_EXPORT PkHandle* pkExportModule(PKVM* vm) { + PkHandle* mylib = pkNewModule(vm, "mylib"); + + pkModuleAddFunction(vm, mylib, "hello", hello, 0); + + return mylib; +} diff --git a/tests/native/dl/pknative.c b/tests/native/dl/pknative.c new file mode 100644 index 0000000..b2c86be --- /dev/null +++ b/tests/native/dl/pknative.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2020-2022 Thakee Nathees + * Copyright (c) 2021-2022 Pocketlang Contributors + * Distributed Under The MIT License + */ + +#include + +// !! THIS FILE IS GENERATED DO NOT EDIT !! + +typedef PkConfiguration (*pkNewConfiguration_t)(); +typedef PKVM* (*pkNewVM_t)(PkConfiguration*); +typedef void (*pkFreeVM_t)(PKVM*); +typedef void (*pkSetUserData_t)(PKVM*, void*); +typedef void* (*pkGetUserData_t)(const PKVM*); +typedef void (*pkRegisterBuiltinFn_t)(PKVM*, const char*, pkNativeFn, int, const char*); +typedef void (*pkAddSearchPath_t)(PKVM*, const char*); +typedef void* (*pkRealloc_t)(PKVM*, void*, size_t); +typedef void (*pkReleaseHandle_t)(PKVM*, PkHandle*); +typedef PkHandle* (*pkNewModule_t)(PKVM*, const char*); +typedef void (*pkRegisterModule_t)(PKVM*, PkHandle*); +typedef void (*pkModuleAddFunction_t)(PKVM*, PkHandle*, const char*, pkNativeFn, int); +typedef PkHandle* (*pkNewClass_t)(PKVM*, const char*, PkHandle*, PkHandle*, pkNewInstanceFn, pkDeleteInstanceFn); +typedef void (*pkClassAddMethod_t)(PKVM*, PkHandle*, const char*, pkNativeFn, int); +typedef void (*pkModuleAddSource_t)(PKVM*, PkHandle*, const char*); +typedef PkResult (*pkRunString_t)(PKVM*, const char*); +typedef PkResult (*pkRunFile_t)(PKVM*, const char*); +typedef PkResult (*pkRunREPL_t)(PKVM*); +typedef void (*pkSetRuntimeError_t)(PKVM*, const char*); +typedef void* (*pkGetSelf_t)(const PKVM*); +typedef int (*pkGetArgc_t)(const PKVM*); +typedef bool (*pkCheckArgcRange_t)(PKVM*, int, int, int); +typedef bool (*pkValidateSlotBool_t)(PKVM*, int, bool*); +typedef bool (*pkValidateSlotNumber_t)(PKVM*, int, double*); +typedef bool (*pkValidateSlotInteger_t)(PKVM*, int, int32_t*); +typedef bool (*pkValidateSlotString_t)(PKVM*, int, const char**, uint32_t*); +typedef bool (*pkValidateSlotType_t)(PKVM*, int, PkVarType); +typedef bool (*pkValidateSlotInstanceOf_t)(PKVM*, int, int); +typedef bool (*pkIsSlotInstanceOf_t)(PKVM*, int, int, bool*); +typedef void (*pkReserveSlots_t)(PKVM*, int); +typedef int (*pkGetSlotsCount_t)(PKVM*); +typedef PkVarType (*pkGetSlotType_t)(PKVM*, int); +typedef bool (*pkGetSlotBool_t)(PKVM*, int); +typedef double (*pkGetSlotNumber_t)(PKVM*, int); +typedef const char* (*pkGetSlotString_t)(PKVM*, int, uint32_t*); +typedef PkHandle* (*pkGetSlotHandle_t)(PKVM*, int); +typedef void* (*pkGetSlotNativeInstance_t)(PKVM*, int); +typedef void (*pkSetSlotNull_t)(PKVM*, int); +typedef void (*pkSetSlotBool_t)(PKVM*, int, bool); +typedef void (*pkSetSlotNumber_t)(PKVM*, int, double); +typedef void (*pkSetSlotString_t)(PKVM*, int, const char*); +typedef void (*pkSetSlotStringLength_t)(PKVM*, int, const char*, uint32_t); +typedef void (*pkSetSlotHandle_t)(PKVM*, int, PkHandle*); +typedef void (*pkPlaceSelf_t)(PKVM*, int); +typedef void (*pkGetClass_t)(PKVM*, int, int); +typedef bool (*pkNewInstance_t)(PKVM*, int, int, int, int); +typedef void (*pkNewRange_t)(PKVM*, int, double, double); +typedef void (*pkNewList_t)(PKVM*, int); +typedef void (*pkNewMap_t)(PKVM*, int); +typedef bool (*pkListInsert_t)(PKVM*, int, int32_t, int); +typedef bool (*pkListPop_t)(PKVM*, int, int32_t, int); +typedef uint32_t (*pkListLength_t)(PKVM*, int); +typedef bool (*pkCallFunction_t)(PKVM*, int, int, int, int); +typedef bool (*pkCallMethod_t)(PKVM*, int, const char*, int, int, int); +typedef bool (*pkGetAttribute_t)(PKVM*, int, const char*, int); +typedef bool (*pkSetAttribute_t)(PKVM*, int, const char*, int); +typedef bool (*pkImportModule_t)(PKVM*, const char*, int); + +typedef struct { + pkNewConfiguration_t pkNewConfiguration_ptr; + pkNewVM_t pkNewVM_ptr; + pkFreeVM_t pkFreeVM_ptr; + pkSetUserData_t pkSetUserData_ptr; + pkGetUserData_t pkGetUserData_ptr; + pkRegisterBuiltinFn_t pkRegisterBuiltinFn_ptr; + pkAddSearchPath_t pkAddSearchPath_ptr; + pkRealloc_t pkRealloc_ptr; + pkReleaseHandle_t pkReleaseHandle_ptr; + pkNewModule_t pkNewModule_ptr; + pkRegisterModule_t pkRegisterModule_ptr; + pkModuleAddFunction_t pkModuleAddFunction_ptr; + pkNewClass_t pkNewClass_ptr; + pkClassAddMethod_t pkClassAddMethod_ptr; + pkModuleAddSource_t pkModuleAddSource_ptr; + pkRunString_t pkRunString_ptr; + pkRunFile_t pkRunFile_ptr; + pkRunREPL_t pkRunREPL_ptr; + pkSetRuntimeError_t pkSetRuntimeError_ptr; + pkGetSelf_t pkGetSelf_ptr; + pkGetArgc_t pkGetArgc_ptr; + pkCheckArgcRange_t pkCheckArgcRange_ptr; + pkValidateSlotBool_t pkValidateSlotBool_ptr; + pkValidateSlotNumber_t pkValidateSlotNumber_ptr; + pkValidateSlotInteger_t pkValidateSlotInteger_ptr; + pkValidateSlotString_t pkValidateSlotString_ptr; + pkValidateSlotType_t pkValidateSlotType_ptr; + pkValidateSlotInstanceOf_t pkValidateSlotInstanceOf_ptr; + pkIsSlotInstanceOf_t pkIsSlotInstanceOf_ptr; + pkReserveSlots_t pkReserveSlots_ptr; + pkGetSlotsCount_t pkGetSlotsCount_ptr; + pkGetSlotType_t pkGetSlotType_ptr; + pkGetSlotBool_t pkGetSlotBool_ptr; + pkGetSlotNumber_t pkGetSlotNumber_ptr; + pkGetSlotString_t pkGetSlotString_ptr; + pkGetSlotHandle_t pkGetSlotHandle_ptr; + pkGetSlotNativeInstance_t pkGetSlotNativeInstance_ptr; + pkSetSlotNull_t pkSetSlotNull_ptr; + pkSetSlotBool_t pkSetSlotBool_ptr; + pkSetSlotNumber_t pkSetSlotNumber_ptr; + pkSetSlotString_t pkSetSlotString_ptr; + pkSetSlotStringLength_t pkSetSlotStringLength_ptr; + pkSetSlotHandle_t pkSetSlotHandle_ptr; + pkPlaceSelf_t pkPlaceSelf_ptr; + pkGetClass_t pkGetClass_ptr; + pkNewInstance_t pkNewInstance_ptr; + pkNewRange_t pkNewRange_ptr; + pkNewList_t pkNewList_ptr; + pkNewMap_t pkNewMap_ptr; + pkListInsert_t pkListInsert_ptr; + pkListPop_t pkListPop_ptr; + pkListLength_t pkListLength_ptr; + pkCallFunction_t pkCallFunction_ptr; + pkCallMethod_t pkCallMethod_ptr; + pkGetAttribute_t pkGetAttribute_ptr; + pkSetAttribute_t pkSetAttribute_ptr; + pkImportModule_t pkImportModule_ptr; +} PkNativeApi; + +static PkNativeApi pk_api; + +PK_EXPORT void pkInitApi(PkNativeApi* api) { + pk_api.pkNewConfiguration_ptr = api->pkNewConfiguration_ptr; + pk_api.pkNewVM_ptr = api->pkNewVM_ptr; + pk_api.pkFreeVM_ptr = api->pkFreeVM_ptr; + pk_api.pkSetUserData_ptr = api->pkSetUserData_ptr; + pk_api.pkGetUserData_ptr = api->pkGetUserData_ptr; + pk_api.pkRegisterBuiltinFn_ptr = api->pkRegisterBuiltinFn_ptr; + pk_api.pkAddSearchPath_ptr = api->pkAddSearchPath_ptr; + pk_api.pkRealloc_ptr = api->pkRealloc_ptr; + pk_api.pkReleaseHandle_ptr = api->pkReleaseHandle_ptr; + pk_api.pkNewModule_ptr = api->pkNewModule_ptr; + pk_api.pkRegisterModule_ptr = api->pkRegisterModule_ptr; + pk_api.pkModuleAddFunction_ptr = api->pkModuleAddFunction_ptr; + pk_api.pkNewClass_ptr = api->pkNewClass_ptr; + pk_api.pkClassAddMethod_ptr = api->pkClassAddMethod_ptr; + pk_api.pkModuleAddSource_ptr = api->pkModuleAddSource_ptr; + pk_api.pkRunString_ptr = api->pkRunString_ptr; + pk_api.pkRunFile_ptr = api->pkRunFile_ptr; + pk_api.pkRunREPL_ptr = api->pkRunREPL_ptr; + pk_api.pkSetRuntimeError_ptr = api->pkSetRuntimeError_ptr; + pk_api.pkGetSelf_ptr = api->pkGetSelf_ptr; + pk_api.pkGetArgc_ptr = api->pkGetArgc_ptr; + pk_api.pkCheckArgcRange_ptr = api->pkCheckArgcRange_ptr; + pk_api.pkValidateSlotBool_ptr = api->pkValidateSlotBool_ptr; + pk_api.pkValidateSlotNumber_ptr = api->pkValidateSlotNumber_ptr; + pk_api.pkValidateSlotInteger_ptr = api->pkValidateSlotInteger_ptr; + pk_api.pkValidateSlotString_ptr = api->pkValidateSlotString_ptr; + pk_api.pkValidateSlotType_ptr = api->pkValidateSlotType_ptr; + pk_api.pkValidateSlotInstanceOf_ptr = api->pkValidateSlotInstanceOf_ptr; + pk_api.pkIsSlotInstanceOf_ptr = api->pkIsSlotInstanceOf_ptr; + pk_api.pkReserveSlots_ptr = api->pkReserveSlots_ptr; + pk_api.pkGetSlotsCount_ptr = api->pkGetSlotsCount_ptr; + pk_api.pkGetSlotType_ptr = api->pkGetSlotType_ptr; + pk_api.pkGetSlotBool_ptr = api->pkGetSlotBool_ptr; + pk_api.pkGetSlotNumber_ptr = api->pkGetSlotNumber_ptr; + pk_api.pkGetSlotString_ptr = api->pkGetSlotString_ptr; + pk_api.pkGetSlotHandle_ptr = api->pkGetSlotHandle_ptr; + pk_api.pkGetSlotNativeInstance_ptr = api->pkGetSlotNativeInstance_ptr; + pk_api.pkSetSlotNull_ptr = api->pkSetSlotNull_ptr; + pk_api.pkSetSlotBool_ptr = api->pkSetSlotBool_ptr; + pk_api.pkSetSlotNumber_ptr = api->pkSetSlotNumber_ptr; + pk_api.pkSetSlotString_ptr = api->pkSetSlotString_ptr; + pk_api.pkSetSlotStringLength_ptr = api->pkSetSlotStringLength_ptr; + pk_api.pkSetSlotHandle_ptr = api->pkSetSlotHandle_ptr; + pk_api.pkPlaceSelf_ptr = api->pkPlaceSelf_ptr; + pk_api.pkGetClass_ptr = api->pkGetClass_ptr; + pk_api.pkNewInstance_ptr = api->pkNewInstance_ptr; + pk_api.pkNewRange_ptr = api->pkNewRange_ptr; + pk_api.pkNewList_ptr = api->pkNewList_ptr; + pk_api.pkNewMap_ptr = api->pkNewMap_ptr; + pk_api.pkListInsert_ptr = api->pkListInsert_ptr; + pk_api.pkListPop_ptr = api->pkListPop_ptr; + pk_api.pkListLength_ptr = api->pkListLength_ptr; + pk_api.pkCallFunction_ptr = api->pkCallFunction_ptr; + pk_api.pkCallMethod_ptr = api->pkCallMethod_ptr; + pk_api.pkGetAttribute_ptr = api->pkGetAttribute_ptr; + pk_api.pkSetAttribute_ptr = api->pkSetAttribute_ptr; + pk_api.pkImportModule_ptr = api->pkImportModule_ptr; +} + +PkConfiguration pkNewConfiguration() { + return pk_api.pkNewConfiguration_ptr(); +} + +PKVM* pkNewVM(PkConfiguration* config) { + return pk_api.pkNewVM_ptr(config); +} + +void pkFreeVM(PKVM* vm) { + pk_api.pkFreeVM_ptr(vm); +} + +void pkSetUserData(PKVM* vm, void* user_data) { + pk_api.pkSetUserData_ptr(vm, user_data); +} + +void* pkGetUserData(const PKVM* vm) { + return pk_api.pkGetUserData_ptr(vm); +} + +void pkRegisterBuiltinFn(PKVM* vm, const char* name, pkNativeFn fn, int arity, const char* docstring) { + pk_api.pkRegisterBuiltinFn_ptr(vm, name, fn, arity, docstring); +} + +void pkAddSearchPath(PKVM* vm, const char* path) { + pk_api.pkAddSearchPath_ptr(vm, path); +} + +void* pkRealloc(PKVM* vm, void* ptr, size_t size) { + return pk_api.pkRealloc_ptr(vm, ptr, size); +} + +void pkReleaseHandle(PKVM* vm, PkHandle* handle) { + pk_api.pkReleaseHandle_ptr(vm, handle); +} + +PkHandle* pkNewModule(PKVM* vm, const char* name) { + return pk_api.pkNewModule_ptr(vm, name); +} + +void pkRegisterModule(PKVM* vm, PkHandle* module) { + pk_api.pkRegisterModule_ptr(vm, module); +} + +void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, pkNativeFn fptr, int arity) { + pk_api.pkModuleAddFunction_ptr(vm, module, name, fptr, arity); +} + +PkHandle* pkNewClass(PKVM* vm, const char* name, PkHandle* base_class, PkHandle* module, pkNewInstanceFn new_fn, pkDeleteInstanceFn delete_fn) { + return pk_api.pkNewClass_ptr(vm, name, base_class, module, new_fn, delete_fn); +} + +void pkClassAddMethod(PKVM* vm, PkHandle* cls, const char* name, pkNativeFn fptr, int arity) { + pk_api.pkClassAddMethod_ptr(vm, cls, name, fptr, arity); +} + +void pkModuleAddSource(PKVM* vm, PkHandle* module, const char* source) { + pk_api.pkModuleAddSource_ptr(vm, module, source); +} + +PkResult pkRunString(PKVM* vm, const char* source) { + return pk_api.pkRunString_ptr(vm, source); +} + +PkResult pkRunFile(PKVM* vm, const char* path) { + return pk_api.pkRunFile_ptr(vm, path); +} + +PkResult pkRunREPL(PKVM* vm) { + return pk_api.pkRunREPL_ptr(vm); +} + +void pkSetRuntimeError(PKVM* vm, const char* message) { + pk_api.pkSetRuntimeError_ptr(vm, message); +} + +void* pkGetSelf(const PKVM* vm) { + return pk_api.pkGetSelf_ptr(vm); +} + +int pkGetArgc(const PKVM* vm) { + return pk_api.pkGetArgc_ptr(vm); +} + +bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { + return pk_api.pkCheckArgcRange_ptr(vm, argc, min, max); +} + +bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) { + return pk_api.pkValidateSlotBool_ptr(vm, slot, value); +} + +bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) { + return pk_api.pkValidateSlotNumber_ptr(vm, slot, value); +} + +bool pkValidateSlotInteger(PKVM* vm, int slot, int32_t* value) { + return pk_api.pkValidateSlotInteger_ptr(vm, slot, value); +} + +bool pkValidateSlotString(PKVM* vm, int slot, const char** value, uint32_t* length) { + return pk_api.pkValidateSlotString_ptr(vm, slot, value, length); +} + +bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type) { + return pk_api.pkValidateSlotType_ptr(vm, slot, type); +} + +bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) { + return pk_api.pkValidateSlotInstanceOf_ptr(vm, slot, cls); +} + +bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) { + return pk_api.pkIsSlotInstanceOf_ptr(vm, inst, cls, val); +} + +void pkReserveSlots(PKVM* vm, int count) { + pk_api.pkReserveSlots_ptr(vm, count); +} + +int pkGetSlotsCount(PKVM* vm) { + return pk_api.pkGetSlotsCount_ptr(vm); +} + +PkVarType pkGetSlotType(PKVM* vm, int index) { + return pk_api.pkGetSlotType_ptr(vm, index); +} + +bool pkGetSlotBool(PKVM* vm, int index) { + return pk_api.pkGetSlotBool_ptr(vm, index); +} + +double pkGetSlotNumber(PKVM* vm, int index) { + return pk_api.pkGetSlotNumber_ptr(vm, index); +} + +const char* pkGetSlotString(PKVM* vm, int index, uint32_t* length) { + return pk_api.pkGetSlotString_ptr(vm, index, length); +} + +PkHandle* pkGetSlotHandle(PKVM* vm, int index) { + return pk_api.pkGetSlotHandle_ptr(vm, index); +} + +void* pkGetSlotNativeInstance(PKVM* vm, int index) { + return pk_api.pkGetSlotNativeInstance_ptr(vm, index); +} + +void pkSetSlotNull(PKVM* vm, int index) { + pk_api.pkSetSlotNull_ptr(vm, index); +} + +void pkSetSlotBool(PKVM* vm, int index, bool value) { + pk_api.pkSetSlotBool_ptr(vm, index, value); +} + +void pkSetSlotNumber(PKVM* vm, int index, double value) { + pk_api.pkSetSlotNumber_ptr(vm, index, value); +} + +void pkSetSlotString(PKVM* vm, int index, const char* value) { + pk_api.pkSetSlotString_ptr(vm, index, value); +} + +void pkSetSlotStringLength(PKVM* vm, int index, const char* value, uint32_t length) { + pk_api.pkSetSlotStringLength_ptr(vm, index, value, length); +} + +void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) { + pk_api.pkSetSlotHandle_ptr(vm, index, handle); +} + +void pkPlaceSelf(PKVM* vm, int index) { + pk_api.pkPlaceSelf_ptr(vm, index); +} + +void pkGetClass(PKVM* vm, int instance, int index) { + pk_api.pkGetClass_ptr(vm, instance, index); +} + +bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv) { + return pk_api.pkNewInstance_ptr(vm, cls, index, argc, argv); +} + +void pkNewRange(PKVM* vm, int index, double first, double last) { + pk_api.pkNewRange_ptr(vm, index, first, last); +} + +void pkNewList(PKVM* vm, int index) { + pk_api.pkNewList_ptr(vm, index); +} + +void pkNewMap(PKVM* vm, int index) { + pk_api.pkNewMap_ptr(vm, index); +} + +bool pkListInsert(PKVM* vm, int list, int32_t index, int value) { + return pk_api.pkListInsert_ptr(vm, list, index, value); +} + +bool pkListPop(PKVM* vm, int list, int32_t index, int popped) { + return pk_api.pkListPop_ptr(vm, list, index, popped); +} + +uint32_t pkListLength(PKVM* vm, int list) { + return pk_api.pkListLength_ptr(vm, list); +} + +bool pkCallFunction(PKVM* vm, int fn, int argc, int argv, int ret) { + return pk_api.pkCallFunction_ptr(vm, fn, argc, argv, ret); +} + +bool pkCallMethod(PKVM* vm, int instance, const char* method, int argc, int argv, int ret) { + return pk_api.pkCallMethod_ptr(vm, instance, method, argc, argv, ret); +} + +bool pkGetAttribute(PKVM* vm, int instance, const char* name, int index) { + return pk_api.pkGetAttribute_ptr(vm, instance, name, index); +} + +bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) { + return pk_api.pkSetAttribute_ptr(vm, instance, name, value); +} + +bool pkImportModule(PKVM* vm, const char* path, int index) { + return pk_api.pkImportModule_ptr(vm, path, index); +} diff --git a/tests/native/example0.c b/tests/native/example0.c index 0fc0735..b70dd47 100644 --- a/tests/native/example0.c +++ b/tests/native/example0.c @@ -13,12 +13,8 @@ int main(int argc, char** argv) { // Run a string. pkRunString(vm, "print('hello world')"); - // TODO: move path module to src/ or write a simple path resolving - // function to support pkRunFile() without requesting someone - // to provide path resolving function. - // - // Run a script from file. - //pkRunFile(vm, "script.pk"); + // Run from file. + pkRunFile(vm, "script.pk"); // Free the VM. pkFreeVM(vm); diff --git a/tests/native/script.pk b/tests/native/script.pk new file mode 100644 index 0000000..d5134fb --- /dev/null +++ b/tests/native/script.pk @@ -0,0 +1,2 @@ + +print('hello from pocket script')