Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
## Description
`dl` can be used to Dynamically Load a library during runtime.
It is a thin wrapper over `LoadLibrary` on Windows, and `dlopen` on Unix.
Using it, you can implement a plugin system for your application.
> **Note**
> We highly recommend using `dl.loader` instead of `dl` directly.
> It provides a more user-friendly API in the V way.
+48
View File
@@ -0,0 +1,48 @@
module dl
pub const version = 1
pub const dl_ext = get_shared_library_extension()
pub const rtld_next = voidptr(-1)
// get_shared_library_extension returns the platform dependent shared library extension
// i.e. .dll on windows, .so on most unixes, .dylib on macos.
@[inline]
pub fn get_shared_library_extension() string {
return $if windows {
'.dll'
} $else $if macos {
'.dylib'
} $else {
'.so'
}
}
// get_libname returns a library name with the operating system specific extension for
// shared libraries.
@[inline]
pub fn get_libname(libname string) string {
return '${libname}${dl_ext}'
}
// open_opt tries to load a given dynamic shared object.
pub fn open_opt(filename string, flags int) !voidptr {
shared_object_handle := open(filename, flags)
if shared_object_handle == 0 {
e := dlerror()
return error(e)
}
return shared_object_handle
}
// sym_opt returns the address of a symbol in a given shared object, if found.
// Unlike sym, sym_opt returns an option.
pub fn sym_opt(shared_object_handle voidptr, symbol string) !voidptr {
sym_handle := sym(shared_object_handle, symbol)
if sym_handle == 0 {
e := dlerror()
return error(e)
}
return sym_handle
}
+49
View File
@@ -0,0 +1,49 @@
module dl
#include <dlfcn.h>
$if linux {
#flag -ldl
}
pub const rtld_now = C.RTLD_NOW
pub const rtld_lazy = C.RTLD_LAZY
pub const rtld_global = C.RTLD_GLOBAL
pub const rtld_local = C.RTLD_LOCAL
pub const rtld_nodelete = C.RTLD_NODELETE
pub const rtld_noload = C.RTLD_NOLOAD
fn C.dlopen(filename &char, flags i32) voidptr
fn C.dlsym(handle voidptr, symbol &char) voidptr
fn C.dlclose(handle voidptr) i32
fn C.dlerror() &char
// open loads a given dynamic shared object.
pub fn open(filename string, flags int) voidptr {
return C.dlopen(&char(filename.str), flags)
}
// close frees a given shared object.
pub fn close(handle voidptr) bool {
return C.dlclose(handle) == 0
}
// sym returns an address of a symbol in a given shared object.
pub fn sym(handle voidptr, symbol string) voidptr {
return C.dlsym(handle, &char(symbol.str))
}
// dlerror provides a text error diagnostic message for functions in `dl`.
// It returns a human-readable string, describing the most recent error
// that occurred from a call to one of the `dl` functions, since the last
// call to dlerror().
pub fn dlerror() string {
sptr := C.dlerror()
if sptr == unsafe { nil } {
return ''
}
return unsafe { cstring_to_vstring(sptr) }
}
+46
View File
@@ -0,0 +1,46 @@
import dl
fn test_dl() {
$if linux {
run_test_invalid_lib_linux()
return
}
$if windows {
run_test_invalid_lib_windows()
run_test_valid_lib_windows()
run_test_invalid_sym_windows()
run_test_valid_sym_windows()
return
} $else {
eprint('currently not implemented on this platform')
}
}
fn run_test_invalid_lib_linux() {
// ensure a not-existing dl won't be loaded
h := dl.open('not-existing-dynamic-link-library', dl.rtld_now)
assert h == 0
}
fn run_test_invalid_lib_windows() {
// ensure a not-existing dl won't be loaded
h := dl.open('not-existing-dynamic-link-library', dl.rtld_now)
assert h == 0
}
fn run_test_valid_lib_windows() {
h := dl.open('shell32', dl.rtld_now)
assert h != 0
}
fn run_test_invalid_sym_windows() {
h := dl.open('shell32', dl.rtld_now)
proc := dl.sym(h, 'CommandLineToArgvW2')
assert proc == 0
}
fn run_test_valid_sym_windows() {
h := dl.open('shell32', dl.rtld_now)
proc := dl.sym(h, 'CommandLineToArgvW')
assert proc != 0
}
+45
View File
@@ -0,0 +1,45 @@
module dl
pub const rtld_now = 0
pub const rtld_lazy = 0
pub const rtld_global = 0
pub const rtld_local = 0
pub const rtld_nodelete = 0
pub const rtld_noload = 0
fn C.LoadLibrary(libfilename &u16) voidptr
fn C.GetProcAddress(handle voidptr, procname &u8) voidptr
fn C.FreeLibrary(handle voidptr) bool
type FN_vinit_caller = fn ()
type FN_vcleanup_caller = fn ()
// open loads a given module into the address space of the calling process.
pub fn open(filename string, flags int) voidptr {
res := C.LoadLibrary(filename.to_wide())
return res
}
// close frees the loaded a given module.
pub fn close(handle voidptr) bool {
return C.FreeLibrary(handle)
}
// sym returns an address of an exported function or variable from a given module.
pub fn sym(handle voidptr, symbol string) voidptr {
return C.GetProcAddress(handle, voidptr(symbol.str))
}
// dlerror provides a text error diagnostic message for functions in `dl`.
// It returns a human-readable string, describing the most recent error
// that occurred from a call to one of the `dl` functions, since the last
// call to dlerror().
pub fn dlerror() string {
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
// Unlike dlerror(), GetLastError returns just an error code, that is function specific.
cerr := int(C.GetLastError())
return 'error code ${cerr}'
}
+36
View File
@@ -0,0 +1,36 @@
## Description
`dl.loader` is an abstraction layer over `dl` that provides a more user-friendly API in the V way.
It can be used to Dynamically Load a library during runtime in scenarios where the library to load
does not have a determined path and can be located in different places.
It also provides a way to load a library from a specific path, or from a list of paths, or from
a custom environment variable that contains a list of paths.
## Usage
```v
import dl.loader
// Load a library from a list of paths
const default_paths = [
'not-existing-dynamic-link-library'
// 'C:\\Windows\\System32\\shell32.dll',
'shell32',
]
fn main() {
mut dl_loader := loader.get_or_create_dynamic_lib_loader(
key: 'LibExample'
env_path: 'LIB_PATH'
paths: default_paths
)!
defer {
dl_loader.unregister()
}
sym := dl_loader.get_sym('CommandLineToArgvW')!
assert !isnil(sym)
}
```
+150
View File
@@ -0,0 +1,150 @@
@[has_globals]
module loader
import dl
import os
const dl_no_path_issue_msg = 'no paths to dynamic library'
const dl_open_issue_msg = 'could not open dynamic library'
const dl_sym_issue_msg = 'could not get optional symbol from dynamic library'
const dl_close_issue_msg = 'could not close dynamic library'
const dl_register_issue_msg = 'could not register dynamic library loader'
pub const dl_no_path_issue_code = 1
pub const dl_open_issue_code = 1
pub const dl_sym_issue_code = 2
pub const dl_close_issue_code = 3
pub const dl_register_issue_code = 4
pub const dl_no_path_issue_err = error_with_code(dl_no_path_issue_msg, dl_no_path_issue_code)
pub const dl_open_issue_err = error_with_code(dl_open_issue_msg, dl_open_issue_code)
pub const dl_sym_issue_err = error_with_code(dl_sym_issue_msg, dl_sym_issue_code)
pub const dl_close_issue_err = error_with_code(dl_close_issue_msg, dl_close_issue_code)
pub const dl_register_issue_err = error_with_code(dl_register_issue_msg, dl_register_issue_code)
__global (
registered_dl_loaders map[string]&DynamicLibLoader
)
fn register_dl_loader(dl_loader &DynamicLibLoader) ! {
if dl_loader.key in registered_dl_loaders {
return dl_register_issue_err
}
registered_dl_loaders[dl_loader.key] = dl_loader
}
// registered_dl_loader_keys returns the keys of registered DynamicLibLoader.
pub fn registered_dl_loader_keys() []string {
return registered_dl_loaders.keys()
}
// DynamicLibLoader is a wrapper around dlopen, dlsym and dlclose.
@[heap]
pub struct DynamicLibLoader {
pub:
key string
flags int = dl.rtld_lazy
paths []string
mut:
handle voidptr
sym_map map[string]voidptr
}
// DynamicLibLoaderConfig is a configuration for DynamicLibLoader.
@[params]
pub struct DynamicLibLoaderConfig {
pub:
// flags is the flags for dlopen.
flags int = dl.rtld_lazy
// key is the key to register the DynamicLibLoader.
key string
// env_path is the environment variable name that contains the path to the dynamic library.
env_path string
// paths is the list of paths to the dynamic library.
paths []string
}
// new_dynamic_lib_loader returns a new DynamicLibLoader.
fn new_dynamic_lib_loader(conf DynamicLibLoaderConfig) !&DynamicLibLoader {
mut paths := []string{}
if conf.env_path != '' {
if env_path := os.getenv_opt(conf.env_path) {
paths << env_path.split(os.path_delimiter)
}
}
paths << conf.paths
if paths.len == 0 {
return dl_no_path_issue_err
}
mut dl_loader := &DynamicLibLoader{
key: conf.key
flags: conf.flags
paths: paths
}
register_dl_loader(dl_loader)!
return dl_loader
}
// get_or_create_dynamic_lib_loader returns a DynamicLibLoader.
// If the DynamicLibLoader is not registered, it creates a new DynamicLibLoader.
pub fn get_or_create_dynamic_lib_loader(conf DynamicLibLoaderConfig) !&DynamicLibLoader {
if dl_loader := registered_dl_loaders[conf.key] {
return dl_loader
}
return new_dynamic_lib_loader(conf)
}
// load loads the dynamic library.
pub fn (mut dl_loader DynamicLibLoader) open() !voidptr {
if !isnil(dl_loader.handle) {
return dl_loader.handle
}
for path in dl_loader.paths {
if handle := dl.open_opt(path, dl_loader.flags) {
dl_loader.handle = handle
return handle
}
}
return dl_open_issue_err
}
// close closes the dynamic library.
pub fn (mut dl_loader DynamicLibLoader) close() ! {
if !isnil(dl_loader.handle) {
if dl.close(dl_loader.handle) {
dl_loader.handle = unsafe { nil }
return
}
}
return dl_close_issue_err
}
// get_sym gets a symbol from the dynamic library.
pub fn (mut dl_loader DynamicLibLoader) get_sym(name string) !voidptr {
if sym := dl_loader.sym_map[name] {
return sym
}
handle := dl_loader.open()!
if sym := dl.sym_opt(handle, name) {
dl_loader.sym_map[name] = sym
return sym
}
dl_loader.close()!
return dl_sym_issue_err
}
// unregister unregisters the DynamicLibLoader.
pub fn (mut dl_loader DynamicLibLoader) unregister() {
dl_loader.close() or {}
registered_dl_loaders.delete(dl_loader.key)
}
+83
View File
@@ -0,0 +1,83 @@
import dl.loader
import dl
fn test_dl_loader() ! {
$if linux {
run_test_invalid_lib_linux()!
return
}
$if windows {
run_test_invalid_lib_windows()!
run_test_valid_lib_windows()!
run_test_invalid_sym_windows()!
run_test_valid_sym_windows()!
return
} $else {
eprint('currently not implemented on this platform')
}
}
fn get_or_create_loader(name string, paths []string) !&loader.DynamicLibLoader {
return loader.get_or_create_dynamic_lib_loader(
key: name
paths: paths
flags: dl.rtld_now
)
}
fn run_test_invalid_lib_linux() ! {
// ensure a not-existing dl won't be loaded
mut dl_loader := get_or_create_loader(@MOD + '.' + @FN + '.' + 'lib', [
'not-existing-dynamic-link-library',
])!
defer {
dl_loader.unregister()
}
h := dl_loader.open() or { unsafe { nil } }
assert isnil(h)
}
fn run_test_invalid_lib_windows() ! {
// ensure a not-existing dl won't be loaded
mut dl_loader := get_or_create_loader(@MOD + '.' + @FN + '.' + 'lib', [
'not-existing-dynamic-link-library',
])!
defer {
dl_loader.unregister()
}
h := dl_loader.open() or { unsafe { nil } }
assert isnil(h)
}
fn run_test_valid_lib_windows() ! {
mut dl_loader := get_or_create_loader(@MOD + '.' + @FN + '.' + 'lib', [
'not-existing-dynamic-link-library',
'shell32',
])!
defer {
dl_loader.unregister()
}
h := dl_loader.open() or { unsafe { nil } }
assert !isnil(h)
}
fn run_test_invalid_sym_windows() ! {
mut dl_loader := get_or_create_loader(@MOD + '.' + @FN + '.' + 'lib', ['shell32'])!
defer {
dl_loader.unregister()
}
proc := dl_loader.get_sym('CommandLineToArgvW2') or { unsafe { nil } }
assert isnil(proc)
}
fn run_test_valid_sym_windows() ! {
mut dl_loader := get_or_create_loader(@MOD + '.' + @FN + '.' + 'lib', [
'not-existing-dynamic-link-library',
'shell32',
])!
defer {
dl_loader.unregister()
}
proc := dl_loader.get_sym('CommandLineToArgvW') or { unsafe { nil } }
assert !isnil(proc)
}
+14
View File
@@ -0,0 +1,14 @@
import dl
type RealOpen = fn (charptr, int, int) int
fn test_rtld_next() {
$if windows {
println('skipping test_rtld_next on windows')
return
}
real_open := RealOpen((dl.sym(dl.rtld_next, 'open')))
println(ptr_str(real_open))
assert real_open != unsafe { nil }
assert dl.dlerror() == ''
}