Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the local builtin with support for dynamic local scopes #703

Open
wants to merge 55 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ccfe8a6
Incomplete: Add local and declare builtins with a dynamic scope
JohnoKing Apr 22, 2021
32672ad
Merge remote-tracking branch 'upstream/master' into local-builtin
JohnoKing Apr 26, 2021
9ff934a
Merge remote-tracking branch 'upstream/master' into local-builtin
JohnoKing May 3, 2021
3b55c89
Merge remote-tracking branch 'upstream/master' into local-builtin
JohnoKing May 8, 2021
03b849c
Merge remote-tracking branch 'upstream/master' into local-builtin
JohnoKing May 12, 2021
99a7bcb
Merge remote-tracking branch 'upstream/master' into local-builtin
JohnoKing May 23, 2021
d369dfd
Merge upstream changes into local-builtin branch
JohnoKing Dec 6, 2021
94013f5
Source common shtests code in local.sh (re: 844e6b24, aed5c6d7)
JohnoKing Dec 6, 2021
03b5fb8
Merge changes from ksh93u+m v1.0.0-beta.2
JohnoKing Dec 17, 2021
cf953aa
Merge current shp cleanup progress
JohnoKing Jan 5, 2022
ad96056
Merge upstream changes
JohnoKing Jan 27, 2022
91d3079
Fix compile of the forever incomplete local builtin
JohnoKing Jan 27, 2022
7721eeb
Experimental version of local builtin with POSIX function scoping
JohnoKing Jan 1, 2024
666cd88
Fix builtins.sh failure by setting NV_GLOBAL in POSIX functions
JohnoKing Jan 1, 2024
c9a2289
Revert leftover changes from failed b_dot_cmd experiment
JohnoKing Jan 1, 2024
eba8be8
Experimental fix for the functions.sh test failure
JohnoKing Jan 1, 2024
3493f2d
More tests
JohnoKing Jan 1, 2024
fa23bd4
Add another safety check
JohnoKing Jan 1, 2024
10f9ad1
Add NV_DYNAMIC to open the door for flexible behavior
JohnoKing Jan 1, 2024
dc7841d
Avoid possible bugs relating to declare/local -T
JohnoKing Jan 1, 2024
c91998b
FINALLY figured out how to move that scoping code away from xec.c
JohnoKing Jan 1, 2024
bffb6b8
Fix oversights in the new regression tests
JohnoKing Jan 1, 2024
33a6f17
Add more (failing) tests
JohnoKing Jan 1, 2024
88d77a5
Abandon unsalvageable ksh93v- scoping code and rewrite scoping
JohnoKing Jan 15, 2024
babebd0
Implement np->dynscope to avoid reuse of NV_TAGGED
JohnoKing Jan 16, 2024
3f695ba
Sync with upstream commits
JohnoKing Jan 16, 2024
50bb787
Remove old vestiges from the original implementation
JohnoKing Jan 16, 2024
a29d218
Update description for `typeset -D`
JohnoKing Jan 16, 2024
81499fc
Fix modernish regression tests failing on init
JohnoKing Jan 16, 2024
6b5534e
Attempt at freeze fix
JohnoKing Jan 16, 2024
9c751fe
Fix the (apparently) getopts freeze in modernish
JohnoKing Jan 16, 2024
3732068
Fix odd regression in memory leak tests
JohnoKing Jan 16, 2024
45d8749
Yet more cleanup
JohnoKing Jan 16, 2024
48a7d1d
Move np->dynscope handling to nv_open
JohnoKing Jan 16, 2024
c07491a
Port POSIX function code from b_dot_cmd()
JohnoKing Jan 16, 2024
c7ae6c8
Undo unnecessary changes
JohnoKing Jan 16, 2024
bfd0052
There is no form of scoping for dotted funcs, so this is useless
JohnoKing Jan 16, 2024
4434780
Add static scoping flag and make other improvements
JohnoKing Jan 16, 2024
83d1324
Improve comments in the test loop
JohnoKing Jan 17, 2024
189736a
Add regression test for functions run as dot scripts
JohnoKing Jan 17, 2024
c08f2bf
Fix compiler warning
JohnoKing Jan 17, 2024
31d2d24
Use sh.infunction more widely when applicable
JohnoKing Jan 17, 2024
2a1ec61
Parital revert to fix a test failure in functions.sh
JohnoKing Jan 17, 2024
6d5b2fa
Indent the large for loop
JohnoKing Jan 17, 2024
010b0e3
Add preliminary support for -D to `typeset -p`
JohnoKing Jan 17, 2024
fe00155
Add documentation for the new scoping behavior to ksh93(1)
JohnoKing Jan 17, 2024
bae563d
Improve the older set of local/declare regression tests
JohnoKing Jan 18, 2024
b601a87
Add regression test for typeset -p output and update some documentation
JohnoKing Jan 18, 2024
532a6c4
Simplify some code related to traps and scoping
JohnoKing Jan 23, 2024
c338eef
Merge with upstream changes
JohnoKing Feb 6, 2024
560d73d
Fix typo
JohnoKing Feb 6, 2024
0d91670
Merge remote-tracking branch 'upstream/dev' into local-builtin
JohnoKing Feb 14, 2024
24757cd
To avoid conflict with 93v- -c, rename static local option to -P
JohnoKing Feb 14, 2024
a473068
Add disabled regression test for https://github.com/ksh93/ksh/pull/70…
JohnoKing Feb 14, 2024
c04159d
Update documentation and error messages for -P change
JohnoKing Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cmd/ksh93/bltins/cflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
int b_return(int n, char *argv[],Shbltin_t *context)
{
/* 'return' outside of function, dotscript and profile behaves like 'exit' */
char do_exit = **argv=='e' || sh.fn_depth==0 && sh.dot_depth==0 && !sh_isstate(SH_PROFILE);
char do_exit = **argv=='e' || !sh.infunction && !sh_isstate(SH_PROFILE);
NOT_USED(context);
while((n = optget(argv, **argv=='e' ? sh_optexit : sh_optreturn))) switch(n)
{
Expand Down
97 changes: 50 additions & 47 deletions src/cmd/ksh93/bltins/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* *
* This software is part of the ast package *
* Copyright (c) 1982-2012 AT&T Intellectual Property *
* Copyright (c) 2020-2023 Contributors to ksh 93u+m *
* Copyright (c) 2020-2024 Contributors to ksh 93u+m *
* and is licensed under the *
* Eclipse Public License, Version 2.0 *
* *
Expand Down Expand Up @@ -56,8 +56,6 @@
#include <times.h>
#endif

#define DOTMAX MAXDEPTH /* maximum level of . nesting */

/*
* Handler function for nv_scan() that unsets a variable's export attribute
*/
Expand Down Expand Up @@ -215,16 +213,17 @@ int b_eval(int argc,char *argv[], Shbltin_t *context)
#endif
int b_dot_cmd(int n,char *argv[],Shbltin_t *context)
{
char *script;
Namval_t *np;
int jmpval;
struct sh_scoped savst, *prevscope = sh.st.self;
char *filename=0, *buffer=0, *tofree;
int fd;
struct dolnod *saveargfor;
volatile struct dolnod *argsave=0;
struct checkpt buff;
Sfio_t *iop=0;
char *script;
Namval_t *np;
struct sh_scoped savst, *prevscope = sh.st.self;
char *filename=0, *buffer=0, *tofree;
int fd, dtret, jmpval, save_invoc_local;
char save_infunction = -1;
struct dolnod *saveargfor;
volatile struct dolnod *argsave=0;
struct checkpt buff;
Sfio_t *iop=0;
Namval_t *nspace = sh.namespace;
NOT_USED(context);
while (n = optget(argv,sh_optdot)) switch (n)
{
Expand All @@ -242,61 +241,61 @@ int b_dot_cmd(int n,char *argv[],Shbltin_t *context)
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NULL));
UNREACHABLE();
}
if(sh.dot_depth >= DOTMAX)
if(sh.dot_depth >= MAXDEPTH)
{
errormsg(SH_DICT,ERROR_exit(1),e_toodeep,script);
UNREACHABLE();
}
if(!(np=sh.posix_fun))
/* check for KornShell style function first */
np = nv_search(script,sh.fun_tree,0);
if(np && is_afunction(np) && !nv_isattr(np,NV_FPOSIX) && !(sh_isoption(SH_POSIX) && sh.bltindata.bnode==SYSDOT))
{
/* check for KornShell style function first */
np = nv_search(script,sh.fun_tree,0);
if(np && is_afunction(np) && !nv_isattr(np,NV_FPOSIX) && !(sh_isoption(SH_POSIX) && sh.bltindata.bnode==SYSDOT))
if(!np->nvalue.ip)
{
if(!np->nvalue.ip)
path_search(script,NULL,0);
if(np->nvalue.ip)
{
path_search(script,NULL,0);
if(np->nvalue.ip)
{
if(nv_isattr(np,NV_FPOSIX))
np = 0;
}
else
{
errormsg(SH_DICT,ERROR_exit(1),e_found,script);
UNREACHABLE();
}
if(nv_isattr(np,NV_FPOSIX))
np = 0;
}
}
else
np = 0;
if(!np)
{
if((fd=path_open(script,path_get(script))) < 0)
else
{
errormsg(SH_DICT,ERROR_system(1),e_open,script);
errormsg(SH_DICT,ERROR_exit(1),e_found,script);
UNREACHABLE();
}
filename = path_fullname(stkptr(sh.stk,PATH_OFFSET));
}
}
else
np = 0;
if(!np)
{
/* Open the dot script */
if((fd=path_open(script,path_get(script))) < 0)
{
errormsg(SH_DICT,ERROR_system(1),e_open,script);
UNREACHABLE();
}
filename = path_fullname(stkptr(sh.stk,PATH_OFFSET));
}
else
{
/* We are executing a KornShell function as a dot script */
save_infunction = sh.infunction;
sh.infunction = FUN_KSHDOT;
}
*prevscope = sh.st;
sh.st.lineno = np?((struct functnod*)nv_funtree(np))->functline:1;
sh.st.lineno = np ? ((struct functnod*)nv_funtree(np))->functline : 1;
sh.st.save_tree = sh.var_tree;
if(filename)
{
sh.st.filename = filename;
sh.st.lineno = 1;
}
sh.st.prevst = prevscope;
sh.st.self = &savst;
sh.topscope = (Shscope_t*)sh.st.self;
prevscope->save_tree = sh.var_tree;
tofree = sh.st.filename;
if(np)
sh.st.filename = np->nvalue.rp->fname;
nv_putval(SH_PATHNAMENOD, sh.st.filename ,NV_NOFREE);
sh.posix_fun = 0;
nv_putval(SH_PATHNAMENOD,sh.st.filename,NV_NOFREE);
if(np || argv[1])
argsave = sh_argnew(argv,&saveargfor);
sh_pushcontext(&buff,SH_JMPDOT);
Expand All @@ -308,9 +307,11 @@ int b_dot_cmd(int n,char *argv[],Shbltin_t *context)
sh.dot_depth++;
update_sh_level();
if(np)
/* Execute the function as though it were a dot script */
sh_exec((Shnode_t*)(nv_funtree(np)),sh_isstate(SH_ERREXIT));
else
{
/* Run the dot script */
buffer = sh_malloc(IOBSIZE+1);
iop = sfnew(NULL,buffer,IOBSIZE,fd,SF_READ);
sh_offstate(SH_NOFORK);
Expand All @@ -331,12 +332,14 @@ int b_dot_cmd(int n,char *argv[],Shbltin_t *context)
prevscope->dolc = sh.st.dolc;
prevscope->dolv = sh.st.dolv;
}
if (sh.st.self != &savst)
if(sh.st.self != &savst)
*sh.st.self = sh.st;
/* only restore the top Shscope_t portion for POSIX functions */
if(save_infunction != -1)
sh.infunction = save_infunction;
/* Only restore the top Shscope_t portion for functions */
memcpy(&sh.st, prevscope, sizeof(Shscope_t));
sh.topscope = (Shscope_t*)prevscope;
nv_putval(SH_PATHNAMENOD, sh.st.filename ,NV_NOFREE);
nv_putval(SH_PATHNAMENOD,sh.st.filename,NV_NOFREE);
if(jmpval && jmpval!=SH_JMPFUN)
siglongjmp(*sh.jmplist,jmpval);
return sh.exitval;
Expand Down
51 changes: 43 additions & 8 deletions src/cmd/ksh93/bltins/typeset.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
* export [-p] [arg...]
* readonly [-p] [arg...]
* typeset [options] [arg...]
* declare [options] [arg...]
* local [options] [arg...]
* autoload [options] [arg...]
* compound [options] [arg...]
* float [options] [arg...]
Expand Down Expand Up @@ -208,6 +210,7 @@ int b_alias(int argc,char *argv[],Shbltin_t *context)
/* for the dictionary generator */
int b_autoload(int argc,char *argv[],Shbltin_t *context){}
int b_compound(int argc,char *argv[],Shbltin_t *context){}
int b_declare(int argc,char *argv[],Shbltin_t *context){}
int b_float(int argc,char *argv[],Shbltin_t *context){}
int b_functions(int argc,char *argv[],Shbltin_t *context){}
int b_integer(int argc,char *argv[],Shbltin_t *context){}
Expand All @@ -221,17 +224,19 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
const char *optstring = sh_opttypeset;
Namdecl_t *ntp = (Namdecl_t*)context->ptr;
Dt_t *troot;
int isfloat=0, isadjust=0, shortint=0, sflag=0;
int isfloat=0, isadjust=0, shortint=0, sflag=0, local, declare, scoping_flags = 0;

memset(&tdata,0,sizeof(tdata));
troot = sh.var_tree;
declare = argv[0][0] == 'd';
local = argv[0][0] == 'l';
if(ntp) /* type declaration command added using 'typeset -T' or 'enum' */
{
tdata.tp = ntp->tp;
opt_info.disc = (Optdisc_t*)ntp->optinfof;
optstring = ntp->optstring;
}
else if(argv[0][0] != 't') /* not <t>ypeset */
else if(argv[0][0] != 't' && !declare && !local) /* not <t>ypeset, <d>eclare or <l>ocal */
{
char **new_argv = (char **)stkalloc(sh.stk, (argc + 2) * sizeof(char*));
error_info.id = new_argv[0] = SYSTYPESET->nvname;
Expand Down Expand Up @@ -430,7 +435,19 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
flag |= (NV_EXPORT|NV_IDENT);
break;
case 'g':
flag &= ~(NV_SCOPES);
flag |= NV_GLOBAL;
scoping_flags++;
break;
case 'D':
flag &= ~(NV_SCOPES);
flag |= NV_DYNAMIC;
scoping_flags++;
break;
case 'P':
flag &= ~(NV_SCOPES);
flag |= NV_STATSCOPE;
scoping_flags++;
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
Expand All @@ -444,6 +461,12 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
endargs:
argv += opt_info.index;
opt_info.disc = 0;
/* 'local' builtin */
if(local && !sh.infunction)
{
errormsg(SH_DICT,ERROR_exit(1), "can only be used in a function");
UNREACHABLE();
}
/* handle argument of + and - specially */
if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-'))
tdata.aflag = *argv[0];
Expand All @@ -466,9 +489,9 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
errormsg(SH_DICT,2,e_optincompat1,"-m");
error_info.errors++;
}
if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN|NV_GLOBAL)))
if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN|NV_SCOPES)))
{
errormsg(SH_DICT,2,e_optincompat2,"-n","other options except -g");
errormsg(SH_DICT,2,e_optincompat2,"-n","other options except -P, -D and -g");
error_info.errors++;
}
if((flag&NV_TYPE) && (flag&~(NV_TYPE|NV_VARNAME|NV_ASSIGN)))
Expand Down Expand Up @@ -497,11 +520,23 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
errormsg(SH_DICT,ERROR_exit(2),"option argument cannot be greater than %d",SHRT_MAX);
UNREACHABLE();
}
if((flag&NV_GLOBAL) && sh.mktype)
if((flag&NV_SCOPES) && sh.mktype)
{
errormsg(SH_DICT,ERROR_exit(2),"-g: type members cannot be global");
errormsg(SH_DICT,ERROR_exit(2),"type members cannot use the scoping flags -P, -D and -g");
UNREACHABLE();
}
if(scoping_flags > 1)
{
errormsg(SH_DICT,ERROR_exit(2),"the scoping flags -P, -D and -g cannot be combined");
UNREACHABLE();
}
if(troot==sh.var_tree && !sh.mktype && sh.infunction && !(flag&(NV_SCOPES)))
{
if(local || declare)
flag |= NV_DYNAMIC;
else if(sh.infunction==FUN_POSIX)
flag |= NV_GLOBAL;
}
if(isfloat)
flag |= NV_DOUBLE;
if(sflag)
Expand All @@ -511,7 +546,7 @@ int b_typeset(int argc,char *argv[],Shbltin_t *context)
else if(!sh.typeinit)
flag |= NV_STATIC|NV_IDENT;
}
if(sh.fn_depth && !tdata.pflag)
if(sh.infunction < FUN_KSHDOT && !tdata.pflag)
flag |= NV_NOSCOPE;
if(tdata.help)
tdata.help = sh_strdup(tdata.help);
Expand Down Expand Up @@ -645,7 +680,7 @@ static int setall(char **argv,int flag,Dt_t *troot,struct tdata *tp)
{
char *name;
char *last = 0;
int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE));
int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE|NV_DYNAMIC));
int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY);
Dt_t *save_vartree = NULL;
Namval_t *save_namespace = NULL;
Expand Down
26 changes: 18 additions & 8 deletions src/cmd/ksh93/data/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const struct shtable3 shtab_builtins[] =
"functions", NV_BLTIN|BLT_ENV, bltin(typeset),
"integer", NV_BLTIN|BLT_ENV|BLT_DCL, bltin(typeset),
"nameref", NV_BLTIN|BLT_ENV|BLT_DCL, bltin(typeset),
"local", NV_BLTIN|BLT_ENV|BLT_DCL, bltin(typeset),
"declare", NV_BLTIN|BLT_ENV|BLT_DCL, bltin(typeset),
"test", NV_BLTIN|BLT_ENV, bltin(test),
"[", NV_BLTIN|BLT_ENV, bltin(test),
"let", NV_BLTIN|BLT_ENV, bltin(let),
Expand Down Expand Up @@ -729,7 +731,7 @@ const char sh_optexport[] =
"[+>0?An error occurred.]"
"}"

