🚀 add coroutine library(beta) and lib function settimer&maketimestamp
This commit is contained in:
parent
d567f5abf8
commit
120ceb429a
130
README.md
130
README.md
|
@ -43,7 +43,8 @@
|
|||
* [v6.5](#version-65-vm-last-update-2021624)
|
||||
* [v7.0](#version-70-vm-last-update-2021108)
|
||||
* [v8.0](#version-80-vm-last-update-2022212)
|
||||
* [v9.0](#version-90-vm-latest)
|
||||
* [v9.0](#version-90-vm-last-update-2022518)
|
||||
* [v10.0](#version-100-vm-latest)
|
||||
* [__Benchmark__](#benchmark)
|
||||
* [v6.5 (i5-8250U windows 10)](#version-65-i5-8250u-windows10-2021619)
|
||||
* [v6.5 (i5-8250U ubuntu-WSL)](#version-70-i5-8250u-ubuntu-wsl-on-windows10-2021629)
|
||||
|
@ -1246,7 +1247,7 @@ The format of output information of bytecodes changes to this:
|
|||
Delete `op_pone` and `op_pzero`.
|
||||
Both of them are meaningless and will be replaced by `op_pnum`.
|
||||
|
||||
### version 9.0 vm (latest)
|
||||
### version 9.0 vm (last update 2022/5/18)
|
||||
|
||||
2022/2/12 update:
|
||||
|
||||
|
@ -1343,6 +1344,131 @@ func <0x2c4>:
|
|||
0x000002ca: 03 00 00 00 23 loadg 0x23
|
||||
```
|
||||
|
||||
### version 10.0 vm (latest)
|
||||
|
||||
2022/5/19 update:
|
||||
|
||||
Now we add coroutine in this runtime:
|
||||
|
||||
```javascript
|
||||
var coroutine={
|
||||
create: func(function){return __builtin_cocreate;},
|
||||
resume: func(co) {return __builtin_coresume;},
|
||||
yield: func(args...) {return __builtin_coyield; },
|
||||
status: func(co) {return __builtin_costatus;},
|
||||
running:func() {return __builtin_corun; }
|
||||
};
|
||||
```
|
||||
|
||||
`coroutine.create` is used to create a new coroutine object using a function.
|
||||
But this coroutine will not run immediately.
|
||||
|
||||
`coroutine.resume` is used to continue running a coroutine.
|
||||
|
||||
`coroutine.yield` is used to interrupt the running of a coroutine and throw some values.
|
||||
These values will be accepted and returned by `coroutine.resume`.
|
||||
And `coroutine.yield` it self returns `vm_nil` in the coroutine function.
|
||||
|
||||
`coroutine.status` is used to see the status of a coroutine.
|
||||
There are 3 types of status:`suspended` means waiting for running,`running` means is running,`dead` means finished running.
|
||||
|
||||
`coroutine.running` is used to judge if there is a coroutine running now.
|
||||
|
||||
__CAUTION:__ coroutine should not be created or running inside another coroutine.
|
||||
|
||||
__We will explain how resume and yield work here:__
|
||||
|
||||
When `op_callb` is called, the stack frame is like this:
|
||||
|
||||
```C++
|
||||
+----------------------------+(main stack)
|
||||
| old pc(vm_ret) | <- top[0]
|
||||
+----------------------------+
|
||||
| old localr(vm_addr) | <- top[-1]
|
||||
+----------------------------+
|
||||
| local scope(nasal_ref) |
|
||||
| ... |
|
||||
+----------------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
In `op_callb`'s progress, next step the stack frame is:
|
||||
|
||||
```C++
|
||||
+----------------------------+(main stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------------+
|
||||
| local scope(nasal_ref) |
|
||||
| ... |
|
||||
+----------------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
Then we call `resume`, this function will change stack.
|
||||
As we can see, coroutine stack already has some values on it,
|
||||
but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`.
|
||||
|
||||
So for safe running, `resume` will return `gc.top[0]`.
|
||||
`op_callb` will do `top[0]=resume()`, so the value does not change.
|
||||
|
||||
```C++
|
||||
+----------------------------+(coroutine stack)
|
||||
| pc:0(vm_ret) | <- now gc.top[0]
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
When we call `yield`, the function will do like this.
|
||||
And we find that `op_callb` has put the `nil` at the top.
|
||||
but where is the returned `local[1]` sent?
|
||||
|
||||
```C++
|
||||
+----------------------------+(coroutine stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------------+
|
||||
| local scope(nasal_ref) |
|
||||
| ... |
|
||||
+----------------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
When `builtin_coyield` is finished, the stack is set to main stack,
|
||||
and the returned `local[1]` in fact is set to the top of the main stack by `op_callb`:
|
||||
|
||||
```C++
|
||||
+----------------------------+(main stack)
|
||||
| return_value(nasal_ref) |
|
||||
+----------------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------------+
|
||||
| local scope(nasal_ref) |
|
||||
| ... |
|
||||
+----------------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
so the main progress feels the value on the top is the returned value of `resume`.
|
||||
but in fact the `resume`'s returned value is set on coroutine stack.
|
||||
so we conclude this:
|
||||
|
||||
```C++
|
||||
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
|
||||
yield (coroutine->main) return a vector. main.top[0] = vector;
|
||||
```
|
||||
|
||||
## Benchmark
|
||||
|
||||

|
||||
|
|
31
lib.nas
31
lib.nas
|
@ -267,6 +267,27 @@ var assert=func(condition,message="assertion failed!"){
|
|||
die(message);
|
||||
}
|
||||
|
||||
# settimer alows infinite loop running a function with a time interval
|
||||
var settimer=func(f,interval,realtime=1){
|
||||
while(1){
|
||||
unix.sleep(interval);
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
# get time stamp, this will return a timestamp object
|
||||
var maketimestamp=func(){
|
||||
var t=0;
|
||||
var millisec=func(){
|
||||
return __builtin_millisec;
|
||||
}
|
||||
return {
|
||||
stamp:func(){t=millisec();},
|
||||
elapsedMSec:func(){return millisec()-t;},
|
||||
elapsedUSec:func(){return (millisec()-t)*1000;}
|
||||
};
|
||||
}
|
||||
|
||||
# md5
|
||||
var md5=func(str){
|
||||
return __builtin_md5(str);
|
||||
|
@ -466,4 +487,12 @@ var closure=func(function,level=1){
|
|||
|
||||
var compile=func(code,filename="<compile>"){
|
||||
die("this runtime uses static code generator");
|
||||
}
|
||||
}
|
||||
|
||||
var coroutine={
|
||||
create: func(function){return __builtin_cocreate;},
|
||||
resume: func(co) {return __builtin_coresume;},
|
||||
yield: func(args...) {return __builtin_coyield; },
|
||||
status: func(co) {return __builtin_costatus;},
|
||||
running:func() {return __builtin_corun; }
|
||||
};
|
3
nasal.h
3
nasal.h
|
@ -1,7 +1,7 @@
|
|||
#ifndef __NASAL_H__
|
||||
#define __NASAL_H__
|
||||
|
||||
#define __nasver "9.0"
|
||||
#define __nasver "10.0"
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
|
@ -26,6 +26,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
|
100
nasal_builtin.h
100
nasal_builtin.h
|
@ -87,6 +87,12 @@ nas_native(builtin_dlcall);
|
|||
nas_native(builtin_platform);
|
||||
nas_native(builtin_gc);
|
||||
nas_native(builtin_md5);
|
||||
nas_native(builtin_cocreate);
|
||||
nas_native(builtin_coresume);
|
||||
nas_native(builtin_coyield);
|
||||
nas_native(builtin_costatus);
|
||||
nas_native(builtin_corun);
|
||||
nas_native(builtin_millisec);
|
||||
|
||||
nasal_ref builtin_err(const char* func_name,const std::string& info)
|
||||
{
|
||||
|
@ -180,6 +186,12 @@ struct
|
|||
{"__builtin_platform",builtin_platform},
|
||||
{"__builtin_gc", builtin_gc },
|
||||
{"__builtin_md5", builtin_md5 },
|
||||
{"__builtin_cocreate",builtin_cocreate},
|
||||
{"__builtin_coresume",builtin_coresume},
|
||||
{"__builtin_coyield", builtin_coyield },
|
||||
{"__builtin_costatus",builtin_costatus},
|
||||
{"__builtin_corun" ,builtin_corun },
|
||||
{"__builtin_millisec",builtin_millisec},
|
||||
{nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -200,6 +212,7 @@ nasal_ref builtin_print(nasal_ref* local,nasal_gc& gc)
|
|||
case vm_hash: i.hash().print(); break;
|
||||
case vm_func: std::cout<<"func(...){...}"; break;
|
||||
case vm_obj: std::cout<<"<object>"; break;
|
||||
case vm_co: std::cout<<"<coroutine>"; break;
|
||||
}
|
||||
std::cout<<std::flush;
|
||||
// generate return value
|
||||
|
@ -657,6 +670,7 @@ nasal_ref builtin_type(nasal_ref* local,nasal_gc& gc)
|
|||
case vm_hash: ret.str()="hash"; break;
|
||||
case vm_func: ret.str()="func"; break;
|
||||
case vm_obj: ret.str()="obj"; break;
|
||||
case vm_co: ret.str()="coroutine";break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1182,7 +1196,7 @@ nasal_ref builtin_dlsym(nasal_ref* local,nasal_gc& gc)
|
|||
if(!func)
|
||||
return builtin_err("dlsym","cannot find symbol \""+sym.str()+"\"");
|
||||
nasal_ref ret=gc.alloc(vm_obj);
|
||||
ret.obj().type=nasal_obj::externfunc;
|
||||
ret.obj().type=nasal_obj::faddr;
|
||||
ret.obj().ptr=func;
|
||||
return ret;
|
||||
}
|
||||
|
@ -1203,7 +1217,7 @@ nasal_ref builtin_dlcall(nasal_ref* local,nasal_gc& gc)
|
|||
{
|
||||
nasal_ref funcptr=local[1];
|
||||
nasal_ref args=local[2];
|
||||
if(!funcptr.objchk(nasal_obj::externfunc))
|
||||
if(!funcptr.objchk(nasal_obj::faddr))
|
||||
return builtin_err("dlcall","\"funcptr\" is not a valid function pointer");
|
||||
typedef nasal_ref (*extern_func)(std::vector<nasal_ref>&,nasal_gc&);
|
||||
extern_func func=(extern_func)funcptr.obj().ptr;
|
||||
|
@ -1321,4 +1335,86 @@ nasal_ref builtin_md5(nasal_ref* local,nasal_gc& gc)
|
|||
res.str()=md5(str.str());
|
||||
return res;
|
||||
}
|
||||
|
||||
nasal_ref builtin_cocreate(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
// +-----------------+
|
||||
// | old pc | <- top[0]
|
||||
// +-----------------+
|
||||
// | old localr | <- top[-1]
|
||||
// +-----------------+
|
||||
// | local scope |
|
||||
// | ... |
|
||||
// +-----------------+ <- local pointer stored in localr
|
||||
// | old funcr | <- old function stored in funcr
|
||||
// +-----------------+
|
||||
nasal_ref func=local[1];
|
||||
if(func.type!=vm_func)
|
||||
return builtin_err("coroutine::create","must use a function to create coroutine");
|
||||
if(gc.coroutine)
|
||||
return builtin_err("coroutine::create","cannot create another coroutine in a coroutine");
|
||||
nasal_ref co=gc.alloc(vm_co);
|
||||
nasal_co& coroutine=co.co();
|
||||
coroutine.pc=func.func().entry-1;
|
||||
|
||||
coroutine.top[0]=nil;
|
||||
coroutine.localr=coroutine.top+1;
|
||||
coroutine.top=coroutine.localr+func.func().lsize;
|
||||
coroutine.localr[0]=func.func().local[0];
|
||||
coroutine.top[0]={vm_addr,(nasal_ref*)nullptr};
|
||||
coroutine.top++;
|
||||
coroutine.top[0]={vm_ret,(uint32_t)0};
|
||||
|
||||
coroutine.funcr=func;
|
||||
coroutine.status=nasal_co::suspended;
|
||||
|
||||
return co;
|
||||
}
|
||||
nasal_ref builtin_coresume(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
if(gc.coroutine)
|
||||
return builtin_err("coroutine::resume","cannot start another coroutine when one is running");
|
||||
nasal_ref co=local[1];
|
||||
if(co.type!=vm_co)
|
||||
return builtin_err("coroutine::resume","must use a coroutine object");
|
||||
if(co.co().status==nasal_co::dead)
|
||||
return nil;
|
||||
gc.ctxchg(co.co());
|
||||
return gc.top[0];
|
||||
}
|
||||
nasal_ref builtin_coyield(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
if(!gc.coroutine)
|
||||
return builtin_err("coroutine::yield","cannot yield, no coroutine is running");
|
||||
gc.ctxreserve();
|
||||
// this will set to main stack top
|
||||
// then builtin_coresume will return it
|
||||
return local[1];
|
||||
}
|
||||
nasal_ref builtin_costatus(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
nasal_ref co=local[1];
|
||||
if(co.type!=vm_co)
|
||||
return builtin_err("coroutine::status","must use a coroutine object");
|
||||
nasal_ref res=gc.alloc(vm_str);
|
||||
switch(co.co().status)
|
||||
{
|
||||
case nasal_co::suspended: res.str()="suspended";break;
|
||||
case nasal_co::running: res.str()="running"; break;
|
||||
case nasal_co::dead: res.str()="dead"; break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
nasal_ref builtin_corun(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
if(gc.coroutine)
|
||||
return one;
|
||||
return zero;
|
||||
}
|
||||
nasal_ref builtin_millisec(nasal_ref* local,nasal_gc& gc)
|
||||
{
|
||||
timeb now;
|
||||
ftime(&now);
|
||||
return {vm_num,(double)(now.time*1000+now.millitm)};
|
||||
}
|
||||
#endif
|
|
@ -94,7 +94,7 @@ void nasal_dbg::stepinfo()
|
|||
end=(1+(pc>>3))<<3;
|
||||
for(uint32_t i=begin;i<end && bytecode[i].op!=op_exit;++i)
|
||||
bytecodeinfo(i==pc?"-->\t":" \t",i);
|
||||
stackinfo(5);
|
||||
stackinfo(10);
|
||||
}
|
||||
|
||||
void nasal_dbg::interact()
|
||||
|
@ -135,8 +135,9 @@ void nasal_dbg::interact()
|
|||
printf("[%zu] %s\n",i,files[i].c_str());
|
||||
else if(res[0]=="g" || res[0]=="global")
|
||||
global_state();
|
||||
else if(res[0]=="l" || res[0]=="local")
|
||||
else if(res[0]=="l" || res[0]=="local"){
|
||||
local_state();
|
||||
}
|
||||
else if(res[0]=="u" || res[0]=="upval")
|
||||
upval_state();
|
||||
else if(res[0]=="a" || res[0]=="all")
|
||||
|
@ -208,13 +209,13 @@ void nasal_dbg::run(
|
|||
goto *code[pc];
|
||||
|
||||
vmexit:
|
||||
if(gc.top>=canary)
|
||||
if(top>=canary)
|
||||
die("stack overflow");
|
||||
gc.clear();
|
||||
imm.clear();
|
||||
printf("[debug] debugger exited\n");
|
||||
return;
|
||||
#define dbg(op) {interact();op();if(gc.top<canary)goto *code[++pc];goto vmexit;}
|
||||
#define dbg(op) {interact();op();if(top<canary)goto *code[++pc];goto vmexit;}
|
||||
|
||||
intg: dbg(opr_intg );
|
||||
intl: dbg(opr_intl );
|
||||
|
|
165
nasal_gc.h
165
nasal_gc.h
|
@ -17,6 +17,7 @@ enum nasal_type
|
|||
vm_hash,
|
||||
vm_upval,
|
||||
vm_obj,
|
||||
vm_co,
|
||||
vm_type_size
|
||||
};
|
||||
|
||||
|
@ -37,7 +38,8 @@ const uint32_t initialize[vm_type_size]=
|
|||
128, // vm_vec
|
||||
64, // vm_hash
|
||||
512, // vm_upval
|
||||
16 // vm_obj
|
||||
16, // vm_obj
|
||||
0 // vm_co
|
||||
};
|
||||
const uint32_t increment[vm_type_size]=
|
||||
{
|
||||
|
@ -54,7 +56,8 @@ const uint32_t increment[vm_type_size]=
|
|||
8192,// vm_vec
|
||||
1024,// vm_hash
|
||||
128, // vm_upval
|
||||
256 // vm_obj
|
||||
256, // vm_obj
|
||||
16 // vm_co
|
||||
};
|
||||
|
||||
struct nasal_vec; // vector
|
||||
|
@ -62,8 +65,10 @@ struct nasal_hash; // hashmap(dict)
|
|||
struct nasal_func; // function(lambda)
|
||||
struct nasal_upval;// upvalue
|
||||
struct nasal_obj; // special objects
|
||||
struct nasal_co; // coroutine
|
||||
struct nasal_val; // nasal_val includes gc-managed types
|
||||
|
||||
|
||||
struct nasal_ref
|
||||
{
|
||||
uint8_t type;
|
||||
|
@ -112,6 +117,7 @@ struct nasal_ref
|
|||
inline nasal_func& func();
|
||||
inline nasal_upval& upval();
|
||||
inline nasal_obj& obj ();
|
||||
inline nasal_co& co ();
|
||||
};
|
||||
|
||||
struct nasal_vec
|
||||
|
@ -172,7 +178,7 @@ struct nasal_obj
|
|||
file=1,
|
||||
dir,
|
||||
dylib,
|
||||
externfunc
|
||||
faddr
|
||||
};
|
||||
/* RAII constructor */
|
||||
/* new object is initialized when creating */
|
||||
|
@ -195,6 +201,50 @@ struct nasal_obj
|
|||
}
|
||||
};
|
||||
|
||||
struct nasal_co
|
||||
{
|
||||
enum coroutine_stat
|
||||
{
|
||||
suspended,
|
||||
running,
|
||||
dead
|
||||
};
|
||||
static const uint32_t depth=1024;
|
||||
nasal_ref stack[depth];
|
||||
|
||||
uint32_t pc;
|
||||
nasal_ref* top;
|
||||
nasal_ref* canary;
|
||||
nasal_ref* localr;
|
||||
nasal_ref* memr;
|
||||
nasal_ref funcr;
|
||||
|
||||
uint32_t status;
|
||||
nasal_co():
|
||||
pc(0),
|
||||
top(stack),
|
||||
canary(stack+depth-1),
|
||||
localr(nullptr),
|
||||
memr(nullptr),
|
||||
funcr({vm_nil,(double)0}),
|
||||
status(nasal_co::suspended)
|
||||
{
|
||||
for(uint32_t i=0;i<depth;++i)
|
||||
stack[i]={vm_nil,(double)0};
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
for(uint32_t i=0;i<depth;++i)
|
||||
stack[i]={vm_nil,(double)0};
|
||||
pc=0;
|
||||
localr=nullptr;
|
||||
memr=nullptr;
|
||||
top=stack;
|
||||
status=nasal_co::suspended;
|
||||
funcr={vm_nil,(double)0};
|
||||
}
|
||||
};
|
||||
|
||||
const uint8_t GC_UNCOLLECTED=0;
|
||||
const uint8_t GC_COLLECTED =1;
|
||||
const uint8_t GC_FOUND =2;
|
||||
|
@ -211,6 +261,7 @@ struct nasal_val
|
|||
nasal_func* func;
|
||||
nasal_upval* upval;
|
||||
nasal_obj* obj;
|
||||
nasal_co* co;
|
||||
}ptr;
|
||||
|
||||
nasal_val(uint8_t);
|
||||
|
@ -327,6 +378,7 @@ nasal_val::nasal_val(uint8_t val_type)
|
|||
case vm_func: ptr.func=new nasal_func; break;
|
||||
case vm_upval:ptr.upval=new nasal_upval;break;
|
||||
case vm_obj: ptr.obj=new nasal_obj; break;
|
||||
case vm_co: ptr.co=new nasal_co; break;
|
||||
}
|
||||
}
|
||||
nasal_val::~nasal_val()
|
||||
|
@ -339,6 +391,7 @@ nasal_val::~nasal_val()
|
|||
case vm_func: delete ptr.func; break;
|
||||
case vm_upval:delete ptr.upval;break;
|
||||
case vm_obj: delete ptr.obj; break;
|
||||
case vm_co: delete ptr.co; break;
|
||||
}
|
||||
type=vm_nil;
|
||||
}
|
||||
|
@ -371,6 +424,7 @@ void nasal_ref::print()
|
|||
case vm_hash: hash().print(); break;
|
||||
case vm_func: std::cout<<"func(..){..}";break;
|
||||
case vm_obj: std::cout<<"<object>"; break;
|
||||
case vm_co: std::cout<<"<coroutine>"; break;
|
||||
}
|
||||
}
|
||||
bool nasal_ref::objchk(uint32_t objtype)
|
||||
|
@ -387,6 +441,7 @@ inline nasal_hash& nasal_ref::hash (){return *value.gcobj->ptr.hash; }
|
|||
inline nasal_func& nasal_ref::func (){return *value.gcobj->ptr.func; }
|
||||
inline nasal_upval& nasal_ref::upval(){return *value.gcobj->ptr.upval;}
|
||||
inline nasal_obj& nasal_ref::obj (){return *value.gcobj->ptr.obj; }
|
||||
inline nasal_co& nasal_ref::co (){return *value.gcobj->ptr.co; }
|
||||
|
||||
const nasal_ref zero={vm_num,(double)0};
|
||||
const nasal_ref one ={vm_num,(double)1};
|
||||
|
@ -395,9 +450,29 @@ const nasal_ref nil ={vm_nil,(double)0};
|
|||
struct nasal_gc
|
||||
{
|
||||
static const uint32_t stack_depth=8192; // depth of value stack
|
||||
nasal_ref funcr; // function register
|
||||
nasal_ref stack[stack_depth]; // the last one is reserved to avoid stack overflow
|
||||
struct
|
||||
{
|
||||
nasal_ref stack[stack_depth];
|
||||
uint32_t pc;
|
||||
nasal_ref* localr;
|
||||
nasal_ref* memr;
|
||||
nasal_ref* canary;
|
||||
nasal_ref funcr;
|
||||
nasal_ref* top;
|
||||
} main_ctx;
|
||||
nasal_ref* global; // global values pointer(this should not be changed)
|
||||
|
||||
/* runtime context */
|
||||
uint32_t pc; // program counter
|
||||
nasal_ref* top; // stack top
|
||||
nasal_ref* localr; // local scope register
|
||||
nasal_ref* memr; // used for mem_call
|
||||
nasal_ref funcr; // function register
|
||||
nasal_ref* canary; // avoid stackoverflow
|
||||
nasal_ref* stack; // stack pointer
|
||||
nasal_co* coroutine; // running coroutin
|
||||
|
||||
/* constants and memory pool */
|
||||
std::vector<nasal_ref> strs; // reserved address for const vm_str
|
||||
std::vector<nasal_val*> memory; // gc memory
|
||||
std::queue<nasal_val*> free_list[vm_type_size]; // gc free list
|
||||
|
@ -407,6 +482,8 @@ struct nasal_gc
|
|||
/* if new functions generated in local scope */
|
||||
/* they will share the same upvalue stored here */
|
||||
std::vector<nasal_ref> upvalue;
|
||||
|
||||
/* values for analysis */
|
||||
uint64_t size[vm_type_size];
|
||||
uint64_t count[vm_type_size];
|
||||
void mark();
|
||||
|
@ -416,17 +493,30 @@ struct nasal_gc
|
|||
void info();
|
||||
nasal_ref alloc(const uint8_t);
|
||||
nasal_ref builtin_alloc(const uint8_t);
|
||||
void ctxchg(nasal_co&);
|
||||
void ctxreserve();
|
||||
};
|
||||
|
||||
/* gc functions */
|
||||
void nasal_gc::mark()
|
||||
{
|
||||
std::queue<nasal_ref> bfs;
|
||||
bfs.push(funcr);
|
||||
|
||||
for(auto& i:upvalue)
|
||||
bfs.push(i);
|
||||
for(nasal_ref* i=stack;i<=top;++i)
|
||||
bfs.push(*i);
|
||||
if(!coroutine)
|
||||
{
|
||||
for(nasal_ref* i=stack;i<=top;++i)
|
||||
bfs.push(*i);
|
||||
bfs.push(funcr);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(nasal_ref* i=main_ctx.stack;i<=main_ctx.top;++i)
|
||||
bfs.push(*i);
|
||||
bfs.push(main_ctx.funcr);
|
||||
}
|
||||
|
||||
while(!bfs.empty())
|
||||
{
|
||||
nasal_ref tmp=bfs.front();
|
||||
|
@ -452,6 +542,12 @@ void nasal_gc::mark()
|
|||
case vm_upval:
|
||||
for(auto& i:tmp.upval().elems)
|
||||
bfs.push(i);
|
||||
break;
|
||||
case vm_co:
|
||||
bfs.push(tmp.co().funcr);
|
||||
for(nasal_ref* i=tmp.co().stack;i<=tmp.co().top;++i)
|
||||
bfs.push(*i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -469,6 +565,7 @@ void nasal_gc::sweep()
|
|||
case vm_func: i->ptr.func->clear(); break;
|
||||
case vm_upval:i->ptr.upval->clear(); break;
|
||||
case vm_obj: i->ptr.obj->clear(); break;
|
||||
case vm_co: i->ptr.co->clear(); break;
|
||||
}
|
||||
free_list[i->type].push(i);
|
||||
i->mark=GC_COLLECTED;
|
||||
|
@ -491,7 +588,10 @@ void nasal_gc::init(const std::vector<std::string>& s)
|
|||
memory.push_back(tmp);
|
||||
free_list[i].push(tmp);
|
||||
}
|
||||
top=stack;
|
||||
global=main_ctx.stack;
|
||||
stack=main_ctx.stack;
|
||||
top=main_ctx.stack;
|
||||
coroutine=nullptr;
|
||||
// init constant strings
|
||||
strs.resize(s.size());
|
||||
for(uint32_t i=0;i<strs.size();++i)
|
||||
|
@ -520,7 +620,8 @@ void nasal_gc::info()
|
|||
"null ","cnt ","addr ",
|
||||
"ret ","nil ","num ",
|
||||
"str ","func ","vec ",
|
||||
"hash ","upval","obj "
|
||||
"hash ","upval","obj ",
|
||||
"co "
|
||||
};
|
||||
std::cout<<"\ngarbage collector info:\n";
|
||||
for(uint8_t i=vm_str;i<vm_type_size;++i)
|
||||
|
@ -573,4 +674,48 @@ nasal_ref nasal_gc::builtin_alloc(uint8_t type)
|
|||
free_list[type].pop();
|
||||
return ret;
|
||||
}
|
||||
void nasal_gc::ctxchg(nasal_co& context)
|
||||
{
|
||||
main_ctx.pc=pc;
|
||||
main_ctx.top=top;
|
||||
main_ctx.localr=localr;
|
||||
main_ctx.memr=memr;
|
||||
main_ctx.funcr=funcr;
|
||||
main_ctx.canary=canary;
|
||||
|
||||
pc=context.pc;
|
||||
top=context.top;
|
||||
localr=context.localr;
|
||||
memr=context.memr;
|
||||
funcr=context.funcr;
|
||||
canary=context.canary;
|
||||
stack=context.stack;
|
||||
coroutine=&context;
|
||||
|
||||
upvalue.push_back(nil);
|
||||
coroutine->status=nasal_co::running;
|
||||
}
|
||||
void nasal_gc::ctxreserve()
|
||||
{
|
||||
if(coroutine->status!=nasal_co::dead)
|
||||
coroutine->status=nasal_co::suspended;
|
||||
// pc=0 means this coroutine is finished, so we use entry to reset it
|
||||
coroutine->pc=pc==0?coroutine->funcr.func().entry:pc;
|
||||
coroutine->top=top;
|
||||
coroutine->localr=localr;
|
||||
coroutine->memr=memr;
|
||||
coroutine->funcr=funcr;
|
||||
coroutine->canary=canary;
|
||||
|
||||
pc=main_ctx.pc;
|
||||
top=main_ctx.top;
|
||||
localr=main_ctx.localr;
|
||||
memr=main_ctx.memr;
|
||||
funcr=main_ctx.funcr;
|
||||
canary=main_ctx.canary;
|
||||
stack=main_ctx.stack;
|
||||
coroutine=nullptr;
|
||||
|
||||
upvalue.pop_back();
|
||||
}
|
||||
#endif
|
417
nasal_vm.h
417
nasal_vm.h
|
@ -5,20 +5,23 @@ class nasal_vm
|
|||
{
|
||||
protected:
|
||||
/* values of nasal_vm */
|
||||
uint32_t pc; // program counter
|
||||
nasal_ref* localr; // local scope register
|
||||
uint32_t& pc; // program counter
|
||||
nasal_ref*& global; // global scope register
|
||||
nasal_ref*& localr; // local scope register
|
||||
nasal_ref*& memr; // used for mem_call
|
||||
nasal_ref& funcr; // function register
|
||||
nasal_ref*& canary; // avoid stackoverflow
|
||||
nasal_ref*& top; // stack top
|
||||
/* constant */
|
||||
const double* num_table;// const numbers, ref from nasal_codegen
|
||||
const std::string* str_table;// const symbols, ref from nasal_codegen
|
||||
std::vector<uint32_t> imm; // immediate number
|
||||
nasal_ref* mem_addr; // used for mem_call
|
||||
/* garbage collector */
|
||||
nasal_gc gc;
|
||||
/* values used for debug */
|
||||
size_t files_size;
|
||||
const std::string* files; // ref from nasal_import
|
||||
const opcode* bytecode; // ref from nasal_codegen
|
||||
/* canary to avoid stackoverflow */
|
||||
nasal_ref* canary;
|
||||
|
||||
void init(
|
||||
const std::vector<std::string>&,
|
||||
|
@ -115,6 +118,14 @@ protected:
|
|||
void opr_mcallh();
|
||||
void opr_ret();
|
||||
public:
|
||||
nasal_vm():
|
||||
pc(gc.pc),
|
||||
global(gc.global),
|
||||
localr(gc.localr),
|
||||
memr(gc.memr),
|
||||
funcr(gc.funcr),
|
||||
canary(gc.canary),
|
||||
top(gc.top){}
|
||||
void run(
|
||||
const nasal_codegen&,
|
||||
const nasal_import&,
|
||||
|
@ -136,9 +147,9 @@ void nasal_vm::init(
|
|||
files_size=filenames.size();
|
||||
/* set canary and program counter */
|
||||
canary=gc.stack+nasal_gc::stack_depth-1; // gc.stack[nasal_gc::stack_depth-1]
|
||||
mem_addr=nullptr;
|
||||
pc=0;
|
||||
memr=nullptr;
|
||||
localr=nullptr;
|
||||
pc=0;
|
||||
}
|
||||
void nasal_vm::valinfo(nasal_ref& val)
|
||||
{
|
||||
|
@ -161,6 +172,7 @@ void nasal_vm::valinfo(nasal_ref& val)
|
|||
case vm_vec: printf("| vec | <0x" PRTHEX64 "> [%zu val]\n",(uint64_t)p,val.vec().size());break;
|
||||
case vm_hash: printf("| hash | <0x" PRTHEX64 "> {%zu val}\n",(uint64_t)p,val.hash().size());break;
|
||||
case vm_obj: printf("| obj | <0x" PRTHEX64 "> obj:0x" PRTHEX64 "\n",(uint64_t)p,(uint64_t)val.obj().ptr);break;
|
||||
case vm_co: printf("| co | <0x" PRTHEX64 "> coroutine\n",(uint64_t)p);break;
|
||||
default: printf("| err | <0x" PRTHEX64 "> unknown object\n",(uint64_t)p);break;
|
||||
}
|
||||
}
|
||||
|
@ -220,10 +232,10 @@ void nasal_vm::bytecodeinfo(const char* header,const uint32_t p)
|
|||
void nasal_vm::traceback()
|
||||
{
|
||||
uint32_t global_size=bytecode[0].num; // bytecode[0] is op_intg
|
||||
nasal_ref* top=gc.top;
|
||||
nasal_ref* t=top;
|
||||
nasal_ref* bottom=gc.stack+global_size;
|
||||
std::stack<uint32_t> ret;
|
||||
for(nasal_ref* i=bottom;i<=top;++i)
|
||||
for(nasal_ref* i=bottom;i<=t;++i)
|
||||
if(i->type==vm_ret)
|
||||
ret.push(i->ret());
|
||||
// push pc to ret stack to store the position program crashed
|
||||
|
@ -248,38 +260,38 @@ void nasal_vm::traceback()
|
|||
void nasal_vm::stackinfo(const uint32_t limit=10)
|
||||
{
|
||||
/* bytecode[0] is op_intg, the .num is the global size */
|
||||
uint32_t gsize=bytecode[0].num;
|
||||
nasal_ref* top=gc.top;
|
||||
uint32_t gsize=gc.stack==gc.main_ctx.stack?bytecode[0].num:0;
|
||||
nasal_ref* t=top;
|
||||
nasal_ref* bottom=gc.stack+gsize;
|
||||
printf("vm stack(0x" PRTHEX64 "<sp+%u>, limit %u, total ",(uint64_t)bottom,gsize,limit);
|
||||
if(top<bottom)
|
||||
if(t<bottom)
|
||||
{
|
||||
printf("0)\n");
|
||||
return;
|
||||
}
|
||||
printf("" PRTINT64 "):\n",(int64_t)(top-bottom+1));
|
||||
for(uint32_t i=0;i<limit && top>=bottom;++i,--top)
|
||||
printf("" PRTINT64 "):\n",(int64_t)(t-bottom+1));
|
||||
for(uint32_t i=0;i<limit && t>=bottom;++i,--t)
|
||||
{
|
||||
printf(" 0x" PRTHEX64_8 "",(uint64_t)(top-gc.stack));
|
||||
valinfo(top[0]);
|
||||
printf(" 0x" PRTHEX64_8 "",(uint64_t)(t-gc.stack));
|
||||
valinfo(t[0]);
|
||||
}
|
||||
}
|
||||
void nasal_vm::global_state()
|
||||
{
|
||||
if(!bytecode[0].num || gc.stack[0].type==vm_none) // bytecode[0].op is op_intg
|
||||
return;
|
||||
printf("global(0x" PRTHEX64 "<sp+0>):\n",(uint64_t)gc.stack);
|
||||
printf("global(0x" PRTHEX64 "<sp+0>):\n",(uint64_t)global);
|
||||
for(uint32_t i=0;i<bytecode[0].num;++i)
|
||||
{
|
||||
printf(" 0x%.8x",i);
|
||||
valinfo(gc.stack[i]);
|
||||
valinfo(global[i]);
|
||||
}
|
||||
}
|
||||
void nasal_vm::local_state()
|
||||
{
|
||||
if(!localr || !gc.funcr.func().lsize)
|
||||
if(!localr || !funcr.func().lsize)
|
||||
return;
|
||||
uint32_t lsize=gc.funcr.func().lsize;
|
||||
uint32_t lsize=funcr.func().lsize;
|
||||
printf("local(0x" PRTHEX64 "<sp+" PRTINT64 ">):\n",(uint64_t)localr,(int64_t)(localr-gc.stack));
|
||||
for(uint32_t i=0;i<lsize;++i)
|
||||
{
|
||||
|
@ -289,10 +301,10 @@ void nasal_vm::local_state()
|
|||
}
|
||||
void nasal_vm::upval_state()
|
||||
{
|
||||
if(gc.funcr.type==vm_nil || gc.funcr.func().upvalue.empty())
|
||||
if(funcr.type==vm_nil || funcr.func().upvalue.empty())
|
||||
return;
|
||||
printf("upvalue:\n");
|
||||
auto& upval=gc.funcr.func().upvalue;
|
||||
auto& upval=funcr.func().upvalue;
|
||||
for(uint32_t i=0;i<upval.size();++i)
|
||||
{
|
||||
printf(" -> upval[%u]:\n",i);
|
||||
|
@ -306,14 +318,14 @@ void nasal_vm::upval_state()
|
|||
}
|
||||
void nasal_vm::detail()
|
||||
{
|
||||
printf("maddr:\n (0x" PRTHEX64 ")\n",(uint64_t)mem_addr);
|
||||
printf("maddr:\n (0x" PRTHEX64 ")\n",(uint64_t)memr);
|
||||
printf("localr:\n (0x" PRTHEX64 ")\n",(uint64_t)localr);
|
||||
if(gc.funcr.type==vm_nil)
|
||||
if(funcr.type==vm_nil)
|
||||
printf("funcr:\n (nil)\n");
|
||||
else
|
||||
printf("funcr:\n (<0x" PRTHEX64 "> entry:0x%x)\n",
|
||||
(uint64_t)gc.funcr.value.gcobj,
|
||||
gc.funcr.func().entry);
|
||||
(uint64_t)funcr.value.gcobj,
|
||||
funcr.func().entry);
|
||||
global_state();
|
||||
local_state();
|
||||
upval_state();
|
||||
|
@ -366,37 +378,37 @@ inline void nasal_vm::opr_intg()
|
|||
{
|
||||
// global values store on stack
|
||||
for(uint32_t i=0;i<imm[pc];++i)
|
||||
(gc.top++)[0].type=vm_nil;
|
||||
--gc.top;// point to the top
|
||||
(top++)[0].type=vm_nil;
|
||||
--top;// point to the top
|
||||
}
|
||||
inline void nasal_vm::opr_intl()
|
||||
{
|
||||
gc.top[0].func().local.resize(imm[pc],nil);
|
||||
gc.top[0].func().lsize=imm[pc];
|
||||
top[0].func().local.resize(imm[pc],nil);
|
||||
top[0].func().lsize=imm[pc];
|
||||
}
|
||||
inline void nasal_vm::opr_loadg()
|
||||
{
|
||||
gc.stack[imm[pc]]=(gc.top--)[0];
|
||||
global[imm[pc]]=(top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_loadl()
|
||||
{
|
||||
localr[imm[pc]]=(gc.top--)[0];
|
||||
localr[imm[pc]]=(top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_loadu()
|
||||
{
|
||||
gc.funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff]=(gc.top--)[0];
|
||||
funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff]=(top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_pnum()
|
||||
{
|
||||
(++gc.top)[0]={vm_num,num_table[imm[pc]]};
|
||||
(++top)[0]={vm_num,num_table[imm[pc]]};
|
||||
}
|
||||
inline void nasal_vm::opr_pnil()
|
||||
{
|
||||
(++gc.top)[0]=nil;
|
||||
(++top)[0]=nil;
|
||||
}
|
||||
inline void nasal_vm::opr_pstr()
|
||||
{
|
||||
(++gc.top)[0]=gc.strs[imm[pc]];
|
||||
(++top)[0]=gc.strs[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_newv()
|
||||
{
|
||||
|
@ -404,28 +416,28 @@ inline void nasal_vm::opr_newv()
|
|||
auto& vec=newv.vec().elems;
|
||||
vec.resize(imm[pc]);
|
||||
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
|
||||
gc.top=gc.top-imm[pc]+1;
|
||||
top=top-imm[pc]+1;
|
||||
for(uint32_t i=0;i<imm[pc];++i)
|
||||
vec[i]=gc.top[i];
|
||||
gc.top[0]=newv;
|
||||
vec[i]=top[i];
|
||||
top[0]=newv;
|
||||
}
|
||||
inline void nasal_vm::opr_newh()
|
||||
{
|
||||
(++gc.top)[0]=gc.alloc(vm_hash);
|
||||
(++top)[0]=gc.alloc(vm_hash);
|
||||
}
|
||||
inline void nasal_vm::opr_newf()
|
||||
{
|
||||
(++gc.top)[0]=gc.alloc(vm_func);
|
||||
nasal_func& func=gc.top[0].func();
|
||||
(++top)[0]=gc.alloc(vm_func);
|
||||
nasal_func& func=top[0].func();
|
||||
func.entry=imm[pc];
|
||||
func.psize=1;
|
||||
|
||||
/* this means you create a new function in local scope */
|
||||
if(localr)
|
||||
{
|
||||
func.upvalue=gc.funcr.func().upvalue;
|
||||
func.upvalue=funcr.func().upvalue;
|
||||
nasal_ref upval=(gc.upvalue.back().type==vm_nil)?gc.alloc(vm_upval):gc.upvalue.back();
|
||||
upval.upval().size=gc.funcr.func().lsize;
|
||||
upval.upval().size=funcr.func().lsize;
|
||||
upval.upval().stk=localr;
|
||||
func.upvalue.push_back(upval);
|
||||
gc.upvalue.back()=upval;
|
||||
|
@ -433,40 +445,40 @@ inline void nasal_vm::opr_newf()
|
|||
}
|
||||
inline void nasal_vm::opr_happ()
|
||||
{
|
||||
gc.top[-1].hash().elems[str_table[imm[pc]]]=gc.top[0];
|
||||
--gc.top;
|
||||
top[-1].hash().elems[str_table[imm[pc]]]=top[0];
|
||||
--top;
|
||||
}
|
||||
inline void nasal_vm::opr_para()
|
||||
{
|
||||
nasal_func& func=gc.top[0].func();
|
||||
nasal_func& func=top[0].func();
|
||||
func.keys[str_table[imm[pc]]]=func.psize;// func->size has 1 place reserved for "me"
|
||||
func.local[func.psize++]={vm_none};
|
||||
}
|
||||
inline void nasal_vm::opr_defpara()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_func& func=(--gc.top)[0].func();
|
||||
nasal_ref val=top[0];
|
||||
nasal_func& func=(--top)[0].func();
|
||||
func.keys[str_table[imm[pc]]]=func.psize;// func->size has 1 place reserved for "me"
|
||||
func.local[func.psize++]=val;
|
||||
}
|
||||
inline void nasal_vm::opr_dynpara()
|
||||
{
|
||||
gc.top[0].func().dynpara=imm[pc];
|
||||
top[0].func().dynpara=imm[pc];
|
||||
}
|
||||
inline void nasal_vm::opr_unot()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref val=top[0];
|
||||
switch(val.type)
|
||||
{
|
||||
case vm_nil:gc.top[0]=one;break;
|
||||
case vm_num:gc.top[0]=val.num()?zero:one;break;
|
||||
case vm_nil:top[0]=one;break;
|
||||
case vm_num:top[0]=val.num()?zero:one;break;
|
||||
case vm_str:
|
||||
{
|
||||
double num=str2num(val.str().c_str());
|
||||
if(std::isnan(num))
|
||||
gc.top[0]={vm_num,(double)val.str().empty()};
|
||||
top[0]={vm_num,(double)val.str().empty()};
|
||||
else
|
||||
gc.top[0]=num?zero:one;
|
||||
top[0]=num?zero:one;
|
||||
}
|
||||
break;
|
||||
default:die("unot: incorrect value type");break;
|
||||
|
@ -474,12 +486,12 @@ inline void nasal_vm::opr_unot()
|
|||
}
|
||||
inline void nasal_vm::opr_usub()
|
||||
{
|
||||
gc.top[0]={vm_num,-gc.top[0].to_number()};
|
||||
top[0]={vm_num,-top[0].to_number()};
|
||||
}
|
||||
|
||||
#define op_calc(type)\
|
||||
nasal_ref val(vm_num,gc.top[-1].to_number() type gc.top[0].to_number());\
|
||||
(--gc.top)[0]=val;
|
||||
nasal_ref val(vm_num,top[-1].to_number() type top[0].to_number());\
|
||||
(--top)[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_add(){op_calc(+);}
|
||||
inline void nasal_vm::opr_sub(){op_calc(-);}
|
||||
|
@ -488,13 +500,13 @@ inline void nasal_vm::opr_div(){op_calc(/);}
|
|||
inline void nasal_vm::opr_lnk()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
val.str()=gc.top[-1].to_string()+gc.top[0].to_string();
|
||||
(--gc.top)[0]=val;
|
||||
val.str()=top[-1].to_string()+top[0].to_string();
|
||||
(--top)[0]=val;
|
||||
}
|
||||
|
||||
#define op_calc_const(type)\
|
||||
nasal_ref val(vm_num,gc.top[0].to_number() type num_table[imm[pc]]);\
|
||||
gc.top[0]=val;
|
||||
nasal_ref val(vm_num,top[0].to_number() type num_table[imm[pc]]);\
|
||||
top[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_addc(){op_calc_const(+);}
|
||||
inline void nasal_vm::opr_subc(){op_calc_const(-);}
|
||||
|
@ -503,15 +515,15 @@ inline void nasal_vm::opr_divc(){op_calc_const(/);}
|
|||
inline void nasal_vm::opr_lnkc()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
val.str()=gc.top[0].to_string()+str_table[imm[pc]];
|
||||
gc.top[0]=val;
|
||||
val.str()=top[0].to_string()+str_table[imm[pc]];
|
||||
top[0]=val;
|
||||
}
|
||||
|
||||
#define op_calc_eq(type)\
|
||||
nasal_ref val(vm_num,mem_addr[0].to_number() type gc.top[-1].to_number());\
|
||||
(--gc.top)[0]=mem_addr[0]=val;\
|
||||
mem_addr=nullptr;\
|
||||
gc.top-=imm[pc];
|
||||
nasal_ref val(vm_num,memr[0].to_number() type top[-1].to_number());\
|
||||
(--top)[0]=memr[0]=val;\
|
||||
memr=nullptr;\
|
||||
top-=imm[pc];
|
||||
|
||||
inline void nasal_vm::opr_addeq(){op_calc_eq(+);}
|
||||
inline void nasal_vm::opr_subeq(){op_calc_eq(-);}
|
||||
|
@ -520,17 +532,17 @@ inline void nasal_vm::opr_diveq(){op_calc_eq(/);}
|
|||
inline void nasal_vm::opr_lnkeq()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
val.str()=mem_addr[0].to_string()+gc.top[-1].to_string();
|
||||
(--gc.top)[0]=mem_addr[0]=val;
|
||||
mem_addr=nullptr;
|
||||
gc.top-=imm[pc];
|
||||
val.str()=memr[0].to_string()+top[-1].to_string();
|
||||
(--top)[0]=memr[0]=val;
|
||||
memr=nullptr;
|
||||
top-=imm[pc];
|
||||
}
|
||||
|
||||
#define op_calc_eq_const(type)\
|
||||
nasal_ref val(vm_num,mem_addr[0].to_number() type num_table[imm[pc]&0x7fffffff]);\
|
||||
gc.top[0]=mem_addr[0]=val;\
|
||||
mem_addr=nullptr;\
|
||||
gc.top-=(imm[pc]>>31);
|
||||
nasal_ref val(vm_num,memr[0].to_number() type num_table[imm[pc]&0x7fffffff]);\
|
||||
top[0]=memr[0]=val;\
|
||||
memr=nullptr;\
|
||||
top-=(imm[pc]>>31);
|
||||
|
||||
inline void nasal_vm::opr_addeqc(){op_calc_eq_const(+);}
|
||||
inline void nasal_vm::opr_subeqc(){op_calc_eq_const(-);}
|
||||
|
@ -539,55 +551,55 @@ inline void nasal_vm::opr_diveqc(){op_calc_eq_const(/);}
|
|||
inline void nasal_vm::opr_lnkeqc()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
val.str()=mem_addr[0].to_string()+str_table[imm[pc]&0x7fffffff];
|
||||
gc.top[0]=mem_addr[0]=val;
|
||||
mem_addr=nullptr;
|
||||
gc.top-=(imm[pc]>>31);
|
||||
val.str()=memr[0].to_string()+str_table[imm[pc]&0x7fffffff];
|
||||
top[0]=memr[0]=val;
|
||||
memr=nullptr;
|
||||
top-=(imm[pc]>>31);
|
||||
}
|
||||
|
||||
inline void nasal_vm::opr_meq()
|
||||
{
|
||||
// pop old mem_addr[0] and replace it
|
||||
// the reason why we should get mem_addr and push the old value on stack
|
||||
// pop old memr[0] and replace it
|
||||
// the reason why we should get memr and push the old value on stack
|
||||
// is that when lnkeq/lnkeqc is called, there will be
|
||||
// a new gc object vm_str which is returned by gc::alloc
|
||||
// this may cause gc, so we should temporarily put it on stack
|
||||
mem_addr[0]=(--gc.top)[0];
|
||||
mem_addr=nullptr;
|
||||
gc.top-=imm[pc];
|
||||
memr[0]=(--top)[0];
|
||||
memr=nullptr;
|
||||
top-=imm[pc];
|
||||
}
|
||||
inline void nasal_vm::opr_eq()
|
||||
{
|
||||
nasal_ref val2=gc.top[0];
|
||||
nasal_ref val1=(--gc.top)[0];
|
||||
nasal_ref val2=top[0];
|
||||
nasal_ref val1=(--top)[0];
|
||||
if(val1.type==vm_nil && val2.type==vm_nil)
|
||||
gc.top[0]=one;
|
||||
top[0]=one;
|
||||
else if(val1.type==vm_str && val2.type==vm_str)
|
||||
gc.top[0]=(val1.str()==val2.str())?one:zero;
|
||||
top[0]=(val1.str()==val2.str())?one:zero;
|
||||
else if((val1.type==vm_num || val2.type==vm_num)
|
||||
&& val1.type!=vm_nil && val2.type!=vm_nil)
|
||||
gc.top[0]=(val1.to_number()==val2.to_number())?one:zero;
|
||||
top[0]=(val1.to_number()==val2.to_number())?one:zero;
|
||||
else
|
||||
gc.top[0]=(val1==val2)?one:zero;
|
||||
top[0]=(val1==val2)?one:zero;
|
||||
}
|
||||
inline void nasal_vm::opr_neq()
|
||||
{
|
||||
nasal_ref val2=gc.top[0];
|
||||
nasal_ref val1=(--gc.top)[0];
|
||||
nasal_ref val2=top[0];
|
||||
nasal_ref val1=(--top)[0];
|
||||
if(val1.type==vm_nil && val2.type==vm_nil)
|
||||
gc.top[0]=zero;
|
||||
top[0]=zero;
|
||||
else if(val1.type==vm_str && val2.type==vm_str)
|
||||
gc.top[0]=(val1.str()!=val2.str())?one:zero;
|
||||
top[0]=(val1.str()!=val2.str())?one:zero;
|
||||
else if((val1.type==vm_num || val2.type==vm_num)
|
||||
&& val1.type!=vm_nil && val2.type!=vm_nil)
|
||||
gc.top[0]=(val1.to_number()!=val2.to_number())?one:zero;
|
||||
top[0]=(val1.to_number()!=val2.to_number())?one:zero;
|
||||
else
|
||||
gc.top[0]=(val1!=val2)?one:zero;
|
||||
top[0]=(val1!=val2)?one:zero;
|
||||
}
|
||||
|
||||
#define op_cmp(type)\
|
||||
--gc.top;\
|
||||
gc.top[0]=(gc.top[0].to_number() type gc.top[1].to_number())?one:zero;
|
||||
--top;\
|
||||
top[0]=(top[0].to_number() type top[1].to_number())?one:zero;
|
||||
|
||||
inline void nasal_vm::opr_less(){op_cmp(<);}
|
||||
inline void nasal_vm::opr_leq(){op_cmp(<=);}
|
||||
|
@ -595,7 +607,7 @@ inline void nasal_vm::opr_grt(){op_cmp(>);}
|
|||
inline void nasal_vm::opr_geq(){op_cmp(>=);}
|
||||
|
||||
#define op_cmp_const(type)\
|
||||
gc.top[0]=(gc.top[0].to_number() type num_table[imm[pc]])?one:zero;
|
||||
top[0]=(top[0].to_number() type num_table[imm[pc]])?one:zero;
|
||||
|
||||
inline void nasal_vm::opr_lessc(){op_cmp_const(<);}
|
||||
inline void nasal_vm::opr_leqc(){op_cmp_const(<=);}
|
||||
|
@ -604,7 +616,7 @@ inline void nasal_vm::opr_geqc(){op_cmp_const(>=);}
|
|||
|
||||
inline void nasal_vm::opr_pop()
|
||||
{
|
||||
--gc.top;
|
||||
--top;
|
||||
}
|
||||
inline void nasal_vm::opr_jmp()
|
||||
{
|
||||
|
@ -612,73 +624,73 @@ inline void nasal_vm::opr_jmp()
|
|||
}
|
||||
inline void nasal_vm::opr_jt()
|
||||
{
|
||||
if(condition(gc.top[0]))
|
||||
if(condition(top[0]))
|
||||
pc=imm[pc]-1;
|
||||
}
|
||||
inline void nasal_vm::opr_jf()
|
||||
{
|
||||
if(!condition(gc.top[0]))
|
||||
if(!condition(top[0]))
|
||||
pc=imm[pc]-1;
|
||||
--gc.top;
|
||||
--top;
|
||||
}
|
||||
inline void nasal_vm::opr_counter()
|
||||
{
|
||||
(++gc.top)[0]={vm_cnt,(int64_t)-1};
|
||||
if(gc.top[-1].type!=vm_vec)
|
||||
(++top)[0]={vm_cnt,(int64_t)-1};
|
||||
if(top[-1].type!=vm_vec)
|
||||
die("cnt: must use vector in forindex/foreach");
|
||||
}
|
||||
inline void nasal_vm::opr_findex()
|
||||
{
|
||||
if((size_t)(++gc.top[0].cnt())>=gc.top[-1].vec().size())
|
||||
if((size_t)(++top[0].cnt())>=top[-1].vec().size())
|
||||
{
|
||||
pc=imm[pc]-1;
|
||||
return;
|
||||
}
|
||||
gc.top[1]={vm_num,(double)gc.top[0].cnt()};
|
||||
++gc.top;
|
||||
top[1]={vm_num,(double)top[0].cnt()};
|
||||
++top;
|
||||
}
|
||||
inline void nasal_vm::opr_feach()
|
||||
{
|
||||
std::vector<nasal_ref>& ref=gc.top[-1].vec().elems;
|
||||
if((size_t)(++gc.top[0].cnt())>=ref.size())
|
||||
std::vector<nasal_ref>& ref=top[-1].vec().elems;
|
||||
if((size_t)(++top[0].cnt())>=ref.size())
|
||||
{
|
||||
pc=imm[pc]-1;
|
||||
return;
|
||||
}
|
||||
gc.top[1]=ref[gc.top[0].cnt()];
|
||||
++gc.top;
|
||||
top[1]=ref[top[0].cnt()];
|
||||
++top;
|
||||
}
|
||||
inline void nasal_vm::opr_callg()
|
||||
{
|
||||
(++gc.top)[0]=gc.stack[imm[pc]];
|
||||
(++top)[0]=global[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_calll()
|
||||
{
|
||||
(++gc.top)[0]=localr[imm[pc]];
|
||||
(++top)[0]=localr[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_upval()
|
||||
{
|
||||
(++gc.top)[0]=gc.funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff];
|
||||
(++top)[0]=funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff];
|
||||
}
|
||||
inline void nasal_vm::opr_callv()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref vec=(--gc.top)[0];
|
||||
nasal_ref val=top[0];
|
||||
nasal_ref vec=(--top)[0];
|
||||
if(vec.type==vm_vec)
|
||||
{
|
||||
gc.top[0]=vec.vec().get_val(val.to_number());
|
||||
if(gc.top[0].type==vm_none)
|
||||
top[0]=vec.vec().get_val(val.to_number());
|
||||
if(top[0].type==vm_none)
|
||||
die("callv: index out of range:"+std::to_string(val.to_number()));
|
||||
}
|
||||
else if(vec.type==vm_hash)
|
||||
{
|
||||
if(val.type!=vm_str)
|
||||
die("callv: must use string as the key");
|
||||
gc.top[0]=vec.hash().get_val(val.str());
|
||||
if(gc.top[0].type==vm_none)
|
||||
top[0]=vec.hash().get_val(val.str());
|
||||
if(top[0].type==vm_none)
|
||||
die("callv: cannot find member \""+val.str()+"\" of this hash");
|
||||
if(gc.top[0].type==vm_func)
|
||||
gc.top[0].func().local[0]=val;// 'me'
|
||||
if(top[0].type==vm_func)
|
||||
top[0].func().local[0]=val;// 'me'
|
||||
}
|
||||
else if(vec.type==vm_str)
|
||||
{
|
||||
|
@ -687,47 +699,47 @@ inline void nasal_vm::opr_callv()
|
|||
int str_size=str.length();
|
||||
if(num<-str_size || num>=str_size)
|
||||
die("callv: index out of range:"+std::to_string(val.to_number()));
|
||||
gc.top[0]={vm_num,double((uint8_t)str[num>=0? num:num+str_size])};
|
||||
top[0]={vm_num,double((uint8_t)str[num>=0? num:num+str_size])};
|
||||
}
|
||||
else
|
||||
die("callv: must call a vector/hash/string");
|
||||
}
|
||||
inline void nasal_vm::opr_callvi()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref val=top[0];
|
||||
if(val.type!=vm_vec)
|
||||
die("callvi: must use a vector");
|
||||
|
||||
// cannot use operator[],because this may cause overflow
|
||||
(++gc.top)[0]=val.vec().get_val(imm[pc]);
|
||||
if(gc.top[0].type==vm_none)
|
||||
(++top)[0]=val.vec().get_val(imm[pc]);
|
||||
if(top[0].type==vm_none)
|
||||
die("callvi: index out of range:"+std::to_string(imm[pc]));
|
||||
}
|
||||
inline void nasal_vm::opr_callh()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref val=top[0];
|
||||
if(val.type!=vm_hash)
|
||||
die("callh: must call a hash");
|
||||
|
||||
gc.top[0]=val.hash().get_val(str_table[imm[pc]]);
|
||||
if(gc.top[0].type==vm_none)
|
||||
top[0]=val.hash().get_val(str_table[imm[pc]]);
|
||||
if(top[0].type==vm_none)
|
||||
die("callh: member \""+str_table[imm[pc]]+"\" does not exist");
|
||||
|
||||
if(gc.top[0].type==vm_func)
|
||||
gc.top[0].func().local[0]=val;// 'me'
|
||||
if(top[0].type==vm_func)
|
||||
top[0].func().local[0]=val;// 'me'
|
||||
}
|
||||
inline void nasal_vm::opr_callfv()
|
||||
{
|
||||
uint32_t argc=imm[pc]; // arguments counter
|
||||
nasal_ref* local=gc.top-argc+1;// arguments begin address
|
||||
uint32_t argc=imm[pc]; // arguments counter
|
||||
nasal_ref* local=top-argc+1;// arguments begin address
|
||||
if(local[-1].type!=vm_func)
|
||||
die("callfv: must call a function");
|
||||
|
||||
auto& func=local[-1].func();
|
||||
nasal_ref tmp=local[-1];
|
||||
local[-1]=gc.funcr;
|
||||
gc.funcr=tmp;
|
||||
if(gc.top-argc+func.lsize+2>=canary) // gc.top-argc+lsize(local) +1(old pc) +1(old localr)
|
||||
local[-1]=funcr;
|
||||
funcr=tmp;
|
||||
if(top-argc+func.lsize+2>=canary) // top-argc+lsize(local) +1(old pc) +1(old localr)
|
||||
die("stack overflow");
|
||||
|
||||
uint32_t psize=func.psize-1; // parameter size is func->psize-1, 1 is reserved for "me"
|
||||
|
@ -735,7 +747,7 @@ inline void nasal_vm::opr_callfv()
|
|||
die("callfv: lack argument(s)");
|
||||
|
||||
nasal_ref dynamic=nil;
|
||||
gc.top=local+func.lsize;
|
||||
top=local+func.lsize;
|
||||
if(func.dynpara>=0)// load dynamic arguments
|
||||
{
|
||||
dynamic=gc.alloc(vm_vec);
|
||||
|
@ -751,29 +763,29 @@ inline void nasal_vm::opr_callfv()
|
|||
if(func.dynpara>=0)
|
||||
local[psize+1]=dynamic;
|
||||
|
||||
gc.top[0]={vm_addr,localr};
|
||||
(++gc.top)[0]={vm_ret,pc};
|
||||
top[0]={vm_addr,localr};
|
||||
(++top)[0]={vm_ret,pc};
|
||||
pc=func.entry-1;
|
||||
localr=local;
|
||||
gc.upvalue.push_back(nil);
|
||||
}
|
||||
inline void nasal_vm::opr_callfh()
|
||||
{
|
||||
auto& hash=gc.top[0].hash().elems;
|
||||
if(gc.top[-1].type!=vm_func)
|
||||
auto& hash=top[0].hash().elems;
|
||||
if(top[-1].type!=vm_func)
|
||||
die("callfh: must call a function");
|
||||
|
||||
auto& func=gc.top[-1].func();
|
||||
nasal_ref tmp=gc.top[-1];
|
||||
gc.top[-1]=gc.funcr;
|
||||
gc.funcr=tmp;
|
||||
if(gc.top+func.lsize+1>=canary) // gc.top -1(hash) +lsize(local) +1(old pc) +1(old localr)
|
||||
auto& func=top[-1].func();
|
||||
nasal_ref tmp=top[-1];
|
||||
top[-1]=funcr;
|
||||
funcr=tmp;
|
||||
if(top+func.lsize+1>=canary) // top -1(hash) +lsize(local) +1(old pc) +1(old localr)
|
||||
die("stack overflow");
|
||||
if(func.dynpara>=0)
|
||||
die("callfh: special call cannot use dynamic argument");
|
||||
|
||||
nasal_ref* local=gc.top;
|
||||
gc.top+=func.lsize;
|
||||
nasal_ref* local=top;
|
||||
top+=func.lsize;
|
||||
for(uint32_t i=0;i<func.lsize;++i)
|
||||
local[i]=func.local[i];
|
||||
|
||||
|
@ -785,48 +797,53 @@ inline void nasal_vm::opr_callfh()
|
|||
die("callfh: lack argument(s): \""+i.first+"\"");
|
||||
}
|
||||
|
||||
gc.top[0]={vm_addr,localr};
|
||||
(++gc.top)[0]={vm_ret,pc}; // rewrite top with vm_ret
|
||||
top[0]={vm_addr,localr};
|
||||
(++top)[0]={vm_ret,pc}; // rewrite top with vm_ret
|
||||
pc=func.entry-1;
|
||||
localr=local;
|
||||
gc.upvalue.push_back(nil);
|
||||
}
|
||||
inline void nasal_vm::opr_callb()
|
||||
{
|
||||
(++gc.top)[0]=(*builtin[imm[pc]].func)(localr,gc);
|
||||
if(gc.top[0].type==vm_none)
|
||||
// reserve place for builtin function return, in fact this code is changed because of coroutine
|
||||
(++top)[0]=nil;
|
||||
// this ++top should not be used like: (++top)[0] here
|
||||
// because if running a builtin function about coroutine
|
||||
// this (top) will be set to another context.top, instead of main_context.top
|
||||
top[0]=(*builtin[imm[pc]].func)(localr,gc);
|
||||
if(top[0].type==vm_none)
|
||||
die("native function error.");
|
||||
}
|
||||
inline void nasal_vm::opr_slcbegin()
|
||||
{
|
||||
// +--------------+
|
||||
// | slice_vector | <-- gc.top[0]
|
||||
// | slice_vector | <-- top[0]
|
||||
// +--------------+
|
||||
// | resource_vec | <-- gc.top[-1]
|
||||
// | resource_vec | <-- top[-1]
|
||||
// +--------------+
|
||||
(++gc.top)[0]=gc.alloc(vm_vec);
|
||||
if(gc.top[-1].type!=vm_vec)
|
||||
(++top)[0]=gc.alloc(vm_vec);
|
||||
if(top[-1].type!=vm_vec)
|
||||
die("slcbegin: must slice a vector");
|
||||
}
|
||||
inline void nasal_vm::opr_slcend()
|
||||
{
|
||||
gc.top[-1]=gc.top[0];
|
||||
--gc.top;
|
||||
top[-1]=top[0];
|
||||
--top;
|
||||
}
|
||||
inline void nasal_vm::opr_slc()
|
||||
{
|
||||
nasal_ref val=(gc.top--)[0];
|
||||
nasal_ref res=gc.top[-1].vec().get_val(val.to_number());
|
||||
nasal_ref val=(top--)[0];
|
||||
nasal_ref res=top[-1].vec().get_val(val.to_number());
|
||||
if(res.type==vm_none)
|
||||
die("slc: index out of range:"+std::to_string(val.to_number()));
|
||||
gc.top[0].vec().elems.push_back(res);
|
||||
top[0].vec().elems.push_back(res);
|
||||
}
|
||||
inline void nasal_vm::opr_slc2()
|
||||
{
|
||||
nasal_ref val2=(gc.top--)[0];
|
||||
nasal_ref val1=(gc.top--)[0];
|
||||
std::vector<nasal_ref>& ref=gc.top[-1].vec().elems;
|
||||
std::vector<nasal_ref>& aim=gc.top[0].vec().elems;
|
||||
nasal_ref val2=(top--)[0];
|
||||
nasal_ref val1=(top--)[0];
|
||||
std::vector<nasal_ref>& ref=top[-1].vec().elems;
|
||||
std::vector<nasal_ref>& aim=top[0].vec().elems;
|
||||
|
||||
uint8_t type1=val1.type,type2=val2.type;
|
||||
int num1=val1.to_number();
|
||||
|
@ -854,33 +871,33 @@ inline void nasal_vm::opr_slc2()
|
|||
}
|
||||
inline void nasal_vm::opr_mcallg()
|
||||
{
|
||||
mem_addr=gc.stack+imm[pc];
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
memr=global+imm[pc];
|
||||
(++top)[0]=memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
inline void nasal_vm::opr_mcalll()
|
||||
{
|
||||
mem_addr=localr+imm[pc];
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
memr=localr+imm[pc];
|
||||
(++top)[0]=memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
inline void nasal_vm::opr_mupval()
|
||||
{
|
||||
mem_addr=&(gc.funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff]);
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
memr=&(funcr.func().upvalue[(imm[pc]>>16)&0xffff].upval()[imm[pc]&0xffff]);
|
||||
(++top)[0]=memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
inline void nasal_vm::opr_mcallv()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref vec=(--gc.top)[0];
|
||||
nasal_ref val=top[0];
|
||||
nasal_ref vec=(--top)[0];
|
||||
if(vec.type==vm_vec)
|
||||
{
|
||||
mem_addr=vec.vec().get_mem(val.to_number());
|
||||
if(!mem_addr)
|
||||
memr=vec.vec().get_mem(val.to_number());
|
||||
if(!memr)
|
||||
die("mcallv: index out of range:"+std::to_string(val.to_number()));
|
||||
}
|
||||
else if(vec.type==vm_hash)
|
||||
|
@ -889,11 +906,11 @@ inline void nasal_vm::opr_mcallv()
|
|||
die("mcallv: must use string as the key");
|
||||
nasal_hash& ref=vec.hash();
|
||||
std::string& str=val.str();
|
||||
mem_addr=ref.get_mem(str);
|
||||
if(!mem_addr)
|
||||
memr=ref.get_mem(str);
|
||||
if(!memr)
|
||||
{
|
||||
ref.elems[str]=nil;
|
||||
mem_addr=ref.get_mem(str);
|
||||
memr=ref.get_mem(str);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -901,44 +918,44 @@ inline void nasal_vm::opr_mcallv()
|
|||
}
|
||||
inline void nasal_vm::opr_mcallh()
|
||||
{
|
||||
nasal_ref hash=gc.top[0];
|
||||
nasal_ref hash=top[0];
|
||||
if(hash.type!=vm_hash)
|
||||
die("mcallh: must call a hash");
|
||||
nasal_hash& ref=hash.hash();
|
||||
const std::string& str=str_table[imm[pc]];
|
||||
mem_addr=ref.get_mem(str);
|
||||
if(!mem_addr) // create a new key
|
||||
memr=ref.get_mem(str);
|
||||
if(!memr) // create a new key
|
||||
{
|
||||
ref.elems[str]=nil;
|
||||
mem_addr=ref.get_mem(str);
|
||||
memr=ref.get_mem(str);
|
||||
}
|
||||
}
|
||||
inline void nasal_vm::opr_ret()
|
||||
{
|
||||
// +-----------------+
|
||||
// | return value | <- gc.top[0]
|
||||
// | return value | <- top[0]
|
||||
// +-----------------+
|
||||
// | old pc | <- gc.top[-1]
|
||||
// | old pc | <- top[-1]
|
||||
// +-----------------+
|
||||
// | old localr | <- gc.top[-2]
|
||||
// | old localr | <- top[-2]
|
||||
// +-----------------+
|
||||
// | local scope |
|
||||
// | ... |
|
||||
// +-----------------+ <- local pointer stored in localr
|
||||
// | old funcr | <- old function stored in gc.funcr
|
||||
// | old funcr | <- old function stored in funcr
|
||||
// +-----------------+
|
||||
nasal_ref ret=gc.top[0];
|
||||
nasal_ref ret=top[0];
|
||||
nasal_ref* local=localr;
|
||||
nasal_ref func=gc.funcr;
|
||||
nasal_ref func=funcr;
|
||||
|
||||
pc=gc.top[-1].ret();
|
||||
localr=gc.top[-2].addr();
|
||||
pc=top[-1].ret();
|
||||
localr=top[-2].addr();
|
||||
|
||||
gc.top=local-1;
|
||||
top=local-1;
|
||||
func.func().local[0]=nil;// get func and set 'me' to nil
|
||||
gc.funcr=gc.top[0];
|
||||
funcr=top[0];
|
||||
|
||||
gc.top[0]=ret; // rewrite func with returned value
|
||||
top[0]=ret; // rewrite func with returned value
|
||||
|
||||
if(gc.upvalue.back().type==vm_upval) // synchronize upvalue
|
||||
{
|
||||
|
@ -948,6 +965,12 @@ inline void nasal_vm::opr_ret()
|
|||
for(uint32_t i=0;i<size;++i)
|
||||
upval.elems.push_back(local[i]);
|
||||
}
|
||||
if(!pc)
|
||||
{
|
||||
gc.coroutine->status=nasal_co::dead;
|
||||
gc.ctxreserve();
|
||||
return;
|
||||
}
|
||||
gc.upvalue.pop_back();
|
||||
}
|
||||
void nasal_vm::run(
|
||||
|
@ -991,7 +1014,7 @@ void nasal_vm::run(
|
|||
goto *code[pc];
|
||||
|
||||
vmexit:
|
||||
if(gc.top>=canary)
|
||||
if(top>=canary)
|
||||
die("stack overflow");
|
||||
if(opcnt)
|
||||
opcallsort(count);
|
||||
|
@ -1001,7 +1024,7 @@ vmexit:
|
|||
imm.clear();
|
||||
return;
|
||||
// may cause stackoverflow
|
||||
#define exec_operand(op,num) {op();++count[num];if(gc.top<canary)goto *code[++pc];goto vmexit;}
|
||||
#define exec_operand(op,num) {op();++count[num];if(top<canary)goto *code[++pc];goto vmexit;}
|
||||
// do not cause stackoverflow
|
||||
#define exec_opnodie(op,num) {op();++count[num];goto *code[++pc];}
|
||||
|
||||
|
|
31
stl/lib.nas
31
stl/lib.nas
|
@ -267,6 +267,27 @@ var assert=func(condition,message="assertion failed!"){
|
|||
die(message);
|
||||
}
|
||||
|
||||
# settimer alows infinite loop running a function with a time interval
|
||||
var settimer=func(f,interval,realtime=1){
|
||||
while(1){
|
||||
unix.sleep(interval);
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
# get time stamp, this will return a timestamp object
|
||||
var maketimestamp=func(){
|
||||
var t=0;
|
||||
var millisec=func(){
|
||||
return __builtin_millisec;
|
||||
}
|
||||
return {
|
||||
stamp:func(){t=millisec();},
|
||||
elapsedMSec:func(){return millisec()-t;},
|
||||
elapsedUSec:func(){return (millisec()-t)*1000;}
|
||||
};
|
||||
}
|
||||
|
||||
# md5
|
||||
var md5=func(str){
|
||||
return __builtin_md5(str);
|
||||
|
@ -466,4 +487,12 @@ var closure=func(function,level=1){
|
|||
|
||||
var compile=func(code,filename="<compile>"){
|
||||
die("this runtime uses static code generator");
|
||||
}
|
||||
}
|
||||
|
||||
var coroutine={
|
||||
create: func(function){return __builtin_cocreate;},
|
||||
resume: func(co) {return __builtin_coresume;},
|
||||
yield: func(args...) {return __builtin_coyield; },
|
||||
status: func(co) {return __builtin_costatus;},
|
||||
running:func() {return __builtin_corun; }
|
||||
};
|
|
@ -37,6 +37,7 @@ var testfile=[
|
|||
"test/calc.nas ",
|
||||
"test/choice.nas ",
|
||||
"test/class.nas ",
|
||||
"test/coroutine.nas ",
|
||||
"test/diff.nas ",
|
||||
"test/exception.nas ",
|
||||
"test/fib.nas ",
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# coroutine.nas by ValKmjolnir
|
||||
# 2022/5/19
|
||||
var fib=func(){
|
||||
var (a,b)=(1,1);
|
||||
if(coroutine.running()){
|
||||
coroutine.yield(a);
|
||||
coroutine.yield(b);
|
||||
}
|
||||
while(1){
|
||||
(a,b)=(b,a+b);
|
||||
if(coroutine.running())
|
||||
coroutine.yield(b);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var co=[coroutine.create(fib),coroutine.create(fib)];
|
||||
for(var i=0;i<45;i+=1){
|
||||
var res=[coroutine.resume(co[0]),coroutine.resume(co[1])];
|
||||
println('coroutine[0]:',res[0]==nil?nil:res[0][0],'\ncoroutine[1]:',res[1]==nil?nil:res[1][0]);
|
||||
}
|
||||
|
||||
fib();
|
|
@ -68,6 +68,7 @@ var filechecksum=func(){
|
|||
"./test/calc.nas ",
|
||||
"./test/choice.nas ",
|
||||
"./test/class.nas ",
|
||||
"./test/coroutine.nas ",
|
||||
"./test/diff.nas ",
|
||||
"./test/exception.nas ",
|
||||
"./test/fib.nas ",
|
||||
|
|
Loading…
Reference in New Issue