🚀 add coroutine library(beta) and lib function settimer&maketimestamp

This commit is contained in:
ValKmjolnir 2022-05-19 20:09:23 +08:00
parent d567f5abf8
commit 120ceb429a
11 changed files with 695 additions and 218 deletions

130
README.md
View File

@ -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
![benchmark](./pic/benchmark.png)

31
lib.nas
View File

@ -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; }
};

View File

@ -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>

View File

@ -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

View File

@ -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 );

View File

@ -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

View File

@ -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];}

View File

@ -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; }
};

View File

@ -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 ",

25
test/coroutine.nas Normal file
View File

@ -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();

View File

@ -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 ",