"[+SEE ALSO?\bsh\b(1), \btypeset\b(1)]"
"[+SEE ALSO?\bsh\b(1), \btypeset\b(1), \blocal\b(1)]"
;

const char sh_optgetopts[] =
Expand Down Expand Up @@ -1541,7 +1543,7 @@ const char sh_optreadonly[] =
"[+>0?An error occurred.]"
"}"

"[+SEE ALSO?\bsh\b(1), \btypeset\b(1)]"
"[+SEE ALSO?\bsh\b(1), \btypeset\b(1), \blocal\b(1)]"
;

const char sh_optredirect[] =
Expand Down Expand Up @@ -1691,6 +1693,7 @@ const char sh_optksh[] =

"[+SEE ALSO?\bset\b(1), \bbuiltin\b(1)]"
;

const char sh_optset[] =
"+[-1c?\n@(#)$Id: set (ksh 93u+m) 2023-05-18 $\n]"
"[--catalog?" SH_DICT "]"
Expand Down Expand Up @@ -1727,11 +1730,9 @@ const char sh_optset[] =
"[+>0?An error occurred.]"
"}"

"[+SEE ALSO?\btypeset\b(1), \bshift\b(1)]"
"[+SEE ALSO?\btypeset\b(1), \blocal\b(1), \bshift\b(1)]"
;



