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

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