Ejemplo n.º 1
0
  def Run(self, cmd_val):
    # type: (cmd_value__Assign) -> int
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    attrs = flag_spec.Parse('readonly', arg_r)
    arg = arg_types.readonly(attrs.attrs)

    if arg.p or len(cmd_val.pairs) == 0:
      return _PrintVariables(self.mem, cmd_val, attrs, True, builtin=_READONLY)

    for pair in cmd_val.pairs:
      if pair.rval is None:
        if arg.a:
          rval = value.MaybeStrArray([])  # type: value_t
        elif arg.A:
          rval = value.AssocArray({})
        else:
          rval = None
      else:
        rval = pair.rval

      rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid)

      # NOTE:
      # - when rval is None, only flags are changed
      # - dynamic scope because flags on locals can be changed, etc.
      self.mem.SetVar(lvalue.Named(pair.var_name), rval, scope_e.Dynamic,
                      flags=state.SetReadOnly)

    return 0
Ejemplo n.º 2
0
  def __call__(self, cmd_val):
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    arg, arg_index = READONLY_SPEC.Parse(arg_r)

    for pair in cmd_val.pairs:
      if pair.rval is None:
        if arg.a:
          rval = value.MaybeStrArray([])
        elif arg.A:
          rval = value.AssocArray({})
        else:
          rval = None
      else:
        rval = pair.rval

      if not _CheckType(rval, arg, self.errfmt, pair.spid):
        return 1

      # NOTE:
      # - when rval is None, only flags are changed
      # - dynamic scope because flags on locals can be changed, etc.
      self.mem.SetVar(pair.lval, rval, (var_flags_e.ReadOnly,), scope_e.Dynamic)

    return 0
Ejemplo n.º 3
0
def SetArrayDynamic(mem, name, a):
  """Set an array by looking up the stack.

  Used for _init_completion.
  """
  assert isinstance(a, list)
  mem.SetVar(sh_lhs_expr.Name(name), value.MaybeStrArray(a), (), scope_e.Dynamic)
Ejemplo n.º 4
0
    def _BindNewArrayWithEntry(self, namespace, lval, val, flags_to_set):
        """Fill 'namespace' with a new indexed array entry."""
        items = [None] * lval.index
        items.append(val.s)
        new_value = value.MaybeStrArray(items)

        # arrays can't be exported; can't have AssocArray flag
        readonly = var_flags_e.ReadOnly in flags_to_set
        namespace[lval.name] = runtime_asdl.cell(new_value, False, readonly)
Ejemplo n.º 5
0
  def __call__(self, cmd_val):
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    arg, arg_index = NEW_VAR_SPEC.Parse(arg_r)

    status = 0

    # NOTE: in bash, -f shows the function body, while -F shows the name.  In
    # osh, they're identical and behave like -F.
    if arg.f or arg.F:  # Lookup and print functions.
      names = [pair.lval.name for pair in cmd_val.pairs]
      if names:
        for name in names:
          if name in self.funcs:
            print(name)
            # TODO: Could print LST, or render LST.  Bash does this.  'trap' too.
            #print(funcs[name])
          else:
            status = 1
      elif arg.F:
        for func_name in sorted(self.funcs):
          print('declare -f %s' % (func_name))
      else:
        raise args.UsageError('declare/typeset -f without args')
      return status

    if arg.p:  # Lookup and print variables.
      names = [pair.lval.name for pair in cmd_val.pairs]
      if names:
        for name in names:
          val = self.mem.GetVar(name)
          if val.tag != value_e.Undef:
            # TODO: Print flags.

            print(name)
          else:
            status = 1
      else:
        raise args.UsageError('declare/typeset -p without args')
      return status

    #
    # Set variables
    #

    #raise args.UsageError("doesn't understand %s" % cmd_val.argv[1:])
    if cmd_val.builtin_id == builtin_e.LOCAL:
      lookup_mode = scope_e.LocalOnly
    else:  # declare/typeset
      if arg.g:  
        lookup_mode = scope_e.GlobalOnly
      else:
        lookup_mode = scope_e.LocalOnly

    flags_to_set = []
    if arg.x == '-': 
      flags_to_set.append(var_flags_e.Exported)
    if arg.r == '-':
      flags_to_set.append(var_flags_e.ReadOnly)

    flags_to_clear = []
    if arg.x == '+': 
      flags_to_clear.append(var_flags_e.Exported)
    if arg.r == '+':
      flags_to_clear.append(var_flags_e.ReadOnly)

    for pair in cmd_val.pairs:
      if pair.rval is None:
        if arg.a:
          rval = value.MaybeStrArray([])
        elif arg.A:
          rval = value.AssocArray({})
        else:
          rval = None
      else:
        rval = pair.rval

      if not _CheckType(rval, arg, self.errfmt, pair.spid):
        return 1
      self.mem.SetVar(pair.lval, rval, flags_to_set, lookup_mode,
                      flags_to_clear=flags_to_clear)

    return status
