From a68bf85f04934bc5c937a011e463d530944b9ba0 Mon Sep 17 00:00:00 2001 From: Valk Richard Li <48872266+ValKmjolnir@users.noreply.github.com> Date: Mon, 31 May 2021 19:10:59 +0800 Subject: [PATCH] bug fixed a gc bug which causes fatal error. add member value collect to make sure that nasal_val is not collected repeatedly. use builtin_alloc in builtin function to avoid incorrect collection of value in use(gc_alloc). change free_list to free_list[vm_type_size] to avoid too many calls of new/delete(but seems useless?) but the most important thing is fixing this bug. --- README.md | 41 +++++++++++- main.cpp | 12 ++-- nasal.h | 5 +- nasal_ast.h | 29 ++++----- nasal_builtin.h | 16 ++--- nasal_codegen.h | 73 +++++++++++---------- nasal_gc.h | 165 ++++++++++++++++++++++++------------------------ nasal_import.h | 27 +++----- nasal_lexer.h | 30 ++++----- nasal_parse.h | 9 +-- nasal_vm.h | 22 +++---- 11 files changed, 234 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index 41cc79e..ff97ff3 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,14 @@ In this update i changed global and local scope from unordered_map to vector. So the bytecode generator changed a lot. +## Version 6.5 + +2021/5/31 update: Now gc can collect garbage correctly without re-collecting,which will cause fatal error. + +Add builtin_alloc to avoid mark-sweep when running a built-in function,which will mark useful items as useless garbage to collect. + +Better use setsize and assignment to get a big array,append is very slow in this situation. + ```javascript for(var i=0;i<4000000;i+=1); ``` @@ -428,4 +436,35 @@ var print=func(elements...) In version 5.0,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 SIGSEGV segmentation error(maybe). -Use import("") to get the nasal file including your built-in functions,then you could use it. \ No newline at end of file +Use import("") to get the nasal file including your built-in functions,then you could use it. + +version 6.5 update: + +Use nasal_gc::builtin_alloc in builtin function if this function uses alloc more then 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 is a fatal error. + +So use builtin_alloc in builtin functions like this: + +```C++ +nasal_val* builtin_getkeys(std::vector& local_scope,nasal_gc& gc) +{ + nasal_val* hash_addr=local_scope[1]; + if(hash_addr->type!=vm_hash) + { + builtin_err("keys","\"hash\" must be hash"); + return nullptr; + } + nasal_val* ret_addr=gc.builtin_alloc(vm_vec); + std::vector& ref_vec=ret_addr->ptr.vec->elems; + for(auto iter:hash_addr->ptr.hash->elems) + { + nasal_val* str_addr=gc.builtin_alloc(vm_str); + *str_addr->ptr.str=iter.first; + ref_vec.push_back(str_addr); + } + return ret_addr; +} +``` \ No newline at end of file diff --git a/main.cpp b/main.cpp index ca12527..d572a35 100644 --- a/main.cpp +++ b/main.cpp @@ -99,13 +99,15 @@ int main() // use chcp 65001 to use unicode io system("chcp 65001"); system("cls"); +#else + system("clear"); #endif logo(); - std::cout<<">> Nasal interpreter ver 6.0 .\n"; - std::cout<<">> Code: https://github.com/ValKmjolnir/Nasal-Interpreter\n"; - std::cout<<">> Code: https://gitee.com/valkmjolnir/Nasal-Interpreter\n"; - std::cout<<">> Thanks: https://github.com/andyross/nasal\n"; - std::cout<<">> Info: http://wiki.flightgear.org/Nasal_scripting_language\n"; + std::cout<<">> Nasal interpreter ver 6.5 efficient gc test .\n"; + std::cout<<">> Thanks to https://github.com/andyross/nasal\n"; + std::cout<<">> Code: https://github.com/ValKmjolnir/Nasal-Interpreter\n"; + std::cout<<">> Code: https://gitee.com/valkmjolnir/Nasal-Interpreter\n"; + std::cout<<">> Info: http://wiki.flightgear.org/Nasal_scripting_language\n"; std::cout<<">> Input \"help\" to get help .\n"; while(1) { diff --git a/nasal.h b/nasal.h index 9c2d0ab..6d42546 100644 --- a/nasal.h +++ b/nasal.h @@ -3,6 +3,7 @@ #pragma GCC optimize(2) +#include #include #include #include @@ -94,7 +95,7 @@ inline double dec_to_double(std::string& str,int len) } return ret*std::pow(10,negative*num_pow); } -double trans_string_to_number(std::string str) +double str2num(std::string str) { bool is_negative=false; int len=str.length(); @@ -118,7 +119,7 @@ double trans_string_to_number(std::string str) trans_number_to_string: convert number to string */ -std::string trans_number_to_string(double number) +std::string num2str(double number) { std::string res; std::stringstream ss; diff --git a/nasal_ast.h b/nasal_ast.h index 853f08b..c43528f 100644 --- a/nasal_ast.h +++ b/nasal_ast.h @@ -103,17 +103,17 @@ public: nasal_ast(const nasal_ast&); ~nasal_ast(); nasal_ast& operator=(const nasal_ast&); - void print_ast(int); - void clear(); - void set_line(int); - void set_type(int); - void set_str(std::string&); - void set_num(double); - void add_child(nasal_ast); - int get_line(); - int get_type(); - double get_num(); - std::string get_str(); + void print_ast(int); + void clear(); + void set_line(int); + void set_type(int); + void set_str(std::string&); + void set_num(double); + void add_child(nasal_ast); + int get_line(); + int get_type(); + double get_num(); + std::string& get_str(); std::vector& get_children(); }; @@ -207,7 +207,7 @@ int nasal_ast::get_type() return type; } -std::string nasal_ast::get_str() +std::string& nasal_ast::get_str() { return str; } @@ -233,9 +233,8 @@ void nasal_ast::print_ast(int depth) else if(type==ast_num) std::cout<<":"<& local_scope,nasal_gc& gc) std::string source=*string_val_addr->ptr.str; int delimeter_len=delimeter.length(); int source_len=source.length(); - nasal_val* ret_addr=gc.gc_alloc(vm_vec); + nasal_val* ret_addr=gc.builtin_alloc(vm_vec); std::vector& ref_vec=ret_addr->ptr.vec->elems; std::string tmp=""; @@ -279,7 +279,7 @@ nasal_val* builtin_split(std::vector& local_scope,nasal_gc& gc) { for(int i=0;iptr.str=source[i]; ref_vec.push_back(str_addr); } @@ -301,7 +301,7 @@ nasal_val* builtin_split(std::vector& local_scope,nasal_gc& gc) { if(tmp.length()) { - nasal_val* str_addr=gc.gc_alloc(vm_str); + nasal_val* str_addr=gc.builtin_alloc(vm_str); *str_addr->ptr.str=tmp; ref_vec.push_back(str_addr); tmp=""; @@ -313,7 +313,7 @@ nasal_val* builtin_split(std::vector& local_scope,nasal_gc& gc) } if(tmp.length()) { - nasal_val* str_addr=gc.gc_alloc(vm_str); + nasal_val* str_addr=gc.builtin_alloc(vm_str); *str_addr->ptr.str=tmp; ref_vec.push_back(str_addr); tmp=""; @@ -585,7 +585,7 @@ nasal_val* builtin_sqrt(std::vector& local_scope,nasal_gc& gc) nasal_val* builtin_atan2(std::vector& local_scope,nasal_gc& gc) { nasal_val* x_val_addr=local_scope[1]; - nasal_val* y_val_addr=local_scope[1]; + nasal_val* y_val_addr=local_scope[2]; if(x_val_addr->type!=vm_num) { builtin_err("atan2","\"x\" must be number"); @@ -628,7 +628,7 @@ nasal_val* builtin_contains(std::vector& local_scope,nasal_gc& gc) return nullptr; } nasal_val* ret_addr=gc.gc_alloc(vm_num); - ret_addr->ptr.num=hash_addr->ptr.hash->check_contain(*key_addr->ptr.str); + ret_addr->ptr.num=hash_addr->ptr.hash->elems.count(*key_addr->ptr.str); return ret_addr; } nasal_val* builtin_delete(std::vector& local_scope,nasal_gc& gc) @@ -657,11 +657,11 @@ nasal_val* builtin_getkeys(std::vector& local_scope,nasal_gc& gc) builtin_err("keys","\"hash\" must be hash"); return nullptr; } - nasal_val* ret_addr=gc.gc_alloc(vm_vec); + nasal_val* ret_addr=gc.builtin_alloc(vm_vec); std::vector& ref_vec=ret_addr->ptr.vec->elems; for(auto iter:hash_addr->ptr.hash->elems) { - nasal_val* str_addr=gc.gc_alloc(vm_str); + nasal_val* str_addr=gc.builtin_alloc(vm_str); *str_addr->ptr.str=iter.first; ref_vec.push_back(str_addr); } diff --git a/nasal_codegen.h b/nasal_codegen.h index 6056b4c..6f0111f 100644 --- a/nasal_codegen.h +++ b/nasal_codegen.h @@ -140,9 +140,9 @@ struct struct opcode { - unsigned char op; - unsigned int num; - opcode(unsigned char _op=op_nop,unsigned int _num=0) + uint8_t op; + uint32_t num; + opcode(uint8_t _op=op_nop,uint32_t _num=0) { op=_op; num=_num; @@ -175,7 +175,7 @@ private: void die(std::string,int); void regist_number(double); void regist_string(std::string&); - void add_sym(std::string); + void add_sym(std::string&); int local_find(std::string&); int global_find(std::string&); void gen(unsigned char,unsigned int); @@ -254,18 +254,18 @@ void nasal_codegen::regist_string(std::string& str) return; } -void nasal_codegen::add_sym(std::string name) +void nasal_codegen::add_sym(std::string& name) { if(local.empty()) { - for(auto sym:global) + for(auto& sym:global) if(sym==name) return; global.push_back(name); } else { - for(auto sym:local.back()) + for(auto& sym:local.back()) if(sym==name) return; local.back().push_back(name); @@ -277,7 +277,7 @@ int nasal_codegen::local_find(std::string& name) { int index=-1; int cnt=0; - for(auto i:local) + for(auto& i:local) { for(int j=0;jsize(); + for(auto& i:local) + exec_code[local_label].num+=i.size(); local.pop_back(); if(!block.get_children().size() || block.get_children().back().get_type()!=ast_return) @@ -445,7 +448,7 @@ void nasal_codegen::call_gen(nasal_ast& ast) void nasal_codegen::call_id(nasal_ast& ast) { - std::string str=ast.get_str(); + std::string& str=ast.get_str(); for(int i=0;builtin_func[i].func;++i) if(builtin_func[i].name==str) { @@ -470,7 +473,7 @@ void nasal_codegen::call_id(nasal_ast& ast) void nasal_codegen::call_hash(nasal_ast& ast) { - std::string str=ast.get_str(); + std::string& str=ast.get_str(); regist_string(str); gen(op_callh,string_table[str]); return; @@ -485,7 +488,7 @@ void nasal_codegen::call_vec(nasal_ast& ast) return; } gen(op_slcbegin,0); - for(auto tmp:ast.get_children()) + for(auto& tmp:ast.get_children()) { if(tmp.get_type()!=ast_subvec) { @@ -535,7 +538,7 @@ void nasal_codegen::mcall(nasal_ast& ast) void nasal_codegen::mcall_id(nasal_ast& ast) { - std::string str=ast.get_str(); + std::string& str=ast.get_str(); int index=local_find(str); if(index>=0) { @@ -561,7 +564,7 @@ void nasal_codegen::mcall_vec(nasal_ast& ast) void nasal_codegen::mcall_hash(nasal_ast& ast) { - std::string str=ast.get_str(); + std::string& str=ast.get_str(); regist_string(str); gen(op_mcallh,string_table[str]); return; @@ -569,7 +572,7 @@ void nasal_codegen::mcall_hash(nasal_ast& ast) void nasal_codegen::single_def(nasal_ast& ast) { - std::string str=ast.get_children()[0].get_str(); + std::string& str=ast.get_children()[0].get_str(); add_sym(str); calc_gen(ast.get_children()[1]); if(local.empty()) @@ -585,7 +588,7 @@ void nasal_codegen::multi_def(nasal_ast& ast) { for(int i=0;i elems; - bool check_contain(std::string&); void print(); nasal_val* get_val(std::string&); nasal_val** get_mem(std::string&); @@ -38,20 +48,22 @@ struct nasal_hash struct nasal_func { - int dynpara;// dynamic parameter name index in hash - int offset; - int entry; + int32_t dynpara;// dynamic parameter name index in hash + uint32_t offset; + uint32_t entry; std::vector default_para; std::unordered_map key_table;// parameter name hash std::vector closure; nasal_func(); + void clear(); }; struct nasal_val { + bool collected; bool mark; - int type; + uint16_t type; union { double num; @@ -64,9 +76,7 @@ struct nasal_val nasal_val(); nasal_val(int); ~nasal_val(); - void clear(); - void set_type(int); - double to_number(); + double to_number(); std::string to_string(); }; @@ -95,7 +105,6 @@ nasal_val** nasal_vec::get_mem(int index) } void nasal_vec::print() { - int size=elems.size(); std::cout<<'['; for(auto i:elems) { @@ -153,31 +162,10 @@ nasal_val** nasal_hash::get_mem(std::string& key) } return nullptr; } -bool nasal_hash::check_contain(std::string& key) -{ - if(elems.count(key)) - return true; - if(elems.count("parents")) - { - nasal_val* val_addr=elems["parents"]; - if(val_addr->type==vm_vec) - { - bool result=false; - for(auto i:val_addr->ptr.vec->elems) - { - if(i->type==vm_hash) - result=i->ptr.hash->check_contain(key); - if(result) - return true; - } - } - } - return false; -} void nasal_hash::print() { std::cout<<'{'; - for(auto i:elems) + for(auto& i:elems) { std::cout< str_addrs; // reserved address for const vm_str std::vector slice_stack; // slice stack for vec[val,val,val:val] std::vector memory; // gc memory - std::queue free_list; // gc free list + std::queue free_list[vm_type_size]; // gc free list std::vector global; std::list > local; void mark(); @@ -296,6 +269,7 @@ struct nasal_gc void gc_init(std::vector&,std::vector&); void gc_clear(); nasal_val* gc_alloc(int); + nasal_val* builtin_alloc(int); }; /* gc functions */ @@ -304,7 +278,7 @@ void nasal_gc::mark() std::queue bfs; for(auto i:global) bfs.push(i); - for(auto i:local) + for(auto& i:local) for(auto j:i) bfs.push(j); for(auto i:slice_stack) @@ -321,7 +295,7 @@ void nasal_gc::mark() for(auto i:tmp->ptr.vec->elems) bfs.push(i); else if(tmp->type==vm_hash) - for(auto i:tmp->ptr.hash->elems) + for(auto& i:tmp->ptr.hash->elems) bfs.push(i.second); else if(tmp->type==vm_func) { @@ -337,10 +311,19 @@ void nasal_gc::sweep() { for(auto i:memory) { - if(!i->mark) + if(!i->collected && !i->mark) { - i->clear(); - free_list.push(i); + switch(i->type) + { + case vm_nil: break; + case vm_num: i->ptr.num=0; break; + case vm_str: i->ptr.str->clear(); break; + case vm_vec: i->ptr.vec->elems.clear(); break; + case vm_hash:i->ptr.hash->elems.clear();break; + case vm_func:i->ptr.func->clear(); break; + } + free_list[i->type].push(i); + i->collected=true; } i->mark=false; } @@ -348,12 +331,13 @@ void nasal_gc::sweep() } void nasal_gc::gc_init(std::vector& nums,std::vector& strs) { - for(int i=0;i<65536;++i) - { - nasal_val* tmp=new nasal_val; - memory.push_back(tmp); - free_list.push(tmp); - } + for(int i=vm_num;iset_type(type); + nasal_val* ret=free_list[type].front(); + ret->collected=false; + free_list[type].pop(); + return ret; +} +nasal_val* nasal_gc::builtin_alloc(int type) +{ + // 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 + if(free_list[type].empty()) + for(int i=0;icollected=false; + free_list[type].pop(); return ret; } #endif \ No newline at end of file diff --git a/nasal_import.h b/nasal_import.h index 94203e8..f268832 100644 --- a/nasal_import.h +++ b/nasal_import.h @@ -24,15 +24,14 @@ public: void nasal_import::die(std::string filename,std::string error_stage) { ++error; - std::cout<<">> [import] in <\""<: error(s) occurred in "<> [import] in <\""<: error(s) occurred in "<& ref_vec=add_root.get_children(); - int size=ref_vec.size(); - for(int i=0;i& ref_vec=root.get_children(); - int size=ref_vec.size(); - for(int i=0;i res; std::vector token_list; - int get_token_type(std::string); + int get_tok_type(std::string); void die(std::string,std::string,int,int); - std::string identifier_gen(); - std::string number_gen(); - std::string string_gen(); + std::string id_gen(); + std::string num_gen(); + std::string str_gen(); public: void openfile(std::string); void scanner(); @@ -140,7 +140,7 @@ void nasal_lexer::openfile(std::string filename) return; } -int nasal_lexer::get_token_type(std::string tk_str) +int nasal_lexer::get_tok_type(std::string tk_str) { for(int i=0;token_table[i].str;++i) if(tk_str==token_table[i].str) @@ -158,7 +158,7 @@ void nasal_lexer::die(std::string code,std::string error_info,int line=-1,int co return; } -std::string nasal_lexer::identifier_gen() +std::string nasal_lexer::id_gen() { std::string token_str=""; while(ptr=res_size) break; if(IS_IDENTIFIER(res[ptr])) { - token_str=identifier_gen(); - token new_token(line,get_token_type(token_str),token_str); + token_str=id_gen(); + token new_token(line,get_tok_type(token_str),token_str); if(!new_token.type) new_token.type=tok_id; token_list.push_back(new_token); } else if(IS_DIGIT(res[ptr])) { - token_str=number_gen(); + token_str=num_gen(); token new_token(line,tok_num,token_str); token_list.push_back(new_token); } else if(IS_STRING(res[ptr])) { - token_str=string_gen(); + token_str=str_gen(); token new_token(line,tok_str,token_str); token_list.push_back(new_token); } @@ -348,7 +348,7 @@ void nasal_lexer::scanner() token_str=""; token_str+=res[ptr]; line_code+=res[ptr]; - token new_token(line,get_token_type(token_str),token_str); + token new_token(line,get_tok_type(token_str),token_str); if(!new_token.type) die(line_code,"incorrect operator.",line,line_code.length()); token_list.push_back(new_token); @@ -367,7 +367,7 @@ void nasal_lexer::scanner() ++ptr; } line_code+=token_str; - token new_token(line,get_token_type(token_str),token_str); + token new_token(line,get_tok_type(token_str),token_str); token_list.push_back(new_token); } else if(IS_CALC_OPERATOR(res[ptr])) @@ -377,7 +377,7 @@ void nasal_lexer::scanner() if(ptr err_lines; err_lines.push_back(error_token[0].line); - for(auto tok:error_token) + for(auto& tok:error_token) if(err_lines.back()!=tok.line) err_lines.push_back(tok.line); ++error; std::cout<<">> [parse] error tokens in line"; - for(auto line:err_lines) + for(auto& line:err_lines) std::cout<<' '<ptr.str; - double number=trans_string_to_number(str); + double number=str2num(str); if(std::isnan(number)) return str.length(); return number; @@ -212,7 +212,7 @@ void nasal_vm::opr_newf() nasal_val* val=gc.gc_alloc(vm_func); val->ptr.func->entry=exec_code[pc].num; if(!gc.local.empty()) - val->ptr.func->closure=gc.local.back(); + val->ptr.func->closure=gc.local.back();// local contains 'me' else val->ptr.func->closure.push_back(gc.nil_addr);// me *(++stack_top)=val; @@ -263,7 +263,7 @@ void nasal_vm::opr_unot() new_val=val->ptr.num?gc.zero_addr:gc.one_addr; else if(type==vm_str) { - double number=trans_string_to_number(*val->ptr.str); + double number=str2num(*val->ptr.str); if(std::isnan(number)) new_val=val->ptr.str->length()?gc.zero_addr:gc.one_addr; else @@ -688,15 +688,15 @@ void nasal_vm::opr_callf() die("callf: special call cannot use dynamic argument"); return; } - for(auto i=ref_keys.begin();i!=ref_keys.end();++i) + for(auto& i:ref_keys) { - if(ref_hash.count(i->first)) - ref_closure[i->second+ref_func.offset]=ref_hash[i->first]; - else if(ref_default[i->second]) - ref_closure[i->second+ref_func.offset]=ref_default[i->second]; + if(ref_hash.count(i.first)) + ref_closure[i.second+ref_func.offset]=ref_hash[i.first]; + else if(ref_default[i.second]) + ref_closure[i.second+ref_func.offset]=ref_default[i.second]; else { - die("callf: lack argument(s): \""+i->first+"\""); + die("callf: lack argument(s): \""+i.first+"\""); return; } } @@ -733,7 +733,7 @@ void nasal_vm::opr_slc() switch(val->type) { case vm_num:num=val->ptr.num;break; - case vm_str:num=trans_string_to_number(*val->ptr.str);break; + case vm_str:num=str2num(*val->ptr.str);break; default:die("slc: error value type");break; } nasal_val* res=(*stack_top)->ptr.vec->get_val((int)num); @@ -800,7 +800,7 @@ void nasal_vm::opr_mcallv() switch(val->type) { case vm_num:num=(int)val->ptr.num;break; - case vm_str:num=(int)trans_string_to_number(*val->ptr.str);break; + case vm_str:num=(int)str2num(*val->ptr.str);break; default:die("mcallv: error value type");break; } nasal_val** res=(*vec_addr)->ptr.vec->get_mem(num);