set -e __ESC="$(printf '\033.')"; __ESC="${__ESC%.}" __RS="$(printf '\036.')"; __RS="${__RS%.}" __US="$(printf '\037.')"; __US="${__US%.}" __sp=0 __fn_name= __fn_tu= __fn_vars= __fn_var_vals= __fn= __prev_tu= __prev_vars= __var= __val= __fn_frame= __sp_inc() { __sp=$((${__sp} + 1)) } __sp_dec() { __sp=$((${__sp} - 1)) } __frame_set() { eval "__frame_${__sp}_name=\${__fn_name}" eval "__frame_${__sp}_tu=\${__fn_tu}" eval "__frame_${__sp}_vars=\${__fn_vars}" eval "__frame_${__sp}_var_vals=\${__fn_var_vals}" } __frame_get() { eval "__fn_name=\${__frame_${__sp}_name}" eval "__fn_tu=\${__frame_${__sp}_tu}" eval "__fn_vars=\${__frame_${__sp}_vars}" eval "__fn_var_vals=\${__frame_${__sp}_var_vals}" } __fn_ctxsw() { unset IFS case "${__prev_tu}" in ${__fn_tu});; ?*) # Unset static variables and functions from previous TU. eval "unset \${__${__prev_tu}_static_vars}" eval "__val=\${__${__prev_tu}_static_fns}" for __fn in ${__val}; do unset -f "${__fn%:*}" done ;; esac case "${__fn_tu}" in ${__prev_tu});; ?*) # Set static variables and function for the current TU. eval "__val=\${__${__fn_tu}_static_var_vals}" eval "${__val}" eval "__val=\${__${__fn_tu}_static_fns}" for __fn in ${__val}; do eval "${__fn%:*}() { ${__fn#*:}; }" done ;; esac unset ${__prev_vars} eval "${__fn_var_vals}" } __fn_update_vars() { for __var in ${__fn_vars}; do __val="$(eval "printf '%s' \"\${${__var}}\"" | \ sed "s|'|'\\\\''|g;")" __fn_var_vals="${__fn_var_vals} ${__var}='${__val}'" done } __fn_start() { # Old state. __prev_tu="${__fn_tu}" __prev_vars="${__fn_vars}" # Update local vars in stack. __fn_update_vars __frame_set # Set stack pointer. __sp_inc # New state. __fn_name="${2}" __fn_tu="${1}" __fn_vars= __fn_var_vals= __frame_set # Switch. __fn_ctxsw } __fn_end() { # Old state. __prev_tu="${__fn_tu}" __prev_vars="${__fn_vars}" # Set stack pointer. __sp_dec # New state. __frame_get # Switch. __fn_ctxsw } __tu_end() { # Save TU static vars. for __var in $(eval "printf '%s' \"\${__${__tu}_static_vars}\""); do __val="$(eval "printf '%s' \"\${${__var}}\"" | \ sed "s|'|'\\\\''|g")" eval "__${__tu}_static_var_vals=\"\${__${__tu}_static_var_vals}\ ${__var}='\${__val}'\"" unset ${__var} done __tu='' } __stack_trace() { __fn_frame=0 while [ ${__fn_frame} -le ${__sp} ]; do eval "printf '%s\n' \"\${__frame_${__fn_frame}_name}\"" __fn_frame=$((${__fn_frame} + 1)) done } __local() { for __var in "${@}"; do case "${__var}" in *=*) __val="${__var#*=}" __var="${__var%%=*}" ;; *) __val='' ;; esac case "${__var}" in [!A-Za-z_]* | *[!0-9A-Za-z_]*) printf 'Syntax error: %s %s\n' \ 'illegal static variable name:' "${__var}" >&2 exit 1 ;; esac eval "${__var}=\${__val}" __fn_vars="${__fn_vars} ${__var}" done } static() { case "${__tu}" in '') printf 'Error: Cannot declare static variables in functions' >&2 exit 1 ;; esac for __var in "${@}"; do case "${__var}" in *=*) __val="${__var#*=}" __var="${__var%%=*}" ;; *) __val='' ;; esac case "${__var}" in [!A-Za-z_]* | *[!0-9A-Za-z_]*) printf 'Syntax error: %s %s\n' \ 'illegal static variable name:' "${__var}" >&2 exit 1 ;; esac eval "${__var}=\${__val}" eval "__${__tu}_static_vars=\"\${__${__tu}_static_vars} \ ${__var}\"" done } EOF(){ :; } : <<'EOF' ################################################################################ # Stack trace test ################################################################################ __tu=tu0 __tu0_static_vars= __tu0_static_var_vals= __tu0_static_fns= a(){ __fn_start tu0 a; b; __fn_end; } b(){ __fn_start tu0 b; c; __fn_end; } c(){ __fn_start tu0 c; printf 'Stack trace:\n'; printf ' * %s()\n' $(__stack_trace); __fn_end; } a __tu_end EOF #: <<'EOF' ################################################################################ # TU 0 ################################################################################ __tu=tu0 __tu0_static_vars= __tu0_static_var_vals= __tu0_static_fns= static v=1 foo() { __fn_start tu0 foo echo "v (should be 1): $v" __fn_end } __tu_end ################################################################################ # TU 1 ################################################################################ __tu=tu1 __tu1_static_vars= __tu1_static_var_vals= __tu1_static_fns= bar() { __fn_start tu1 bar echo "v (should not be 1): $v" foo echo "v (should not be 1): $v" __fn_end } __tu_end bar EOF : <<'EOF' ################################################################################ # TU 0 ################################################################################ __tu=tu0 __tu0_static_vars= __tu0_static_var_vals= __tu0_static_fns= a() { __fn_start tu0 a __local v=1 echo "v (should be 1): $v" b echo "v (should be 1): $v" __fn_end } b() { __fn_start tu0 b echo "v (should be empty): $v" __fn_end } __tu_end a EOF : <<'EOF' ################################################################################ # TU 0 ################################################################################ __tu=tu0 __tu0_static_vars= __tu0_static_var_vals= __tu0_static_fns= a() { __fn_start tu0 a static v=1 __fn_end } __tu_end a EOF