add dylib.dlopen dylib.dlsym dylib.dlclose dylib.dlcall

now you could add your own modules into nasal without changing the source code!
This commit is contained in:
ValKmjolnir 2021-12-03 19:31:03 +08:00
parent aa191a9feb
commit 9861ecd03e
8 changed files with 337 additions and 94 deletions

303
README.md
View File

@ -889,7 +889,7 @@ a.set(114514);
println(a.get()); println(a.get());
``` ```
### __Native Functions(This is for library developers)__ ### __Native Functions__
You could add builtin functions of your own You could add builtin functions of your own
(written in C/C++) to help you calculate things more quickly. (written in C/C++) to help you calculate things more quickly.
@ -1010,6 +1010,126 @@ nasal_ref builtin_keys(std::vector<nasal_ref>& local,nasal_gc& gc)
} }
``` ```
### __Modules(This is for library developers)__
If there is only one way to add your own functions into nasal,
that is really inconvenient.
Luckily, we have developed some useful native-functions to help you add modules that created by you.
After 2021/12/3, there are some new functions added to `lib.nas`:
```javascript
var dylib=
{
dlopen: func(libname){return __builtin_dlopen;},
dlsym: func(lib,sym){return __builtin_dlsym; },
dlclose: func(lib){return __builtin_dlclose; },
dlcall: func(funcptr,args...){return __builtin_dlcall}
};
```
Aha, as you could see, these functions are used to load dynamic libraries into the nasal runtime and execute.
Let's see how they work.
First, write a cpp file that you want to generate the dynamic lib, take the `fib.cpp` as the example(example codes are in `./module`):
```C++
// add header file nasal.h to get api
#include "nasal.h"
double fibonaci(double x){
if(x<=2)
return x;
return fibonaci(x-1)+fibonaci(x-2);
}
// remember to use extern "C",
// so you could search the symbol quickly
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
// the arguments are generated into a vm_vec: args
// get values from the vector that must be used here
nasal_ref num=args[0];
// if you want your function safer, try this
// builtin_err will print the error info on screen
// and return vm_null for runtime to interrupt
if(num.type!=vm_num)
return builtin_err("extern_fib","\"num\" must be number");
// ok, you must know that vm_num now is not managed by gc
// if want to return a gc object, use gc.alloc(type)
// usage of gc is the same as adding a native function
return {vm_num,fibonaci(num.to_number())};
}
```
Next, compile this `fib.cpp` into dynamic lib.
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac: same as Linux, but remember to generate `.dylib`
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
Then we write a test nasal file to run this fib function, this example runs on Linux:
```javascript
import("lib.nas");
var dlhandle=dylib.dlopen("./module/libfib.so");
var fib=dylib.dlsym(dlhandle,"fib");
for(var i=1;i<30;i+=1)
println(dylib.dlcall(fib,i));
dylib.dlclose(dlhandle);
```
`dylib.dlopen` is used to load dynamic library.
`dylib.dlsym` is used to get the function address.
`dylib.dlcall` is used to call the function, the first argument is the function address, make sure this argument is vm_obj and type=obj_extern.
`dylib.dlclose` is used to unload the library, at the moment that you call the function, all the function addresses that gotten from it are invalid.
If get this, Congratulations!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
## Difference Between Andy's Nasal Interpreter and This Interpreter ## Difference Between Andy's Nasal Interpreter and This Interpreter
This interpreter uses more strict syntax to make sure it is easier for you to program and debug. This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
@ -1098,22 +1218,17 @@ Function 'die' is used to throw error and crash.
```javascript ```javascript
hello hello
[vm] error: error occurred this line [vm] error: error occurred this line
[vm] error at 0x0000009b: native function error. [vm] native function error.
trace back: trace back:
0x0000009b: callb 0x22 <__builtin_die> (lib.nas line 85) 0x00000088: callb 0x22 <__builtin_die@0x417620> (<lib.nas> line 19)
0x00000182: callfv 0x1 (a.nas line 6) 0x000002af: callfv 0x1 (<a.nas> line 5)
0x00000186: callfv 0x0 (a.nas line 8) 0x000002b3: callfv 0x0 (<a.nas> line 7)
vm stack(limit 10): vm stack(limit 10, total 5):
null | | null |
func | <0x8b0f50> func{entry=0x9b} | addr | 0x2af
func | <0x8b1db0> func{entry=0x17c} | func | <0x6c62c0> func{entry=0x88}
num | 57.295780 | addr | 0x2b3
num | 1852.000000 | func | <0x6c8910> func{entry=0x2a9}
num | 1.943800
num | 0.000540
num | 39.370100
num | 3.280800
num | 0.453600
``` ```
Here is an example of stack overflow: Here is an example of stack overflow:
@ -1133,13 +1248,22 @@ And the trace back info:
```javascript ```javascript
[vm] stack overflow [vm] stack overflow
trace back: trace back:
0x0000000f: callfv 0x1 (a.nas line 5) 0x0000000d: calll 0x1 (<a.nas> line 5)
0x0000000f: 4090 same call(s) ... 0x0000000f: callfv 0x1 (<a.nas> line 5)
0x00000007: callfv 0x1 (a.nas line 2) 0x0000000f: 2044 same call(s)
0x00000013: callfv 0x1 (a.nas line 3) 0x00000007: callfv 0x1 (<a.nas> line 2)
vm stack(limit 10): 0x00000013: callfv 0x1 (<a.nas> line 3)
func | <0xc511e0> func{entry=0xd} vm stack(limit 10, total 4095):
... | 9 same value(s) | func | <0x24f1f10> func{entry=0xd}
| addr | 0xf
| func | <0x24f1f10> func{entry=0xd}
| addr | 0xf
| func | <0x24f1f10> func{entry=0xd}
| addr | 0xf
| func | <0x24f1f10> func{entry=0xd}
| addr | 0xf
| func | <0x24f1f10> func{entry=0xd}
| addr | 0xf
``` ```
Error will be thrown if there's a fatal error when executing: Error will be thrown if there's a fatal error when executing:
@ -1153,11 +1277,11 @@ func(){
And the trace back info: And the trace back info:
```javascript ```javascript
[vm] error at 0x00000008: callv: must call a vector/hash/string [vm] callv: must call a vector/hash/string
trace back: trace back:
0x00000008: callv 0x0 (a.nas line 3) 0x00000008: callv 0x0 (<a.nas> line 3)
vm stack(limit 10): vm stack(limit 10, total 1):
num | 0.000000 | num | 0.000000
``` ```
Use command `-d` or `--detail` the trace back info will be this: Use command `-d` or `--detail` the trace back info will be this:
@ -1167,67 +1291,70 @@ hello world
[vm] error: exception test [vm] error: exception test
[vm] native function error. [vm] native function error.
trace back: trace back:
0x0000008f: callb 0x22 <__builtin_die> (<lib.nas> line 20) 0x00000088: callb 0x22 <__builtin_die@0x417620> (<lib.nas> line 19)
0x00000214: callfv 0x1 (<test/exception.nas> line 16) 0x000002d2: callfv 0x1 (<test/exception.nas> line 16)
0x00000248: callfv 0x0 (<test/exception.nas> line 39) 0x00000306: callfv 0x0 (<test/exception.nas> line 39)
vm stack(limit 10): vm stack(limit 10, total 5):
null | | null |
func | <0x23bc3f0> func{entry=0x8f} | addr | 0x2d2
func | <0x23bdc50> func{entry=0x20e} | func | <0x827750> func{entry=0x88}
mcall address: 0x24a4b88 | addr | 0x306
| func | <0x829ee0> func{entry=0x2cc}
mcall address: 0x90e498
global: global:
[0] func | <0x23d3960> func{entry=0x5} [0x00000000] | func | <0x83da50> func{entry=0x5}
[1] func | <0x23bb8b0> func{entry=0xc} [0x00000001] | func | <0x826cb0> func{entry=0xc}
[2] func | <0x23bb950> func{entry=0x14} [0x00000002] | func | <0x826d50> func{entry=0x14}
[3] func | <0x23bb9f0> func{entry=0x1c} [0x00000003] | func | <0x826df0> func{entry=0x1c}
[4] func | <0x23bba90> func{entry=0x23} [0x00000004] | func | <0x826e90> func{entry=0x23}
[5] func | <0x23bbb30> func{entry=0x29} [0x00000005] | func | <0x826f30> func{entry=0x29}
[6] func | <0x23bbbd0> func{entry=0x30} [0x00000006] | func | <0x826fd0> func{entry=0x31}
[7] func | <0x23bbc70> func{entry=0x38} [0x00000007] | func | <0x827070> func{entry=0x39}
[8] func | <0x23bbd10> func{entry=0x40} [0x00000008] | func | <0x827110> func{entry=0x40}
[9] func | <0x23bbdb0> func{entry=0x47} [0x00000009] | func | <0x8271b0> func{entry=0x47}
[10] func | <0x23bbe50> func{entry=0x4e} [0x0000000a] | func | <0x827250> func{entry=0x4e}
[11] func | <0x23bbef0> func{entry=0x55} [0x0000000b] | func | <0x8272f0> func{entry=0x55}
[12] func | <0x23bbf90> func{entry=0x5c} [0x0000000c] | func | <0x827390> func{entry=0x5c}
[13] func | <0x23bc030> func{entry=0x63} [0x0000000d] | func | <0x827430> func{entry=0x63}
[14] func | <0x23bc0d0> func{entry=0x6a} [0x0000000e] | func | <0x8274d0> func{entry=0x6b}
[15] func | <0x23bc170> func{entry=0x72} [0x0000000f] | func | <0x827570> func{entry=0x73}
[16] func | <0x23bc210> func{entry=0x7a} [0x00000010] | func | <0x827610> func{entry=0x7a}
[17] func | <0x23bc2b0> func{entry=0x81} [0x00000011] | func | <0x8276b0> func{entry=0x81}
[18] func | <0x23bc350> func{entry=0x88} [0x00000012] | func | <0x827750> func{entry=0x88}
[19] func | <0x23bc3f0> func{entry=0x8f} [0x00000013] | func | <0x8277f0> func{entry=0x8f}
[20] func | <0x23bc490> func{entry=0x96} [0x00000014] | func | <0x827890> func{entry=0x98}
[21] func | <0x23bc530> func{entry=0x9f} [0x00000015] | func | <0x827930> func{entry=0xa0}
[22] func | <0x23bc5d0> func{entry=0xa7} [0x00000016] | func | <0x8279d0> func{entry=0xa8}
[23] func | <0x23bc670> func{entry=0xaf} [0x00000017] | func | <0x827a70> func{entry=0xb0}
[24] func | <0x23bc710> func{entry=0xb7} [0x00000018] | func | <0x827b10> func{entry=0xb8}
[25] func | <0x23bc7b0> func{entry=0xbf} [0x00000019] | func | <0x827bb0> func{entry=0xbf}
[26] func | <0x23bc850> func{entry=0xc6} [0x0000001a] | func | <0x827c50> func{entry=0xc6}
[27] func | <0x23bc8f0> func{entry=0xcd} [0x0000001b] | hash | <0x8f4ce0> {14 member}
[28] hash | <0x248ae70> {14 member} [0x0000001c] | hash | <0x8f4d40> {9 member}
[29] hash | <0x248aed0> {9 member} [0x0000001d] | hash | <0x8f4da0> {13 member}
[30] hash | <0x248af30> {12 member} [0x0000001e] | num | 0.017453
[31] num | 0.017453 [0x0000001f] | num | 0.592500
[32] num | 0.592500 [0x00000020] | num | 0.304800
[33] num | 0.304800 [0x00000021] | num | 3.785400
[34] num | 3.785400 [0x00000022] | num | 0.025400
[35] num | 0.025400 [0x00000023] | num | 2.204600
[36] num | 2.204600 [0x00000024] | num | 1.687800
[37] num | 1.687800 [0x00000025] | num | 0.514400
[38] num | 0.514400 [0x00000026] | num | 0.264200
[39] num | 0.264200 [0x00000027] | num | 0.453600
[40] num | 0.453600 [0x00000028] | num | 3.280800
[41] num | 3.280800 [0x00000029] | num | 39.370100
[42] num | 39.370100 [0x0000002a] | num | 0.000540
[43] num | 0.000540 [0x0000002b] | num | 1.943800
[44] num | 1.943800 [0x0000002c] | num | 1852.000000
[45] num | 1852.000000 [0x0000002d] | num | 57.295780
[46] num | 57.295780 [0x0000002e] | hash | <0x8f4e00> {16 member}
[47] hash | <0x248af90> {3 member} [0x0000002f] | hash | <0x8f4e60> {4 member}
[48] func | <0x23bdcf0> func{entry=0x21e} [0x00000030] | hash | <0x8f4ec0> {3 member}
[49] func | <0x23bdd90> func{entry=0x22d} [0x00000031] | func | <0x829f80> func{entry=0x2dc}
[50] func | <0x23bde30> func{entry=0x237} [0x00000032] | func | <0x82a020> func{entry=0x2eb}
[0x00000033] | func | <0x82a0c0> func{entry=0x2f5}
local: local:
[0] nil | [0x00000000] | nil |
[1] str | <0x249abd0> exception test [0x00000001] | str | <0x9057f0> exception test
``` ```

12
lib.nas
View File

@ -65,6 +65,8 @@ var math=
{ {
e: 2.7182818284590452354, e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288, pi: 3.14159265358979323846264338327950288,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); }, sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); }, cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); }, tan: func(x) {return __builtin_tan(x); },
@ -73,8 +75,6 @@ var math=
ln: func(x) {return __builtin_ln(x); }, ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); }, sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);}, atan2: func(x,y){return __builtin_atan2(x,y);},
inf: 1/0,
nan: 0/0,
isnan: func(x) {return __builtin_isnan(x); } isnan: func(x) {return __builtin_isnan(x); }
}; };
var D2R=math.pi/180; var D2R=math.pi/180;
@ -112,4 +112,12 @@ var unix=
environ: func(){die("not supported yet");}, environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();}, getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);} getenv: func(envvar){return __builtin_getenv(envvar);}
};
var dylib=
{
dlopen: func(libname){return __builtin_dlopen;},
dlsym: func(lib,sym){return __builtin_dlsym; },
dlclose: func(lib){return __builtin_dlclose; },
dlcall: func(funcptr,args...){return __builtin_dlcall}
}; };