Ejemplo n.º 6
0
    def Run(self, cmd_val):
        # type: (cmd_value__Assign) -> int
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()
        attrs = flag_spec.Parse('new_var', arg_r)
        arg = arg_types.new_var(attrs.attrs)

        status = 0

        if arg.f:
            names = arg_r.Rest()
            if len(names):
                # NOTE: in bash, -f shows the function body, while -F shows the name.
                # Right now we just show the name.
                status = self._PrintFuncs(names)
            else:
                e_usage('passed -f without args')
            return status

        if arg.F:
            names = arg_r.Rest()
            if len(names):
                status = self._PrintFuncs(names)
            else:  # weird bash quirk: they're printed in a different format!
                for func_name in sorted(self.funcs):
                    print('declare -f %s' % (func_name))
            return status

        if arg.p:  # Lookup and print variables.
            return _PrintVariables(self.mem, cmd_val, attrs, True)
        elif len(cmd_val.pairs) == 0:
            return _PrintVariables(self.mem, cmd_val, attrs, False)

        #
        # Set variables
        #

        #raise error.Usage("doesn't understand %s" % cmd_val.argv[1:])
        if cmd_val.builtin_id == builtin_i.local:
            lookup_mode = scope_e.LocalOnly
        else:  # declare/typeset
            if arg.g:
                lookup_mode = scope_e.GlobalOnly
            else:
                lookup_mode = scope_e.LocalOnly

        flags = 0
        if arg.x == '-':
            flags |= state.SetExport
        if arg.r == '-':
            flags |= state.SetReadOnly
        if arg.n == '-':
            flags |= state.SetNameref

        flags_to_clear = 0
        if arg.x == '+':
            flags |= state.ClearExport
        if arg.r == '+':
            flags |= state.ClearReadOnly
        if arg.n == '+':
            flags |= state.ClearNameref

        for pair in cmd_val.pairs:
            rval = pair.rval
            # declare -a foo=(a b); declare -a foo;  should not reset to empty array
            if rval is None and (arg.a or arg.A):
                old_val = self.mem.GetVar(pair.var_name)
                if arg.a:
                    if old_val.tag_() != value_e.MaybeStrArray:
                        rval = value.MaybeStrArray([])
                elif arg.A:
                    if old_val.tag_() != value_e.AssocArray:
                        rval = value.AssocArray({})

            rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid)
            self.mem.SetVar(lvalue.Named(pair.var_name),
                            rval,
                            lookup_mode,
                            flags=flags)

        return status
Ejemplo n.º 7
0
def SetGlobalArray(mem, name, a):
    """Helper for completion."""
    assert isinstance(a, list)
    mem.SetVar(lhs_expr.LhsName(name), value.MaybeStrArray(a), (),
               scope_e.GlobalOnly)