const char sh_optshift[] =
"[-1c?\n@(#)$Id: shift (AT&T Research) 1999-07-07 $\n]"
"[--catalog?" SH_DICT "]"
Expand Down Expand Up @@ -1856,7 +1857,11 @@ const char sh_opttypeset[] =
"[+?When \btypeset\b is called inside a function defined with the "
"\bfunction\b reserved word, and \aname\a does not contain a "
"\b.\b, then a local variable statically scoped to that function "
"will be created.]"
"will be created. If \btypeset\b is used in a POSIX function, a "
"variable is created in the global scope.]"
"[+?\btypeset\b can also be invoked as \bdeclare\b, or (in functions "
"only) as \blocal\b. A variable created or modified by either \bdeclare\b "
"or \blocal\b is given a dynamic scope.]"
"[+?Not all option combinations are possible. For example, the numeric "
"options \b-i\b, \b-E\b, and \b-F\b cannot be specified with "
"the justification options \b-L\b, \b-R\b, and \b-Z\b.]"
Expand Down Expand Up @@ -1912,7 +1917,10 @@ const char sh_opttypeset[] =
"[n?Name reference. The value is the name of a variable that \aname\a "
"references. \aname\a cannot contain a \b.\b.]"
"[p?Causes the output to be in a format that can be used as input to the "
"shell to recreate the attributes for variables.]"
"shell to recreate the attributes for variables. If this flag "
"is used by \btypeset\b in a POSIX function without also passing "
"\b-D\b or \b-P\b, the local scope is ignored and \btypeset\b will "
"only use the global scope.]"
"[r?Enables readonly. Once enabled it cannot be disabled. See "
"\breadonly\b(1).]"
"[s?Used with \b-i\b to restrict integer size to short.]"
Expand Down Expand Up @@ -1962,6 +1970,8 @@ const char sh_opttypeset[] =
"unset prior to processing the assignment list.]"
"[T]:?[tname?\atname\a is the name of a type name given to each \aname\a.]"
"[Z]#?[n?Zero fill. If \an\a is given it represents the field width.]"
"[D?Modifies variables using a dynamic local scope in functions.]"
"[P?Modifies variables using a static local scope in functions.]"
"\n"
"\n[name[=value]...]\n"
" -f [-tu] [name...]\n"
Expand Down Expand Up @@ -2075,7 +2085,7 @@ const char sh_optunset[] =
"or an error occurred.]"
"}"

"[+SEE ALSO?\btypeset\b(1)]"
"[+SEE ALSO?\btypeset\b(1), \blocal\b(1)]"
;

const char sh_optunalias[] =
Expand Down