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

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