Ejemplo n.º 8
0
    def GetVar(self, name, lookup_mode=scope_e.Dynamic):
        assert isinstance(name, str), name

        # TODO: Short-circuit down to _FindCellAndNamespace by doing a single hash
        # lookup:
        # COMPUTED_VARS = {'PIPESTATUS': 1, 'FUNCNAME': 1, ...}
        # if name not in COMPUTED_VARS: ...

        if name == 'ARGV':
            # TODO:
            # - Reuse the MaybeStrArray?
            # - @@ could be an alias for ARGV (in command mode, but not expr mode)
            return value.MaybeStrArray(self.GetArgv())

        if name == 'PIPESTATUS':
            return value.MaybeStrArray([str(i) for i in self.pipe_status[-1]])

        # 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().
            strs = []
            for func_name, source_name, _, _, _ in reversed(self.debug_stack):
                if func_name:
                    strs.append(func_name)
                if source_name:
                    strs.append('source')  # bash doesn't give name
                # Temp stacks are ignored

            if self.has_main:
                strs.append('main')  # bash does this
            return value.MaybeStrArray(strs)  # TODO: Reuse this object too?

        # This isn't the call source, it's the source of the function DEFINITION
        # (or the sourced # file itself).
        if name == 'BASH_SOURCE':
            return value.MaybeStrArray(list(reversed(self.bash_source)))

        # This is how bash source SHOULD be defined, but it's not!
        if name == 'CALL_SOURCE':
            strs = []
            for func_name, source_name, call_spid, _, _ in reversed(
                    self.debug_stack):
                # should only happen for the first entry
                if call_spid == const.NO_INTEGER:
                    continue
                span = self.arena.GetLineSpan(call_spid)
                source_str = self.arena.GetLineSourceString(span.line_id)
                strs.append(source_str)
            if self.has_main:
                strs.append('-')  # Bash does this to line up with main?
            return value.MaybeStrArray(strs)  # TODO: Reuse this object too?

        if name == 'BASH_LINENO':
            strs = []
            for _, _, call_spid, _, _ in reversed(self.debug_stack):
                # should only happen for the first entry
                if call_spid == const.NO_INTEGER:
                    continue
                span = self.arena.GetLineSpan(call_spid)
                line_num = self.arena.GetLineNumber(span.line_id)
                strs.append(str(line_num))
            if self.has_main:
                strs.append('0')  # Bash does this to line up with main?
            return value.MaybeStrArray(strs)  # TODO: Reuse this object too?

        if name == 'LINENO':
            span = self.arena.GetLineSpan(self.current_spid)
            # TODO: maybe use interned GetLineNumStr?
            s = str(self.arena.GetLineNumber(span.line_id))
            self.line_num.s = s
            return self.line_num

        # This is OSH-specific.  Get rid of it in favor of ${BASH_SOURCE[0]} ?
        if name == 'SOURCE_NAME':
            # Update and reuse an object.
            span = self.arena.GetLineSpan(self.current_spid)
            self.source_name.s = self.arena.GetLineSourceString(span.line_id)
            return self.source_name

        cell, _ = self._FindCellAndNamespace(name, lookup_mode)

        if cell:
            return cell.val

        return value.Undef()
Ejemplo n.º 9
0
def OldValue(lval, mem, exec_opts):
  # type: (lvalue_t, Mem, optview.Exec) -> value_t
  """
  Used by s+='x' and (( i += 1 ))

  TODO: We need a stricter and less ambiguous version for Oil.

  Problem:

  - why does lvalue have Indexed and Keyed, while sh_lhs_expr only has
    IndexedName?
    - should I have lvalue.Named and lvalue.Indexed only?
    - and Indexed uses the index_t type?
      - well that might be Str or Int
  """
  assert isinstance(lval, lvalue_t), lval

  # TODO: refactor lvalue_t to make this simpler
  UP_lval = lval
  with tagswitch(lval) as case:
    if case(lvalue_e.Named):  # (( i++ ))
      lval = cast(lvalue__Named, UP_lval)
      var_name = lval.name
    elif case(lvalue_e.Indexed):  # (( a[i]++ ))
      lval = cast(lvalue__Indexed, UP_lval)
      var_name = lval.name
    elif case(lvalue_e.Keyed):  # (( A['K']++ )) ?  I think this works
      lval = cast(lvalue__Keyed, UP_lval)
      var_name = lval.name
    else:
      raise AssertionError()

  val = _LookupVar(var_name, mem, exec_opts)

  UP_val = val
  with tagswitch(lval) as case:
    if case(lvalue_e.Named):
      return val

    elif case(lvalue_e.Indexed):
      lval = cast(lvalue__Indexed, UP_lval)

      array_val = None  # type: value__MaybeStrArray
      with tagswitch(val) as case2:
        if case2(value_e.Undef):
          array_val = value.MaybeStrArray([])
        elif case2(value_e.MaybeStrArray):
          tmp = cast(value__MaybeStrArray, UP_val)
          # mycpp rewrite: add tmp.  cast() creates a new var in inner scope
          array_val = tmp
        else:
          e_die("Can't use [] on value of type %s", ui.ValType(val))

      s = word_eval.GetArrayItem(array_val.strs, lval.index)

      if s is None:
        val = value.Str('')  # NOTE: Other logic is value.Undef()?  0?
      else:
        assert isinstance(s, str), s
        val = value.Str(s)

    elif case(lvalue_e.Keyed):
      lval = cast(lvalue__Keyed, UP_lval)

      assoc_val = None  # type: value__AssocArray
      with tagswitch(val) as case2:
        if case2(value_e.Undef):
          # This never happens, because undef[x]+= is assumed to
          raise AssertionError()
        elif case2(value_e.AssocArray):
          tmp2 = cast(value__AssocArray, UP_val)
          # mycpp rewrite: add tmp.  cast() creates a new var in inner scope
          assoc_val = tmp2
        else:
          e_die("Can't use [] on value of type %s", ui.ValType(val))

      s = assoc_val.d.get(lval.key)
      if s is None:
        val = value.Str('')
      else:
        val = value.Str(s)

    else:
      raise AssertionError()

  return val
