OILS / core / state.py View on Github | oils.pub

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