diff --git a/README.md b/README.md index 9e2daa5..8af9ccb 100644 --- a/README.md +++ b/README.md @@ -615,9 +615,8 @@ double fibonaci(double x){ return x; return fibonaci(x-1)+fibonaci(x-2); } -// remember to use extern "C", -// so you could search the symbol quickly -extern "C" var fib(std::vector& args,gc& ngc){ +// module functions' parameter list example +var fib(var* args,usize size,gc* ngc){ // the arguments are generated into a vm_vec: args // get values from the vector that must be used here var num=args[0]; @@ -627,10 +626,22 @@ extern "C" var fib(std::vector& args,gc& ngc){ if(num.type!=vm_num) return nas_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 ngc.alloc(type) + // if want to return a gc object, use ngc->alloc(type) // usage of gc is the same as adding a native function return {vm_num,fibonaci(num.tonum())}; } + +// must write this function, this will help nasal to +// get the function pointer by name +// the reason why using this way to get function pointer +// is because `var` has constructors, which is not compatiable in C +// so "extern "C" var fib" may get compilation warnings +extern "C" mod get(const char* n){ + string name=n; + if(name=="fib") + return fib; + return nullptr; +} ``` Next, compile this `fib.cpp` into dynamic lib. diff --git a/doc/README_zh.md b/doc/README_zh.md index c012c0b..a4c154a 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -590,19 +590,30 @@ double fibonaci(double x){ return x; return fibonaci(x-1)+fibonaci(x-2); } -// 记得用extern "C" -// 这样找符号会更加快速便捷,不要在意编译时的warning -extern "C" var fib(std::vector& args,gc& ngc){ - // 传参会被送到一个vm_vec类型中送过来,而不是上文中那种指针直接指向局部作用域 + +// 模块函数的参数列表一律以这个为准 +var fib(var* args,usize size,gc* ngc){ + // 传参会给予一个var指针,指向一个vm_vec的data() var num=args[0]; // 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查 - // builtin_err会输出错误信息并返回错误类型让虚拟机终止执行 + // nas_err会输出错误信息并返回错误类型让虚拟机终止执行 if(num.type!=vm_num) return nas_err("extern_fib","\"num\" must be number"); // vm_num作为普通的数字类型,不是内存管理的对象,所以无需申请 - // 如果需要返回内存管理的对象,请使用ngc.alloc(type) + // 如果需要返回内存管理的对象,请使用ngc->alloc(type) return {vm_num,fibonaci(num.tonum())}; } + +// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针 +// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的 +// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致 +// 类似 "extern "C" var fib" 的写法会得到编译错误 +extern "C" mod get(const char* n){ + string name=n; + if(name=="fib") + return fib; + return nullptr; +} ``` 接着我们把`fib.cpp`编译成动态库。 diff --git a/module/fib.cpp b/module/fib.cpp index bac813d..cdac2ff 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -6,18 +6,20 @@ double fibonaci(double x){ return x; return fibonaci(x-1)+fibonaci(x-2); } -extern "C" var fib(std::vector& args,gc& ngc){ + +var fib(var* args,usize size,gc* ngc){ std::cout<<"[mod] this is the first test module of nasal\n"; - if(!args.size()) + if(!size) return nas_err("fib","lack arguments"); var num=args[0]; if(num.type!=vm_num) return nas_err("extern_fib","\"num\" must be number"); return {vm_num,fibonaci(num.tonum())}; } -extern "C" var quick_fib(std::vector& args,gc& ngc){ + +var quick_fib(var* args,usize size,gc* ngc){ std::cout<<"[mod] this is the first test module of nasal\n"; - if(!args.size()) + if(!size) return nas_err("fib","lack arguments"); var num=args[0]; if(num.type!=vm_num) @@ -31,4 +33,13 @@ extern "C" var quick_fib(std::vector& args,gc& ngc){ b=res; } return {vm_num,res}; +} + +extern "C" mod get(const char* n){ + string name=n; + if(name=="fib") + return fib; + else if(name=="quick_fib") + return quick_fib; + return nullptr; } \ No newline at end of file diff --git a/module/keyboard.cpp b/module/keyboard.cpp index dc70729..280a743 100644 --- a/module/keyboard.cpp +++ b/module/keyboard.cpp @@ -71,14 +71,25 @@ public: }; noecho_input this_window; -extern "C" var nas_getch(std::vector& args,gc& ngc){ +var nas_getch(var* args,usize size,gc* ngc){ return {vm_num,(double)this_window.noecho_getch()}; } -extern "C" var nas_kbhit(std::vector& args,gc& ngc){ +var nas_kbhit(var* args,usize size,gc* ngc){ return {vm_num,(double)this_window.noecho_kbhit()}; } -extern "C" var nas_noblock(std::vector& args,gc& ngc){ +var nas_noblock(var* args,usize size,gc* ngc){ if(this_window.noecho_kbhit()) return {vm_num,(double)this_window.noecho_getch()}; return nil; +} + +extern "C" mod get(const char* n){ + string name=n; + if(name=="nas_getch") + return nas_getch; + else if(name=="nas_kbhit") + return nas_kbhit; + else if(name=="nas_noblock") + return nas_noblock; + return nullptr; } \ No newline at end of file diff --git a/module/makefile b/module/makefile index 5a74b84..9fb0d48 100644 --- a/module/makefile +++ b/module/makefile @@ -3,36 +3,41 @@ STD=14 libfib.so: fib.cpp - $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o - $(CXX) -shared -o libfib.so fib.o - rm fib.o + @ echo "[build] libfib.so" + @ $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o + @ $(CXX) -shared -o libfib.so fib.o + @ rm fib.o libfib.dll: fib.cpp - $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o - $(CXX) -shared -o libfib.dll fib.o - del fib.o + @ echo [build] libfib.dll + @ $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o + @ $(CXX) -shared -o libfib.dll fib.o + @ del fib.o libkey.so: keyboard.cpp - $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o - $(CXX) -shared -o libkey.so keyboard.o - rm keyboard.o + @ echo "[build] libkey.so" + @ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o + @ $(CXX) -shared -o libkey.so keyboard.o + @ rm keyboard.o libkey.dll: keyboard.cpp - $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static - $(CXX) -shared -o libkey.dll keyboard.o -static - del keyboard.o + @ echo [build] libkey.dll + @ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static + @ $(CXX) -shared -o libkey.dll keyboard.o -static + @ del keyboard.o libnasock.so: nasocket.cpp - $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o - $(CXX) -shared -o libnasock.so nasocket.o - rm nasocket.o + @ echo "[build] libnasock.so" + @ $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o + @ $(CXX) -shared -o libnasock.so nasocket.o + @ rm nasocket.o libnasock.dll: nasocket.cpp - $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static - $(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static - del nasocket.o + @ echo [build] libnasock.dll + @ $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static + @ $(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static + @ del nasocket.o clean: -@ rm *.so *.dll *.dylib - @ echo "done" all: libfib.so libkey.so libnasock.so - @ echo "build done" + @ echo "[build] done" mingw-all: libfib.dll libkey.dll libnasock.dll - @ echo build done \ No newline at end of file + @ echo [build] done diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 5b12768..5c4a408 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -25,14 +25,14 @@ static WSAmanager win; #include #endif -extern "C" var nas_socket(std::vector& args,gc& ngc){ +var nas_socket(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num) return nas_err("socket","\"af\", \"type\", \"protocol\" should be number"); int sd=socket(args[0].num(),args[1].num(),args[2].num()); return {vm_num,(double)sd}; } -extern "C" var nas_closesocket(std::vector& args,gc& ngc){ +var nas_closesocket(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("closesocket","\"\" should be number"); #ifdef _WIN32 @@ -42,7 +42,7 @@ extern "C" var nas_closesocket(std::vector& args,gc& ngc){ #endif } -extern "C" var nas_shutdown(std::vector& args,gc& ngc){ +var nas_shutdown(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("shutdown","\"sd\" must be a number"); if(args[1].type!=vm_num) @@ -50,7 +50,7 @@ extern "C" var nas_shutdown(std::vector& args,gc& ngc){ return {vm_num,(double)shutdown(args[0].num(),args[1].num())}; } -extern "C" var nas_bind(std::vector& args,gc& ngc){ +var nas_bind(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("bind","\"sd\" muse be a number"); if(args[1].type!=vm_str) @@ -65,7 +65,7 @@ extern "C" var nas_bind(std::vector& args,gc& ngc){ return {vm_num,(double)bind(args[0].num(),(sockaddr*)&server,sizeof(server))}; } -extern "C" var nas_listen(std::vector& args,gc& ngc){ +var nas_listen(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("listen","\"sd\" must be a number"); if(args[1].type!=vm_num) @@ -73,7 +73,7 @@ extern "C" var nas_listen(std::vector& args,gc& ngc){ return{vm_num,(double)listen(args[0].num(),args[1].num())}; } -extern "C" var nas_connect(std::vector& args,gc& ngc){ +var nas_connect(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("connect","\"sd\" must be a number"); if(args[1].type!=vm_str) @@ -89,7 +89,7 @@ extern "C" var nas_connect(std::vector& args,gc& ngc){ return {vm_num,(double)connect(args[0].num(),(sockaddr*)&addr,sizeof(sockaddr_in))}; } -extern "C" var nas_accept(std::vector& args,gc& ngc){ +var nas_accept(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("accept","\"sd\" must be a number"); sockaddr_in client; @@ -99,15 +99,15 @@ extern "C" var nas_accept(std::vector& args,gc& ngc){ #else int client_sd=accept(args[0].num(),(sockaddr*)&client,(socklen_t*)&socklen); #endif - var res=ngc.temp=ngc.alloc(vm_hash); + var res=ngc->temp=ngc->alloc(vm_hash); auto& hash=res.hash().elems; hash["sd"]={vm_num,(double)client_sd}; - hash["ip"]=ngc.newstr(inet_ntoa(client.sin_addr)); - ngc.temp=nil; + hash["ip"]=ngc->newstr(inet_ntoa(client.sin_addr)); + ngc->temp=nil; return res; } -extern "C" var nas_send(std::vector& args,gc& ngc){ +var nas_send(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("send","\"sd\" must be a number"); if(args[1].type!=vm_str) @@ -117,7 +117,7 @@ extern "C" var nas_send(std::vector& args,gc& ngc){ return {vm_num,(double)send(args[0].num(),args[1].str().c_str(),args[1].str().length(),args[2].num())}; } -extern "C" var nas_sendto(std::vector& args,gc& ngc){ +var nas_sendto(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("sendto","\"sd\" must be a number"); if(args[1].type!=vm_str) @@ -137,7 +137,7 @@ extern "C" var nas_sendto(std::vector& args,gc& ngc){ return {vm_num,(double)sendto(args[0].num(),args[3].str().c_str(),args[3].str().length(),args[4].num(),(sockaddr*)&addr,sizeof(sockaddr_in))}; } -extern "C" var nas_recv(std::vector& args,gc& ngc){ +var nas_recv(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("recv","\"sd\" must be a number"); if(args[1].type!=vm_num) @@ -146,17 +146,17 @@ extern "C" var nas_recv(std::vector& args,gc& ngc){ return nas_err("recv","\"len\" out of range"); if(args[2].type!=vm_num) return nas_err("recv","\"flags\" muse be a number"); - var res=ngc.temp=ngc.alloc(vm_hash); + var res=ngc->temp=ngc->alloc(vm_hash); auto& hash=res.hash().elems; char* buf=new char[(int)args[1].num()]; hash["size"]={vm_num,(double)recv(args[0].num(),buf,args[1].num(),args[2].num())}; - hash["str"]=ngc.newstr(buf); + hash["str"]=ngc->newstr(buf); delete[] buf; - ngc.temp=nil; + ngc->temp=nil; return res; } -extern "C" var nas_recvfrom(std::vector& args,gc& ngc){ +var nas_recvfrom(var* args,usize size,gc* ngc){ if(args[0].type!=vm_num) return nas_err("recvfrom","\"sd\" must be a number"); if(args[1].type!=vm_num) @@ -167,7 +167,7 @@ extern "C" var nas_recvfrom(std::vector& args,gc& ngc){ return nas_err("recvfrom","\"flags\" muse be a number"); sockaddr_in addr; int socklen=sizeof(sockaddr_in); - var res=ngc.temp=ngc.alloc(vm_hash); + var res=ngc->temp=ngc->alloc(vm_hash); auto& hash=res.hash().elems; char* buf=new char[(int)args[1].num()+1]; #ifdef _WIN32 @@ -176,13 +176,42 @@ extern "C" var nas_recvfrom(std::vector& args,gc& ngc){ hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,(socklen_t*)&socklen)}; #endif buf[(int)hash["size"].num()]=0; - hash["str"]=ngc.newstr(buf); + hash["str"]=ngc->newstr(buf); delete[] buf; - hash["fromip"]=ngc.newstr(inet_ntoa(addr.sin_addr)); - ngc.temp=nil; + hash["fromip"]=ngc->newstr(inet_ntoa(addr.sin_addr)); + ngc->temp=nil; return res; } -extern "C" var nas_errno(std::vector& args,gc& ngc){ - return ngc.newstr(strerror(errno)); +var nas_errno(var* args,usize size,gc* ngc){ + return ngc->newstr(strerror(errno)); +} + +extern "C" mod get(const char* n){ + string name=n; + if(name=="nas_socket") + return nas_socket; + else if(name=="nas_closesocket") + return nas_closesocket; + else if(name=="nas_shutdown") + return nas_shutdown; + else if(name=="nas_bind") + return nas_bind; + else if(name=="nas_listen") + return nas_listen; + else if(name=="nas_connect") + return nas_connect; + else if(name=="nas_accept") + return nas_accept; + else if(name=="nas_send") + return nas_send; + else if(name=="nas_sendto") + return nas_sendto; + else if(name=="nas_recv") + return nas_recv; + else if(name=="nas_recvfrom") + return nas_recvfrom; + else if(name=="nas_errno") + return nas_errno; + return nullptr; } \ No newline at end of file diff --git a/nasal_builtin.h b/nasal_builtin.h index 8bd4b43..5828da7 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -130,7 +130,7 @@ var builtin_split(var* local,gc& ngc) // avoid being sweeped var res=ngc.temp=ngc.alloc(vm_vec); - std::vector& vec=res.vec().elems; + auto& vec=res.vec().elems; if(!deli.length()) { @@ -850,7 +850,7 @@ var builtin_getenv(var* local,gc& ngc) char* res=getenv(envvar.str().c_str()); return res?ngc.newstr(res):nil; } -void obj_dylib_dtor(void* ptr) +void dylib_dtor(void* ptr) { #ifdef _WIN32 FreeLibrary((HMODULE)ptr); @@ -869,7 +869,7 @@ var builtin_dlopen(var* local,gc& ngc) return nas_err("dlopen","malloc failed"); memset(str,0,sizeof(wchar_t)*dlname.str().size()+1); mbstowcs(str,dlname.str().c_str(),dlname.str().size()+1); - void* ptr=LoadLibraryW(str); + void* ptr=LoadLibraryA(dlname.str().c_str()); delete []str; #else void* ptr=dlopen(dlname.str().c_str(),RTLD_LOCAL|RTLD_LAZY); @@ -877,7 +877,7 @@ var builtin_dlopen(var* local,gc& ngc) if(!ptr) return nas_err("dlopen","cannot open dynamic lib <"+dlname.str()+">"); var ret=ngc.alloc(vm_obj); - ret.obj().set(nas_obj::dylib,ptr,obj_dylib_dtor); + ret.obj().set(nas_obj::dylib,ptr,dylib_dtor); return ret; } var builtin_dlsym(var* local,gc& ngc) @@ -888,15 +888,20 @@ var builtin_dlsym(var* local,gc& ngc) return nas_err("dlsym","\"lib\" is not a valid dynamic lib"); if(sym.type!=vm_str) return nas_err("dlsym","\"sym\" must be string"); + // "get" function #ifdef _WIN32 - void* func=(void*)GetProcAddress((HMODULE)lib.obj().ptr,sym.str().c_str()); + void* func=(void*)GetProcAddress((HMODULE)lib.obj().ptr,"get"); #else - void* func=dlsym(lib.obj().ptr,sym.str().c_str()); + void* func=dlsym(lib.obj().ptr,"get"); #endif if(!func) - return nas_err("dlsym","cannot find symbol \""+sym.str()+"\""); + return nas_err("dlsym","cannot find get function"); + // get function pointer by name + void* ptr=(void*)((getptr)func)(sym.str().c_str()); + if(!ptr) + return nas_err("dlsym","cannot find function <"+sym.str()+">"); var ret=ngc.alloc(vm_obj); - ret.obj().set(nas_obj::faddr,func); + ret.obj().set(nas_obj::faddr,ptr); return ret; } var builtin_dlclose(var* local,gc& ngc) @@ -918,9 +923,8 @@ var builtin_dlcall(var* local,gc& ngc) var args=local[2]; if(!fp.objchk(nas_obj::faddr)) return nas_err("dlcall","\"funcptr\" is not a valid function pointer"); - typedef var (*externs)(std::vector&,gc&); - externs func=(externs)fp.obj().ptr; - return func(args.vec().elems,ngc); + auto& vec=args.vec().elems; + return ((mod)fp.obj().ptr)(vec.data(),vec.size(),&ngc); } var builtin_platform(var* local,gc& ngc) diff --git a/nasal_gc.h b/nasal_gc.h index 3fa21d6..8caa574 100644 --- a/nasal_gc.h +++ b/nasal_gc.h @@ -460,16 +460,18 @@ struct gc } mctx; /* runtime context */ - u32& pc; // program counter - var*& localr;// local scope register - var*& memr; // used for mem_call - var& funcr; // function register - var& upvalr;// upvalue register - var*& canary;// avoid stackoverflow - var*& top; // stack top - var* stack; // stack pointer - nas_co* cort;// running coroutine - var temp; // temporary place used in builtin/module functions + u32& pc; // program counter + var*& localr; // local scope register + var*& memr; // used for mem_call + var& funcr; // function register + var& upvalr; // upvalue register + var*& canary; // avoid stackoverflow + var*& top; // stack top + var* stack; // stack pointer + nas_co* cort; // running coroutine + + /* native function used */ + var temp; // temporary place used in builtin/module functions /* constants and memory pool */ std::vector strs; // reserved address for const vm_str @@ -720,4 +722,8 @@ var nas_err(const string& err_f,const string& info) std::cerr<<"[vm] "<& ref=top[-1].vec().elems; + auto& ref=top[-1].vec().elems; if((usize)(++top[0].cnt())>=ref.size()) { pc=imm[pc]-1; @@ -800,8 +800,8 @@ inline void vm::o_slc2() { var val2=(top--)[0]; var val1=(top--)[0]; - std::vector& ref=top[-1].vec().elems; - std::vector& aim=top[0].vec().elems; + auto& ref=top[-1].vec().elems; + auto& aim=top[0].vec().elems; u8 type1=val1.type,type2=val2.type; i32 num1=val1.tonum(); diff --git a/test/httptest.nas b/test/httptest.nas index 97fd283..347bf23 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -5,7 +5,10 @@ var http=func(){ return { establish:func(ip,port){ sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP); - socket.bind(sd,ip,port); + if(socket.bind(sd,ip,port)<0){ + println("failed to bind socket "~sd~" at IP: "~ip~" port: "~port~"."); + return; + } socket.listen(sd,1); println("[",os.time(),"] start server at [",ip,":",port,"]"); }, @@ -40,7 +43,6 @@ var http=func(){ }(); http.establish("127.0.0.1",8080); - var highlight_style="