fix: release temporary split arguments
This commit is contained in:
@@ -27,7 +27,7 @@ Verified in this checkout on 2026-09-22:
|
|||||||
- `./claro doctor`: all checks `OK`.
|
- `./claro doctor`: all checks `OK`.
|
||||||
- `./claro validate`: validation passed.
|
- `./claro validate`: validation passed.
|
||||||
|
|
||||||
This run's narrow memory slice releases the previous deep value when a runtime variable or map entry is overwritten. Focused coverage is `tools/validate_memory_cleanup.py`; it reports `PASS: expression token allocations are released` under an ASan/UBSan build with LeakSanitizer checking and verifies the overwrite cleanup helper is present. Remaining memory-growth areas include final runtime teardown, loaded program storage, and other expression temporaries. The `RUN COMMAND` path remains trusted shell execution, not a sandbox.
|
This run's narrow memory slice releases the previous deep value when a runtime variable or map entry is overwritten, and releases temporary split argument arrays and strings from `DO`, `CALL`, `TEXT ... CONTAINS`, and `RANDOM`. Focused coverage is `tools/validate_memory_cleanup.py`; it reports `PASS: expression token allocations are released` under an ASan/UBSan build with LeakSanitizer checking and exercises repeated four-argument calls plus string/list/map overwrites. Remaining memory-growth areas include final runtime teardown, loaded program storage, and other expression temporaries. The `RUN COMMAND` path remains trusted shell execution, not a sandbox.
|
||||||
|
|
||||||
The `RUN COMMAND` path now decodes POSIX `pclose()` wait status before storing `LASTEXIT`, so a child that exits with code 3 exposes `3` rather than the encoded status `768`. Focused coverage is `tests/42_last_exit_code.claro`. This slice does not yet fix HTTP buffering/status handling or the remaining memory-growth boundaries. Claro remains a trusted-script interpreter, not a sandbox.
|
The `RUN COMMAND` path now decodes POSIX `pclose()` wait status before storing `LASTEXIT`, so a child that exits with code 3 exposes `3` rather than the encoded status `768`. Focused coverage is `tests/42_last_exit_code.claro`. This slice does not yet fix HTTP buffering/status handling or the remaining memory-growth boundaries. Claro remains a trusted-script interpreter, not a sandbox.
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -23,6 +23,10 @@ The validator builds an AddressSanitizer/UndefinedBehaviorSanitizer binary, runs
|
|||||||
|
|
||||||
## Overwritten-value cleanup
|
## Overwritten-value cleanup
|
||||||
|
|
||||||
Runtime variables and map entries own deep copies of their values. Replacing an existing variable or map entry now releases the previous string, list, or map value before storing its replacement. This is intentionally limited to overwrite boundaries; final runtime teardown and discarded expression temporaries remain follow-up cleanup slices.
|
Runtime variables and map entries own deep copies of their values. Replacing an existing variable or map entry now releases the previous string, list, or map value before storing its replacement. This is intentionally limited to overwrite boundaries; final runtime teardown remains a follow-up cleanup slice.
|
||||||
|
|
||||||
Focused verification also checks the cleanup helper in the ASan/UBSan build and exercises string, list, and map overwrites without sanitizer errors.
|
## Split-argument cleanup
|
||||||
|
|
||||||
|
Command argument lists created by `DO`, `CALL`, `TEXT ... CONTAINS`, and `RANDOM` are temporary parser storage. They now share one cleanup helper, so repeated calls do not retain the duplicated argument strings or pointer array. The helper does not change argument evaluation or syntax compatibility.
|
||||||
|
|
||||||
|
Focused verification builds with AddressSanitizer/UndefinedBehaviorSanitizer, repeatedly exercises a four-argument `DO`, and checks the cleanup helper before confirming the existing string, list, and map overwrite behavior.
|
||||||
|
|||||||
+6
-4
@@ -189,6 +189,8 @@ static char *expr_after_word(const char *line,const char *word){ const char *p=l
|
|||||||
static char *substr(const char *a,const char *b){ if(b<a) b=a; char *s=(char*)xmalloc((size_t)(b-a)+1); memcpy(s,a,(size_t)(b-a)); s[b-a]=0; return s; }
|
static char *substr(const char *a,const char *b){ if(b<a) b=a; char *s=(char*)xmalloc((size_t)(b-a)+1); memcpy(s,a,(size_t)(b-a)); s[b-a]=0; return s; }
|
||||||
static const char *find_word_ci(const char *s,const char *word){ size_t n=strlen(word); int inq=0, depth=0; const char *p=s; while(*p){ if(*p=='"'&&(p==s||p[-1]!='\\')) inq=!inq; if(!inq){ if(*p=='(') depth++; else if(*p==')') depth--; if(depth==0 && (p==s||isspace((unsigned char)p[-1])) && strnicmp2(p,word,n)==0 && (p[n]==0||isspace((unsigned char)p[n]))) return p; } p++; } return NULL; }
|
static const char *find_word_ci(const char *s,const char *word){ size_t n=strlen(word); int inq=0, depth=0; const char *p=s; while(*p){ if(*p=='"'&&(p==s||p[-1]!='\\')) inq=!inq; if(!inq){ if(*p=='(') depth++; else if(*p==')') depth--; if(depth==0 && (p==s||isspace((unsigned char)p[-1])) && strnicmp2(p,word,n)==0 && (p[n]==0||isspace((unsigned char)p[n]))) return p; } p++; } return NULL; }
|
||||||
static int split_args(const char *s,char ***out){ int count=0, cap=0; char **arr=NULL; int inq=0, depth=0; const char *st=s,*p=s; while(1){ char c=*p; if(c=='"'&&(p==s||p[-1]!='\\')) inq=!inq; if(!inq){ if(c=='(') depth++; else if(c==')') depth--; if((c==','&&depth==0)||c==0){ char *part=substr(st,p); char *t=trim_inplace(part); if(count>=cap){ cap=cap?cap*2:4; arr=(char**)xrealloc(arr,sizeof(char*)*cap);} arr[count++]=xstrdup(t); free(part); if(c==0) break; st=p+1; } } p++; } *out=arr; return count; }
|
static int split_args(const char *s,char ***out){ int count=0, cap=0; char **arr=NULL; int inq=0, depth=0; const char *st=s,*p=s; while(1){ char c=*p; if(c=='"'&&(p==s||p[-1]!='\\')) inq=!inq; if(!inq){ if(c=='(') depth++; else if(c==')') depth--; if((c==','&&depth==0)||c==0){ char *part=substr(st,p); char *t=trim_inplace(part); if(count>=cap){ cap=cap?cap*2:4; arr=(char**)xrealloc(arr,sizeof(char*)*cap);} arr[count++]=xstrdup(t); free(part); if(c==0) break; st=p+1; } } p++; } *out=arr; return count; }
|
||||||
|
static void split_args_free(char **parts, int count){ int i; for(i=0;i<count;i++) free(parts[i]); free(parts); }
|
||||||
|
static void call_args_free(Value *args, int count){ int i; for(i=0;i<count;i++) value_free(args[i]); free(args); }
|
||||||
|
|
||||||
static int is_word_token(const char *s){ if(!s||!*s) return 0; if(!(isalpha((unsigned char)s[0])||s[0]=='_')) return 0; for(s++;*s;s++) if(!(isalnum((unsigned char)*s)||*s=='_')) return 0; return 1; }
|
static int is_word_token(const char *s){ if(!s||!*s) return 0; if(!(isalpha((unsigned char)s[0])||s[0]=='_')) return 0; for(s++;*s;s++) if(!(isalnum((unsigned char)*s)||*s=='_')) return 0; return 1; }
|
||||||
static char *text_upper_copy(const char *s){ char *o=xstrdup(s?s:""); int i; for(i=0;o[i];i++) o[i]=(char)toupper((unsigned char)o[i]); return o; }
|
static char *text_upper_copy(const char *s){ char *o=xstrdup(s?s:""); int i; for(i=0;o[i];i++) o[i]=(char)toupper((unsigned char)o[i]); return o; }
|
||||||
@@ -412,12 +414,12 @@ static void exec_line(Runtime *rt,Program *p,int *pcp,const char *raw){ char buf
|
|||||||
else { const char *pcur=rest; char *name=unquote_token(&pcur); char *n=trim_inplace(name); const char *save=pcur; char *maybe=unquote_token(&pcur); char *ty=trim_inplace(maybe); if(claro_is_type_word(ty)){ char *after=trim_inplace((char*)pcur); Value v=*after?eval_expr(rt,after):claro_default_for_type(ty); if(*n) rt_set_checked(rt,p->path,pc+1,n,ty,v); } else { Value v=eval_expr(rt,save); if(*n) rt_set_checked(rt,p->path,pc+1,n,NULL,v); } free(maybe); free(name); } return; }
|
else { const char *pcur=rest; char *name=unquote_token(&pcur); char *n=trim_inplace(name); const char *save=pcur; char *maybe=unquote_token(&pcur); char *ty=trim_inplace(maybe); if(claro_is_type_word(ty)){ char *after=trim_inplace((char*)pcur); Value v=*after?eval_expr(rt,after):claro_default_for_type(ty); if(*n) rt_set_checked(rt,p->path,pc+1,n,ty,v); } else { Value v=eval_expr(rt,save); if(*n) rt_set_checked(rt,p->path,pc+1,n,NULL,v); } free(maybe); free(name); } return; }
|
||||||
if(strcmp(up,"ASK")==0){ const char *as=find_word_ci(t,"AS"); char input[1024]; char *prompt_text=NULL; char *var=NULL; char *type=NULL; if(as){ char *prompt_expr=substr(t+3,as); const char *pcur=as+2; var=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } { Value prompt=eval_expr(rt,prompt_expr); prompt_text=v_to_string(prompt); } free(prompt_expr); } else { const char *pcur=t+3; prompt_text=unquote_token(&pcur); var=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } } if(prompt_text&&prompt_text[0]){ if(rt->capture){ str_add(&rt->captured,prompt_text); str_ch(&rt->captured,'\n'); } else { printf("%s\n",prompt_text); fflush(stdout); } } if(var&&*trim_inplace(var)){ Value val=v_str(""); if(fgets(input,sizeof(input),stdin)){ size_t n=strlen(input); while(n&&(input[n-1]=='\n'||input[n-1]=='\r')) input[--n]=0; } else input[0]=0; if(type&&*type){ if(!claro_value_from_text(type,input,&val)){ rt_error(rt,p->path,pc+1,"%s needs %s input. Try a value like %s.",trim_inplace(var),type,ci_eq(type,"NUMBER")?"5":(ci_eq(type,"YESNO")?"YES":"text")); } else rt_set_checked(rt,p->path,pc+1,trim_inplace(var),type,val); } else rt_set_checked(rt,p->path,pc+1,trim_inplace(var),NULL,v_str(input)); } else rt_error(rt,p->path,pc+1,"ASK needs a variable name. Try: ASK \"What is your name?\" name"); free(prompt_text); free(var); free(type); return; }
|
if(strcmp(up,"ASK")==0){ const char *as=find_word_ci(t,"AS"); char input[1024]; char *prompt_text=NULL; char *var=NULL; char *type=NULL; if(as){ char *prompt_expr=substr(t+3,as); const char *pcur=as+2; var=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } { Value prompt=eval_expr(rt,prompt_expr); prompt_text=v_to_string(prompt); } free(prompt_expr); } else { const char *pcur=t+3; prompt_text=unquote_token(&pcur); var=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } } if(prompt_text&&prompt_text[0]){ if(rt->capture){ str_add(&rt->captured,prompt_text); str_ch(&rt->captured,'\n'); } else { printf("%s\n",prompt_text); fflush(stdout); } } if(var&&*trim_inplace(var)){ Value val=v_str(""); if(fgets(input,sizeof(input),stdin)){ size_t n=strlen(input); while(n&&(input[n-1]=='\n'||input[n-1]=='\r')) input[--n]=0; } else input[0]=0; if(type&&*type){ if(!claro_value_from_text(type,input,&val)){ rt_error(rt,p->path,pc+1,"%s needs %s input. Try a value like %s.",trim_inplace(var),type,ci_eq(type,"NUMBER")?"5":(ci_eq(type,"YESNO")?"YES":"text")); } else rt_set_checked(rt,p->path,pc+1,trim_inplace(var),type,val); } else rt_set_checked(rt,p->path,pc+1,trim_inplace(var),NULL,v_str(input)); } else rt_error(rt,p->path,pc+1,"ASK needs a variable name. Try: ASK \"What is your name?\" name"); free(prompt_text); free(var); free(type); return; }
|
||||||
if(strcmp(up,"IF")==0){ int elsepos=-1; int end=match_block(p,pc,"IF","ENDIF","ELSE",&elsepos); char *e=expr_after_word(t,"IF"); Value cond=eval_expr(rt,e); free(e); if(v_truth(cond)) exec_range(rt,p,pc+1,elsepos>=0?elsepos:end); else if(elsepos>=0) exec_range(rt,p,elsepos+1,end); *pcp=end<0?pc:end; return; }
|
if(strcmp(up,"IF")==0){ int elsepos=-1; int end=match_block(p,pc,"IF","ENDIF","ELSE",&elsepos); char *e=expr_after_word(t,"IF"); Value cond=eval_expr(rt,e); free(e); if(v_truth(cond)) exec_range(rt,p,pc+1,elsepos>=0?elsepos:end); else if(elsepos>=0) exec_range(rt,p,elsepos+1,end); *pcp=end<0?pc:end; return; }
|
||||||
if(strcmp(up,"DO")==0){ int end=find_done(p,pc); const char *times=find_word_ci(t,"TIMES"); if(!times){ const char *pcur=t+2; char *name=unquote_token(&pcur); char **parts=NULL; int ac=0,i; Value *args=NULL; char *n=trim_inplace(name); if(*trim_inplace((char*)pcur)) ac=split_args(pcur,&parts); args=(Value*)xmalloc(sizeof(Value)*(ac?ac:1)); for(i=0;i<ac;i++) args[i]=eval_expr(rt,parts[i]); call_function(rt,n,args,ac); free(name); return; } else { char *e=substr(t+2,times); int n=(int)v_number(eval_expr(rt,e)); int i; free(e); for(i=0;i<n && !rt->error && !rt->returning;i++) exec_range(rt,p,pc+1,end); *pcp=end<0?pc:end; return; } }
|
if(strcmp(up,"DO")==0){ int end=find_done(p,pc); const char *times=find_word_ci(t,"TIMES"); if(!times){ const char *pcur=t+2; char *name=unquote_token(&pcur); char **parts=NULL; int ac=0,i; Value *args=NULL; char *n=trim_inplace(name); if(*trim_inplace((char*)pcur)) ac=split_args(pcur,&parts); args=(Value*)xmalloc(sizeof(Value)*(ac?ac:1)); for(i=0;i<ac;i++) args[i]=eval_expr(rt,parts[i]); call_function(rt,n,args,ac); split_args_free(parts,ac); call_args_free(args,ac); free(name); return; } else { char *e=substr(t+2,times); int n=(int)v_number(eval_expr(rt,e)); int i; free(e); for(i=0;i<n && !rt->error && !rt->returning;i++) exec_range(rt,p,pc+1,end); *pcp=end<0?pc:end; return; } }
|
||||||
if(strcmp(up,"FOR")==0){ int end=find_done(p,pc); if(starts_ci(t,"FOR EACH")){ char *r=t+8; const char *in=find_word_ci(r,"IN"); char *vars=substr(r,in?in:r); char *expr=xstrdup(in?in+2:""); Value col=eval_expr(rt,expr); char *comma=strchr(vars,','); if(comma&&col.type==V_MAP){ *comma=0; char *k=trim_inplace(vars), *v=trim_inplace(comma+1); int i; for(i=0;i<col.map->count&&!rt->error&&!rt->returning;i++){ rt_set(rt,k,v_str(col.map->keys[i])); rt_set(rt,v,col.map->vals[i]); exec_range(rt,p,pc+1,end); } } else if(col.type==V_LIST){ char *v=trim_inplace(vars); int i; for(i=0;i<col.list->count&&!rt->error&&!rt->returning;i++){ rt_set(rt,v,col.list->items[i]); exec_range(rt,p,pc+1,end); } } free(vars); free(expr); } else { char var[128]; const char *from=find_word_ci(t,"FROM"), *to=find_word_ci(t,"TO"), *step=find_word_ci(t,"STEP"); double a,b,s=1; if(from&&to){ snprintf(var,sizeof(var),"%.*s",(int)(from-(t+3)),t+3); { char *vt=trim_inplace(var); memmove(var,vt,strlen(vt)+1); } { char *ea=substr(from+4,to); char *eb=step?substr(to+2,step):xstrdup(to+2); a=v_number(eval_expr(rt,ea)); b=v_number(eval_expr(rt,eb)); if(step) s=v_number(eval_expr(rt,step+4)); free(ea); free(eb); } if(s==0) s=1; if(s>0){ for(;a<=b&&!rt->error&&!rt->returning;a+=s){ rt_set(rt,var,v_num(a)); exec_range(rt,p,pc+1,end);} } else { for(;a>=b&&!rt->error&&!rt->returning;a+=s){ rt_set(rt,var,v_num(a)); exec_range(rt,p,pc+1,end);} } } } *pcp=end<0?pc:end; return; }
|
if(strcmp(up,"FOR")==0){ int end=find_done(p,pc); if(starts_ci(t,"FOR EACH")){ char *r=t+8; const char *in=find_word_ci(r,"IN"); char *vars=substr(r,in?in:r); char *expr=xstrdup(in?in+2:""); Value col=eval_expr(rt,expr); char *comma=strchr(vars,','); if(comma&&col.type==V_MAP){ *comma=0; char *k=trim_inplace(vars), *v=trim_inplace(comma+1); int i; for(i=0;i<col.map->count&&!rt->error&&!rt->returning;i++){ rt_set(rt,k,v_str(col.map->keys[i])); rt_set(rt,v,col.map->vals[i]); exec_range(rt,p,pc+1,end); } } else if(col.type==V_LIST){ char *v=trim_inplace(vars); int i; for(i=0;i<col.list->count&&!rt->error&&!rt->returning;i++){ rt_set(rt,v,col.list->items[i]); exec_range(rt,p,pc+1,end); } } free(vars); free(expr); } else { char var[128]; const char *from=find_word_ci(t,"FROM"), *to=find_word_ci(t,"TO"), *step=find_word_ci(t,"STEP"); double a,b,s=1; if(from&&to){ snprintf(var,sizeof(var),"%.*s",(int)(from-(t+3)),t+3); { char *vt=trim_inplace(var); memmove(var,vt,strlen(vt)+1); } { char *ea=substr(from+4,to); char *eb=step?substr(to+2,step):xstrdup(to+2); a=v_number(eval_expr(rt,ea)); b=v_number(eval_expr(rt,eb)); if(step) s=v_number(eval_expr(rt,step+4)); free(ea); free(eb); } if(s==0) s=1; if(s>0){ for(;a<=b&&!rt->error&&!rt->returning;a+=s){ rt_set(rt,var,v_num(a)); exec_range(rt,p,pc+1,end);} } else { for(;a>=b&&!rt->error&&!rt->returning;a+=s){ rt_set(rt,var,v_num(a)); exec_range(rt,p,pc+1,end);} } } } *pcp=end<0?pc:end; return; }
|
||||||
if(strcmp(up,"REPEAT")==0){ int end=match_block(p,pc,"REPEAT","UNTIL",NULL,NULL); char *cond=p->lines[end]+5; do{ exec_range(rt,p,pc+1,end); if(rt->error||rt->returning) break; }while(!v_truth(eval_expr(rt,cond))); *pcp=end<0?pc:end; return; }
|
if(strcmp(up,"REPEAT")==0){ int end=match_block(p,pc,"REPEAT","UNTIL",NULL,NULL); char *cond=p->lines[end]+5; do{ exec_range(rt,p,pc+1,end); if(rt->error||rt->returning) break; }while(!v_truth(eval_expr(rt,cond))); *pcp=end<0?pc:end; return; }
|
||||||
if(strcmp(up,"TEACH")==0){ int end=match_block(p,pc,"TEACH","LEARNED",NULL,NULL); *pcp=end<0?pc:end; return; }
|
if(strcmp(up,"TEACH")==0){ int end=match_block(p,pc,"TEACH","LEARNED",NULL,NULL); *pcp=end<0?pc:end; return; }
|
||||||
if(strcmp(up,"RETURN")==0){ char *e=expr_after_word(t,"RETURN"); rt->ret=eval_expr(rt,e); rt->returning=1; free(e); return; }
|
if(strcmp(up,"RETURN")==0){ char *e=expr_after_word(t,"RETURN"); rt->ret=eval_expr(rt,e); rt->returning=1; free(e); return; }
|
||||||
if(strcmp(up,"CALL")==0){ char *rest=t+4; const char *with=find_word_ci(rest,"WITH"); char *name=with?substr(rest,with):xstrdup(rest); char **parts=NULL; int ac=0,i; Value *args=NULL; char *n=trim_inplace(name); if(with) ac=split_args(with+4,&parts); args=(Value*)xmalloc(sizeof(Value)*(ac?ac:1)); for(i=0;i<ac;i++) args[i]=eval_expr(rt,parts[i]); call_function(rt,n,args,ac); free(name); return; }
|
if(strcmp(up,"CALL")==0){ char *rest=t+4; const char *with=find_word_ci(rest,"WITH"); char *name=with?substr(rest,with):xstrdup(rest); char **parts=NULL; int ac=0,i; Value *args=NULL; char *n=trim_inplace(name); if(with) ac=split_args(with+4,&parts); args=(Value*)xmalloc(sizeof(Value)*(ac?ac:1)); for(i=0;i<ac;i++) args[i]=eval_expr(rt,parts[i]); call_function(rt,n,args,ac); split_args_free(parts,ac); call_args_free(args,ac); free(name); return; }
|
||||||
if(strcmp(up,"IMPORT")==0){ const char *pcur=t+6; char *path=unquote_token(&pcur); char *ns=NULL; while(*pcur&&isspace((unsigned char)*pcur)) pcur++; if(starts_ci(pcur,"AS")){ pcur+=2; ns=xstrdup(trim_inplace((char*)pcur)); } do_import(rt,path,ns,p->path,pc+1); free(path); free(ns); return; }
|
if(strcmp(up,"IMPORT")==0){ const char *pcur=t+6; char *path=unquote_token(&pcur); char *ns=NULL; while(*pcur&&isspace((unsigned char)*pcur)) pcur++; if(starts_ci(pcur,"AS")){ pcur+=2; ns=xstrdup(trim_inplace((char*)pcur)); } do_import(rt,path,ns,p->path,pc+1); free(path); free(ns); return; }
|
||||||
if(strcmp(up,"ADD")==0){ const char *to=find_word_ci(t,"TO"); if(to){ char *ex=substr(t+3,to); char *var=xstrdup(trim_inplace((char*)to+2)); Value v=eval_expr(rt,ex); Value l=rt_get(rt,var); if(l.type==V_LIST){ list_add(l.list,v); rt_set(rt,var,l); } free(ex); free(var);} return; }
|
if(strcmp(up,"ADD")==0){ const char *to=find_word_ci(t,"TO"); if(to){ char *ex=substr(t+3,to); char *var=xstrdup(trim_inplace((char*)to+2)); Value v=eval_expr(rt,ex); Value l=rt_get(rt,var); if(l.type==V_LIST){ list_add(l.list,v); rt_set(rt,var,l); } free(ex); free(var);} return; }
|
||||||
if(strcmp(up,"COUNT")==0){ const char *as=find_word_ci(t,"AS"); if(as){ if(starts_ci(t+5," ARGUMENTS")){ char *var=xstrdup(trim_inplace((char*)as+2)); rt_set(rt,var,v_num(rt->script_argc)); free(var); } else { char *ex=substr(t+5,as); char *var=xstrdup(trim_inplace((char*)as+2)); Value v=eval_expr(rt,ex); rt_set(rt,var,v_num(v.type==V_LIST?v.list->count:(v.type==V_MAP?v.map->count:0))); free(ex); free(var); } } return; }
|
if(strcmp(up,"COUNT")==0){ const char *as=find_word_ci(t,"AS"); if(as){ if(starts_ci(t+5," ARGUMENTS")){ char *var=xstrdup(trim_inplace((char*)as+2)); rt_set(rt,var,v_num(rt->script_argc)); free(var); } else { char *ex=substr(t+5,as); char *var=xstrdup(trim_inplace((char*)as+2)); Value v=eval_expr(rt,ex); rt_set(rt,var,v_num(v.type==V_LIST?v.list->count:(v.type==V_MAP?v.map->count:0))); free(ex); free(var); } } return; }
|
||||||
@@ -436,9 +438,9 @@ static void exec_line(Runtime *rt,Program *p,int *pcp,const char *raw){ char buf
|
|||||||
if(strcmp(up,"TEXT")==0){ char restup[64],op[64]; const char *as=find_word_ci(t,"AS"); const char *pcur=t+4; first_word(pcur,op,sizeof(op)); upper_copy(restup,op,sizeof(restup)); while(*pcur&&isspace((unsigned char)*pcur)) pcur++; while(*pcur&&!isspace((unsigned char)*pcur)) pcur++; if(as){ char *var=xstrdup(trim_inplace((char*)as+2));
|
if(strcmp(up,"TEXT")==0){ char restup[64],op[64]; const char *as=find_word_ci(t,"AS"); const char *pcur=t+4; first_word(pcur,op,sizeof(op)); upper_copy(restup,op,sizeof(restup)); while(*pcur&&isspace((unsigned char)*pcur)) pcur++; while(*pcur&&!isspace((unsigned char)*pcur)) pcur++; if(as){ char *var=xstrdup(trim_inplace((char*)as+2));
|
||||||
if(!strcmp(restup,"STARTS")||!strcmp(restup,"ENDS")){ const char *with=find_word_ci(pcur,"WITH"); if(with){ char *left=substr(pcur,with); char *right=substr(with+4,as); Value lv=eval_expr(rt,left), rv=eval_expr(rt,right); char *ls=v_to_string(lv), *rs=v_to_string(rv); size_t ll=strlen(ls), rl=strlen(rs); int ok=!strcmp(restup,"STARTS")?(rl<=ll&&strncmp(ls,rs,rl)==0):(rl<=ll&&strcmp(ls+ll-rl,rs)==0); rt_set(rt,var,v_bool(ok)); free(left); free(right); free(ls); free(rs); } }
|
if(!strcmp(restup,"STARTS")||!strcmp(restup,"ENDS")){ const char *with=find_word_ci(pcur,"WITH"); if(with){ char *left=substr(pcur,with); char *right=substr(with+4,as); Value lv=eval_expr(rt,left), rv=eval_expr(rt,right); char *ls=v_to_string(lv), *rs=v_to_string(rv); size_t ll=strlen(ls), rl=strlen(rs); int ok=!strcmp(restup,"STARTS")?(rl<=ll&&strncmp(ls,rs,rl)==0):(rl<=ll&&strcmp(ls+ll-rl,rs)==0); rt_set(rt,var,v_bool(ok)); free(left); free(right); free(ls); free(rs); } }
|
||||||
else if(!strcmp(restup,"REPLACE")){ const char *from=find_word_ci(pcur,"FROM"); const char *to=from?find_word_ci(from+4,"TO"):NULL; if(from&&to){ char *src=substr(pcur,from); char *old=substr(from+4,to); char *neu=substr(to+2,as); Value sv=eval_expr(rt,src), ov=eval_expr(rt,old), nv=eval_expr(rt,neu); char *ss=v_to_string(sv), *os=v_to_string(ov), *ns=v_to_string(nv); Str b; char *pos; str_init(&b); if(os[0]){ char *cur=ss; while((pos=strstr(cur,os))){ str_addn(&b,cur,(size_t)(pos-cur)); str_add(&b,ns); cur=pos+strlen(os); } str_add(&b,cur); rt_set(rt,var,v_str(b.s?b.s:"")); } else rt_set(rt,var,v_str(ss)); free(b.s); free(src); free(old); free(neu); free(ss); free(os); free(ns); } }
|
else if(!strcmp(restup,"REPLACE")){ const char *from=find_word_ci(pcur,"FROM"); const char *to=from?find_word_ci(from+4,"TO"):NULL; if(from&&to){ char *src=substr(pcur,from); char *old=substr(from+4,to); char *neu=substr(to+2,as); Value sv=eval_expr(rt,src), ov=eval_expr(rt,old), nv=eval_expr(rt,neu); char *ss=v_to_string(sv), *os=v_to_string(ov), *ns=v_to_string(nv); Str b; char *pos; str_init(&b); if(os[0]){ char *cur=ss; while((pos=strstr(cur,os))){ str_addn(&b,cur,(size_t)(pos-cur)); str_add(&b,ns); cur=pos+strlen(os); } str_add(&b,cur); rt_set(rt,var,v_str(b.s?b.s:"")); } else rt_set(rt,var,v_str(ss)); free(b.s); free(src); free(old); free(neu); free(ss); free(os); free(ns); } }
|
||||||
else { char *ex=substr(pcur,as); Value v=eval_expr(rt,ex); char *txt=v_to_string(v); if(!strcmp(restup,"UPPER")){ char *r=text_upper_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"LOWER")){ char *r=text_lower_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"TRIM")){ char *r=text_trim_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"LENGTH")){ rt_set(rt,var,v_num((double)strlen(txt))); } else if(!strcmp(restup,"CONTAINS")){ char *left=NULL,*right=NULL; const char *needle=find_word_ci(pcur,"NEEDLE"); if(!needle) needle=find_word_ci(pcur,"TEXT"); if(needle){ left=substr(pcur,needle); right=substr(needle+6,as); } else { char **args=NULL; int n=split_args(pcur,&args); if(n>=2){ left=xstrdup(args[0]); right=substr(args[1],as); } } if(left&&right){ Value lv=eval_expr(rt,left), rv=eval_expr(rt,right); char *ls=v_to_string(lv), *rs=v_to_string(rv); rt_set(rt,var,v_bool(strstr(ls,rs)!=NULL)); free(ls); free(rs); } free(left); free(right); } free(txt); free(ex); }
|
else { char *ex=substr(pcur,as); Value v=eval_expr(rt,ex); char *txt=v_to_string(v); if(!strcmp(restup,"UPPER")){ char *r=text_upper_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"LOWER")){ char *r=text_lower_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"TRIM")){ char *r=text_trim_copy(txt); rt_set(rt,var,v_str(r)); free(r); } else if(!strcmp(restup,"LENGTH")){ rt_set(rt,var,v_num((double)strlen(txt))); } else if(!strcmp(restup,"CONTAINS")){ char *left=NULL,*right=NULL; const char *needle=find_word_ci(pcur,"NEEDLE"); if(!needle) needle=find_word_ci(pcur,"TEXT"); if(needle){ left=substr(pcur,needle); right=substr(needle+6,as); } else { char **args=NULL; int n=split_args(pcur,&args); if(n>=2){ left=xstrdup(args[0]); right=substr(args[1],as); } split_args_free(args,n); } if(left&&right){ Value lv=eval_expr(rt,left), rv=eval_expr(rt,right); char *ls=v_to_string(lv), *rs=v_to_string(rv); rt_set(rt,var,v_bool(strstr(ls,rs)!=NULL)); free(ls); free(rs); } free(left); free(right); } free(txt); free(ex); }
|
||||||
free(var); } return; }
|
free(var); } return; }
|
||||||
if(strcmp(up,"RANDOM")==0){ const char *as=find_word_ci(t,"AS"); if(as&&starts_ci(t+6," NUMBER")){ char *range=substr(t+13,as); char *var=xstrdup(trim_inplace((char*)as+2)); char **args=NULL; int n=split_args(range,&args); double a=1,b=100; if(n>=2){ a=v_number(eval_expr(rt,args[0])); b=v_number(eval_expr(rt,args[1])); } else { char *tmp=range; char *one=unquote_token((const char**)&tmp); char *two=unquote_token((const char**)&tmp); if(*one) a=v_number(eval_expr(rt,one)); if(*two) b=v_number(eval_expr(rt,two)); free(one); free(two); } if(b<a){ double c=a; a=b; b=c; } rt_set(rt,var,v_num((int)a + (rand()%((int)(b-a+1))))); free(range); free(var); } return; }
|
if(strcmp(up,"RANDOM")==0){ const char *as=find_word_ci(t,"AS"); if(as&&starts_ci(t+6," NUMBER")){ char *range=substr(t+13,as); char *var=xstrdup(trim_inplace((char*)as+2)); char **args=NULL; int n=split_args(range,&args); double a=1,b=100; if(n>=2){ a=v_number(eval_expr(rt,args[0])); b=v_number(eval_expr(rt,args[1])); } else { char *tmp=range; char *one=unquote_token((const char**)&tmp); char *two=unquote_token((const char**)&tmp); if(*one) a=v_number(eval_expr(rt,one)); if(*two) b=v_number(eval_expr(rt,two)); free(one); free(two); } split_args_free(args,n); if(b<a){ double c=a; a=b; b=c; } rt_set(rt,var,v_num((int)a + (rand()%((int)(b-a+1))))); free(range); free(var); } return; }
|
||||||
if(strcmp(up,"RUN")==0){ const char *as=find_word_ci(t,"AS"); if(as&&starts_ci(t+3," COMMAND")){ char *ex=substr(t+11,as); char *var=xstrdup(trim_inplace((char*)as+2)); Value cv=eval_expr(rt,ex); char *cmd=v_to_string(cv); int code=0; char *out=capture_command_output(cmd,&code); rt_set(rt,var,v_str(out)); rt_set(rt,"LASTEXIT",v_num(code)); free(out); free(cmd); free(ex); free(var); } return; }
|
if(strcmp(up,"RUN")==0){ const char *as=find_word_ci(t,"AS"); if(as&&starts_ci(t+3," COMMAND")){ char *ex=substr(t+11,as); char *var=xstrdup(trim_inplace((char*)as+2)); Value cv=eval_expr(rt,ex); char *cmd=v_to_string(cv); int code=0; char *out=capture_command_output(cmd,&code); rt_set(rt,var,v_str(out)); rt_set(rt,"LASTEXIT",v_num(code)); free(out); free(cmd); free(ex); free(var); } return; }
|
||||||
if(strcmp(up,"SORT")==0){ char *name=xstrdup(trim_inplace(t+4)); Value v=rt_get(rt,name); value_list_sort(v); rt_set(rt,name,v); free(name); return; }
|
if(strcmp(up,"SORT")==0){ char *name=xstrdup(trim_inplace(t+4)); Value v=rt_get(rt,name); value_list_sort(v); rt_set(rt,name,v); free(name); return; }
|
||||||
if(strcmp(up,"REVERSE")==0){ char *name=xstrdup(trim_inplace(t+7)); Value v=rt_get(rt,name); value_list_reverse(v); rt_set(rt,name,v); free(name); return; }
|
if(strcmp(up,"REVERSE")==0){ char *name=xstrdup(trim_inplace(t+7)); Value v=rt_get(rt,name); value_list_reverse(v); rt_set(rt,name,v); free(name); return; }
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ def main() -> int:
|
|||||||
binary = tmp_path / "claro-lsan"
|
binary = tmp_path / "claro-lsan"
|
||||||
script = tmp_path / "overwrite.claro"
|
script = tmp_path / "overwrite.claro"
|
||||||
script.write_text(
|
script.write_text(
|
||||||
|
'TEACH consume first, second, third, fourth\n'
|
||||||
|
' RETURN first\n'
|
||||||
|
'END\n'
|
||||||
'SET value TO "first"\n'
|
'SET value TO "first"\n'
|
||||||
'SET value TO "second"\n'
|
'SET value TO "second"\n'
|
||||||
'SET items TO LIST\n'
|
'SET items TO LIST\n'
|
||||||
@@ -26,7 +29,8 @@ def main() -> int:
|
|||||||
'SET profile TO MAP\n'
|
'SET profile TO MAP\n'
|
||||||
'PUT profile KEY "name" VALUE "Ada"\n'
|
'PUT profile KEY "name" VALUE "Ada"\n'
|
||||||
'PUT profile KEY "name" VALUE "Grace"\n'
|
'PUT profile KEY "name" VALUE "Grace"\n'
|
||||||
'SAY profile\n',
|
+ ''.join('DO consume "a", "b", "c", "d"\n' for _ in range(2000))
|
||||||
|
+ 'SAY profile\n',
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
build = subprocess.run(
|
build = subprocess.run(
|
||||||
@@ -50,6 +54,9 @@ def main() -> int:
|
|||||||
print(run.stderr, end="", file=sys.stderr)
|
print(run.stderr, end="", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
source = (ROOT / "src" / "claro.c").read_text(encoding="utf-8")
|
source = (ROOT / "src" / "claro.c").read_text(encoding="utf-8")
|
||||||
|
if "static void split_args_free(char **parts, int count)" not in source:
|
||||||
|
print("FAIL: split argument storage has no cleanup boundary")
|
||||||
|
return 1
|
||||||
if "static void value_free(Value v)" not in source or "value_free(v->val);" not in source:
|
if "static void value_free(Value v)" not in source or "value_free(v->val);" not in source:
|
||||||
print("FAIL: overwritten runtime values are not released")
|
print("FAIL: overwritten runtime values are not released")
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
Reference in New Issue
Block a user