From bbd4d1907b2e83c9c9471ad934532ea14f6ed678 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 20 Jun 2024 00:29:14 +0800 Subject: [PATCH] :art: optimize implementation of dlopen --- doc/tutorial.md | 6 ++-- doc/tutorial_zh.md | 7 ++--- makefile | 2 ++ module/libfib.nas | 2 +- module/libkey.nas | 2 +- module/libmat.nas | 2 +- module/libnasock.nas | 2 +- src/natives/dylib_lib.cpp | 66 +++++++++++++++++++++++++++++++++------ src/natives/dylib_lib.h | 5 +++ src/natives/io_lib.cpp | 2 +- src/natives/unix_lib.cpp | 2 +- std/dylib.nas | 16 ---------- test/module_test.nas | 2 +- 13 files changed, 76 insertions(+), 40 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 6188298..1b4cad7 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -639,11 +639,11 @@ Windows(`.dll`): `g++ -shared -o libfib.dll fib.o` -Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program: +Then we write a test nasal file to run this fib function: ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); @@ -660,7 +660,7 @@ dylib.dlclose(dlhandle.lib); ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter for(var i = 1; i<30; i += 1) diff --git a/doc/tutorial_zh.md b/doc/tutorial_zh.md index 62910ad..bc2220a 100644 --- a/doc/tutorial_zh.md +++ b/doc/tutorial_zh.md @@ -618,12 +618,11 @@ Windows(`.dll`): `g++ -shared -o libfib.dll fib.o` -好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。 -下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: +好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了: ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); @@ -640,7 +639,7 @@ dylib.dlclose(dlhandle.lib); ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter for(var i = 1; i<30; i += 1) diff --git a/makefile b/makefile index 808bda3..dea3eef 100644 --- a/makefile +++ b/makefile @@ -189,6 +189,8 @@ build/dylib_lib.o: \ src/nasal.h\ src/nasal_type.h\ src/nasal_gc.h\ + src/util/util.h\ + src/util/fs.h\ src/natives/dylib_lib.h src/natives/dylib_lib.cpp | build $(CXX) $(CXXFLAGS) src/natives/dylib_lib.cpp -o build/dylib_lib.o diff --git a/module/libfib.nas b/module/libfib.nas index 9df5c2c..b5a2519 100644 --- a/module/libfib.nas +++ b/module/libfib.nas @@ -1,7 +1,7 @@ use std.dylib; use std.os; -var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var _dl = dylib.dlopen("libfib"); var _fib = _dl.fib; diff --git a/module/libkey.nas b/module/libkey.nas index 26f46da..3475100 100644 --- a/module/libkey.nas +++ b/module/libkey.nas @@ -6,7 +6,7 @@ var ( getch, nonblock ) = func { - var lib = dylib.dlopen("libkey"~(os.platform()=="windows"? ".dll":".so")); + var lib = dylib.dlopen("libkey"); var kb = lib.nas_kbhit; var gt = lib.nas_getch; var nb = lib.nas_noblock; diff --git a/module/libmat.nas b/module/libmat.nas index cbf02c9..fe7793f 100644 --- a/module/libmat.nas +++ b/module/libmat.nas @@ -1,7 +1,7 @@ use std.dylib; use std.os; -var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); +var _dl = dylib.dlopen("libmat"); var _vec2 = _dl.nas_vec2; diff --git a/module/libnasock.nas b/module/libnasock.nas index e686cf1..661226c 100644 --- a/module/libnasock.nas +++ b/module/libnasock.nas @@ -2,7 +2,7 @@ use std.dylib; use std.os; var socket = func() { - var lib = dylib.dlopen("libnasock"~(os.platform()=="windows"? ".dll":".so")); + var lib = dylib.dlopen("libnasock"); var sock = lib.nas_socket; var closesocket = lib.nas_closesocket; diff --git a/src/natives/dylib_lib.cpp b/src/natives/dylib_lib.cpp index 329d8ed..1af6fcf 100644 --- a/src/natives/dylib_lib.cpp +++ b/src/natives/dylib_lib.cpp @@ -1,9 +1,14 @@ #include "natives/dylib_lib.h" +#include "util/util.h" +#include "util/fs.h" + +#include +#include namespace nasal { -const auto dynamic_library_type_name = "dylib"; -const auto function_address_type_name = "faddr"; +const auto dynamic_library_type_name = "nasal::dynamic_library"; +const auto function_address_type_name = "nasal::function_address"; void dynamic_library_destructor(void* pointer) { #ifdef _WIN32 @@ -13,33 +18,74 @@ void dynamic_library_destructor(void* pointer) { #endif } +std::string search_dynamic_library_path(const std::string& dlname) { + const auto ext = (util::is_windows()? ".dll":".so"); + const auto lib_path = (util::is_windows()? ".\\":"./") + dlname + ext; + if (fs::exists(lib_path)) { + return lib_path; + } + const auto env_path = std::string(getenv("PATH")); + const auto sep = (util::is_windows()? ";":":"); + + // do split string + std::vector env_path_vec = {}; + usize last = 0; + usize pos = env_path.find(sep, 0); + while(pos!=std::string::npos) { + if (pos>last) { + env_path_vec.push_back(env_path.substr(last, pos-last)); + } + last = pos + 1; + pos = env_path.find(sep, last); + } + if (last!=env_path.length()) { + env_path_vec.push_back(env_path.substr(last)); + } + + const auto path_front = util::is_windows()? "\\module\\":"/module/"; + for(auto& p : env_path_vec) { + p += path_front + lib_path; + if (fs::exists(p)) { + return p; + } + } + return ""; +} + var builtin_dlopen(context* ctx, gc* ngc) { - auto dlname = ctx->localr[1]; - if (!dlname.is_str()) { + auto dl = ctx->localr[1]; + if (!dl.is_str()) { return nas_err("dylib::dlopen", "\"libname\" must be string"); } + const auto dlname = search_dynamic_library_path(dl.str()); + if (dlname.empty()) { + return nas_err("dylib::dlopen", + "cannot find dynamic lib <" + dl.str() + ">" + ); + } + // get library pointer #ifdef _WIN32 - wchar_t* wide_string = new wchar_t[dlname.str().size()+1]; + wchar_t* wide_string = new wchar_t[dlname.size()+1]; if (!wide_string) { return nas_err("dylib::dlopen", "malloc failed"); } - memset(wide_string, 0, sizeof(wchar_t) * dlname.str().size() + 1); - mbstowcs(wide_string, dlname.str().c_str(), dlname.str().size() + 1); + memset(wide_string, 0, sizeof(wchar_t) * dlname.size() + 1); + mbstowcs(wide_string, dlname.c_str(), dlname.size() + 1); // load library by using wide string name - void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str()); + void* dynamic_library_pointer = LoadLibraryA(dlname.c_str()); delete []wide_string; #else void* dynamic_library_pointer = dlopen( - dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY + dlname.c_str(), RTLD_LOCAL|RTLD_LAZY ); #endif // check library pointer and insert into returned hashmap if (!dynamic_library_pointer) { return nas_err("dylib::dlopen", - "cannot open dynamic lib <" + dlname.str() + ">" + "cannot open dynamic lib <" + dl.str() + ">" ); } auto return_hash = ngc->temp = ngc->alloc(vm_type::vm_hash); diff --git a/src/natives/dylib_lib.h b/src/natives/dylib_lib.h index 3b49dea..67a9ef0 100644 --- a/src/natives/dylib_lib.h +++ b/src/natives/dylib_lib.h @@ -11,10 +11,15 @@ #include #endif +#include +#include + namespace nasal { void dynamic_library_destructor(void*); +std::string search_dynamic_library_path(const std::string&); + var builtin_dlopen(context*, gc*); var builtin_dlclose(context*, gc*); var builtin_dlcallv(context*, gc*); diff --git a/src/natives/io_lib.cpp b/src/natives/io_lib.cpp index 83f81dc..d7f0c8e 100644 --- a/src/natives/io_lib.cpp +++ b/src/natives/io_lib.cpp @@ -6,7 +6,7 @@ namespace nasal { -const auto file_type_name = "file"; +const auto file_type_name = "nasal::FILE"; void filehandle_destructor(void* ptr) { fclose(static_cast(ptr)); diff --git a/src/natives/unix_lib.cpp b/src/natives/unix_lib.cpp index a23ddd5..3296dab 100644 --- a/src/natives/unix_lib.cpp +++ b/src/natives/unix_lib.cpp @@ -2,7 +2,7 @@ namespace nasal { -const auto dir_type_name = "dir"; +const auto dir_type_name = "nasal::DIR"; void dir_entry_destructor(void* ptr) { #ifndef _MSC_VER diff --git a/std/dylib.nas b/std/dylib.nas index a63b0a0..1b8fb12 100644 --- a/std/dylib.nas +++ b/std/dylib.nas @@ -8,22 +8,6 @@ use std.unix; # open dynamic lib. return a hash including dl pointer and function pointers var dlopen = func(libname) { - # find dynamic lib from local dir first - libname = (os.platform()=="windows"? ".\\":"./")~libname; - if (io.exists(libname)) - return __dlopen(libname); - # find dynamic lib through PATH - var envpath = split(os.platform()=="windows"? ";":":", unix.getenv("PATH")); - # first find ./module - append(envpath, "."); - var path = os.platform()=="windows"? "\\module\\":"/module/"; - foreach(var p;envpath) { - p ~= path~libname; - if (io.exists(p)) { - libname = p; - break; - } - } return __dlopen(libname); } diff --git a/test/module_test.nas b/test/module_test.nas index 8c5ebdb..b64c3f3 100644 --- a/test/module_test.nas +++ b/test/module_test.nas @@ -23,7 +23,7 @@ func() { }(); var speed_test = func() { - var dd = dylib.dlopen("libfib."~(os.platform()=="windows"? "dll":"so")); + var dd = dylib.dlopen("libfib"); println("[dylib ] ", dd); var fd = dd.quick_fib; var vec_call = dylib.dlcall;