Permalink
Browse files

Fix assertion crash in ${#array[@]} (when it's unquoted).

Fixes issue #114.
  • Loading branch information...
Andy Chu
Andy Chu committed May 25, 2018
1 parent 7ca83b0 commit e5d373a165c3ce661f9c921a8648a3e2016e9713
Showing with 24 additions and 19 deletions.
  1. +21 −16 core/word_eval.py
  2. +2 −2 spec/array.test.sh
  3. +1 −1 test/spec.sh
View
@@ -189,6 +189,10 @@ def _EvalVarNum(self, var_num):
return self.mem.GetArgNum(var_num)
def _EvalSpecialVar(self, op_id, quoted):
"""Returns (val, bool maybe_decay_array).
TODO: Should that boolean be part of the value?
"""
# $@ is special -- it need to know whether it is in a double quoted
# context.
#
@@ -210,7 +214,7 @@ def _EvalSpecialVar(self, op_id, quoted):
return runtime.Str(s), False
else:
val = self.mem.GetSpecialVar(op_id)
return val, False # dont' decay
return val, False # don't decay
def _ApplyTestOp(self, val, op, quoted, part_vals):
"""
@@ -402,7 +406,7 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
"""
# We have four types of operator that interact.
#
# 1. Bracket: value -> (value, bool decay_array)
# 1. Bracket: value -> (value, bool maybe_decay_array)
#
# 2. Then these four cases are mutually exclusive:
#
@@ -413,9 +417,9 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
#
# That is, we don't have both prefix and suffix operators.
#
# 3. Process decay_array here before returning.
# 3. Process maybe_decay_array here before returning.
decay_array = False # for $*, ${a[*]}, etc.
maybe_decay_array = False # for $*, ${a[*]}, etc.
var_name = None # For ${foo=default}
@@ -430,10 +434,10 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
val = self._EvalVarNum(var_num)
else:
# $* decays
val, decay_array = self._EvalSpecialVar(part.token.id, quoted)
val, maybe_decay_array = self._EvalSpecialVar(part.token.id, quoted)
# 2. Bracket: value -> (value v, bool decay_array)
# decay_array is for joining ${a[*]} and unquoted ${a[@]} AFTER suffix ops
# 2. Bracket: value -> (value v, bool maybe_decay_array)
# maybe_decay_array is for joining ${a[*]} and unquoted ${a[@]} AFTER suffix ops
# are applied. If we take the length with a prefix op, the distinction is
# ignored.
if part.bracket_op:
@@ -442,7 +446,7 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
if op_id == Id.Lit_At:
if not quoted:
decay_array = True # ${a[@]} decays but "${a[@]}" doesn't
maybe_decay_array = True # ${a[@]} decays but "${a[@]}" doesn't
if val.tag == value_e.Undef:
val = self._EmptyStrArrayOrError(part.token)
elif val.tag == value_e.Str:
@@ -451,13 +455,13 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
val = runtime.StrArray(val.strs)
elif op_id == Id.Arith_Star:
decay_array = True # both ${a[*]} and "${a[*]}" decay
maybe_decay_array = True # both ${a[*]} and "${a[*]}" decay
if val.tag == value_e.Undef:
val = self._EmptyStrArrayOrError(part.token)
elif val.tag == value_e.Str:
e_die("Can't index string with *: %r", val, part=part)
elif val.tag == value_e.StrArray:
# Always decay_array with ${a[*]} or "${a[*]}"
# Always maybe_decay_array with ${a[*]} or "${a[*]}"
val = runtime.StrArray(val.strs)
else:
@@ -487,7 +491,8 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
if part.prefix_op:
val = self._EmptyStrOrError(val) # maybe error
val = self._ApplyPrefixOp(val, part.prefix_op)
# At least for length, we can't have a test or suffix afterward.
# NOTE: When applying the length operator, we can't have a test or
# suffix afterward. And we don't want to decay the array
elif part.suffix_op:
op = part.suffix_op
@@ -585,8 +590,8 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
else:
raise AssertionError(val.__class__.__name__)
# After applying suffixes, process decay_array here.
if decay_array:
# After applying suffixes, process maybe_decay_array here.
if maybe_decay_array and val.tag == value_e.StrArray:
val = self._DecayArray(val)
# No prefix or suffix ops
@@ -655,7 +660,7 @@ def _EvalWordPart(self, part, part_vals, quoted=False):
part_vals.append(v)
elif part.tag == word_part_e.SimpleVarSub:
decay_array = False
maybe_decay_array = False
# 1. Evaluate from (var_name, var_num, token) -> defined, value
if part.token.id == Id.VSub_Name:
var_name = part.token.val[1:]
@@ -664,11 +669,11 @@ def _EvalWordPart(self, part, part_vals, quoted=False):
var_num = int(part.token.val[1:])
val = self._EvalVarNum(var_num)
else:
val, decay_array = self._EvalSpecialVar(part.token.id, quoted)
val, maybe_decay_array = self._EvalSpecialVar(part.token.id, quoted)
#log('SIMPLE %s', part)
val = self._EmptyStrOrError(val, token=part.token)
if decay_array:
if maybe_decay_array and val.tag == value_e.StrArray:
val = self._DecayArray(val)
v = _ValueToPartValue(val, quoted)
part_vals.append(v)
View
@@ -249,8 +249,8 @@ argv.py "${a[@]:i-4:2}"
### Number of elements
a=(1 '2 3')
echo "${#a[@]}"
# stdout: 2
echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
# stdout: 2 2
### Length of an element
a=(1 '2 3')
View
@@ -476,7 +476,7 @@ array() {
}
array-compat() {
sh-spec spec/array-compat.test.sh --osh-failures-allowed 7 \
sh-spec spec/array-compat.test.sh --osh-failures-allowed 6 \
$BASH $MKSH $OSH "$@"
}

0 comments on commit e5d373a

Please sign in to comment.