OILS / core / state.py View on Github | oilshell.org

2798 lines, 1416 significant
1# Copyright 2016 Andy Chu. All rights reserved.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7"""
8state.py - Interpreter state
9"""
10from __future__ import print_function
11import time as time_ # avoid name conflict
12
13from _devbuild.gen.id_kind_asdl import Id
14from _devbuild.gen.option_asdl import option_i
15from _devbuild.gen.runtime_asdl import (scope_e, scope_t, Cell)
16from _devbuild.gen.syntax_asdl import (loc, loc_t, Token, debug_frame,
17 debug_frame_e, debug_frame_t)
18from _devbuild.gen.types_asdl import opt_group_i
19from _devbuild.gen.value_asdl import (value, value_e, value_t, Obj, sh_lvalue,
20 sh_lvalue_e, sh_lvalue_t, LeftName,
21 y_lvalue_e, regex_match, regex_match_e,
22 regex_match_t, RegexMatch)
23from core import error
24from core.error import e_usage, e_die
25from core import num
26from core import optview
27from display import ui
28from core import util
29from frontend import consts
30from frontend import location
31from frontend import match
32from mycpp import mops
33from mycpp import mylib
34from mycpp.mylib import (log, print_stderr, str_switch, tagswitch, iteritems,
35 NewDict)
36from pylib import os_path
37
38import posix_ as posix
39
40from typing import Tuple, List, Dict, Optional, Any, cast, TYPE_CHECKING
41
42if TYPE_CHECKING:
43 from _devbuild.gen.option_asdl import option_t
44 from core import alloc
45 from osh import sh_expr_eval
46
47_ = log
48
49# flags for mem.SetValue()
50SetReadOnly = 1 << 0
51ClearReadOnly = 1 << 1
52SetExport = 1 << 2
53ClearExport = 1 << 3
54SetNameref = 1 << 4
55ClearNameref = 1 << 5
56
57
58class ctx_Source(object):
59 """For source builtin."""
60
61 def __init__(self, mem, source_name, argv):
62 # type: (Mem, str, List[str]) -> None
63 mem.PushSource(source_name, argv)
64 self.mem = mem
65 self.argv = argv
66
67 # Whenever we're sourcing, the 'is-main' builtin will return 1 (false)
68 self.to_restore = self.mem.is_main
69 self.mem.is_main = False
70
71 def __enter__(self):
72 # type: () -> None
73 pass
74
75 def __exit__(self, type, value, traceback):
76 # type: (Any, Any, Any) -> None
77 self.mem.PopSource(self.argv)
78
79 self.mem.is_main = self.to_restore
80
81
82class ctx_DebugTrap(object):
83 """For trap DEBUG."""
84
85 def __init__(self, mem):
86 # type: (Mem) -> None
87 mem.running_debug_trap = True
88 self.mem = mem
89
90 def __enter__(self):
91 # type: () -> None
92 pass
93
94 def __exit__(self, type, value, traceback):
95 # type: (Any, Any, Any) -> None
96 self.mem.running_debug_trap = False
97
98
99class ctx_ErrTrap(object):
100 """For trap ERR."""
101
102 def __init__(self, mem):
103 # type: (Mem) -> None
104 mem.running_err_trap = True
105 self.mem = mem
106
107 def __enter__(self):
108 # type: () -> None
109 pass
110
111 def __exit__(self, type, value, traceback):
112 # type: (Any, Any, Any) -> None
113 self.mem.running_err_trap = False
114
115
116class ctx_Option(object):
117 """Shopt --unset errexit { false }"""
118
119 def __init__(self, mutable_opts, opt_nums, b):
120 # type: (MutableOpts, List[int], bool) -> None
121 for opt_num in opt_nums:
122 mutable_opts.Push(opt_num, b)
123 if opt_num == option_i.errexit:
124 # it wasn't disabled
125 mutable_opts.errexit_disabled_tok.append(None)
126
127 self.mutable_opts = mutable_opts
128 self.opt_nums = opt_nums
129
130 def __enter__(self):
131 # type: () -> None
132 pass
133
134 def __exit__(self, type, value, traceback):
135 # type: (Any, Any, Any) -> None
136 for opt_num in self.opt_nums: # don't bother to do it in reverse order
137 if opt_num == option_i.errexit:
138 self.mutable_opts.errexit_disabled_tok.pop()
139 self.mutable_opts.Pop(opt_num)
140
141
142class ctx_AssignBuiltin(object):
143 """Local x=$(false) is disallowed."""
144
145 def __init__(self, mutable_opts):
146 # type: (MutableOpts) -> None
147 self.strict = False
148 if mutable_opts.Get(option_i.strict_errexit):
149 mutable_opts.Push(option_i._allow_command_sub, False)
150 mutable_opts.Push(option_i._allow_process_sub, False)
151 self.strict = True
152
153 self.mutable_opts = mutable_opts
154
155 def __enter__(self):
156 # type: () -> None
157 pass
158
159 def __exit__(self, type, value, traceback):
160 # type: (Any, Any, Any) -> None
161 if self.strict:
162 self.mutable_opts.Pop(option_i._allow_command_sub)
163 self.mutable_opts.Pop(option_i._allow_process_sub)
164
165
166class ctx_YshExpr(object):
167 """Command sub must fail in 'mystring' ++ $(false)"""
168
169 def __init__(self, mutable_opts):
170 # type: (MutableOpts) -> None
171
172 # Similar to $LIB_OSH/bash-strict.sh
173
174 # TODO: consider errexit:all group, or even ysh:all
175 # It would be nice if this were more efficient
176 mutable_opts.Push(option_i.command_sub_errexit, True)
177 mutable_opts.Push(option_i.errexit, True)
178 mutable_opts.Push(option_i.pipefail, True)
179 mutable_opts.Push(option_i.inherit_errexit, True)
180 mutable_opts.Push(option_i.strict_errexit, True)
181
182 # What about nounset? This has a similar pitfall -- it's not running
183 # like YSH.
184 # e.g. var x = $(echo $zz)
185
186 self.mutable_opts = mutable_opts
187
188 def __enter__(self):
189 # type: () -> None
190 pass
191
192 def __exit__(self, type, value, traceback):
193 # type: (Any, Any, Any) -> None
194 self.mutable_opts.Pop(option_i.command_sub_errexit)
195 self.mutable_opts.Pop(option_i.errexit)
196 self.mutable_opts.Pop(option_i.pipefail)
197 self.mutable_opts.Pop(option_i.inherit_errexit)
198 self.mutable_opts.Pop(option_i.strict_errexit)
199
200
201class ctx_ErrExit(object):
202 """Manages the errexit setting.
203
204 - The user can change it with builtin 'set' at any point in the code.
205 - These constructs implicitly disable 'errexit':
206 - if / while / until conditions
207 - ! (part of pipeline)
208 - && ||
209 """
210
211 def __init__(self, mutable_opts, b, disabled_tok):
212 # type: (MutableOpts, bool, Optional[Token]) -> None
213
214 # If we're disabling it, we need a span ID. If not, then we should NOT
215 # have one.
216 assert b == (disabled_tok is None)
217
218 mutable_opts.Push(option_i.errexit, b)
219 mutable_opts.errexit_disabled_tok.append(disabled_tok)
220
221 self.strict = False
222 if mutable_opts.Get(option_i.strict_errexit):
223 mutable_opts.Push(option_i._allow_command_sub, False)
224 mutable_opts.Push(option_i._allow_process_sub, False)
225 self.strict = True
226
227 self.mutable_opts = mutable_opts
228
229 def __enter__(self):
230 # type: () -> None
231 pass
232
233 def __exit__(self, type, value, traceback):
234 # type: (Any, Any, Any) -> None
235 self.mutable_opts.errexit_disabled_tok.pop()
236 self.mutable_opts.Pop(option_i.errexit)
237
238 if self.strict:
239 self.mutable_opts.Pop(option_i._allow_command_sub)
240 self.mutable_opts.Pop(option_i._allow_process_sub)
241
242
243class OptHook(object):
244 """Interface for option hooks."""
245
246 def __init__(self):
247 # type: () -> None
248 """Empty constructor for mycpp."""
249 pass
250
251 def OnChange(self, opt0_array, opt_name, b):
252 # type: (List[bool], str, bool) -> bool
253 """This method is called whenever an option is changed.
254
255 Returns success or failure.
256 """
257 return True
258
259
260def InitOpts():
261 # type: () -> List[bool]
262
263 opt0_array = [False] * option_i.ARRAY_SIZE
264 for opt_num in consts.DEFAULT_TRUE:
265 opt0_array[opt_num] = True
266 return opt0_array
267
268
269def MakeOpts(mem, environ, opt_hook):
270 # type: (Mem, Dict[str, str], OptHook) -> Tuple[optview.Parse, optview.Exec, MutableOpts]
271
272 # Unusual representation: opt0_array + opt_stacks. For two features:
273 #
274 # - POSIX errexit disable semantics
275 # - YSH shopt --set nullglob { ... }
276 #
277 # We could do it with a single List of stacks. But because shopt --set
278 # random_option { ... } is very uncommon, we optimize and store the ZERO
279 # element of the stack in a flat array opt0_array (default False), and then
280 # the rest in opt_stacks, where the value could be None. By allowing the
281 # None value, we save ~50 or so list objects in the common case.
282
283 opt0_array = InitOpts()
284 # Overrides, including errexit
285 no_stack = None # type: List[bool] # for mycpp
286 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
287
288 parse_opts = optview.Parse(opt0_array, opt_stacks)
289 exec_opts = optview.Exec(opt0_array, opt_stacks)
290 mutable_opts = MutableOpts(mem, environ, opt0_array, opt_stacks, opt_hook)
291
292 return parse_opts, exec_opts, mutable_opts
293
294
295def _SetGroup(opt0_array, opt_nums, b):
296 # type: (List[bool], List[int], bool) -> None
297 for opt_num in opt_nums:
298 b2 = not b if opt_num in consts.DEFAULT_TRUE else b
299 opt0_array[opt_num] = b2
300
301
302def MakeYshParseOpts():
303 # type: () -> optview.Parse
304 opt0_array = InitOpts()
305 _SetGroup(opt0_array, consts.YSH_ALL, True)
306
307 no_stack = None # type: List[bool]
308 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
309
310 parse_opts = optview.Parse(opt0_array, opt_stacks)
311 return parse_opts
312
313
314def _AnyOptionNum(opt_name, ignore_shopt_not_impl):
315 # type: (str, bool) -> option_t
316 opt_num = consts.OptionNum(opt_name)
317 if opt_num == 0:
318 if ignore_shopt_not_impl:
319 opt_num = consts.UnimplOptionNum(opt_name)
320 if opt_num == 0:
321 e_usage('got invalid option %r' % opt_name, loc.Missing)
322
323 # Note: we relaxed this for YSH so we can do 'shopt --unset errexit' consistently
324 #if opt_num not in consts.SHOPT_OPTION_NUMS:
325 # e_usage("doesn't own option %r (try 'set')" % opt_name)
326
327 return opt_num
328
329
330def _SetOptionNum(opt_name):
331 # type: (str) -> option_t
332 opt_num = consts.OptionNum(opt_name)
333 if opt_num == 0:
334 e_usage('got invalid option %r' % opt_name, loc.Missing)
335
336 if opt_num not in consts.SET_OPTION_NUMS:
337 e_usage("invalid option %r (try shopt)" % opt_name, loc.Missing)
338
339 return opt_num
340
341
342class MutableOpts(object):
343
344 def __init__(self, mem, environ, opt0_array, opt_stacks, opt_hook):
345 # type: (Mem, Dict[str, str], List[bool], List[List[bool]], OptHook) -> None
346 self.mem = mem
347 self.environ = environ
348 self.opt0_array = opt0_array
349 self.opt_stacks = opt_stacks
350 self.errexit_disabled_tok = [] # type: List[Token]
351
352 # Used for 'set -o vi/emacs'
353 self.opt_hook = opt_hook
354
355 def Init(self):
356 # type: () -> None
357
358 # This comes after all the 'set' options.
359 shellopts = self.mem.GetValue('SHELLOPTS')
360
361 # True in OSH, but not in YSH (no_init_globals)
362 if shellopts.tag() == value_e.Str:
363 s = cast(value.Str, shellopts).s
364 self._InitOptionsFromEnv(s)
365
366 def _InitOptionsFromEnv(self, shellopts):
367 # type: (str) -> None
368 # e.g. errexit:nounset:pipefail
369 lookup = shellopts.split(':')
370 for opt_num in consts.SET_OPTION_NUMS:
371 name = consts.OptionName(opt_num)
372 if name in lookup:
373 self._SetOldOption(name, True)
374
375 def Push(self, opt_num, b):
376 # type: (int, bool) -> None
377 overlay = self.opt_stacks[opt_num]
378 if overlay is None or len(overlay) == 0:
379 self.opt_stacks[opt_num] = [b] # Allocate a new list
380 else:
381 overlay.append(b)
382
383 def Pop(self, opt_num):
384 # type: (int) -> bool
385 overlay = self.opt_stacks[opt_num]
386 assert overlay is not None
387 return overlay.pop()
388
389 def PushDynamicScope(self, b):
390 # type: (bool) -> None
391 """B: False if it's a proc, and True if it's a shell function."""
392 # If it's already disabled, keep it disabled
393 if not self.Get(option_i.dynamic_scope):
394 b = False
395 self.Push(option_i.dynamic_scope, b)
396
397 def PopDynamicScope(self):
398 # type: () -> None
399 self.Pop(option_i.dynamic_scope)
400
401 def Get(self, opt_num):
402 # type: (int) -> bool
403 # Like _Getter in core/optview.py
404 overlay = self.opt_stacks[opt_num]
405 if overlay is None or len(overlay) == 0:
406 return self.opt0_array[opt_num]
407 else:
408 return overlay[-1] # the top value
409
410 def _Set(self, opt_num, b):
411 # type: (int, bool) -> None
412 """Used to disable errexit.
413
414 For bash compatibility in command sub.
415 """
416
417 # Like _Getter in core/optview.py
418 overlay = self.opt_stacks[opt_num]
419 if overlay is None or len(overlay) == 0:
420 self.opt0_array[opt_num] = b
421 else:
422 overlay[-1] = b # The top value
423
424 def set_interactive(self):
425 # type: () -> None
426 self._Set(option_i.interactive, True)
427
428 def set_redefine_const(self):
429 # type: () -> None
430 """For interactive shells."""
431 self._Set(option_i.redefine_const, True)
432
433 def set_redefine_source(self):
434 # type: () -> None
435 """For interactive shells. For source-guard"""
436 self._Set(option_i.redefine_source, True)
437
438 def set_emacs(self):
439 # type: () -> None
440 self._Set(option_i.emacs, True)
441
442 def _SetArrayByNum(self, opt_num, b):
443 # type: (int, bool) -> None
444 if (opt_num in consts.PARSE_OPTION_NUMS and
445 not self.mem.ParsingChangesAllowed()):
446 e_die('Syntax options must be set at the top level '
447 '(outside any function)')
448
449 self._Set(opt_num, b)
450
451 def SetDeferredErrExit(self, b):
452 # type: (bool) -> None
453 """Set the errexit flag, possibly deferring it.
454
455 Implements the unusual POSIX "defer" behavior. Callers: set -o
456 errexit, shopt -s ysh:all, ysh:upgrade
457 """
458 #log('Set %s', b)
459
460 # Defer it until we pop by setting the BOTTOM OF THE STACK.
461 self.opt0_array[option_i.errexit] = b
462
463 def DisableErrExit(self):
464 # type: () -> None
465 """Called by core/process.py to implement bash quirks."""
466 self._Set(option_i.errexit, False)
467
468 def ErrExitDisabledToken(self):
469 # type: () -> Optional[Token]
470 """If errexit is disabled by POSIX rules, return Token for construct.
471
472 e.g. the Token for 'if' or '&&' etc.
473 """
474 # Bug fix: The errexit disabling inherently follows a STACK DISCIPLINE.
475 # But we run trap handlers in the MAIN LOOP, which break this. So just
476 # declare that it's never disabled in a trap.
477 if self.Get(option_i._running_trap):
478 return None
479
480 if len(self.errexit_disabled_tok) == 0:
481 return None
482
483 return self.errexit_disabled_tok[-1]
484
485 def ErrExitIsDisabled(self):
486 # type: () -> bool
487 """
488 Similar to ErrExitDisabledToken, for ERR trap
489 """
490 if len(self.errexit_disabled_tok) == 0:
491 return False
492
493 return self.errexit_disabled_tok[-1] is not None
494
495 def _SetOldOption(self, opt_name, b):
496 # type: (str, bool) -> None
497 """Private version for synchronizing from SHELLOPTS."""
498 assert '_' not in opt_name
499 assert opt_name in consts.SET_OPTION_NAMES
500
501 opt_num = consts.OptionNum(opt_name)
502 assert opt_num != 0, opt_name
503
504 if opt_num == option_i.errexit:
505 self.SetDeferredErrExit(b)
506 else:
507 if opt_num == option_i.verbose and b:
508 print_stderr('Warning: set -o verbose not implemented')
509 self._SetArrayByNum(opt_num, b)
510
511 # note: may FAIL before we get here.
512
513 success = self.opt_hook.OnChange(self.opt0_array, opt_name, b)
514
515 def SetOldOption(self, opt_name, b):
516 # type: (str, bool) -> None
517 """For set -o, set +o, or shopt -s/-u -o."""
518 unused = _SetOptionNum(opt_name) # validate it
519 self._SetOldOption(opt_name, b)
520
521 if not self.Get(option_i.no_init_globals):
522 UP_val = self.mem.GetValue('SHELLOPTS')
523 assert UP_val.tag() == value_e.Str, UP_val
524 val = cast(value.Str, UP_val)
525 shellopts = val.s
526
527 # Now check if SHELLOPTS needs to be updated. It may be exported.
528 #
529 # NOTE: It might be better to skip rewriting SEHLLOPTS in the common case
530 # where it is not used. We could do it lazily upon GET.
531
532 # Also, it would be slightly more efficient to update SHELLOPTS if
533 # settings were batched, Examples:
534 # - set -eu
535 # - shopt -s foo bar
536 if b:
537 if opt_name not in shellopts:
538 new_val = value.Str('%s:%s' % (shellopts, opt_name))
539 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
540 else:
541 if opt_name in shellopts:
542 names = [n for n in shellopts.split(':') if n != opt_name]
543 new_val = value.Str(':'.join(names))
544 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
545
546 def SetAnyOption(self, opt_name, b, ignore_shopt_not_impl=False):
547 # type: (str, bool, bool) -> None
548 """For shopt -s/-u and sh -O/+O."""
549
550 # shopt -s ysh:all turns on all YSH options, which includes all strict
551 # options
552 opt_group = consts.OptionGroupNum(opt_name)
553 if opt_group == opt_group_i.YshUpgrade:
554 _SetGroup(self.opt0_array, consts.YSH_UPGRADE, b)
555 self.SetDeferredErrExit(b) # Special case
556 if b: # ENV dict
557 self.mem.MaybeInitEnvDict(self.environ)
558 return
559
560 if opt_group == opt_group_i.YshAll:
561 _SetGroup(self.opt0_array, consts.YSH_ALL, b)
562 self.SetDeferredErrExit(b) # Special case
563 if b: # ENV dict
564 self.mem.MaybeInitEnvDict(self.environ)
565 return
566
567 if opt_group == opt_group_i.StrictAll:
568 _SetGroup(self.opt0_array, consts.STRICT_ALL, b)
569 return
570
571 opt_num = _AnyOptionNum(opt_name, ignore_shopt_not_impl)
572
573 if opt_num == option_i.errexit:
574 self.SetDeferredErrExit(b)
575 return
576
577 self._SetArrayByNum(opt_num, b)
578
579
580class _ArgFrame(object):
581 """Stack frame for arguments array."""
582
583 def __init__(self, argv):
584 # type: (List[str]) -> None
585 self.argv = argv
586 self.num_shifted = 0
587
588 def __repr__(self):
589 # type: () -> str
590 return '<_ArgFrame %s %d at %x>' % (self.argv, self.num_shifted,
591 id(self))
592
593 def Dump(self):
594 # type: () -> Dict[str, value_t]
595 items = [value.Str(s) for s in self.argv] # type: List[value_t]
596 argv = value.List(items)
597 return {
598 'argv': argv,
599 'num_shifted': num.ToBig(self.num_shifted),
600 }
601
602 def GetArgNum(self, arg_num):
603 # type: (int) -> value_t
604
605 # $0 is handled elsewhere
606 assert 1 <= arg_num, arg_num
607
608 index = self.num_shifted + arg_num - 1
609 if index >= len(self.argv):
610 return value.Undef
611
612 return value.Str(self.argv[index])
613
614 def GetArgv(self):
615 # type: () -> List[str]
616 return self.argv[self.num_shifted:]
617
618 def GetNumArgs(self):
619 # type: () -> int
620 return len(self.argv) - self.num_shifted
621
622 def SetArgv(self, argv):
623 # type: (List[str]) -> None
624 self.argv = argv
625 self.num_shifted = 0
626
627
628def _DumpVarFrame(frame):
629 # type: (Dict[str, Cell]) -> Dict[str, value_t]
630 """Dump the stack frame as reasonably compact and readable JSON."""
631
632 vars_json = {} # type: Dict[str, value_t]
633 for name, cell in iteritems(frame):
634 cell_json = {} # type: Dict[str, value_t]
635
636 buf = mylib.BufWriter()
637 if cell.exported:
638 buf.write('x')
639 if cell.readonly:
640 buf.write('r')
641 flags = buf.getvalue()
642 if len(flags):
643 cell_json['flags'] = value.Str(flags)
644
645 # TODO:
646 # - Use packle for crash dumps! Then we can represent object cycles
647 # - Right now the JSON serializer will probably crash
648 # - although BashArray and BashAssoc may need 'type' tags
649 # - they don't round trip correctly
650 # - maybe add value.Tombstone here or something?
651 # - value.{Func,Eggex,...} may have value.Tombstone and
652 # vm.ValueIdString()?
653
654 with tagswitch(cell.val) as case:
655 if case(value_e.Undef):
656 cell_json['val'] = value.Null
657
658 elif case(value_e.Str, value_e.BashArray, value_e.BashAssoc):
659 cell_json['val'] = cell.val
660
661 else:
662 # TODO: should we show the object ID here?
663 pass
664
665 vars_json[name] = value.Dict(cell_json)
666
667 return vars_json
668
669
670def _LineNumber(tok):
671 # type: (Optional[Token]) -> str
672 """ For $BASH_LINENO """
673 if tok is None:
674 return '-1'
675 return str(tok.line.line_num)
676
677
678def _AddCallToken(d, token):
679 # type: (Dict[str, value_t], Optional[Token]) -> None
680 if token is None:
681 return
682 d['call_source'] = value.Str(ui.GetLineSourceString(token.line))
683 d['call_line_num'] = num.ToBig(token.line.line_num)
684 d['call_line'] = value.Str(token.line.content)
685
686
687class ctx_FuncCall(object):
688 """For func calls."""
689
690 def __init__(self, mem, func):
691 # type: (Mem, value.Func) -> None
692
693 self.saved_globals = mem.var_stack[0]
694
695 assert func.module_frame is not None
696 mem.var_stack[0] = func.module_frame
697
698 frame = NewDict() # type: Dict[str, Cell]
699 mem.var_stack.append(frame)
700
701 mem.PushCall(func.name, func.parsed.name)
702
703 self.mem = mem
704
705 def __enter__(self):
706 # type: () -> None
707 pass
708
709 def __exit__(self, type, value, traceback):
710 # type: (Any, Any, Any) -> None
711 self.mem.PopCall()
712 self.mem.var_stack.pop()
713
714 self.mem.var_stack[0] = self.saved_globals
715
716
717class ctx_ProcCall(object):
718 """For proc calls, including shell functions."""
719
720 def __init__(self, mem, mutable_opts, proc, argv):
721 # type: (Mem, MutableOpts, value.Proc, List[str]) -> None
722
723 # TODO:
724 # should we separate procs and shell functions?
725 # - dynamic scope is one difference
726 # - '$@" shift etc. are another difference
727
728 self.saved_globals = mem.var_stack[0]
729
730 assert proc.module_frame is not None
731 mem.var_stack[0] = proc.module_frame
732
733 frame = NewDict() # type: Dict[str, Cell]
734
735 assert argv is not None
736 if proc.sh_compat:
737 # shell function
738 mem.argv_stack.append(_ArgFrame(argv))
739 else:
740 # procs
741 # - open: is equivalent to ...ARGV
742 # - closed: ARGV is empty list
743 frame['ARGV'] = _MakeArgvCell(argv)
744
745 mem.var_stack.append(frame)
746
747 mem.PushCall(proc.name, proc.name_tok)
748
749 # Dynamic scope is only for shell functions
750 mutable_opts.PushDynamicScope(proc.sh_compat)
751
752 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
753 # 'if p' should be allowed.
754 self.mem = mem
755 self.mutable_opts = mutable_opts
756 self.sh_compat = proc.sh_compat
757
758 def __enter__(self):
759 # type: () -> None
760 pass
761
762 def __exit__(self, type, value, traceback):
763 # type: (Any, Any, Any) -> None
764 self.mutable_opts.PopDynamicScope()
765 self.mem.PopCall()
766 self.mem.var_stack.pop()
767
768 if self.sh_compat:
769 self.mem.argv_stack.pop()
770
771 self.mem.var_stack[0] = self.saved_globals
772
773
774class ctx_Temp(object):
775 """ POSIX shell FOO=bar mycommand """
776
777 def __init__(self, mem):
778 # type: (Mem) -> None
779 self.mem = mem
780 mem.PushTemp()
781
782 def __enter__(self):
783 # type: () -> None
784 pass
785
786 def __exit__(self, type, value, traceback):
787 # type: (Any, Any, Any) -> None
788 self.mem.PopTemp()
789
790
791class ctx_EnvObj(object):
792 """YSH FOO=bar my-command"""
793
794 def __init__(self, mem, bindings):
795 # type: (Mem, Dict[str, value_t]) -> None
796 self.mem = mem
797 mem.PushEnvObj(bindings)
798
799 def __enter__(self):
800 # type: () -> None
801 pass
802
803 def __exit__(self, type, value, traceback):
804 # type: (Any, Any, Any) -> None
805 self.mem.PopEnvObj()
806
807
808class ctx_Registers(object):
809 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
810
811 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
812 """
813
814 def __init__(self, mem):
815 # type: (Mem) -> None
816
817 # Because some prompts rely on the status leaking. See issue #853.
818 # PS1 also does.
819 last = mem.last_status[-1]
820 mem.last_status.append(last)
821 mem.try_status.append(0)
822 mem.try_error.append(value.Dict({}))
823
824 # TODO: We should also copy these values! Turn the whole thing into a
825 # frame.
826 mem.pipe_status.append([])
827 mem.process_sub_status.append([])
828
829 mem.regex_match.append(regex_match.No)
830
831 self.mem = mem
832
833 def __enter__(self):
834 # type: () -> None
835 pass
836
837 def __exit__(self, type, value, traceback):
838 # type: (Any, Any, Any) -> None
839 self.mem.regex_match.pop()
840
841 self.mem.process_sub_status.pop()
842 self.mem.pipe_status.pop()
843
844 self.mem.try_error.pop()
845 self.mem.try_status.pop()
846 self.mem.last_status.pop()
847
848
849class ctx_ThisDir(object):
850 """For $_this_dir."""
851
852 def __init__(self, mem, filename):
853 # type: (Mem, Optional[str]) -> None
854 self.do_pop = False
855 if filename is not None: # script_name in main() may be -c, etc.
856 d = os_path.dirname(os_path.abspath(filename))
857 mem.this_dir.append(d)
858 self.do_pop = True
859
860 self.mem = mem
861
862 def __enter__(self):
863 # type: () -> None
864 pass
865
866 def __exit__(self, type, value, traceback):
867 # type: (Any, Any, Any) -> None
868 if self.do_pop:
869 self.mem.this_dir.pop()
870
871
872def _MakeArgvCell(argv):
873 # type: (List[str]) -> Cell
874 items = [value.Str(a) for a in argv] # type: List[value_t]
875 return Cell(False, False, False, value.List(items))
876
877
878class ctx_LoopFrame(object):
879
880 def __init__(self, mem, name1):
881 # type: (Mem, str) -> None
882 self.mem = mem
883 self.name1 = name1
884 self.do_new_frame = name1 == '__hack__'
885
886 if self.do_new_frame:
887 to_enclose = self.mem.var_stack[-1]
888 self.new_frame = NewDict() # type: Dict[str, Cell]
889 self.new_frame['__E__'] = Cell(False, False, False,
890 value.Frame(to_enclose))
891 mem.var_stack.append(self.new_frame)
892
893 def __enter__(self):
894 # type: () -> None
895 pass
896
897 def __exit__(self, type, value, traceback):
898 # type: (Any, Any, Any) -> None
899 if self.do_new_frame:
900 self.mem.var_stack.pop()
901
902
903class ctx_EnclosedFrame(object):
904 """
905 Usages:
906
907 - io->evalToDict(), which is a primitive used for Hay and the Dict proc
908 - lexical scope aka static scope for block args to user-defined procs
909 - Including the "closures in a loop" problem, which will be used for Hay
910
911 var mutated = 'm'
912 var shadowed = 's'
913
914 Dict (&d) {
915 shadowed = 42
916 mutated = 'new' # this is equivalent to var mutated
917
918 setvar mutated = 'new'
919 }
920 echo $shadowed # restored to 's'
921 echo $mutated # new
922
923 Or maybe we disallow the setvar lookup?
924 """
925
926 def __init__(
927 self,
928 mem, # type: Mem
929 to_enclose, # type: Dict[str, Cell]
930 module_frame, # type: Dict[str, Cell]
931 out_dict, # type: Optional[Dict[str, value_t]]
932 ):
933 # type: (...) -> None
934 self.mem = mem
935 self.to_enclose = to_enclose
936 self.module_frame = module_frame
937 self.out_dict = out_dict
938
939 if module_frame is not None:
940 self.saved_globals = self.mem.var_stack[0]
941 self.mem.var_stack[0] = module_frame
942
943 # __E__ gets a lookup rule
944 self.new_frame = NewDict() # type: Dict[str, Cell]
945 self.new_frame['__E__'] = Cell(False, False, False,
946 value.Frame(to_enclose))
947
948 mem.var_stack.append(self.new_frame)
949
950 def __enter__(self):
951 # type: () -> None
952 pass
953
954 def __exit__(self, type, value, traceback):
955 # type: (Any, Any, Any) -> None
956
957 if self.out_dict is not None:
958 for name, cell in iteritems(self.new_frame):
959 #log('name %r', name)
960 #log('cell %r', cell)
961
962 # User can hide variables with _ suffix
963 # e.g. for i_ in foo bar { echo $i_ }
964 if name.endswith('_'):
965 continue
966
967 self.out_dict[name] = cell.val
968
969 # Restore
970 self.mem.var_stack.pop()
971
972 if self.module_frame is not None:
973 self.mem.var_stack[0] = self.saved_globals
974
975
976class ctx_ModuleEval(object):
977 """Evaluate a module with a new global stack frame.
978
979 e.g. setglobal in the new module doesn't leak
980
981 Different from ctx_EnclosedFrame because the new code can't see variables in
982 the old frame.
983 """
984
985 def __init__(self, mem, out_dict, out_errors):
986 # type: (Mem, Dict[str, value_t], List[str]) -> None
987 self.mem = mem
988 self.out_dict = out_dict
989 self.out_errors = out_errors
990
991 self.new_frame = NewDict() # type: Dict[str, Cell]
992 self.saved_frame = mem.var_stack[0]
993
994 # Somewhat of a hack for tracing within a module.
995 # Other solutions:
996 # - PS4 can be __builtin__, but that would break shell compatibility
997 # - We can have a separate YSH mechanism that uses a different settings
998 # - We probably still want it to be scoped, like shvar PS4=z { ... }
999 #
1000 # Note: there's a similar issue with HOSTNAME UID EUID etc. But those
1001 # could be io.hostname() io.getuid(), or lazy constants, etc.
1002
1003 ps4 = self.saved_frame.get('PS4')
1004 if ps4:
1005 self.new_frame['PS4'] = ps4
1006 # ENV is not in __builtins__ because it's mutable -- we want
1007 # 'setglobal' to work
1008 env = self.saved_frame.get('ENV')
1009 if env:
1010 self.new_frame['ENV'] = env
1011
1012 assert len(mem.var_stack) == 1
1013 mem.var_stack[0] = self.new_frame
1014
1015 def __enter__(self):
1016 # type: () -> None
1017 pass
1018
1019 def __exit__(self, type, value_, traceback):
1020 # type: (Any, Any, Any) -> None
1021
1022 assert len(self.mem.var_stack) == 1
1023 self.mem.var_stack[0] = self.saved_frame
1024
1025 # Now look in __export__ for the list of names to expose
1026
1027 cell = self.new_frame.get('__provide__')
1028 if cell is None:
1029 self.out_errors.append("Module is missing __provide__ List")
1030 return
1031
1032 provide_val = cell.val
1033 with tagswitch(provide_val) as case:
1034 if case(value_e.List):
1035 for val in cast(value.List, provide_val).items:
1036 if val.tag() == value_e.Str:
1037 name = cast(value.Str, val).s
1038
1039 cell = self.new_frame.get(name)
1040 if cell is None:
1041 self.out_errors.append(
1042 "Name %r was provided, but not defined" % name)
1043 continue
1044
1045 self.out_dict[name] = cell.val
1046 else:
1047 self.out_errors.append(
1048 "Expected Str in __provide__ List, got %s" %
1049 ui.ValType(val))
1050
1051 else:
1052 self.out_errors.append("__provide__ should be a List, got %s" %
1053 ui.ValType(provide_val))
1054
1055
1056class ctx_Eval(object):
1057 """Push temporary set of variables, $0, $1, $2, etc."""
1058
1059 def __init__(
1060 self,
1061 mem, # type: Mem
1062 dollar0, # type: Optional[str]
1063 pos_args, # type: Optional[List[str]]
1064 vars, # type: Optional[Dict[str, value_t]]
1065 ):
1066 # type: (...) -> None
1067 self.mem = mem
1068 self.dollar0 = dollar0
1069 self.pos_args = pos_args
1070 self.vars = vars
1071
1072 # $0 needs to have lexical scoping. So we store it with other locals.
1073 # As "0" cannot be parsed as an lvalue, we can safely store dollar0 there.
1074 if dollar0 is not None:
1075 #assert mem.GetValue("0", scope_e.LocalOnly).tag() == value_e.Undef
1076 #self.dollar0_lval = LeftName("0", loc.Missing)
1077 #mem.SetLocalName(self.dollar0_lval, value.Str(dollar0))
1078
1079 self.restore_dollar0 = self.mem.dollar0
1080 self.mem.dollar0 = dollar0
1081
1082 if pos_args is not None:
1083 mem.argv_stack.append(_ArgFrame(pos_args))
1084
1085 if vars is not None:
1086 self.restore = [] # type: List[Tuple[LeftName, value_t]]
1087 self._Push(vars)
1088
1089 def __enter__(self):
1090 # type: () -> None
1091 pass
1092
1093 def __exit__(self, type, value_, traceback):
1094 # type: (Any, Any, Any) -> None
1095 if self.vars is not None:
1096 self._Pop()
1097
1098 if self.pos_args is not None:
1099 self.mem.argv_stack.pop()
1100
1101 if self.dollar0 is not None:
1102 #self.mem.SetLocalName(self.dollar0_lval, value.Undef)
1103 self.mem.dollar0 = self.restore_dollar0
1104
1105 # Note: _Push and _Pop are separate methods because the C++ translation
1106 # doesn't like when they are inline in __init__ and __exit__.
1107 def _Push(self, vars):
1108 # type: (Dict[str, value_t]) -> None
1109 for name in vars:
1110 lval = location.LName(name)
1111 # LocalOnly because we are only overwriting the current scope
1112 old_val = self.mem.GetValue(name, scope_e.LocalOnly)
1113 self.restore.append((lval, old_val))
1114 self.mem.SetNamed(lval, vars[name], scope_e.LocalOnly)
1115
1116 def _Pop(self):
1117 # type: () -> None
1118 for lval, old_val in self.restore:
1119 if old_val.tag() == value_e.Undef:
1120 self.mem.Unset(lval, scope_e.LocalOnly)
1121 else:
1122 self.mem.SetNamed(lval, old_val, scope_e.LocalOnly)
1123
1124
1125def _FrameLookup(frame, name):
1126 # type: (Dict[str, Cell], str) -> Tuple[Optional[Cell], Dict[str, Cell]]
1127 """
1128 Look for a name in the frame, then recursively into the enclosing __E__
1129 frame, if it exists
1130 """
1131 cell = frame.get(name)
1132 if cell:
1133 return cell, frame
1134
1135 rear_cell = frame.get('__E__') # ctx_EnclosedFrame() sets this
1136 if rear_cell:
1137 rear_val = rear_cell.val
1138 assert rear_val, rear_val
1139 if rear_val.tag() == value_e.Frame:
1140 to_enclose = cast(value.Frame, rear_val).frame
1141 return _FrameLookup(to_enclose, name) # recursive call
1142
1143 return None, None
1144
1145
1146class Mem(object):
1147 """For storing variables.
1148
1149 Callers:
1150 User code: assigning and evaluating variables, in command context or
1151 arithmetic context.
1152 Completion engine: for COMP_WORDS, etc.
1153 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1154
1155 Modules: cmd_eval, word_eval, expr_eval, completion
1156 """
1157
1158 def __init__(self,
1159 dollar0,
1160 argv,
1161 arena,
1162 debug_stack,
1163 env_dict,
1164 defaults=None):
1165 # type: (str, List[str], alloc.Arena, List[debug_frame_t], Dict[str, value_t], Dict[str, value_t]) -> None
1166 """
1167 Args:
1168 arena: currently unused
1169 """
1170 # circular dep initialized out of line
1171 self.exec_opts = None # type: optview.Exec
1172 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1173
1174 self.dollar0 = dollar0
1175 # If you only use YSH procs and funcs, this will remain at length 1.
1176 self.argv_stack = [_ArgFrame(argv)]
1177
1178 frame = NewDict() # type: Dict[str, Cell]
1179
1180 frame['ARGV'] = _MakeArgvCell(argv)
1181
1182 self.var_stack = [frame]
1183
1184 # The debug_stack isn't strictly necessary for execution. We use it
1185 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1186 # BASH_LINENO.
1187 self.debug_stack = debug_stack
1188
1189 self.env_dict = env_dict
1190 self.env_object = Obj(None, env_dict) # initial state
1191
1192 if defaults is None: # for unit tests only
1193 self.defaults = NewDict() # type: Dict[str, value_t]
1194 else:
1195 self.defaults = defaults
1196
1197 self.pwd = None # type: Optional[str]
1198 self.seconds_start = time_.time()
1199
1200 self.token_for_line = None # type: Optional[Token]
1201 self.loc_for_expr = loc.Missing # type: loc_t
1202
1203 self.last_arg = '' # $_ is initially empty, NOT unset
1204 self.line_num = value.Str('')
1205
1206 # Done ONCE on initialization
1207 self.root_pid = posix.getpid()
1208
1209 # TODO:
1210 # - These are REGISTERS mutated by user code.
1211 # - Call it self.reg_stack? with ctx_Registers
1212 # - push-registers builtin
1213 self.last_status = [0] # type: List[int] # a stack
1214 self.try_status = [0] # type: List[int] # a stack
1215 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1216 self.pipe_status = [[]] # type: List[List[int]] # stack
1217 self.process_sub_status = [[]] # type: List[List[int]] # stack
1218
1219 # A stack but NOT a register?
1220 self.this_dir = [] # type: List[str]
1221 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1222
1223 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1224
1225 self.running_debug_trap = False # set by ctx_DebugTrap()
1226 self.running_err_trap = False # set by ctx_ErrTrap
1227 self.is_main = True # we start out in main
1228
1229 # For the ctx builtin
1230 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1231
1232 self.builtins = NewDict() # type: Dict[str, value_t]
1233
1234 # Note: Python 2 and 3 have __builtins__
1235 # This is just for inspection
1236 builtins_module = Obj(None, self.builtins)
1237
1238 # Code in any module can see __builtins__
1239 self.builtins['__builtins__'] = builtins_module
1240
1241 self.did_ysh_env = False # only initialize ENV once per process
1242
1243 from core import sh_init
1244 self.env_config = sh_init.EnvConfig(self, defaults)
1245
1246 def __repr__(self):
1247 # type: () -> str
1248 parts = [] # type: List[str]
1249 parts.append('<Mem')
1250 for i, frame in enumerate(self.var_stack):
1251 parts.append(' -- %d --' % i)
1252 for n, v in frame.iteritems():
1253 parts.append(' %s %s' % (n, v))
1254 parts.append('>')
1255 return '\n'.join(parts) + '\n'
1256
1257 def AddBuiltin(self, name, val):
1258 # type: (str, value_t) -> None
1259 self.builtins[name] = val
1260
1261 def SetPwd(self, pwd):
1262 # type: (str) -> None
1263 """Used by builtins."""
1264 self.pwd = pwd
1265
1266 def ParsingChangesAllowed(self):
1267 # type: () -> bool
1268 """For checking that syntax options are only used at the top level."""
1269
1270 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1271 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1272 # ALLOW FOO=bar : pushes var_stack
1273 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1274
1275 def Dump(self):
1276 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1277 """Copy state before unwinding the stack."""
1278 var_stack = [
1279 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1280 ] # type: List[value_t]
1281 argv_stack = [value.Dict(frame.Dump())
1282 for frame in self.argv_stack] # type: List[value_t]
1283
1284 debug_stack = [] # type: List[value_t]
1285
1286 # Reuse these immutable objects
1287 t_call = value.Str('Call')
1288 t_source = value.Str('Source')
1289 t_main = value.Str('Main')
1290
1291 for frame in reversed(self.debug_stack):
1292 UP_frame = frame
1293 with tagswitch(frame) as case:
1294 if case(debug_frame_e.Call):
1295 frame = cast(debug_frame.Call, UP_frame)
1296 d = {
1297 'type': t_call,
1298 'func_name': value.Str(frame.func_name)
1299 } # type: Dict[str, value_t]
1300
1301 _AddCallToken(d, frame.call_tok)
1302 # TODO: Add def_tok
1303
1304 elif case(debug_frame_e.Source):
1305 frame = cast(debug_frame.Source, UP_frame)
1306 d = {
1307 'type': t_source,
1308 'source_name': value.Str(frame.source_name)
1309 }
1310 _AddCallToken(d, frame.call_tok)
1311
1312 elif case(debug_frame_e.Main):
1313 frame = cast(debug_frame.Main, UP_frame)
1314 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1315
1316 debug_stack.append(value.Dict(d))
1317 return var_stack, argv_stack, debug_stack
1318
1319 def SetLastArgument(self, s):
1320 # type: (str) -> None
1321 """For $_"""
1322 self.last_arg = s
1323
1324 def SetTokenForLine(self, tok):
1325 # type: (Token) -> None
1326 """Set a token to compute $LINENO
1327
1328 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1329 case, etc. -- anything that evaluates a word. Example: there was a bug
1330 with 'case $LINENO'
1331
1332 This token also used as a "least-specific" / fallback location for
1333 errors in ExecuteAndCatch().
1334
1335 Although most of that should be taken over by 'with ui.ctx_Location()`,
1336 for the errfmt.
1337 """
1338 if self.running_debug_trap or self.running_err_trap:
1339 return
1340
1341 #if tok.span_id == runtime.NO_SPID:
1342 # NOTE: This happened in the osh-runtime benchmark for yash.
1343 #log('Warning: span_id undefined in SetTokenForLine')
1344
1345 #import traceback
1346 #traceback.print_stack()
1347 #return
1348
1349 self.token_for_line = tok
1350
1351 def SetLocationForExpr(self, blame_loc):
1352 # type: (loc_t) -> None
1353 """
1354 A more specific fallback location, like the $[ in
1355
1356 echo $[len(42)]
1357 """
1358 self.loc_for_expr = blame_loc
1359
1360 def GetFallbackLocation(self):
1361 # type: () -> loc_t
1362
1363 if self.loc_for_expr != loc.Missing: # more specific
1364 return self.loc_for_expr
1365
1366 if self.token_for_line: # less specific
1367 return self.token_for_line
1368
1369 return loc.Missing
1370
1371 #
1372 # Status Variable Stack (for isolating $PS1 and $PS4)
1373 #
1374
1375 def LastStatus(self):
1376 # type: () -> int
1377 return self.last_status[-1]
1378
1379 def TryStatus(self):
1380 # type: () -> int
1381 return self.try_status[-1]
1382
1383 def TryError(self):
1384 # type: () -> value.Dict
1385 return self.try_error[-1]
1386
1387 def PipeStatus(self):
1388 # type: () -> List[int]
1389 return self.pipe_status[-1]
1390
1391 def SetLastStatus(self, x):
1392 # type: (int) -> None
1393 self.last_status[-1] = x
1394
1395 def SetTryStatus(self, x):
1396 # type: (int) -> None
1397 self.try_status[-1] = x
1398
1399 def SetTryError(self, x):
1400 # type: (value.Dict) -> None
1401 self.try_error[-1] = x
1402
1403 def SetPipeStatus(self, x):
1404 # type: (List[int]) -> None
1405 self.pipe_status[-1] = x
1406
1407 def SetSimplePipeStatus(self, status):
1408 # type: (int) -> None
1409
1410 # Optimization to avoid allocations
1411 top = self.pipe_status[-1]
1412 if len(top) == 1:
1413 top[0] = status
1414 else:
1415 self.pipe_status[-1] = [status]
1416
1417 def SetProcessSubStatus(self, x):
1418 # type: (List[int]) -> None
1419 self.process_sub_status[-1] = x
1420
1421 #
1422 # Call Stack
1423 #
1424
1425 def PushCall(self, func_name, def_tok):
1426 # type: (str, Token) -> None
1427 """Push argv, var, and debug stack frames.
1428
1429 Currently used for proc and func calls. TODO: New func evaluator may
1430 not use it.
1431
1432 Args:
1433 def_tok: Token where proc or func was defined, used to compute
1434 BASH_SOURCE.
1435 """
1436 # self.token_for_line can be None?
1437 self.debug_stack.append(
1438 debug_frame.Call(self.token_for_line, def_tok, func_name))
1439
1440 def PopCall(self):
1441 # type: () -> None
1442 """
1443 Args:
1444 should_pop_argv_stack: Pass False if PushCall was given None for argv
1445 True for proc, False for func
1446 """
1447 self.debug_stack.pop()
1448
1449 def ShouldRunDebugTrap(self):
1450 # type: () -> bool
1451
1452 # TODO: RunLastPart of pipeline can disable this
1453
1454 # Don't recursively run DEBUG trap
1455 if self.running_debug_trap:
1456 return False
1457
1458 # Don't run it inside functions
1459 if len(self.var_stack) > 1:
1460 return False
1461
1462 return True
1463
1464 def IsGlobalScope(self):
1465 # type: () -> bool
1466 """
1467 local -g uses this, probably because bash does the wrong thing and
1468 prints LOCALS, not globals.
1469 """
1470 return len(self.var_stack) == 1
1471
1472 def InsideFunction(self):
1473 # type: () -> bool
1474 """For the ERR trap, and use builtin"""
1475
1476 # TODO: Should this be unified with ParsingChangesAllowed()? Slightly
1477 # different logic.
1478
1479 # Don't run it inside functions
1480 return len(self.var_stack) > 1
1481
1482 def GlobalFrame(self):
1483 # type: () -> Dict[str, Cell]
1484 """For defining the global scope of modules.
1485
1486 It's affected by ctx_ModuleEval()
1487 """
1488 return self.var_stack[0]
1489
1490 def CurrentFrame(self):
1491 # type: () -> Dict[str, Cell]
1492 """For attaching a stack frame to a value.Block"""
1493 return self.var_stack[-1]
1494
1495 def PushSource(self, source_name, argv):
1496 # type: (str, List[str]) -> None
1497 """ For 'source foo.sh 1 2 3' """
1498 if len(argv):
1499 self.argv_stack.append(_ArgFrame(argv))
1500
1501 # self.token_for_line can be None?
1502 self.debug_stack.append(
1503 debug_frame.Source(self.token_for_line, source_name))
1504
1505 def PopSource(self, argv):
1506 # type: (List[str]) -> None
1507 self.debug_stack.pop()
1508
1509 if len(argv):
1510 self.argv_stack.pop()
1511
1512 def PushTemp(self):
1513 # type: () -> None
1514 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1515
1516 Also for PS4 evaluation with more variables.
1517 """
1518 # We don't want the 'read' builtin to write to this frame!
1519 frame = NewDict() # type: Dict[str, Cell]
1520 self.var_stack.append(frame)
1521
1522 def PopTemp(self):
1523 # type: () -> None
1524 self.var_stack.pop()
1525
1526 def _BindEnvObj(self):
1527 # type: () -> None
1528 self.SetNamed(location.LName('ENV'), self.env_object,
1529 scope_e.GlobalOnly)
1530
1531 def MaybeInitEnvDict(self, environ):
1532 # type: (Dict[str, str]) -> None
1533 if self.did_ysh_env:
1534 return
1535
1536 for name, s in iteritems(environ):
1537 self.env_dict[name] = value.Str(s)
1538
1539 self._BindEnvObj()
1540 self.did_ysh_env = True
1541
1542 def PushEnvObj(self, bindings):
1543 # type: (Dict[str, value_t]) -> None
1544 """Push "bindings" as the MOST visible part of the ENV Obj
1545
1546 i.e. first() / propView()
1547 """
1548 self.env_object = Obj(self.env_object, bindings)
1549 self._BindEnvObj()
1550
1551 def PopEnvObj(self):
1552 # type: () -> None
1553 """Pop a Dict of bindings."""
1554 self.env_object = self.env_object.prototype
1555 if self.env_object is None:
1556 # TODO: Better error, or users shouldn't be able to mutate it
1557 e_die('PopEnvObj: ENV.prototype is null', loc.Missing)
1558
1559 self._BindEnvObj()
1560
1561 #
1562 # Argv
1563 #
1564
1565 def Shift(self, n):
1566 # type: (int) -> int
1567 frame = self.argv_stack[-1]
1568 num_args = len(frame.argv)
1569
1570 if (frame.num_shifted + n) <= num_args:
1571 frame.num_shifted += n
1572 return 0 # success
1573 else:
1574 return 1 # silent error
1575
1576 def GetArg0(self):
1577 # type: () -> value.Str
1578 """Like GetArgNum(0) but with a more specific type."""
1579 return value.Str(self.dollar0)
1580
1581 def GetArgNum(self, arg_num):
1582 # type: (int) -> value_t
1583 if arg_num == 0:
1584 # Disabled
1585 if 0:
1586 # Problem: Doesn't obey enclosing frame?
1587 # Yeah it needs FrameLookup
1588 cell, _ = _FrameLookup(self.var_stack[-1], '0')
1589 if cell is not None:
1590 val = cell.val
1591 if val.tag() != value_e.Undef:
1592 return val
1593
1594 return value.Str(self.dollar0)
1595
1596 return self.argv_stack[-1].GetArgNum(arg_num)
1597
1598 def GetArgv(self):
1599 # type: () -> List[str]
1600 """For $* and $@."""
1601 return self.argv_stack[-1].GetArgv()
1602
1603 def SetArgv(self, argv):
1604 # type: (List[str]) -> None
1605 """For set -- 1 2 3."""
1606 # from set -- 1 2 3
1607 self.argv_stack[-1].SetArgv(argv)
1608
1609 #
1610 # Special Vars
1611 #
1612
1613 def GetSpecialVar(self, op_id):
1614 # type: (int) -> value_t
1615 if op_id == Id.VSub_Bang: # $!
1616 n = self.last_bg_pid
1617 if n == -1:
1618 return value.Undef # could be an error
1619
1620 elif op_id == Id.VSub_QMark: # $?
1621 # External commands need WIFEXITED test. What about subshells?
1622 n = self.last_status[-1]
1623
1624 elif op_id == Id.VSub_Pound: # $#
1625 n = self.argv_stack[-1].GetNumArgs()
1626
1627 elif op_id == Id.VSub_Dollar: # $$
1628 n = self.root_pid
1629
1630 else:
1631 raise NotImplementedError(op_id)
1632
1633 return value.Str(str(n))
1634
1635 #
1636 # Named Vars
1637 #
1638
1639 def _ResolveNameOnly(self, name, which_scopes):
1640 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1641 """Helper for getting and setting variable.
1642
1643 Returns:
1644 cell: The cell corresponding to looking up 'name' with the given mode, or
1645 None if it's not found.
1646 var_frame: The frame it should be set to or deleted from.
1647 """
1648 if which_scopes == scope_e.Dynamic:
1649 for i in xrange(len(self.var_stack) - 1, -1, -1):
1650 var_frame = self.var_stack[i]
1651 cell, result_frame = _FrameLookup(var_frame, name)
1652 if cell:
1653 return cell, result_frame
1654 return None, self.var_stack[0] # set in global var_frame
1655
1656 if which_scopes == scope_e.LocalOnly:
1657 var_frame = self.var_stack[-1]
1658 cell, result_frame = _FrameLookup(var_frame, name)
1659 if cell:
1660 return cell, result_frame
1661 return None, var_frame
1662
1663 if which_scopes == scope_e.GlobalOnly:
1664 var_frame = self.var_stack[0]
1665 cell, result_frame = _FrameLookup(var_frame, name)
1666 if cell:
1667 return cell, result_frame
1668
1669 return None, var_frame
1670
1671 if which_scopes == scope_e.LocalOrGlobal:
1672 # Local
1673 var_frame = self.var_stack[-1]
1674 cell, result_frame = _FrameLookup(var_frame, name)
1675 if cell:
1676 return cell, result_frame
1677
1678 # Global
1679 var_frame = self.var_stack[0]
1680 cell, result_frame = _FrameLookup(var_frame, name)
1681 if cell:
1682 return cell, result_frame
1683
1684 return None, var_frame
1685
1686 raise AssertionError()
1687
1688 def _ResolveNameOrRef(
1689 self,
1690 name, # type: str
1691 which_scopes, # type: scope_t
1692 ref_trail=None, # type: Optional[List[str]]
1693 ):
1694 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1695 """Look up a cell and namespace, but respect the nameref flag.
1696
1697 Resolving namerefs does RECURSIVE calls.
1698 """
1699 cell, var_frame = self._ResolveNameOnly(name, which_scopes)
1700
1701 if cell is None or not cell.nameref:
1702 return cell, var_frame, name # not a nameref
1703
1704 val = cell.val
1705 UP_val = val
1706 with tagswitch(val) as case:
1707 if case(value_e.Undef):
1708 # This is 'local -n undef_ref', which is kind of useless, because the
1709 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1710 # references themselves with local -n ref=new.
1711 if self.exec_opts.strict_nameref():
1712 e_die('nameref %r is undefined' % name)
1713 else:
1714 return cell, var_frame, name # fallback
1715
1716 elif case(value_e.Str):
1717 val = cast(value.Str, UP_val)
1718 new_name = val.s
1719
1720 else:
1721 # SetValue() protects the invariant that nameref is Undef or Str
1722 raise AssertionError(val.tag())
1723
1724 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1725 # 'printf -v' with MakeArithParser
1726 if not match.IsValidVarName(new_name):
1727 # e.g. '#' or '1' or ''
1728 if self.exec_opts.strict_nameref():
1729 e_die('nameref %r contains invalid variable name %r' %
1730 (name, new_name))
1731 else:
1732 # Bash has this odd behavior of clearing the nameref bit when
1733 # ref=#invalid#. strict_nameref avoids it.
1734 cell.nameref = False
1735 return cell, var_frame, name # fallback
1736
1737 # Check for circular namerefs.
1738 if ref_trail is None:
1739 ref_trail = [name]
1740 else:
1741 if new_name in ref_trail:
1742 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1743 ref_trail.append(new_name)
1744
1745 # 'declare -n' uses dynamic scope.
1746 cell, var_frame, cell_name = self._ResolveNameOrRef(
1747 new_name, scope_e.Dynamic, ref_trail=ref_trail)
1748 return cell, var_frame, cell_name
1749
1750 def IsBashAssoc(self, name):
1751 # type: (str) -> bool
1752 """Returns whether a name resolve to a cell with an associative array.
1753
1754 We need to know this to evaluate the index expression properly
1755 -- should it be coerced to an integer or not?
1756 """
1757 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1758 # foo=([key]=value)
1759 return cell is not None and cell.val.tag() == value_e.BashAssoc
1760
1761 def SetPlace(self, place, val, blame_loc):
1762 # type: (value.Place, value_t, loc_t) -> None
1763
1764 yval = place.lval
1765 UP_yval = yval
1766 with tagswitch(yval) as case:
1767 if case(y_lvalue_e.Local):
1768 yval = cast(LeftName, UP_yval)
1769
1770 if 0:
1771 # Check that the frame is still alive
1772 # Note: Disabled because it doesn't work with modules. the
1773 # Place captures a frame in def-test.ysh, which we want to
1774 # mutate while Dict is executing in the module_frame for
1775 # def.ysh. See ctx_ModuleEval
1776 found = False
1777 for i in xrange(len(self.var_stack) - 1, -1, -1):
1778 frame = self.var_stack[i]
1779 if frame is place.frame:
1780 found = True
1781 #log('FOUND %s', found)
1782 break
1783 if not found:
1784 e_die(
1785 "Can't assign to place that's no longer on the call stack.",
1786 blame_loc)
1787
1788 frame = place.frame
1789 cell = frame.get(yval.name)
1790 if cell is None:
1791 cell = Cell(False, False, False, val)
1792 frame[yval.name] = cell
1793 else:
1794 cell.val = val
1795
1796 elif case(y_lvalue_e.Container):
1797 e_die('Container place not implemented', blame_loc)
1798
1799 else:
1800 raise AssertionError()
1801
1802 def SetLocalName(self, lval, val):
1803 # type: (LeftName, value_t) -> None
1804 """
1805 Set a name in the local scope - used for func/proc param binding, etc.
1806 """
1807
1808 # Equivalent to
1809 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1810 var_frame = self.var_stack[-1]
1811 cell = var_frame.get(lval.name)
1812
1813 if cell:
1814 if cell.readonly:
1815 e_die("Can't assign to readonly value %r" % lval.name,
1816 lval.blame_loc)
1817 cell.val = val # Mutate value_t
1818 else:
1819 cell = Cell(False, False, False, val)
1820 var_frame[lval.name] = cell
1821
1822 def SetNamed(self, lval, val, which_scopes, flags=0):
1823 # type: (LeftName, value_t, scope_t, int) -> None
1824 if flags & SetNameref or flags & ClearNameref:
1825 # declare -n ref=x # refers to the ref itself
1826 cell, var_frame = self._ResolveNameOnly(lval.name, which_scopes)
1827 cell_name = lval.name
1828 else:
1829 # ref=x # mutates THROUGH the reference
1830
1831 # Note on how to implement declare -n ref='a[42]'
1832 # 1. Call _ResolveNameOnly()
1833 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1834 # BracedVarSub
1835 # 3. Turn BracedVarSub into an sh_lvalue, and call
1836 # self.unsafe_arith.SetValue() wrapper with ref_trail
1837 cell, var_frame, cell_name = self._ResolveNameOrRef(
1838 lval.name, which_scopes)
1839
1840 if cell:
1841 # Clear before checking readonly bit.
1842 # NOTE: Could be cell.flags &= flag_clear_mask
1843 if flags & ClearExport:
1844 cell.exported = False
1845 if flags & ClearReadOnly:
1846 cell.readonly = False
1847 if flags & ClearNameref:
1848 cell.nameref = False
1849
1850 if val is not None: # e.g. declare -rx existing
1851 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1852 # But that's true for 'readonly' too, and hoisting it makes more
1853 # sense anyway.
1854 if cell.readonly:
1855 e_die("Can't assign to readonly value %r" % lval.name,
1856 lval.blame_loc)
1857 cell.val = val # CHANGE VAL
1858
1859 # NOTE: Could be cell.flags |= flag_set_mask
1860 if flags & SetExport:
1861 cell.exported = True
1862 if flags & SetReadOnly:
1863 cell.readonly = True
1864 if flags & SetNameref:
1865 cell.nameref = True
1866
1867 else:
1868 if val is None: # declare -rx nonexistent
1869 # set -o nounset; local foo; echo $foo # It's still undefined!
1870 val = value.Undef # export foo, readonly foo
1871
1872 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1873 bool(flags & SetNameref), val)
1874 var_frame[cell_name] = cell
1875
1876 # Maintain invariant that only strings and undefined cells can be
1877 # exported.
1878 assert cell.val is not None, cell
1879
1880 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1881 if cell.exported:
1882 if self.exec_opts.strict_array():
1883 e_die("Only strings can be exported (strict_array)",
1884 lval.blame_loc)
1885 if cell.nameref:
1886 e_die("nameref must be a string", lval.blame_loc)
1887
1888 def SetValue(self, lval, val, which_scopes, flags=0):
1889 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1890 """
1891 Args:
1892 lval: sh_lvalue
1893 val: value, or None if only changing flags
1894 which_scopes:
1895 Local | Global | Dynamic - for builtins, PWD, etc.
1896 flags: packed pair (keyword_id, bit mask of set/clear flags)
1897
1898 Note: in bash, PWD=/ changes the directory. But not in dash.
1899 """
1900 # STRICTNESS / SANENESS:
1901 #
1902 # 1) Don't create arrays automatically, e.g. a[1000]=x
1903 # 2) Never change types? yeah I think that's a good idea, at least for YSH
1904 # (not sh, for compatibility). set -o strict_types or something. That
1905 # means arrays have to be initialized with let arr = [], which is fine.
1906 # This helps with stuff like IFS. It starts off as a string, and assigning
1907 # it to a list is an error. I guess you will have to turn this no for
1908 # bash?
1909 #
1910 # TODO:
1911 # - COMPUTED vars can't be set
1912 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1913 # readonly.
1914 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1915 # - Other validity: $HOME could be checked for existence
1916
1917 UP_lval = lval
1918 with tagswitch(lval) as case:
1919 if case(sh_lvalue_e.Var):
1920 lval = cast(LeftName, UP_lval)
1921
1922 self.SetNamed(lval, val, which_scopes, flags=flags)
1923
1924 elif case(sh_lvalue_e.Indexed):
1925 lval = cast(sh_lvalue.Indexed, UP_lval)
1926
1927 # There is no syntax 'declare a[x]'
1928 assert val is not None, val
1929
1930 # TODO: relax this for YSH
1931 assert val.tag() == value_e.Str, val
1932 rval = cast(value.Str, val)
1933
1934 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1935 left_loc = lval.blame_loc
1936
1937 # bash/mksh have annoying behavior of letting you do LHS assignment to
1938 # Undef, which then turns into an INDEXED array. (Undef means that set
1939 # -o nounset fails.)
1940 cell, var_frame, _ = self._ResolveNameOrRef(
1941 lval.name, which_scopes)
1942 if not cell:
1943 self._BindNewArrayWithEntry(var_frame, lval, rval, flags)
1944 return
1945
1946 if cell.readonly:
1947 e_die("Can't assign to readonly array", left_loc)
1948
1949 UP_cell_val = cell.val
1950 # undef[0]=y is allowed
1951 with tagswitch(UP_cell_val) as case2:
1952 if case2(value_e.Undef):
1953 self._BindNewArrayWithEntry(var_frame, lval, rval,
1954 flags)
1955 return
1956
1957 elif case2(value_e.Str):
1958 # s=x
1959 # s[1]=y # invalid
1960 e_die("Can't assign to items in a string", left_loc)
1961
1962 elif case2(value_e.BashArray):
1963 cell_val = cast(value.BashArray, UP_cell_val)
1964 strs = cell_val.strs
1965
1966 n = len(strs)
1967 index = lval.index
1968 if index < 0: # a[-1]++ computes this twice; could we avoid it?
1969 index += n
1970
1971 if 0 <= index and index < n:
1972 strs[index] = rval.s
1973 else:
1974 # Fill it in with None. It could look like this:
1975 # ['1', 2, 3, None, None, '4', None]
1976 # Then ${#a[@]} counts the entries that are not None.
1977 n = index - len(strs) + 1
1978 for i in xrange(n):
1979 strs.append(None)
1980 strs[lval.index] = rval.s
1981 return
1982
1983 # This could be an object, eggex object, etc. It won't be
1984 # BashAssoc shouldn because we query IsBashAssoc before evaluating
1985 # sh_lhs. Could conslidate with s[i] case above
1986 e_die(
1987 "Value of type %s can't be indexed" % ui.ValType(cell.val),
1988 left_loc)
1989
1990 elif case(sh_lvalue_e.Keyed):
1991 lval = cast(sh_lvalue.Keyed, UP_lval)
1992
1993 # There is no syntax 'declare A["x"]'
1994 assert val is not None, val
1995 assert val.tag() == value_e.Str, val
1996 rval = cast(value.Str, val)
1997
1998 left_loc = lval.blame_loc
1999
2000 cell, var_frame, _ = self._ResolveNameOrRef(
2001 lval.name, which_scopes)
2002 if cell.readonly:
2003 e_die("Can't assign to readonly associative array",
2004 left_loc)
2005
2006 # We already looked it up before making the sh_lvalue
2007 assert cell.val.tag() == value_e.BashAssoc, cell
2008 cell_val2 = cast(value.BashAssoc, cell.val)
2009
2010 cell_val2.d[lval.key] = rval.s
2011
2012 else:
2013 raise AssertionError(lval.tag())
2014
2015 def _BindNewArrayWithEntry(self, var_frame, lval, val, flags):
2016 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
2017 """Fill 'var_frame' with a new indexed array entry."""
2018 no_str = None # type: Optional[str]
2019 items = [no_str] * lval.index
2020 items.append(val.s)
2021 new_value = value.BashArray(items)
2022
2023 # arrays can't be exported; can't have BashAssoc flag
2024 readonly = bool(flags & SetReadOnly)
2025 var_frame[lval.name] = Cell(False, readonly, False, new_value)
2026
2027 def InternalSetGlobal(self, name, new_val):
2028 # type: (str, value_t) -> None
2029 """For setting read-only globals internally.
2030
2031 Args:
2032 name: string (not Lhs)
2033 new_val: value
2034
2035 The variable must already exist.
2036
2037 Use case: SHELLOPTS.
2038 """
2039 cell = self.var_stack[0][name]
2040 cell.val = new_val
2041
2042 def GetValue(self, name, which_scopes=scope_e.Shopt):
2043 # type: (str, scope_t) -> value_t
2044 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
2045 assert isinstance(name, str), name
2046
2047 if which_scopes == scope_e.Shopt:
2048 which_scopes = self.ScopesForReading()
2049
2050 with str_switch(name) as case:
2051 # "Registers"
2052 if case('_status'): # deprecated in favor of _error.code
2053 return num.ToBig(self.TryStatus())
2054
2055 elif case('_error'):
2056 return self.TryError()
2057
2058 elif case('_this_dir'):
2059 if len(self.this_dir) == 0:
2060 # e.g. osh -c '' doesn't have it set
2061 # Should we give a custom error here?
2062 # If you're at the interactive shell, 'source mymodule.ysh' will still
2063 # work because 'source' sets it.
2064 return value.Undef
2065 else:
2066 return value.Str(self.this_dir[-1]) # top of stack
2067
2068 elif case('PIPESTATUS'):
2069 strs2 = [str(i)
2070 for i in self.pipe_status[-1]] # type: List[str]
2071 return value.BashArray(strs2)
2072
2073 elif case('_pipeline_status'):
2074 items = [num.ToBig(i)
2075 for i in self.pipe_status[-1]] # type: List[value_t]
2076 return value.List(items)
2077
2078 elif case('_process_sub_status'): # YSH naming convention
2079 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
2080 return value.List(items)
2081
2082 elif case('BASH_REMATCH'):
2083 top_match = self.regex_match[-1]
2084 with tagswitch(top_match) as case2:
2085 if case2(regex_match_e.No):
2086 groups = [] # type: List[str]
2087 elif case2(regex_match_e.Yes):
2088 m = cast(RegexMatch, top_match)
2089 groups = util.RegexGroupStrings(m.s, m.indices)
2090 return value.BashArray(groups)
2091
2092 # Do lookup of system globals before looking at user variables. Note: we
2093 # could optimize this at compile-time like $?. That would break
2094 # ${!varref}, but it's already broken for $?.
2095
2096 elif case('FUNCNAME'):
2097 # bash wants it in reverse order. This is a little inefficient but we're
2098 # not depending on deque().
2099 strs = [] # type: List[str]
2100 for frame in reversed(self.debug_stack):
2101 UP_frame = frame
2102 with tagswitch(frame) as case2:
2103 if case2(debug_frame_e.Call):
2104 frame = cast(debug_frame.Call, UP_frame)
2105 strs.append(frame.func_name)
2106
2107 elif case2(debug_frame_e.Source):
2108 # bash doesn't tell you the filename sourced
2109 strs.append('source')
2110
2111 elif case2(debug_frame_e.Main):
2112 strs.append('main') # also bash behavior
2113
2114 return value.BashArray(strs) # TODO: Reuse this object too?
2115
2116 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2117 #
2118 # ${BASH_LINENO[$i]} is the line number in the source file
2119 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2120 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2121 #
2122 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2123
2124 elif case('BASH_SOURCE'):
2125 strs = []
2126 for frame in reversed(self.debug_stack):
2127 UP_frame = frame
2128 with tagswitch(frame) as case2:
2129 if case2(debug_frame_e.Call):
2130 frame = cast(debug_frame.Call, UP_frame)
2131
2132 # Weird bash behavior
2133 assert frame.def_tok.line is not None
2134 source_str = ui.GetLineSourceString(
2135 frame.def_tok.line)
2136 strs.append(source_str)
2137
2138 elif case2(debug_frame_e.Source):
2139 frame = cast(debug_frame.Source, UP_frame)
2140 # Is this right?
2141 strs.append(frame.source_name)
2142
2143 elif case2(debug_frame_e.Main):
2144 frame = cast(debug_frame.Main, UP_frame)
2145 strs.append(frame.dollar0)
2146
2147 return value.BashArray(strs) # TODO: Reuse this object too?
2148
2149 elif case('BASH_LINENO'):
2150 strs = []
2151 for frame in reversed(self.debug_stack):
2152 UP_frame = frame
2153 with tagswitch(frame) as case2:
2154 if case2(debug_frame_e.Call):
2155 frame = cast(debug_frame.Call, UP_frame)
2156 strs.append(_LineNumber(frame.call_tok))
2157
2158 elif case2(debug_frame_e.Source):
2159 frame = cast(debug_frame.Source, UP_frame)
2160 strs.append(_LineNumber(frame.call_tok))
2161
2162 elif case2(debug_frame_e.Main):
2163 # Bash does this to line up with 'main'
2164 strs.append('0')
2165
2166 return value.BashArray(strs) # TODO: Reuse this object too?
2167
2168 elif case('LINENO'):
2169 assert self.token_for_line is not None
2170 # Reuse object with mutation
2171 # TODO: maybe use interned GetLineNumStr?
2172 self.line_num.s = str(self.token_for_line.line.line_num)
2173 return self.line_num
2174
2175 elif case('BASHPID'): # TODO: YSH io->getpid()
2176 return value.Str(str(posix.getpid()))
2177
2178 elif case('_'):
2179 return value.Str(self.last_arg)
2180
2181 elif case('SECONDS'):
2182 f = time_.time() - self.seconds_start
2183 ok, big_int = mops.FromFloat(f)
2184 assert ok, f # should never be NAN or INFINITY
2185 return value.Int(big_int)
2186
2187 else:
2188 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2189 # fix this:
2190 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2191 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2192 # We still need a ref_trail to detect cycles.
2193 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2194 if cell:
2195 return cell.val
2196
2197 builtin_val = self.builtins.get(name)
2198 if builtin_val:
2199 return builtin_val
2200
2201 # TODO: Can look in the builtins module, which is a value.Obj
2202 return value.Undef
2203
2204 def GetCell(self, name, which_scopes=scope_e.Shopt):
2205 # type: (str, scope_t) -> Cell
2206 """Get both the value and flags.
2207
2208 Usages:
2209 - the 'pp' builtin.
2210 - declare -p
2211 - ${x@a}
2212 - to test of 'TZ' is exported in printf? Why?
2213
2214 Note: consulting __builtins__ doesn't see necessary for any of these
2215 """
2216 if which_scopes == scope_e.Shopt:
2217 which_scopes = self.ScopesForReading()
2218
2219 cell, _ = self._ResolveNameOnly(name, which_scopes)
2220 return cell
2221
2222 def Unset(self, lval, which_scopes):
2223 # type: (sh_lvalue_t, scope_t) -> bool
2224 """
2225 Returns:
2226 Whether the cell was found.
2227 """
2228 # TODO: Refactor sh_lvalue type to avoid this
2229 UP_lval = lval
2230
2231 with tagswitch(lval) as case:
2232 if case(sh_lvalue_e.Var): # unset x
2233 lval = cast(LeftName, UP_lval)
2234 var_name = lval.name
2235 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2236 lval = cast(sh_lvalue.Indexed, UP_lval)
2237 var_name = lval.name
2238 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2239 lval = cast(sh_lvalue.Keyed, UP_lval)
2240 var_name = lval.name
2241 else:
2242 raise AssertionError()
2243
2244 if which_scopes == scope_e.Shopt:
2245 which_scopes = self.ScopesForWriting()
2246
2247 cell, var_frame, cell_name = self._ResolveNameOrRef(
2248 var_name, which_scopes)
2249 if not cell:
2250 return False # 'unset' builtin falls back on functions
2251 if cell.readonly:
2252 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2253
2254 with tagswitch(lval) as case:
2255 if case(sh_lvalue_e.Var): # unset x
2256 # Make variables in higher scopes visible.
2257 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2258 mylib.dict_erase(var_frame, cell_name)
2259
2260 # alternative that some shells use:
2261 # var_frame[cell_name].val = value.Undef
2262 # cell.exported = False
2263
2264 # This should never happen because we do recursive lookups of namerefs.
2265 assert not cell.nameref, cell
2266
2267 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2268 lval = cast(sh_lvalue.Indexed, UP_lval)
2269 # Note: Setting an entry to None and shifting entries are pretty
2270 # much the same in shell.
2271
2272 val = cell.val
2273 UP_val = val
2274 if val.tag() != value_e.BashArray:
2275 raise error.Runtime("%r isn't an array" % var_name)
2276
2277 val = cast(value.BashArray, UP_val)
2278 strs = val.strs
2279
2280 n = len(strs)
2281 last_index = n - 1
2282 index = lval.index
2283 if index < 0:
2284 index += n
2285
2286 if index == last_index:
2287 # Special case: The array SHORTENS if you unset from the end. You
2288 # can tell with a+=(3 4)
2289 strs.pop()
2290 elif 0 <= index and index < last_index:
2291 strs[index] = None
2292 else:
2293 # If it's not found, it's not an error. In other words, 'unset'
2294 # ensures that a value doesn't exist, regardless of whether it
2295 # existed. It's idempotent.
2296 # (Ousterhout specifically argues that the strict behavior was a
2297 # mistake for Tcl!)
2298 pass
2299
2300 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2301 lval = cast(sh_lvalue.Keyed, UP_lval)
2302
2303 val = cell.val
2304 UP_val = val
2305
2306 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2307 #if val.tag() != value_e.BashAssoc:
2308 # raise error.Runtime("%r isn't an associative array" % lval.name)
2309
2310 val = cast(value.BashAssoc, UP_val)
2311 mylib.dict_erase(val.d, lval.key)
2312
2313 else:
2314 raise AssertionError(lval)
2315
2316 return True
2317
2318 def ScopesForReading(self):
2319 # type: () -> scope_t
2320 """Read scope."""
2321 return (scope_e.Dynamic
2322 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2323
2324 def ScopesForWriting(self):
2325 # type: () -> scope_t
2326 """Write scope."""
2327 return (scope_e.Dynamic
2328 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2329
2330 def ClearFlag(self, name, flag):
2331 # type: (str, int) -> bool
2332 """Used for export -n.
2333
2334 We don't use SetValue() because even if rval is None, it will make an
2335 Undef value in a scope.
2336 """
2337 cell, var_frame = self._ResolveNameOnly(name, self.ScopesForReading())
2338 if cell:
2339 if flag & ClearExport:
2340 cell.exported = False
2341 if flag & ClearNameref:
2342 cell.nameref = False
2343 return True
2344 else:
2345 return False
2346
2347 def _FillWithExported(self, new_env):
2348 # type: (Dict[str, str]) -> None
2349
2350 # Search from globals up. Names higher on the stack will overwrite
2351 # names lower on the stack.
2352 for scope in self.var_stack:
2353 for name, cell in iteritems(scope):
2354 if cell.exported and cell.val.tag() == value_e.Str:
2355 val = cast(value.Str, cell.val)
2356 new_env[name] = val.s
2357
2358 def _FillEnvObj(self, new_env, env_object):
2359 # type: (Dict[str, str], Obj) -> None
2360
2361 # Do the LEAST visible parts first
2362 if env_object.prototype is not None:
2363 self._FillEnvObj(new_env, env_object.prototype)
2364
2365 # Overwrite with MOST visible parts
2366 for name, val in iteritems(env_object.d):
2367 if val.tag() != value_e.Str:
2368 continue
2369 new_env[name] = cast(value.Str, val).s
2370
2371 def GetEnv(self):
2372 # type: () -> Dict[str, str]
2373 """
2374 Get the environment that should be used for launching processes.
2375
2376 Note: This is run on every SimpleCommand. Should we have a dirty
2377 flag? We could notice these things:
2378
2379 - If an exported variable is changed
2380 - If the set of exported variables changes.
2381 """
2382 new_env = {} # type: Dict[str, str]
2383
2384 # Note: ysh:upgrade has both of these behaviors
2385
2386 # OSH: Consult exported vars
2387 if not self.exec_opts.no_exported():
2388 self._FillWithExported(new_env)
2389
2390 # YSH: Consult the ENV dict
2391 if self.exec_opts.env_obj():
2392 self._FillEnvObj(new_env, self.env_object)
2393
2394 return new_env
2395
2396 def VarNames(self):
2397 # type: () -> List[str]
2398 """For internal OSH completion and compgen -A variable.
2399
2400 NOTE: We could also add $? $$ etc.?
2401 """
2402 ret = [] # type: List[str]
2403 # Look up the stack, yielding all variables. Bash seems to do this.
2404 for scope in self.var_stack:
2405 for name in scope:
2406 ret.append(name)
2407 return ret
2408
2409 def VarNamesStartingWith(self, prefix):
2410 # type: (str) -> List[str]
2411 """For ${!prefix@}"""
2412 # Look up the stack, yielding all variables. Bash seems to do this.
2413 names = [] # type: List[str]
2414 for scope in self.var_stack:
2415 for name in scope:
2416 if name.startswith(prefix):
2417 names.append(name)
2418 return names
2419
2420 def GetAllVars(self):
2421 # type: () -> Dict[str, str]
2422 """Get all variables and their values, for 'set' builtin."""
2423 result = {} # type: Dict[str, str]
2424 for scope in self.var_stack:
2425 for name, cell in iteritems(scope):
2426 # TODO: Show other types?
2427 val = cell.val
2428 if val.tag() == value_e.Str:
2429 str_val = cast(value.Str, val)
2430 result[name] = str_val.s
2431 return result
2432
2433 def GetAllCells(self, which_scopes):
2434 # type: (scope_t) -> Dict[str, Cell]
2435 """Get all variables and their values, for 'set' builtin."""
2436 result = {} # type: Dict[str, Cell]
2437
2438 if which_scopes == scope_e.Dynamic:
2439 scopes = self.var_stack
2440 elif which_scopes == scope_e.LocalOnly:
2441 scopes = self.var_stack[-1:]
2442 elif which_scopes == scope_e.GlobalOnly:
2443 scopes = self.var_stack[0:1]
2444 elif which_scopes == scope_e.LocalOrGlobal:
2445 scopes = [self.var_stack[0]]
2446 if len(self.var_stack) > 1:
2447 scopes.append(self.var_stack[-1])
2448 else:
2449 raise AssertionError()
2450
2451 for scope in scopes:
2452 for name, cell in iteritems(scope):
2453 result[name] = cell
2454 return result
2455
2456 def SetRegexMatch(self, match):
2457 # type: (regex_match_t) -> None
2458 self.regex_match[-1] = match
2459
2460 def GetRegexMatch(self):
2461 # type: () -> regex_match_t
2462 return self.regex_match[-1]
2463
2464 def PushContextStack(self, context):
2465 # type: (Dict[str, value_t]) -> None
2466 self.ctx_stack.append(context)
2467
2468 def GetContext(self):
2469 # type: () -> Optional[Dict[str, value_t]]
2470 if len(self.ctx_stack):
2471 return self.ctx_stack[-1]
2472 return None
2473
2474 def PopContextStack(self):
2475 # type: () -> Dict[str, value_t]
2476 assert self.ctx_stack, "Empty context stack"
2477 return self.ctx_stack.pop()
2478
2479
2480def ValueIsInvokableObj(val):
2481 # type: (value_t) -> Tuple[Optional[value_t], Optional[Obj]]
2482 """
2483 Returns:
2484 (__invoke__ Proc or BuiltinProc, self Obj) if the value is invokable
2485 (None, None) otherwise
2486 """
2487 if val.tag() != value_e.Obj:
2488 return None, None
2489
2490 obj = cast(Obj, val)
2491 if not obj.prototype:
2492 return None, None
2493
2494 invoke_val = obj.prototype.d.get('__invoke__')
2495 if invoke_val is None:
2496 return None, None
2497
2498 # TODO: __invoke__ of wrong type could be fatal error?
2499 if invoke_val.tag() in (value_e.Proc, value_e.BuiltinProc):
2500 return invoke_val, obj
2501
2502 return None, None
2503
2504
2505def _AddNames(unique, frame):
2506 # type: (Dict[str, bool], Dict[str, Cell]) -> None
2507 for name in frame:
2508 val = frame[name].val
2509 if val.tag() == value_e.Proc:
2510 unique[name] = True
2511 proc, _ = ValueIsInvokableObj(val)
2512 if proc is not None:
2513 unique[name] = True
2514
2515
2516class Procs(object):
2517 """
2518 Terminology:
2519
2520 - invokable - these are INTERIOR
2521 - value.Proc - which can be shell function in __sh_funcs__ namespace, or
2522 YSH proc
2523 - value.Obj with __invoke__
2524 - exterior - external commands, extern builtin
2525
2526 Note: the YSH 'invoke' builtin can generalize YSH 'runproc' builtin, shell command/builtin,
2527 and also type / type -a
2528 """
2529
2530 def __init__(self, mem):
2531 # type: (Mem) -> None
2532 self.mem = mem
2533 self.sh_funcs = {} # type: Dict[str, value.Proc]
2534
2535 def DefineShellFunc(self, name, proc):
2536 # type: (str, value.Proc) -> None
2537 self.sh_funcs[name] = proc
2538
2539 def IsShellFunc(self, name):
2540 # type: (str) -> bool
2541 return name in self.sh_funcs
2542
2543 def GetShellFunc(self, name):
2544 # type: (str) -> Optional[value.Proc]
2545 return self.sh_funcs.get(name)
2546
2547 def EraseShellFunc(self, to_del):
2548 # type: (str) -> None
2549 """Undefine a sh-func with name `to_del`, if it exists."""
2550 mylib.dict_erase(self.sh_funcs, to_del)
2551
2552 def ShellFuncNames(self):
2553 # type: () -> List[str]
2554 """Returns a *sorted* list of all shell function names
2555
2556 Callers:
2557 declare -f -F
2558 """
2559 names = self.sh_funcs.keys()
2560 names.sort()
2561 return names
2562
2563 def DefineProc(self, name, proc):
2564 # type: (str, value.Proc) -> None
2565 """
2566 procs are defined in the local scope.
2567 """
2568 self.mem.var_stack[-1][name] = Cell(False, False, False, proc)
2569
2570 def IsProc(self, name):
2571 # type: (str) -> bool
2572
2573 maybe_proc = self.mem.GetValue(name)
2574 # Could be Undef
2575 return maybe_proc.tag() == value_e.Proc
2576
2577 def IsInvokableObj(self, name):
2578 # type: (str) -> bool
2579
2580 val = self.mem.GetValue(name)
2581 proc, _ = ValueIsInvokableObj(val)
2582 return proc is not None
2583
2584 def InvokableNames(self):
2585 # type: () -> List[str]
2586 """Returns a *sorted* list of all invokable names
2587
2588 Callers:
2589 complete -A function
2590 pp proc - should deprecate this
2591 """
2592 unique = NewDict() # type: Dict[str, bool]
2593 for name in self.sh_funcs:
2594 unique[name] = True
2595
2596 top_frame = self.mem.var_stack[-1]
2597 _AddNames(unique, top_frame)
2598
2599 global_frame = self.mem.var_stack[0]
2600 #log('%d %d', id(top_frame), id(global_frame))
2601 if global_frame is not top_frame:
2602 _AddNames(unique, global_frame)
2603
2604 #log('%s', unique)
2605
2606 names = unique.keys()
2607 names.sort()
2608
2609 return names
2610
2611 def GetInvokable(self, name):
2612 # type: (str) -> Tuple[Optional[value_t], Optional[Obj]]
2613 """Find a proc, invokable Obj, or sh-func, in that order
2614
2615 Callers:
2616 executor.py: to actually run
2617 meta_oils.py runproc lookup - this is not 'invoke', because it is
2618 INTERIOR shell functions, procs, invokable Obj
2619 """
2620 val = self.mem.GetValue(name)
2621
2622 if val.tag() == value_e.Proc:
2623 return cast(value.Proc, val), None
2624
2625 proc, self_val = ValueIsInvokableObj(val)
2626 if proc:
2627 return proc, self_val
2628
2629 if name in self.sh_funcs:
2630 return self.sh_funcs[name], None
2631
2632 return None, None
2633
2634
2635#
2636# Wrappers to Set Variables
2637#
2638
2639
2640def OshLanguageSetValue(mem, lval, val, flags=0):
2641 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2642 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2643
2644 That is, it respects shopt --unset dynamic_scope.
2645
2646 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2647 """
2648 which_scopes = mem.ScopesForWriting()
2649 mem.SetValue(lval, val, which_scopes, flags=flags)
2650
2651
2652def BuiltinSetValue(mem, lval, val):
2653 # type: (Mem, sh_lvalue_t, value_t) -> None
2654 """Equivalent of x=$y
2655
2656 Called by BuiltinSetString and BuiltinSetArray Used directly by
2657 printf -v because it can mutate an array
2658 """
2659 mem.SetValue(lval, val, mem.ScopesForWriting())
2660
2661
2662def BuiltinSetString(mem, name, s):
2663 # type: (Mem, str, str) -> None
2664 """Set a string by looking up the stack.
2665
2666 Used for 'read', 'getopts', completion builtins, etc.
2667 """
2668 assert isinstance(s, str)
2669 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2670
2671
2672def BuiltinSetArray(mem, name, a):
2673 # type: (Mem, str, List[str]) -> None
2674 """Set an array by looking up the stack.
2675
2676 Used by compadjust, read -a, etc.
2677 """
2678 assert isinstance(a, list)
2679 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2680
2681
2682def SetGlobalString(mem, name, s):
2683 # type: (Mem, str, str) -> None
2684 """Helper for completion, etc."""
2685 assert isinstance(s, str)
2686 val = value.Str(s)
2687 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2688
2689
2690def SetGlobalArray(mem, name, a):
2691 # type: (Mem, str, List[str]) -> None
2692 """Used by completion, shell initialization, etc."""
2693 assert isinstance(a, list)
2694 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2695
2696
2697def SetGlobalValue(mem, name, val):
2698 # type: (Mem, str, value_t) -> None
2699 """Helper for completion, etc."""
2700 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2701
2702
2703def SetLocalValue(mem, name, val):
2704 # type: (Mem, str, value_t) -> None
2705 """For 'use' builtin."""
2706 mem.SetNamed(location.LName(name), val, scope_e.LocalOnly)
2707
2708
2709def ExportGlobalString(mem, name, s):
2710 # type: (Mem, str, str) -> None
2711 """Helper for completion, $PWD, $OLDPWD, etc."""
2712 assert isinstance(s, str)
2713 val = value.Str(s)
2714 mem.SetNamed(location.LName(name),
2715 val,
2716 scope_e.GlobalOnly,
2717 flags=SetExport)
2718
2719
2720# TODO: remove in favor of EnvConfig
2721def SetStringInEnv(mem, var_name, s):
2722 # type: (Mem, str, str) -> None
2723 if mem.exec_opts.env_obj(): # e.g. ENV.YSH_HISTFILE
2724 mem.env_dict[var_name] = value.Str(s)
2725 else: # e.g. $YSH_HISTFILE
2726 SetGlobalString(mem, var_name, s)
2727
2728
2729#
2730# Wrappers to Get Variables
2731#
2732
2733
2734def DynamicGetVar(mem, name, which_scopes):
2735 # type: (Mem, str, scope_t) -> value_t
2736 """
2737 For getVar() and shvarGet()
2738 """
2739 val = mem.GetValue(name, which_scopes=which_scopes)
2740
2741 # Undef is not a user-visible value!
2742 # There's no way to distinguish null from undefined.
2743 if val.tag() == value_e.Undef:
2744 return value.Null
2745
2746 return val
2747
2748
2749def GetString(mem, name):
2750 # type: (Mem, str) -> str
2751 """Wrapper around GetValue().
2752
2753 Check that HOME, PWD, OLDPWD, etc. are strings. bash doesn't have these
2754 errors because ${array} is ${array[0]}.
2755
2756 TODO: We could also check this when you're storing variables?
2757 """
2758 val = mem.GetValue(name)
2759 UP_val = val
2760 with tagswitch(val) as case:
2761 if case(value_e.Undef):
2762 raise error.Runtime("$%s isn't defined" % name)
2763 elif case(value_e.Str):
2764 return cast(value.Str, UP_val).s
2765 else:
2766 # User would have to 'unset HOME' to get rid of exported flag
2767 raise error.Runtime("$%s should be a string" % name)
2768
2769
2770def MaybeString(mem, name):
2771 # type: (Mem, str) -> Optional[str]
2772 """Like GetString(), but doesn't throw an exception."""
2773 try:
2774 return GetString(mem, name)
2775 except error.Runtime:
2776 return None
2777
2778
2779def GetInteger(mem, name):
2780 # type: (Mem, str) -> int
2781 """For OPTIND variable used in getopts builtin.
2782
2783 TODO: it could be value.Int() ?
2784 """
2785 val = mem.GetValue(name)
2786 if val.tag() != value_e.Str:
2787 raise error.Runtime('$%s should be a string, got %s' %
2788 (name, ui.ValType(val)))
2789 s = cast(value.Str, val).s
2790 try:
2791 i = int(s)
2792 except ValueError:
2793 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2794 (name, s))
2795 return i
2796
2797
2798# vim: sw=4