Skip to content

Commit

Permalink
[spec/introspect] Add test
Browse files Browse the repository at this point in the history
And rename code to distinguish call locations vs. definition locations.
  • Loading branch information
Andy C committed Sep 6, 2023
1 parent 325bb70 commit bbebf3b
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 14 deletions.
31 changes: 18 additions & 13 deletions core/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,12 +1000,12 @@ def _GetWorkingDir():


class DebugFrame(object):
def __init__(self, bash_source, func_name, source_name, def_loc, argv_i,
def __init__(self, bash_source, func_name, source_name, call_loc, argv_i,
var_i):
# type: (Optional[str], Optional[str], Optional[str], debug_frame_loc_t, int, int) -> None
"""
Args:
def_loc: location of the function definition
call_loc: location the function was called from
"""
self.bash_source = bash_source

Expand All @@ -1014,7 +1014,7 @@ def __init__(self, bash_source, func_name, source_name, def_loc, argv_i,
self.func_name = func_name
self.source_name = source_name

self.def_loc = def_loc
self.call_loc = call_loc
self.argv_i = argv_i
self.var_i = var_i

Expand Down Expand Up @@ -1389,9 +1389,9 @@ def Dump(self):
else:
pass # It's a frame for FOO=bar? Or the top one?

with tagswitch(frame.def_loc) as case:
with tagswitch(frame.call_loc) as case:
if case(debug_frame_loc_e.Token):
token = cast(Token, frame.def_loc)
token = cast(Token, frame.call_loc)
assert token.line is not None
d['call_source'] = ui.GetLineSourceString(token.line)
d['call_line_num'] = token.line.line_num
Expand Down Expand Up @@ -1565,6 +1565,8 @@ def PushTemp(self):
# We don't want the 'read' builtin to write to this frame!
frame = NewDict() # type: Dict[str, Cell]
self.var_stack.append(frame)

# TODO: Is this necessary?
self._PushDebugStack(None, None, None)

def PopTemp(self):
Expand All @@ -1579,7 +1581,9 @@ def TopNamespace(self):

def _PushDebugStack(self, bash_source, func_name, source_name):
# type: (Optional[str], Optional[str], Optional[str]) -> None

"""
All 3 values are optional
"""
# These integers are handles/pointers, for use in CrashDumper.
argv_i = len(self.argv_stack) - 1
var_i = len(self.var_stack) - 1
Expand All @@ -1595,12 +1599,12 @@ def _PushDebugStack(self, bash_source, func_name, source_name):
# be called before any SetTokenForLine(). TODO: clean this up.

if self.token_for_line is None:
def_loc = debug_frame_loc.Unknown # type: debug_frame_loc_t
call_loc = debug_frame_loc.Unknown # type: debug_frame_loc_t
else:
def_loc = self.token_for_line
call_loc = self.token_for_line

self.debug_stack.append(
DebugFrame(bash_source, func_name, source_name, def_loc, argv_i,
DebugFrame(bash_source, func_name, source_name, call_loc, argv_i,
var_i))

def _PopDebugStack(self):
Expand Down Expand Up @@ -2082,6 +2086,7 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
# Do lookup of system globals before looking at user variables. Note: we
# could optimize this at compile-time like $?. That would break
# ${!varref}, but it's already broken for $?.

if name == 'FUNCNAME':
# bash wants it in reverse order. This is a little inefficient but we're
# not depending on deque().
Expand All @@ -2094,8 +2099,8 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
# Temp stacks are ignored
return value.BashArray(strs2) # TODO: Reuse this object too?

# This isn't the call source, it's the source of the function DEFINITION
# (or the sourced # file itself).
# $BASH_SOURCE and $BASH_LINENO come from the location of the call, not
# of the definition.
if name == 'BASH_SOURCE':
strs = []
for frame in reversed(self.debug_stack):
Expand All @@ -2106,9 +2111,9 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
if name == 'BASH_LINENO':
strs = []
for frame in reversed(self.debug_stack):
with tagswitch(frame.def_loc) as case:
with tagswitch(frame.call_loc) as case:
if case(debug_frame_loc_e.Token):
tok = cast(Token, frame.def_loc)
tok = cast(Token, frame.call_loc)
line_num = tok.line.line_num
strs.append(str(line_num))

Expand Down
2 changes: 1 addition & 1 deletion frontend/syntax.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ module syntax
| Command(command c)

debug_frame_loc =
Unknown # possible in cmopletion
Unknown # possible for proc run by completion
| Main # sentinel to match bash behavior
| Token %Token

Expand Down
12 changes: 12 additions & 0 deletions spec/introspect.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,15 @@ f # line 9
['G', '6', '10']
['end F', '10']
## END

#### Locations with temp frame

$SH $REPO_ROOT/spec/testdata/bash-source-pushtemp.sh

## STDOUT:
F
G
STACK:testdata/bash-source-pushtemp.sh:g:4
STACK:testdata/bash-source-pushtemp.sh:f:25
STACK:testdata/bash-source-pushtemp.sh:main:0
## END
31 changes: 31 additions & 0 deletions spec/testdata/bash-source-pushtemp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
f() {
echo 'F'
#VAR=val g
g
}

g() {
echo 'G'

local n=${#BASH_SOURCE[@]}
for (( i = 0; i < n; ++i)); do
#echo "STACK:${BASH_SOURCE[i]}:${FUNCNAME[i]}:${BASH_LINENO[i]}"

local src=${BASH_SOURCE[i]}

# Paper over difference between bash and OSH
src="${src//*'spec/'/}"

echo "STACK:$src:${FUNCNAME[i]}:${BASH_LINENO[i]}"
done
}

# TODO: enable these frames
#VAR2=val f
f

# These are wrong
#set -x
#PS4='${BASH_SOURCE[0]}:${BASH_LINENO[0]}:'
#PS4='${BASH_SOURCE[-1]}:${BASH_LINENO[-1]}:'

0 comments on commit bbebf3b

Please sign in to comment.