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

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