✨ improve function of ghost_register_table
This commit is contained in:
parent
774ad60c42
commit
7cc9ef436e
|
@ -9,6 +9,7 @@ set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
|||
|
||||
# generate release executables
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
||||
|
||||
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
|
||||
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
|
@ -25,3 +26,10 @@ target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR})
|
|||
add_executable(nasal main.cpp)
|
||||
target_link_libraries(nasal dl)
|
||||
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
|
||||
add_custom_command(
|
||||
TARGET nasal POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/build/nasal
|
||||
${CMAKE_SOURCE_DIR}/nasal
|
||||
)
|
|
@ -1,6 +1,10 @@
|
|||
// module for test
|
||||
|
||||
#include <iostream>
|
||||
#include "../nasal.h"
|
||||
|
||||
namespace nasal_fib_module {
|
||||
|
||||
double fibonaci(double x) {
|
||||
if (x<=2) {
|
||||
return x;
|
||||
|
@ -33,12 +37,56 @@ var quick_fib(var* args, usize size, gc* ngc) {
|
|||
return var::num(res);
|
||||
}
|
||||
|
||||
mod_func func_tbl[]={
|
||||
{"fib",fib},
|
||||
{"quick_fib",quick_fib},
|
||||
{nullptr, nullptr},
|
||||
u32 ghost_for_test;
|
||||
|
||||
void ghost_for_test_destructor(void* ptr) {
|
||||
std::cout<<"delete "<<ptr<<"\n";
|
||||
delete (u32*)ptr;
|
||||
}
|
||||
|
||||
var create_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res=ngc->alloc(vm_obj);
|
||||
res.obj().set(ghost_for_test, new u32);
|
||||
return res;
|
||||
}
|
||||
|
||||
var set_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res=args[0];
|
||||
if (!res.objchk(ghost_for_test)) {
|
||||
std::cout<<"set_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
std::cout<<"set_new_ghost: successfully set ghost.\n";
|
||||
f64 num=args[1].num();
|
||||
*((u32*)res.obj().ptr)=static_cast<u32>(num);
|
||||
return nil;
|
||||
}
|
||||
|
||||
var print_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res=args[0];
|
||||
if (!res.objchk(ghost_for_test)) {
|
||||
std::cout<<"print_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
std::cout<<"print_new_ghost: result = "<<*((u32*)res.obj().ptr)<<"\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
module_func_info func_tbl[]={
|
||||
{"fib", fib},
|
||||
{"quick_fib", quick_fib},
|
||||
{"create_ghost", create_new_ghost},
|
||||
{"set_ghost", set_new_ghost},
|
||||
{"print_ghost", print_new_ghost},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" mod_func* get(ghost_register_table* table) {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
extern "C" module_func_info* get(ghost_register_table* table) {
|
||||
nasal_fib_module::ghost_for_test=table->register_ghost_type(
|
||||
"fib_for_test",
|
||||
nasal_fib_module::ghost_for_test_destructor
|
||||
);
|
||||
return nasal_fib_module::func_tbl;
|
||||
}
|
|
@ -88,13 +88,13 @@ var nas_noblock(var* args, usize size, gc* ngc) {
|
|||
return nil;
|
||||
}
|
||||
|
||||
mod_func func_tbl[]={
|
||||
module_func_info func_tbl[]={
|
||||
{"nas_getch",nas_getch},
|
||||
{"nas_kbhit",nas_kbhit},
|
||||
{"nas_noblock",nas_noblock},
|
||||
{nullptr,nullptr}
|
||||
};
|
||||
|
||||
extern "C" mod_func* get(ghost_register_table* table) {
|
||||
extern "C" module_func_info* get(ghost_register_table* table) {
|
||||
return func_tbl;
|
||||
}
|
|
@ -2,9 +2,28 @@ var libfib=func(){
|
|||
var dl=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var fib=dl.fib;
|
||||
var qfib=dl.quick_fib;
|
||||
var create_ghost=dl.create_ghost;
|
||||
var set_ghost=dl.set_ghost;
|
||||
var print_ghost=dl.print_ghost;
|
||||
var zero_call=dylib.limitcall(0);
|
||||
var call=dylib.limitcall(1);
|
||||
return {
|
||||
fib: func(x){return call(fib,x)},
|
||||
qfib:func(x){return call(qfib,x)}
|
||||
var test_call=dylib.limitcall(2);
|
||||
var res={
|
||||
fib: func(x) {return call(fib,x)},
|
||||
qfib: func(x) {return call(qfib,x)},
|
||||
create_ghost: func() {return zero_call(create_ghost)},
|
||||
set_ghost: func(object, x) {return test_call(set_ghost, object, x)},
|
||||
print_ghost: func(object) {return call(print_ghost, object)}
|
||||
};
|
||||
|
||||
res.test_ghost=func() {
|
||||
var ghost=res.create_ghost();
|
||||
res.print_ghost(nil); # err
|
||||
res.print_ghost(ghost); # random
|
||||
res.set_ghost(nil, 114); # err
|
||||
res.set_ghost(ghost, 114); # success
|
||||
res.print_ghost(ghost); # 114
|
||||
}
|
||||
|
||||
return res;
|
||||
}();
|
|
@ -266,7 +266,7 @@ var nas_vec3_dot(var* args, usize size, gc* ngc) {
|
|||
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num()+v0[2].num()*v1[2].num());
|
||||
}
|
||||
|
||||
mod_func func_tbl[]={
|
||||
module_func_info func_tbl[]={
|
||||
{"nas_vec2",nas_vec2},
|
||||
{"nas_vec2_add",nas_vec2_add},
|
||||
{"nas_vec2_sub",nas_vec2_sub},
|
||||
|
@ -291,6 +291,6 @@ mod_func func_tbl[]={
|
|||
{nullptr,nullptr}
|
||||
};
|
||||
|
||||
extern "C" mod_func* get(ghost_register_table* table) {
|
||||
extern "C" module_func_info* get(ghost_register_table* table) {
|
||||
return func_tbl;
|
||||
}
|
|
@ -190,7 +190,7 @@ var nas_errno(var* args, usize size, gc* ngc) {
|
|||
return ngc->newstr(strerror(errno));
|
||||
}
|
||||
|
||||
mod_func func_tbl[]={
|
||||
module_func_info func_tbl[]={
|
||||
{"nas_socket",nas_socket},
|
||||
{"nas_closesocket",nas_closesocket},
|
||||
{"nas_shutdown",nas_shutdown},
|
||||
|
@ -206,6 +206,6 @@ mod_func func_tbl[]={
|
|||
{nullptr,nullptr}
|
||||
};
|
||||
|
||||
extern "C" mod_func* get(ghost_register_table* table) {
|
||||
extern "C" module_func_info* get(ghost_register_table* table) {
|
||||
return func_tbl;
|
||||
}
|
|
@ -544,13 +544,13 @@ var builtin_open(var* local, gc& ngc) {
|
|||
return nas_err("open", "failed to open file <"+name.str()+">");
|
||||
}
|
||||
var ret=ngc.alloc(vm_obj);
|
||||
ret.obj().set(ghost_file, res);
|
||||
ret.obj().set(global_ghost_type_table.ghost_file, res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_close(var* local, gc& ngc) {
|
||||
var fd=local[1];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("close", "not a valid filehandle");
|
||||
}
|
||||
fd.obj().clear();
|
||||
|
@ -561,7 +561,7 @@ var builtin_read(var* local, gc& ngc) {
|
|||
var fd=local[1];
|
||||
var buf=local[2];
|
||||
var len=local[3];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("read", "not a valid filehandle");
|
||||
}
|
||||
if (buf.type!=vm_str || buf.val.gcobj->unmut) {
|
||||
|
@ -587,7 +587,7 @@ var builtin_read(var* local, gc& ngc) {
|
|||
var builtin_write(var* local, gc& ngc) {
|
||||
var fd=local[1];
|
||||
var str=local[2];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("write", "not a valid filehandle");
|
||||
}
|
||||
if (str.type!=vm_str) {
|
||||
|
@ -600,7 +600,7 @@ var builtin_seek(var* local, gc& ngc) {
|
|||
var fd=local[1];
|
||||
var pos=local[2];
|
||||
var whence=local[3];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("seek", "not a valid filehandle");
|
||||
}
|
||||
return var::num((f64)fseek((FILE*)fd.obj().ptr, pos.num(), whence.num()));
|
||||
|
@ -608,7 +608,7 @@ var builtin_seek(var* local, gc& ngc) {
|
|||
|
||||
var builtin_tell(var* local, gc& ngc) {
|
||||
var fd=local[1];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("tell", "not a valid filehandle");
|
||||
}
|
||||
return var::num((f64)ftell((FILE*)fd.obj().ptr));
|
||||
|
@ -616,7 +616,7 @@ var builtin_tell(var* local, gc& ngc) {
|
|||
|
||||
var builtin_readln(var* local, gc& ngc) {
|
||||
var fd=local[1];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("readln", "not a valid filehandle");
|
||||
}
|
||||
var str=ngc.alloc(vm_str);
|
||||
|
@ -664,7 +664,7 @@ var builtin_stat(var* local, gc& ngc) {
|
|||
|
||||
var builtin_eof(var* local, gc& ngc) {
|
||||
var fd=local[1];
|
||||
if (!fd.objchk(ghost_file)) {
|
||||
if (!fd.objchk(global_ghost_type_table.ghost_file)) {
|
||||
return nas_err("readln", "not a valid filehandle");
|
||||
}
|
||||
return var::num((f64)feof((FILE*)fd.obj().ptr));
|
||||
|
@ -849,13 +849,13 @@ var builtin_opendir(var* local, gc& ngc) {
|
|||
}
|
||||
#endif
|
||||
var ret=ngc.alloc(vm_obj);
|
||||
ret.obj().set(ghost_dir,p);
|
||||
ret.obj().set(global_ghost_type_table.ghost_dir,p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_readdir(var* local, gc& ngc) {
|
||||
var handle=local[1];
|
||||
if (!handle.objchk(ghost_dir)) {
|
||||
if (!handle.objchk(global_ghost_type_table.ghost_dir)) {
|
||||
return nas_err("readdir", "not a valid dir handle");
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
|
@ -872,7 +872,7 @@ var builtin_readdir(var* local, gc& ngc) {
|
|||
|
||||
var builtin_closedir(var* local, gc& ngc) {
|
||||
var handle=local[1];
|
||||
if (!handle.objchk(ghost_dir)) {
|
||||
if (!handle.objchk(global_ghost_type_table.ghost_dir)) {
|
||||
return nas_err("closedir", "not a valid dir handle");
|
||||
}
|
||||
handle.obj().clear();
|
||||
|
@ -936,7 +936,7 @@ var builtin_dlopen(var* local, gc& ngc) {
|
|||
}
|
||||
var ret=ngc.temp=ngc.alloc(vm_hash);
|
||||
var lib=ngc.alloc(vm_obj);
|
||||
lib.obj().set(ghost_dylib, ptr);
|
||||
lib.obj().set(global_ghost_type_table.ghost_dylib, ptr);
|
||||
ret.hash().elems["lib"]=lib;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -948,14 +948,14 @@ var builtin_dlopen(var* local, gc& ngc) {
|
|||
return nas_err("dlopen", "cannot find <get> function");
|
||||
}
|
||||
// get function pointer by name
|
||||
mod_func* tbl=(mod_func*)((getptr)func)(&global_ghost_type_table);
|
||||
module_func_info* tbl=((get_func_ptr)func)(&global_ghost_type_table);
|
||||
if (!tbl) {
|
||||
return nas_err("dlopen", "failed to get module functions");
|
||||
}
|
||||
for(u32 i=0;tbl[i].name;++i) {
|
||||
void* p=(void*)tbl[i].fd;
|
||||
var tmp=ngc.alloc(vm_obj);
|
||||
tmp.obj().set(ghost_faddr, p);
|
||||
tmp.obj().set(global_ghost_type_table.ghost_faddr, p);
|
||||
ret.hash().elems[tbl[i].name]=tmp;
|
||||
}
|
||||
|
||||
|
@ -965,7 +965,7 @@ var builtin_dlopen(var* local, gc& ngc) {
|
|||
|
||||
var builtin_dlclose(var* local, gc& ngc) {
|
||||
var libptr=local[1];
|
||||
if (!libptr.objchk(ghost_dylib)) {
|
||||
if (!libptr.objchk(global_ghost_type_table.ghost_dylib)) {
|
||||
return nas_err("dlclose", "\"lib\" is not a valid dynamic lib");
|
||||
}
|
||||
libptr.obj().clear();
|
||||
|
@ -975,23 +975,27 @@ var builtin_dlclose(var* local, gc& ngc) {
|
|||
var builtin_dlcallv(var* local, gc& ngc) {
|
||||
var fp=local[1];
|
||||
var args=local[2];
|
||||
if (!fp.objchk(ghost_faddr)) {
|
||||
if (!fp.objchk(global_ghost_type_table.ghost_faddr)) {
|
||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
||||
}
|
||||
auto& vec=args.vec().elems;
|
||||
return ((mod)fp.obj().ptr)(vec.data(), vec.size(), &ngc);
|
||||
return ((module_func)fp.obj().ptr)(
|
||||
vec.data(),
|
||||
vec.size(),
|
||||
&ngc
|
||||
);
|
||||
}
|
||||
|
||||
var builtin_dlcall(var* local, gc& ngc) {
|
||||
var fp=local[1];
|
||||
if (!fp.objchk(ghost_faddr)) {
|
||||
if (!fp.objchk(global_ghost_type_table.ghost_faddr)) {
|
||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
||||
}
|
||||
|
||||
var* local_frame_start=local+2;
|
||||
usize local_frame_size=ngc.rctx->top-local_frame_start;
|
||||
// arguments' stored place begins at local +2
|
||||
return ((mod)fp.obj().ptr)(
|
||||
return ((module_func)fp.obj().ptr)(
|
||||
local_frame_start,
|
||||
local_frame_size,
|
||||
&ngc
|
||||
|
|
113
nasal_gc.h
113
nasal_gc.h
|
@ -55,13 +55,13 @@ enum class gc_status:u8 {
|
|||
found
|
||||
};
|
||||
|
||||
struct nas_vec; // vector
|
||||
struct nas_hash; // hashmap(dict)
|
||||
struct nas_func; // function(lambda)
|
||||
struct nas_upval;// upvalue
|
||||
struct nas_obj; // special objects
|
||||
struct nas_co; // coroutine
|
||||
struct nas_val; // nas_val includes gc-managed types
|
||||
struct nas_vec; // vector
|
||||
struct nas_hash; // hashmap(dict)
|
||||
struct nas_func; // function(lambda)
|
||||
struct nas_upval; // upvalue
|
||||
struct nas_obj; // special objects
|
||||
struct nas_co; // coroutine
|
||||
struct nas_val; // nas_val includes gc-managed types
|
||||
|
||||
struct var {
|
||||
public:
|
||||
|
@ -154,6 +154,7 @@ struct nas_func {
|
|||
};
|
||||
|
||||
struct nas_upval {
|
||||
public:
|
||||
/* on stack, use these variables */
|
||||
bool onstk;
|
||||
u32 size;
|
||||
|
@ -162,19 +163,28 @@ struct nas_upval {
|
|||
/* not on stack, use this */
|
||||
std::vector<var> elems;
|
||||
|
||||
nas_upval() {onstk=true;stk=nullptr;size=0;}
|
||||
var& operator[](usize n) {return onstk? stk[n]:elems[n];}
|
||||
void clear() {onstk=true;elems.clear();size=0;}
|
||||
public:
|
||||
nas_upval(): onstk(true), size(0), stk(nullptr) {}
|
||||
|
||||
var& operator[](usize n) {
|
||||
return onstk? stk[n]:elems[n];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
onstk=true;
|
||||
elems.clear();
|
||||
size=0;
|
||||
}
|
||||
};
|
||||
|
||||
void file_dtor(void* ptr) {
|
||||
void filehandle_destructor(void* ptr) {
|
||||
if ((FILE*)ptr==stdin) {
|
||||
return;
|
||||
}
|
||||
fclose((FILE*)ptr);
|
||||
}
|
||||
|
||||
void dir_dtor(void* ptr) {
|
||||
void dir_entry_destructor(void* ptr) {
|
||||
#ifndef _MSC_VER
|
||||
closedir((DIR*)ptr);
|
||||
#else
|
||||
|
@ -182,7 +192,7 @@ void dir_dtor(void* ptr) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void dylib_dtor(void* ptr) {
|
||||
void dylib_destructor(void* ptr) {
|
||||
#ifdef _WIN32
|
||||
FreeLibrary((HMODULE)ptr);
|
||||
#else
|
||||
|
@ -190,9 +200,7 @@ void dylib_dtor(void* ptr) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void faddr_dtor(void* ptr) {}
|
||||
|
||||
usize ghost_file, ghost_dir, ghost_dylib, ghost_faddr;
|
||||
void func_addr_destructor(void* ptr) {}
|
||||
|
||||
struct ghost_register_table {
|
||||
private:
|
||||
|
@ -202,17 +210,25 @@ private:
|
|||
std::unordered_map<string,usize> mapper;
|
||||
std::vector<dtor> destructors;
|
||||
|
||||
public:
|
||||
// reserved ghost type only for native functions
|
||||
usize ghost_file;
|
||||
usize ghost_dir;
|
||||
usize ghost_dylib;
|
||||
usize ghost_faddr;
|
||||
|
||||
public:
|
||||
ghost_register_table() {
|
||||
ghost_file=register_ghost_type("file", file_dtor);
|
||||
ghost_dir=register_ghost_type("dir", dir_dtor);
|
||||
ghost_dylib=register_ghost_type("dylib", dylib_dtor);
|
||||
ghost_faddr=register_ghost_type("faddr", faddr_dtor);
|
||||
ghost_file=register_ghost_type("file", filehandle_destructor);
|
||||
ghost_dir=register_ghost_type("dir", dir_entry_destructor);
|
||||
ghost_dylib=register_ghost_type("dylib", dylib_destructor);
|
||||
ghost_faddr=register_ghost_type("faddr", func_addr_destructor);
|
||||
}
|
||||
|
||||
usize register_ghost_type(const std::string& name, dtor ptr) {
|
||||
if (mapper.count(name)) {
|
||||
std::cerr<<"ghost type \""<<name<<"\" already exists.\n\n";
|
||||
std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: ";
|
||||
std::cerr<<"ghost type \""<<name<<"\" already exists.\n";
|
||||
std::exit(-1);
|
||||
}
|
||||
auto res=destructors.size();
|
||||
|
@ -578,11 +594,27 @@ public:
|
|||
void clear();
|
||||
void info();
|
||||
var alloc(const u8);
|
||||
var newstr(char);
|
||||
var newstr(const char*);
|
||||
var newstr(const string&);
|
||||
void ctxchg(nas_co&);
|
||||
void ctxreserve();
|
||||
|
||||
public:
|
||||
var newstr(char c) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=c;
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const char* buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=buff;
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const string& buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=buff;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
void gc::mark() {
|
||||
|
@ -689,7 +721,8 @@ void gc::extend(u8 type) {
|
|||
nas_val* tmp=new nas_val(type);
|
||||
|
||||
if (!tmp) {
|
||||
std::cerr<<"failed to allocate new memory\n";
|
||||
std::cerr<<"nasal_gc.h: gc::extend: ";
|
||||
std::cerr<<"failed to allocate memory\n";
|
||||
std::exit(-1);
|
||||
}
|
||||
|
||||
|
@ -811,24 +844,6 @@ var gc::alloc(u8 type) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
var gc::newstr(char c) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=c;
|
||||
return s;
|
||||
}
|
||||
|
||||
var gc::newstr(const char* buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=buff;
|
||||
return s;
|
||||
}
|
||||
|
||||
var gc::newstr(const string& buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str()=buff;
|
||||
return s;
|
||||
}
|
||||
|
||||
void gc::ctxchg(nas_co& co) {
|
||||
// store running state to main context
|
||||
mctx=*rctx;
|
||||
|
@ -860,19 +875,19 @@ void gc::ctxreserve() {
|
|||
}
|
||||
|
||||
// use to print error log and return error value
|
||||
var nas_err(const string& err_f, const string& info) {
|
||||
std::cerr<<"[vm] "<<err_f<<": "<<info<<"\n";
|
||||
var nas_err(const string& error_function_name, const string& info) {
|
||||
std::cerr<<"[vm] "<<error_function_name<<": "<<info<<"\n";
|
||||
return var::none();
|
||||
}
|
||||
|
||||
// module function type
|
||||
typedef var (*mod)(var*, usize, gc*);
|
||||
typedef var (*module_func)(var*, usize, gc*);
|
||||
|
||||
// module function stores in tables with this type, end with {nullptr,nullptr}
|
||||
struct mod_func {
|
||||
struct module_func_info {
|
||||
const char* name;
|
||||
mod fd;
|
||||
module_func fd;
|
||||
};
|
||||
|
||||
// module function "get" type
|
||||
typedef mod_func* (*getptr)(ghost_register_table*);
|
||||
typedef module_func_info* (*get_func_ptr)(ghost_register_table*);
|
||||
|
|
Loading…
Reference in New Issue