Ejemplo n.º 10
0
    def testSetVarClearFlag(self):
        mem = _InitMem()
        print(mem)

        mem.PushCall('my-func', 0, ['ONE'])
        self.assertEqual(2, len(mem.var_stack))  # internal details

        # local x=y
        mem.SetVar(lvalue.Named('x'), value.Str('y'), (), scope_e.LocalOnly)
        self.assertEqual('y', mem.var_stack[-1]['x'].val.s)

        # New frame
        mem.PushCall('my-func', 0, ['TWO'])
        self.assertEqual(3, len(mem.var_stack))  # internal details

        # x=y -- test out dynamic scope
        mem.SetVar(lvalue.Named('x'), value.Str('YYY'), (), scope_e.Dynamic)
        self.assertEqual('YYY', mem.var_stack[-2]['x'].val.s)
        self.assertEqual(None, mem.var_stack[-1].get('x'))

        # myglobal=g
        mem.SetVar(lvalue.Named('myglobal'), value.Str('g'), (),
                   scope_e.Dynamic)
        self.assertEqual('g', mem.var_stack[0]['myglobal'].val.s)
        self.assertEqual(False, mem.var_stack[0]['myglobal'].exported)

        # 'export PYTHONPATH=/'
        mem.SetVar(lvalue.Named('PYTHONPATH'), value.Str('/'),
                   (var_flags_e.Exported, ), scope_e.Dynamic)
        self.assertEqual('/', mem.var_stack[0]['PYTHONPATH'].val.s)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        ex = mem.GetExported()
        self.assertEqual('/', ex['PYTHONPATH'])

        mem.SetVar(lvalue.Named('PYTHONPATH'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        # 'export myglobal'.  None means don't touch it.  Undef would be confusing
        # because it might mean "unset", but we have a separated API for that.
        mem.SetVar(lvalue.Named('myglobal'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['myglobal'].exported)

        # export g2  -- define and export empty
        mem.SetVar(lvalue.Named('g2'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(value_e.Undef, mem.var_stack[0]['g2'].val.tag)
        self.assertEqual(True, mem.var_stack[0]['g2'].exported)

        # readonly myglobal
        self.assertEqual(False, mem.var_stack[0]['myglobal'].readonly)
        mem.SetVar(lvalue.Named('myglobal'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['myglobal'].readonly)

        mem.SetVar(lvalue.Named('PYTHONPATH'), value.Str('/lib'), (),
                   scope_e.Dynamic)
        self.assertEqual('/lib', mem.var_stack[0]['PYTHONPATH'].val.s)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        # COMPREPLY=(1 2 3)
        # invariant to enforce: arrays can't be exported
        mem.SetVar(lvalue.Named('COMPREPLY'),
                   value.MaybeStrArray(['1', '2', '3']), (),
                   scope_e.GlobalOnly)
        self.assertEqual(['1', '2', '3'],
                         mem.var_stack[0]['COMPREPLY'].val.strs)

        # export COMPREPLY
        try:
            mem.SetVar(lvalue.Named('COMPREPLY'), None,
                       (var_flags_e.Exported, ), scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")

        # readonly r=1
        mem.SetVar(lvalue.Named('r'), value.Str('1'), (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual('1', mem.var_stack[0]['r'].val.s)
        self.assertEqual(False, mem.var_stack[0]['r'].exported)
        self.assertEqual(True, mem.var_stack[0]['r'].readonly)
        print(mem)

        # r=newvalue
        try:
            mem.SetVar(lvalue.Named('r'), value.Str('newvalue'), (),
                       scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")

        # readonly r2  -- define empty readonly
        mem.SetVar(lvalue.Named('r2'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(value_e.Undef, mem.var_stack[0]['r2'].val.tag)
        self.assertEqual(True, mem.var_stack[0]['r2'].readonly)

        # export -n PYTHONPATH
        # Remove the exported property.  NOTE: scope is LocalOnly for Oil?
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)
        mem.ClearFlag('PYTHONPATH', var_flags_e.Exported, scope_e.Dynamic)
        self.assertEqual(False, mem.var_stack[0]['PYTHONPATH'].exported)

        lhs = lvalue.Indexed('a', 1)
        lhs.spids.append(0)
        # a[1]=2
        mem.SetVar(lhs, value.Str('2'), (), scope_e.Dynamic)
        self.assertEqual([None, '2'], mem.var_stack[0]['a'].val.strs)

        # a[1]=3
        mem.SetVar(lhs, value.Str('3'), (), scope_e.Dynamic)
        self.assertEqual([None, '3'], mem.var_stack[0]['a'].val.strs)

        # a[1]=(x y z)  # illegal but doesn't parse anyway
        if 0:
            try:
                mem.SetVar(lhs, value.MaybeStrArray(['x', 'y', 'z']), (),
                           scope_e.Dynamic)
            except error.FatalRuntime as e:
                pass
            else:
                self.fail("Expected failure")

        # readonly a
        mem.SetVar(lvalue.Named('a'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['a'].readonly)

        try:
            # a[1]=3
            mem.SetVar(lhs, value.Str('3'), (), scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")
Ejemplo n.º 11
0
  def Run(self, cmd_val):
    # type: (cmd_value__Assign) -> int
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    attrs = flag_spec.Parse('new_var', arg_r)
    arg = arg_types.new_var(attrs.attrs)

    status = 0

    if arg.f:
      names = arg_r.Rest()
      if len(names):
        # This is only used for a STATUS QUERY now.  We only show the name,
        # not the body.
        status = self._PrintFuncs(names)
      else:
        # Disallow this since it would be incompatible.
        e_usage('with -f expects function names')
      return status

    if arg.F:
      names = arg_r.Rest()
      if len(names):
        status = self._PrintFuncs(names)
      else:
        # bash quirk: with no names, they're printed in a different format!
        for func_name in sorted(self.funcs):
          print('declare -f %s' % (func_name))
      return status

    if arg.p:  # Lookup and print variables.
      return _PrintVariables(self.mem, cmd_val, attrs, True)
    elif len(cmd_val.pairs) == 0:
      return _PrintVariables(self.mem, cmd_val, attrs, False)

    #
    # Set variables
    #

    #raise error.Usage("doesn't understand %s" % cmd_val.argv[1:])
    if cmd_val.builtin_id == builtin_i.local:
      which_scopes = scope_e.LocalOnly
    else:  # declare/typeset
      if arg.g:  
        which_scopes = scope_e.GlobalOnly
      else:
        which_scopes = scope_e.LocalOnly

    flags = 0
    if arg.x == '-': 
      flags |= state.SetExport
    if arg.r == '-':
      flags |= state.SetReadOnly
    if arg.n == '-':
      flags |= state.SetNameref

    flags_to_clear = 0
    if arg.x == '+': 
      flags |= state.ClearExport
    if arg.r == '+':
      flags |= state.ClearReadOnly
    if arg.n == '+':
      flags |= state.ClearNameref

    for pair in cmd_val.pairs:
      rval = pair.rval
      # declare -a foo=(a b); declare -a foo;  should not reset to empty array
      if rval is None and (arg.a or arg.A):
        old_val = self.mem.GetValue(pair.var_name)
        if arg.a:
          if old_val.tag_() != value_e.MaybeStrArray:
            rval = value.MaybeStrArray([])
        elif arg.A:
          if old_val.tag_() != value_e.AssocArray:
            rval = value.AssocArray({})

      rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid)
      self.mem.SetValue(lvalue.Named(pair.var_name), rval, which_scopes,
                        flags=flags)

    return status