View File

@ -1,6 +1,6 @@
.PHONY=test .PHONY=test
nasal:main.cpp nasal_ast.h nasal_builtin.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal.h nasal:main.cpp nasal_ast.h nasal_builtin.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal.h
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -Wshadow -Wall clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
test:nasal test:nasal
./nasal test/ascii-art.nas ./nasal test/ascii-art.nas
./nasal -c test/bf.nas ./nasal -c test/bf.nas

13
module/fib.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "../nasal.h"
double fibonaci(double x){
if(x<=2)
return x;
return fibonaci(x-1)+fibonaci(x-2);
}
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
nasal_ref num=args[0];
if(num.type!=vm_num)
return builtin_err("extern_fib","\"num\" must be number");
return {vm_num,fibonaci(num.to_number())};
}

6
module/makefile Normal file
View File

@ -0,0 +1,6 @@
.PHONY=clean
libfib.so: fib.cpp
clang++ -c -O3 fib.cpp -fPIC -o fib.o
clang++ -shared -o libfib.so fib.o
clean:
rm *.o *.so *.dll *.dylib

View File

@ -23,6 +23,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
inline double hex_to_double(const char* str) inline double hex_to_double(const char* str)
{ {
double ret=0; double ret=0;

View File

@ -11,6 +11,8 @@ enum obj_type
{ {
obj_file=1, obj_file=1,
obj_dir, obj_dir,
obj_dylib,
obj_extern
}; };
// declaration of builtin functions // declaration of builtin functions
// to add new builtin function, declare it here and write the definition below // to add new builtin function, declare it here and write the definition below
@ -77,11 +79,15 @@ nas_native(builtin_closedir);
nas_native(builtin_chdir); nas_native(builtin_chdir);
nas_native(builtin_getcwd); nas_native(builtin_getcwd);
nas_native(builtin_getenv); nas_native(builtin_getenv);
nas_native(builtin_dlopen);
nas_native(builtin_dlsym);
nas_native(builtin_dlclose);
nas_native(builtin_dlcall);
nasal_ref builtin_err(const char* func_name,std::string info) nasal_ref builtin_err(const char* func_name,std::string info)
{ {
std::cout<<"[vm] "<<func_name<<": "<<info<<".\n"; std::cout<<"[vm] "<<func_name<<": "<<info<<".\n";
return {vm_none}; return {vm_none,nullptr};
} }
// register builtin function's name and it's address here in this table below // register builtin function's name and it's address here in this table below
@ -154,6 +160,10 @@ struct
{"__builtin_chdir", builtin_chdir }, {"__builtin_chdir", builtin_chdir },
{"__builtin_getcwd", builtin_getcwd }, {"__builtin_getcwd", builtin_getcwd },
{"__builtin_getenv", builtin_getenv }, {"__builtin_getenv", builtin_getenv },
{"__builtin_dlopen", builtin_dlopen },
{"__builtin_dlsym", builtin_dlsym },
{"__builtin_dlclose", builtin_dlclose },
{"__builtin_dlcall", builtin_dlcall },
{nullptr, nullptr } {nullptr, nullptr }
}; };
@ -973,4 +983,69 @@ nasal_ref builtin_getenv(std::vector<nasal_ref>& local,nasal_gc& gc)
*str.str()=res; *str.str()=res;
return str; return str;
} }
nasal_ref builtin_dlopen(std::vector<nasal_ref>& local,nasal_gc& gc)
{
nasal_ref dlname=local[1];
if(dlname.type!=vm_str)
return builtin_err("dlopen","\"libname\" must be string");
#ifdef _WIN32
// wchar_t* str=new wchar_t[dlname.str()->size()+1];
// memset(str,0,sizeof(wchar_t)*dlname.str()->size()+1);
// mbstowcs(str,dlname.str()->c_str(),dlname.str()->size()+1);
// void* ptr=LoadLibrary(str);
// delete []str;
void* ptr=LoadLibrary(dlname.str()->c_str());
#else
void* ptr=dlopen(dlname.str()->c_str(),RTLD_LOCAL|RTLD_LAZY);
#endif
if(!ptr)
return builtin_err("dlopen","cannot open dynamic lib \""+*dlname.str()+"\"");
nasal_ref ret=gc.alloc(vm_obj);
ret.obj()->type=obj_dylib;
ret.obj()->ptr=ptr;
return ret;
}
nasal_ref builtin_dlsym(std::vector<nasal_ref>& local,nasal_gc& gc)
{
nasal_ref libptr=local[1];
nasal_ref sym=local[2];
if(libptr.type!=vm_obj || libptr.obj()->type!=obj_dylib)
return builtin_err("dlsym","\"lib\" is not a correct dynamic lib entry");
if(sym.type!=vm_str)
return builtin_err("dlsym","\"sym\" must be string");
void* func=nullptr;
#ifdef _WIN32
func=(void*)GetProcAddress((HMODULE)libptr.obj()->ptr,sym.str()->c_str());
#else
func=dlsym(libptr.obj()->ptr,sym.str()->c_str());
#endif
if(!func)
return builtin_err("dlsym","cannot find symbol \""+*sym.str()+"\"");
nasal_ref ret=gc.alloc(vm_obj);
ret.obj()->type=obj_extern;
ret.obj()->ptr=func;
return ret;
}
nasal_ref builtin_dlclose(std::vector<nasal_ref>& local,nasal_gc& gc)
{
nasal_ref libptr=local[1];
if(libptr.type!=vm_obj || libptr.obj()->type!=obj_dylib)
return builtin_err("dlclose","\"lib\" is not a correct dynamic lib entry");
#ifdef _WIN32
FreeLibrary((HMODULE)libptr.obj()->ptr);
#else
dlclose(libptr.obj()->ptr);
#endif
return gc.nil;
}
nasal_ref builtin_dlcall(std::vector<nasal_ref>& local,nasal_gc& gc)
{
nasal_ref funcptr=local[1];
nasal_ref args=local[2];
if(funcptr.type!=vm_obj || funcptr.obj()->type!=obj_extern)
return builtin_err("dlcall","\"funcptr\" is not a correct function pointer");
typedef nasal_ref (*extern_func)(std::vector<nasal_ref>&,nasal_gc&);
extern_func func=(extern_func)funcptr.obj()->ptr;
return func(args.vec()->elems,gc);
}
#endif #endif

View File

@ -65,6 +65,8 @@ var math=
{ {
e: 2.7182818284590452354, e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288, pi: 3.14159265358979323846264338327950288,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); }, sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); }, cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); }, tan: func(x) {return __builtin_tan(x); },
@ -73,8 +75,6 @@ var math=
ln: func(x) {return __builtin_ln(x); }, ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); }, sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);}, atan2: func(x,y){return __builtin_atan2(x,y);},
inf: 1/0,
nan: 0/0,
isnan: func(x) {return __builtin_isnan(x); } isnan: func(x) {return __builtin_isnan(x); }
}; };
var D2R=math.pi/180; var D2R=math.pi/180;
@ -112,4 +112,12 @@ var unix=
environ: func(){die("not supported yet");}, environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();}, getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);} getenv: func(envvar){return __builtin_getenv(envvar);}
};
var dylib=
{
dlopen: func(libname){return __builtin_dlopen;},
dlsym: func(lib,sym){return __builtin_dlsym; },
dlclose: func(lib){return __builtin_dlclose; },
dlcall: func(funcptr,args...){return __builtin_dlcall}
}; };