optimize codes. details:

1. delete gc::builtin_alloc
2. add convenient way of getting new string object: gc::newstr, and shrink the size of codes
3. update doc
4. add gc::temp to be used in native/module functions to avoid being recognized as garbage incorrectly when triggered mark-sweep
This commit is contained in:
ValKmjolnir 2022-07-07 17:51:30 +08:00
parent 75c46fa727
commit fb25a4973c
7 changed files with 128 additions and 209 deletions

View File

@ -678,22 +678,18 @@ If you don't warp built-in function in a normal nasal function,
this built-in function may cause a fault when searching arguments,
which will cause __segmentation error__.
Use `import("filename.nas")` to get the nasal file including your built-in functions,
then you could use it.
Use `import("filename.nas")` to get the nasal file including your built-in functions, then you could use it.
Also there's another way of importing nasal files, the two way of importing have the same function:
version 6.5 update:
```javascript
import.dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
Use `gc::builtin_alloc` in builtin function if this function uses alloc more than one time.
When running a builtin function, alloc will run more than one time, this may cause mark-sweep in `gc::alloc`.
The value got before will be collected, but stil in use in this builtin function, this will cause a fatal error.
When running a builtin function,alloc will run more than one time,
this may cause mark-sweep in `gc::alloc`.
The value got before will be collected,but stil in use in this builtin function,
this is a fatal error.
So use `gc::builtin_alloc` in builtin functions to allocate a new object.
Or use `gc::alloc` like this to avoid sweeping objects incorrectly:
So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
```C++
nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
@ -701,19 +697,13 @@ nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
nasal_ref hash=local[1];
if(hash.type!=vm_hash)
return builtin_err("keys","\"hash\" must be hash");
// push vector into local scope to avoid being sweeped
if(gc.top+1>=gc.canary)
return builtin_err("keys","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
// avoid being sweeped
nasal_ref res=gc.temp=gc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
{
nasal_ref str=gc.alloc(vm_str);
str.str()=iter.first;
vec.push_back(str);
}
--gc.top;
return gc.top[1];
vec.push_back(gc.newstr(iter.first));
gc.temp=nil;
return res;
}
```

View File

@ -642,18 +642,17 @@ var print=func(elems...){
一定要注意如果你不把内置函数包装到一个普通的nasal函数中那么直接调用这个内置函数会在参数传入阶段出现严重的错误这个错误会导致 __segmentation error__。也就是大家的老朋友段错误。
在nasal文件中使用`import("文件名.nas")`可以导入该文件中你包装的所有内置函数,接下来你就可以使用他们了。
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
v6.5 更新:
在内置函数中使用`gc::builtin_alloc`来避免在内置函数中多次申请需要内存管理的数据类型时可能出现的垃圾收集器的问题。
```javascript
import.dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
当运行内置函数的时候内存分配器如果运行超过一次那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
所以如果不放心,那就在内置函数中用`gc::builtin_alloc`来申请新的数据。
或者你也可以这样使用`gc::alloc`来规避错误的垃圾回收,目前我们大多使用这种方式,因为`gc::builtin_alloc`没那么靠谱:
所以请使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++
nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
@ -661,20 +660,13 @@ nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
nasal_ref hash=local[1];
if(hash.type!=vm_hash)
return builtin_err("keys","\"hash\" must be hash");
// 把数组提前push到操作数栈上来避免被收集
// 但是一定要检查会不会栈溢出
if(gc.top+1>=gc.canary)
return builtin_err("keys","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
// 使用gc.temp来存储gc管理的变量防止错误的回收
nasal_ref res=gc.temp=gc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
{
nasal_ref str=gc.alloc(vm_str);
str.str()=iter.first;
vec.push_back(str);
}
--gc.top;
return gc.top[1];
vec.push_back(gc.newstr(iter.first));
gc.temp=nil;
return res;
}
```

View File

@ -109,11 +109,10 @@ void execute(const std::string& file,const std::vector<std::string>& argv,const
}
else if(cmd&VM_EXECTIME)
{
timeb begin,end;
ftime(&begin);
auto start=std::chrono::high_resolution_clock::now();
vm.run(gen,linker,argv,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
ftime(&end);
std::cout<<"process exited after "<<((end.time-begin.time)*1.0+end.millitm/1000.0-begin.millitm/1000.0)<<"s.\n";
auto end=std::chrono::high_resolution_clock::now();
std::cout<<"process exited after "<<(end-start).count()*1.0/std::chrono::high_resolution_clock::duration::period::den<<"s.\n";
}
else if(cmd&VM_EXEC)
vm.run(gen,linker,argv,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);

View File

@ -99,15 +99,12 @@ extern "C" nasal_ref nas_accept(std::vector<nasal_ref>& args,nasal_gc& gc){
#else
int client_sd=accept(args[0].num(),(sockaddr*)&client,(socklen_t*)&socklen);
#endif
if(gc.top+1>=gc.canary)
return builtin_err("accept","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_hash);
auto& hash=gc.top[0].hash().elems;
nasal_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
hash["sd"]={vm_num,(double)client_sd};
hash["ip"]=gc.alloc(vm_str);
hash["ip"].str()=inet_ntoa(client.sin_addr);
--gc.top;
return gc.top[1];
hash["ip"]=gc.newstr(inet_ntoa(client.sin_addr));
gc.temp=nil;
return res;
}
extern "C" nasal_ref nas_send(std::vector<nasal_ref>& args,nasal_gc& gc){
@ -149,17 +146,14 @@ extern "C" nasal_ref nas_recv(std::vector<nasal_ref>& args,nasal_gc& gc){
return builtin_err("recv","\"len\" out of range");
if(args[2].type!=vm_num)
return builtin_err("recv","\"flags\" muse be a number");
if(gc.top+1>=gc.canary)
return builtin_err("recv","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_hash);
auto& hash=gc.top[0].hash().elems;
hash["str"]=gc.alloc(vm_str);
nasal_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
char* buf=new char[(int)args[1].num()];
hash["size"]={vm_num,(double)recv(args[0].num(),buf,args[1].num(),args[2].num())};
hash["str"].str()=buf;
hash["str"]=gc.newstr(buf);
delete[] buf;
--gc.top;
return gc.top[1];
gc.temp=nil;
return res;
}
extern "C" nasal_ref nas_recvfrom(std::vector<nasal_ref>& args,nasal_gc& gc){
@ -171,13 +165,10 @@ extern "C" nasal_ref nas_recvfrom(std::vector<nasal_ref>& args,nasal_gc& gc){
return builtin_err("recvfrom","\"len\" out of range");
if(args[2].type!=vm_num)
return builtin_err("recvfrom","\"flags\" muse be a number");
if(gc.top+1>=gc.canary)
return builtin_err("recvfrom","expand temporary space error:stackoverflow");
sockaddr_in addr;
int socklen=sizeof(sockaddr_in);
(++gc.top)[0]=gc.alloc(vm_hash);
auto& hash=gc.top[0].hash().elems;
hash["str"]=gc.alloc(vm_str);
nasal_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
char* buf=new char[(int)args[1].num()+1];
#ifdef _WIN32
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,&socklen)};
@ -185,16 +176,13 @@ extern "C" nasal_ref nas_recvfrom(std::vector<nasal_ref>& args,nasal_gc& gc){
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,(socklen_t*)&socklen)};
#endif
buf[(int)hash["size"].num()]=0;
hash["str"].str()=buf;
hash["str"]=gc.newstr(buf);
delete[] buf;
hash["fromip"]=gc.alloc(vm_str);
hash["fromip"].str()=inet_ntoa(addr.sin_addr);
--gc.top;
return gc.top[1];
hash["fromip"]=gc.newstr(inet_ntoa(addr.sin_addr));
gc.temp=nil;
return res;
}
extern "C" nasal_ref nas_errno(std::vector<nasal_ref>& args,nasal_gc& gc){
nasal_ref res=gc.alloc(vm_str);
res.str()=strerror(errno);
return res;
return gc.newstr(strerror(errno));
}

View File

@ -295,11 +295,9 @@ nasal_ref builtin_fin(nasal_ref* local,nasal_gc& gc)
std::ifstream fin(filename);
if(!fin.fail())
{
nasal_ref ret=gc.alloc(vm_str);
std::stringstream rd;
rd<<fin.rdbuf();
ret.str()=rd.str();
return ret;
return gc.newstr(rd.str());
}
return builtin_err("io.fin","cannot open \""+filename+"\"");
}
@ -330,21 +328,16 @@ nasal_ref builtin_split(nasal_ref* local,nasal_gc& gc)
size_t delimeter_len=delimeter.length();
size_t source_len=source.length();
// push it to local scope to avoid being sweeped
if(gc.top+1>=gc.canary)
return builtin_err("split","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
// avoid being sweeped
nasal_ref res=gc.temp=gc.alloc(vm_vec);
std::vector<nasal_ref>& vec=res.vec().elems;
std::vector<nasal_ref>& vec=gc.top[0].vec().elems;
if(!delimeter_len)
{
for(size_t i=0;i<source_len;++i)
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=source[i];
}
--gc.top;
return gc.top[1];
vec.push_back(gc.newstr(source[i]));
gc.temp=nil;
return res;
}
std::string tmp="";
@ -363,8 +356,7 @@ nasal_ref builtin_split(nasal_ref* local,nasal_gc& gc)
{
if(tmp.length())
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=tmp;
vec.push_back(gc.newstr(tmp));
tmp="";
}
i+=delimeter_len-1;
@ -374,12 +366,11 @@ nasal_ref builtin_split(nasal_ref* local,nasal_gc& gc)
}
if(tmp.length())
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=tmp;
vec.push_back(gc.newstr(tmp));
tmp="";
}
--gc.top;
return gc.top[1];
gc.temp=nil;
return res;
}
nasal_ref builtin_rand(nasal_ref* local,nasal_gc& gc)
{
@ -399,14 +390,12 @@ nasal_ref builtin_rand(nasal_ref* local,nasal_gc& gc)
nasal_ref builtin_id(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
nasal_ref ret=gc.alloc(vm_str);
char buf[32];
if(val.type>vm_num)
sprintf(buf,"%p",val.value.gcobj);
else
sprintf(buf,"0");
ret.str()=buf;
return ret;
return gc.newstr(buf);
}
nasal_ref builtin_int(nasal_ref* local,nasal_gc& gc)
{
@ -455,9 +444,7 @@ nasal_ref builtin_str(nasal_ref* local,nasal_gc& gc)
std::string tmp=std::to_string(val.num());
tmp.erase(tmp.find_last_not_of('0')+1,std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1,std::string::npos);
nasal_ref ret=gc.alloc(vm_str);
ret.str()=tmp;
return ret;
return gc.newstr(tmp);
}
nasal_ref builtin_size(nasal_ref* local,nasal_gc& gc)
{
@ -637,19 +624,13 @@ nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
nasal_ref hash=local[1];
if(hash.type!=vm_hash)
return builtin_err("keys","\"hash\" must be hash");
// push vector into local scope to avoid being sweeped
if(gc.top+1>=gc.canary)
return builtin_err("keys","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
// avoid being sweeped
nasal_ref res=gc.temp=gc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
{
nasal_ref str=gc.alloc(vm_str);
str.str()=iter.first;
vec.push_back(str);
}
--gc.top;
return gc.top[1];
vec.push_back(gc.newstr(iter.first));
gc.temp=nil;
return res;
}
nasal_ref builtin_die(nasal_ref* local,nasal_gc& gc)
{
@ -674,21 +655,19 @@ nasal_ref builtin_find(nasal_ref* local,nasal_gc& gc)
}
nasal_ref builtin_type(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
nasal_ref ret=gc.alloc(vm_str);
switch(val.type)
switch(local[1].type)
{
case vm_none: ret.str()="undefined";break;
case vm_nil: ret.str()="nil"; break;
case vm_num: ret.str()="num"; break;
case vm_str: ret.str()="str"; break;
case vm_vec: ret.str()="vec"; break;
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;
case vm_none: return gc.newstr("undefined");break;
case vm_nil: return gc.newstr("nil"); break;
case vm_num: return gc.newstr("num"); break;
case vm_str: return gc.newstr("str"); break;
case vm_vec: return gc.newstr("vec"); break;
case vm_hash: return gc.newstr("hash"); break;
case vm_func: return gc.newstr("func"); break;
case vm_obj: return gc.newstr("obj"); break;
case vm_co: return gc.newstr("coroutine");break;
}
return ret;
return nil;
}
nasal_ref builtin_substr(nasal_ref* local,nasal_gc& gc)
{
@ -709,9 +688,7 @@ nasal_ref builtin_substr(nasal_ref* local,nasal_gc& gc)
size_t length=(size_t)len.num();
if(begin>=str.str().length() || begin+length>str.str().length())
return builtin_err("susbtr","index out of range");
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(begin,length);
return ret;
return gc.newstr(str.str().substr(begin,length));
}
nasal_ref builtin_streq(nasal_ref* local,nasal_gc& gc)
{
@ -730,9 +707,7 @@ nasal_ref builtin_left(nasal_ref* local,nasal_gc& gc)
int length=(int)len.num();
if(length<0)
length=0;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(0,length);
return ret;
return gc.newstr(str.str().substr(0,length));
}
nasal_ref builtin_right(nasal_ref* local,nasal_gc& gc)
{
@ -748,9 +723,7 @@ nasal_ref builtin_right(nasal_ref* local,nasal_gc& gc)
length=srclen;
if(length<0)
length=0;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(srclen-length, srclen);
return ret;
return gc.newstr(str.str().substr(srclen-length, srclen));
}
nasal_ref builtin_cmp(nasal_ref* local,nasal_gc& gc)
{
@ -785,15 +758,12 @@ nasal_ref builtin_chr(nasal_ref* local,nasal_gc& gc)
nasal_ref code=local[1];
if(code.type!=vm_num)
return builtin_err("chr","\"code\" must be number");
nasal_ref ret=gc.alloc(vm_str);
int num=code.num();
if(0<=num && num<128)
ret.str()=(char)num;
return gc.newstr((char)num);
else if(128<=num && num<256)
ret.str()=extend[num-128];
else
ret.str()=" ";
return ret;
return gc.newstr(extend[num-128]);
return gc.newstr(" ");
}
nasal_ref builtin_values(nasal_ref* local,nasal_gc& gc)
{
@ -1106,9 +1076,7 @@ nasal_ref builtin_readdir(nasal_ref* local,nasal_gc& gc)
dirent* p=readdir((DIR*)handle.obj().ptr);
if(!p)
return nil;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=p->d_name;
return ret;
return gc.newstr(p->d_name);
}
nasal_ref builtin_closedir(nasal_ref* local,nasal_gc& gc)
{
@ -1131,28 +1099,22 @@ nasal_ref builtin_chdir(nasal_ref* local,nasal_gc& gc)
nasal_ref builtin_environ(nasal_ref* local,nasal_gc& gc)
{
char** env=environ;
if(gc.top+1>=gc.canary)
return builtin_err("environ","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
nasal_ref res=gc.temp=gc.alloc(vm_vec);
auto& vec=res.vec().elems;
while(*env)
{
auto s=gc.alloc(vm_str);
s.str()=*env;
vec.push_back(s);
vec.push_back(gc.newstr(*env));
++env;
}
--gc.top;
return gc.top[1];
gc.temp=nil;
return res;
}
nasal_ref builtin_getcwd(nasal_ref* local,nasal_gc& gc)
{
char buf[1024];
if(!getcwd(buf,sizeof(buf)))
return builtin_err("getcwd","failed to call getcwd");
nasal_ref str=gc.alloc(vm_str);
str.str()=buf;
return str;
return gc.newstr(buf);
}
nasal_ref builtin_getenv(nasal_ref* local,nasal_gc& gc)
{
@ -1162,9 +1124,7 @@ nasal_ref builtin_getenv(nasal_ref* local,nasal_gc& gc)
char* res=getenv(envvar.str().c_str());
if(!res)
return nil;
nasal_ref str=gc.alloc(vm_str);
str.str()=res;
return str;
return gc.newstr(res);
}
void obj_dylib_destructor(void* ptr)
{
@ -1241,15 +1201,14 @@ nasal_ref builtin_dlcall(nasal_ref* local,nasal_gc& gc)
}
nasal_ref builtin_platform(nasal_ref* local,nasal_gc& gc)
{
nasal_ref ret=gc.alloc(vm_str);
#if defined _WIN32 || defined _WIN64
ret.str()="windows";
return gc.newstr("windows");
#elif defined __linux__
ret.str()="linux";
return gc.newstr("linux");
#elif defined __APPLE__
ret.str()="macOS";
return gc.newstr("macOS");
#endif
return ret;
return gc.newstr("unknown");
}
nasal_ref builtin_gc(nasal_ref* local,nasal_gc& gc)
{
@ -1347,9 +1306,7 @@ nasal_ref builtin_md5(nasal_ref* local,nasal_gc& gc)
nasal_ref str=local[1];
if(str.type!=vm_str)
return builtin_err("md5","\"str\" must be a string");
nasal_ref res=gc.alloc(vm_str);
res.str()=md5(str.str());
return res;
return gc.newstr(md5(str.str()));
}
nasal_ref builtin_cocreate(nasal_ref* local,nasal_gc& gc)
@ -1416,14 +1373,13 @@ 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;
case nasal_co::suspended: return gc.newstr("suspended");break;
case nasal_co::running: return gc.newstr("running"); break;
case nasal_co::dead: return gc.newstr("dead"); break;
}
return res;
return nil;
}
nasal_ref builtin_corun(nasal_ref* local,nasal_gc& gc)
{
@ -1449,8 +1405,6 @@ nasal_ref builtin_logtime(nasal_ref* local,nasal_gc& gc)
tm* tm_t=localtime(&t);
char s[128];
sprintf(s,"%d-%.2d-%.2d %.2d:%.2d:%.2d",tm_t->tm_year+1900,tm_t->tm_mon+1,tm_t->tm_mday,tm_t->tm_hour,tm_t->tm_min,tm_t->tm_sec);
nasal_ref res=gc.alloc(vm_str);
res.str()=s;
return res;
return gc.newstr(s);
}
#endif

View File

@ -456,6 +456,7 @@ struct nasal_gc
nasal_ref*& top; // stack top
nasal_ref* stack; // stack pointer
nasal_co* coroutine; // running coroutine
nasal_ref temp; // temporary place used in builtin/module functions
/* constants and memory pool */
std::vector<nasal_ref> strs; // reserved address for const vm_str
@ -483,14 +484,17 @@ struct nasal_gc
upvalr(_upvalr),
canary(_canary),
top(_top),
stack(_stk){}
stack(_stk),
temp(nil){}
void mark();
void sweep();
void init(const std::vector<std::string>&,const std::vector<std::string>&);
void clear();
void info();
nasal_ref alloc(const uint8_t);
nasal_ref builtin_alloc(const uint8_t);
nasal_ref newstr(char);
nasal_ref newstr(const char*);
nasal_ref newstr(const std::string&);
void ctxchg(nasal_co&);
void ctxreserve();
};
@ -507,6 +511,7 @@ void nasal_gc::mark()
bfs.push(*i);
bfs.push(funcr);
bfs.push(upvalr);
bfs.push(temp);
if(coroutine) // scan main process stack
{
for(nasal_ref* i=main_ctx.stack;i<=main_ctx.top;++i)
@ -654,28 +659,23 @@ nasal_ref nasal_gc::alloc(uint8_t type)
unused[index].pop();
return ret;
}
nasal_ref nasal_gc::builtin_alloc(uint8_t type)
nasal_ref nasal_gc::newstr(char c)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc::alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
const uint8_t index=type-vm_str;
++allocc[index];
if(unused[index].empty())
{
++size[index];
for(uint32_t i=0;i<incr[index];++i)
{
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
unused[index].push(tmp);
}
}
nasal_ref ret={type,unused[index].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
unused[index].pop();
return ret;
nasal_ref s=alloc(vm_str);
s.str()=c;
return s;
}
nasal_ref nasal_gc::newstr(const char* buff)
{
nasal_ref s=alloc(vm_str);
s.str()=buff;
return s;
}
nasal_ref nasal_gc::newstr(const std::string& buff)
{
nasal_ref s=alloc(vm_str);
s.str()=buff;
return s;
}
void nasal_gc::ctxchg(nasal_co& context)
{

View File

@ -526,8 +526,7 @@ inline void nasal_vm::opr_mul(){op_calc(*);}
inline void nasal_vm::opr_div(){op_calc(/);}
inline void nasal_vm::opr_lnk()
{
nasal_ref val=gc.alloc(vm_str);
val.str()=top[-1].tostr()+top[0].tostr();
nasal_ref val=gc.newstr(top[-1].tostr()+top[0].tostr());
(--top)[0]=val;
}
@ -541,8 +540,7 @@ inline void nasal_vm::opr_mulc(){op_calc_const(*);}
inline void nasal_vm::opr_divc(){op_calc_const(/);}
inline void nasal_vm::opr_lnkc()
{
nasal_ref val=gc.alloc(vm_str);
val.str()=top[0].tostr()+str_table[imm[pc]];
nasal_ref val=gc.newstr(top[0].tostr()+str_table[imm[pc]]);
top[0]=val;
}
@ -558,8 +556,7 @@ inline void nasal_vm::opr_muleq(){op_calc_eq(*);}
inline void nasal_vm::opr_diveq(){op_calc_eq(/);}
inline void nasal_vm::opr_lnkeq()
{
nasal_ref val=gc.alloc(vm_str);
val.str()=memr[0].tostr()+top[-1].tostr();
nasal_ref val=gc.newstr(memr[0].tostr()+top[-1].tostr());
(--top)[0]=memr[0]=val;
memr=nullptr;
top-=imm[pc];
@ -577,8 +574,7 @@ inline void nasal_vm::opr_muleqc(){op_calc_eq_const(*);}
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()=memr[0].tostr()+str_table[imm[pc]&0x7fffffff];
nasal_ref val=gc.newstr(memr[0].tostr()+str_table[imm[pc]&0x7fffffff]);
top[0]=memr[0]=val;
memr=nullptr;
top-=(imm[pc]>>31);