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

2811 lines, 1433 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 # Whenever we're use-ing, the 'is-main' builtin will return 1 (false)
1021 self.to_restore = self.mem.is_main
1022 self.mem.is_main = False
1023
1024 def __enter__(self):
1025 # type: () -> None
1026 pass
1027
1028 def __exit__(self, type, value_, traceback):
1029 # type: (Any, Any, Any) -> None
1030
1031 self.mem.is_main = self.to_restore
1032
1033 assert len(self.mem.var_stack) == 1
1034 self.mem.var_stack[0] = self.saved_frame
1035
1036 # Now look in __export__ for the list of names to expose
1037
1038 cell = self.new_frame.get('__provide__')
1039 if cell is None:
1040 self.out_errors.append("Module is missing __provide__ List")
1041 return
1042
1043 provide_val = cell.val
1044 with tagswitch(provide_val) as case:
1045 if case(value_e.List):
1046 for val in cast(value.List, provide_val).items:
1047 if val.tag() == value_e.Str:
1048 name = cast(value.Str, val).s
1049
1050 cell = self.new_frame.get(name)
1051 if cell is None:
1052 self.out_errors.append(
1053 "Name %r was provided, but not defined" % name)
1054 continue
1055
1056 self.out_dict[name] = cell.val
1057 else:
1058 self.out_errors.append(
1059 "Expected Str in __provide__ List, got %s" %
1060 ui.ValType(val))
1061
1062 else:
1063 self.out_errors.append("__provide__ should be a List, got %s" %
1064 ui.ValType(provide_val))
1065
1066
1067class ctx_Eval(object):
1068 """Push temporary set of variables, $0, $1, $2, etc."""
1069
1070 def __init__(
1071 self,
1072 mem, # type: Mem
1073 dollar0, # type: Optional[str]
1074 pos_args, # type: Optional[List[str]]
1075 vars, # type: Optional[Dict[str, value_t]]
1076 ):
1077 # type: (...) -> None
1078 self.mem = mem
1079 self.dollar0 = dollar0
1080 self.pos_args = pos_args
1081 self.vars = vars
1082
1083 # $0 needs to have lexical scoping. So we store it with other locals.
1084 # As "0" cannot be parsed as an lvalue, we can safely store dollar0 there.
1085 if dollar0 is not None:
1086 #assert mem.GetValue("0", scope_e.LocalOnly).tag() == value_e.Undef
1087 #self.dollar0_lval = LeftName("0", loc.Missing)
1088 #mem.SetLocalName(self.dollar0_lval, value.Str(dollar0))
1089
1090 self.restore_dollar0 = self.mem.dollar0
1091 self.mem.dollar0 = dollar0
1092
1093 if pos_args is not None:
1094 mem.argv_stack.append(_ArgFrame(pos_args))
1095
1096 if vars is not None:
1097 self.restore = [] # type: List[Tuple[LeftName, value_t]]
1098 self._Push(vars)
1099
1100 def __enter__(self):
1101 # type: () -> None
1102 pass
1103
1104 def __exit__(self, type, value_, traceback):
1105 # type: (Any, Any, Any) -> None
1106 if self.vars is not None:
1107 self._Pop()
1108
1109 if self.pos_args is not None:
1110 self.mem.argv_stack.pop()
1111
1112 if self.dollar0 is not None:
1113 #self.mem.SetLocalName(self.dollar0_lval, value.Undef)
1114 self.mem.dollar0 = self.restore_dollar0
1115
1116 # Note: _Push and _Pop are separate methods because the C++ translation
1117 # doesn't like when they are inline in __init__ and __exit__.
1118 def _Push(self, vars):
1119 # type: (Dict[str, value_t]) -> None
1120 for name in vars:
1121 lval = location.LName(name)
1122 # LocalOnly because we are only overwriting the current scope
1123 old_val = self.mem.GetValue(name, scope_e.LocalOnly)
1124 self.restore.append((lval, old_val))
1125 self.mem.SetNamed(lval, vars[name], scope_e.LocalOnly)
1126
1127 def _Pop(self):
1128 # type: () -> None
1129 for lval, old_val in self.restore:
1130 if old_val.tag() == value_e.Undef:
1131 self.mem.Unset(lval, scope_e.LocalOnly)
1132 else:
1133 self.mem.SetNamed(lval, old_val, scope_e.LocalOnly)
1134
1135
1136def _FrameLookup(frame, name):
1137 # type: (Dict[str, Cell], str) -> Tuple[Optional[Cell], Dict[str, Cell]]
1138 """
1139 Look for a name in the frame, then recursively into the enclosing __E__
1140 frame, if it exists
1141 """
1142 cell = frame.get(name)
1143 if cell:
1144 return cell, frame
1145
1146 rear_cell = frame.get('__E__') # ctx_EnclosedFrame() sets this
1147 if rear_cell:
1148 rear_val = rear_cell.val
1149 assert rear_val, rear_val
1150 if rear_val.tag() == value_e.Frame:
1151 to_enclose = cast(value.Frame, rear_val).frame
1152 return _FrameLookup(to_enclose, name) # recursive call
1153
1154 return None, None
1155
1156
1157class Mem(object):
1158 """For storing variables.
1159
1160 Callers:
1161 User code: assigning and evaluating variables, in command context or
1162 arithmetic context.
1163 Completion engine: for COMP_WORDS, etc.
1164 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1165
1166 Modules: cmd_eval, word_eval, expr_eval, completion
1167 """
1168
1169 def __init__(
1170 self,
1171 dollar0, # type: str
1172 argv, # type: List[str]
1173 arena, # type: alloc.Arena
1174 debug_stack, # type: List[debug_frame_t]
1175 env_dict, # type: Dict[str, value_t]
1176 defaults=None, # type: Dict[str, value_t]
1177 ):
1178 # type: (...) -> None
1179 """
1180 Args:
1181 arena: currently unused
1182 """
1183 # circular dep initialized out of line
1184 self.exec_opts = None # type: optview.Exec
1185 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1186
1187 self.dollar0 = dollar0
1188 # If you only use YSH procs and funcs, this will remain at length 1.
1189 self.argv_stack = [_ArgFrame(argv)]
1190
1191 frame = NewDict() # type: Dict[str, Cell]
1192
1193 frame['ARGV'] = _MakeArgvCell(argv)
1194
1195 self.var_stack = [frame]
1196
1197 # The debug_stack isn't strictly necessary for execution. We use it
1198 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1199 # BASH_LINENO.
1200 self.debug_stack = debug_stack
1201
1202 self.env_dict = env_dict
1203 self.env_object = Obj(None, env_dict) # initial state
1204
1205 if defaults is None: # for unit tests only
1206 self.defaults = NewDict() # type: Dict[str, value_t]
1207 else:
1208 self.defaults = defaults
1209
1210 self.pwd = None # type: Optional[str]
1211 self.seconds_start = time_.time()
1212
1213 self.token_for_line = None # type: Optional[Token]
1214 self.loc_for_expr = loc.Missing # type: loc_t
1215
1216 self.last_arg = '' # $_ is initially empty, NOT unset
1217 self.line_num = value.Str('')
1218
1219 # Done ONCE on initialization
1220 self.root_pid = posix.getpid()
1221
1222 # TODO:
1223 # - These are REGISTERS mutated by user code.
1224 # - Call it self.reg_stack? with ctx_Registers
1225 # - push-registers builtin
1226 self.last_status = [0] # type: List[int] # a stack
1227 self.try_status = [0] # type: List[int] # a stack
1228 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1229 self.pipe_status = [[]] # type: List[List[int]] # stack
1230 self.process_sub_status = [[]] # type: List[List[int]] # stack
1231
1232 # A stack but NOT a register?
1233 self.this_dir = [] # type: List[str]
1234 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1235
1236 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1237
1238 self.running_debug_trap = False # set by ctx_DebugTrap()
1239 self.running_err_trap = False # set by ctx_ErrTrap
1240 self.is_main = True # we start out in main
1241
1242 # For the ctx builtin
1243 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1244
1245 self.builtins = NewDict() # type: Dict[str, value_t]
1246
1247 # Note: Python 2 and 3 have __builtins__
1248 # This is just for inspection
1249 builtins_module = Obj(None, self.builtins)
1250
1251 # Code in any module can see __builtins__
1252 self.builtins['__builtins__'] = builtins_module
1253
1254 self.did_ysh_env = False # only initialize ENV once per process
1255
1256 from core import sh_init
1257 self.env_config = sh_init.EnvConfig(self, defaults)
1258
1259 def __repr__(self):
1260 # type: () -> str
1261 parts = [] # type: List[str]
1262 parts.append('<Mem')
1263 for i, frame in enumerate(self.var_stack):
1264 parts.append(' -- %d --' % i)
1265 for n, v in frame.iteritems():
1266 parts.append(' %s %s' % (n, v))
1267 parts.append('>')
1268 return '\n'.join(parts) + '\n'
1269
1270 def AddBuiltin(self, name, val):
1271 # type: (str, value_t) -> None
1272 self.builtins[name] = val
1273
1274 def SetPwd(self, pwd):
1275 # type: (str) -> None
1276 """Used by builtins."""
1277 self.pwd = pwd
1278
1279 def ParsingChangesAllowed(self):
1280 # type: () -> bool
1281 """For checking that syntax options are only used at the top level."""
1282
1283 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1284 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1285 # ALLOW FOO=bar : pushes var_stack
1286 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1287
1288 def Dump(self):
1289 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1290 """Copy state before unwinding the stack."""
1291 var_stack = [
1292 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1293 ] # type: List[value_t]
1294 argv_stack = [value.Dict(frame.Dump())
1295 for frame in self.argv_stack] # type: List[value_t]
1296
1297 debug_stack = [] # type: List[value_t]
1298
1299 # Reuse these immutable objects
1300 t_call = value.Str('Call')
1301 t_source = value.Str('Source')
1302 t_main = value.Str('Main')
1303
1304 for frame in reversed(self.debug_stack):
1305 UP_frame = frame
1306 with tagswitch(frame) as case:
1307 if case(debug_frame_e.Call):
1308 frame = cast(debug_frame.Call, UP_frame)
1309 d = {
1310 'type': t_call,
1311 'func_name': value.Str(frame.func_name)
1312 } # type: Dict[str, value_t]
1313
1314 _AddCallToken(d, frame.call_tok)
1315 # TODO: Add def_tok
1316
1317 elif case(debug_frame_e.Source):
1318 frame = cast(debug_frame.Source, UP_frame)
1319 d = {
1320 'type': t_source,
1321 'source_name': value.Str(frame.source_name)
1322 }
1323 _AddCallToken(d, frame.call_tok)
1324
1325 elif case(debug_frame_e.Main):
1326 frame = cast(debug_frame.Main, UP_frame)
1327 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1328
1329 debug_stack.append(value.Dict(d))
1330 return var_stack, argv_stack, debug_stack
1331
1332 def SetLastArgument(self, s):
1333 # type: (str) -> None
1334 """For $_"""
1335 self.last_arg = s
1336
1337 def SetTokenForLine(self, tok):
1338 # type: (Token) -> None
1339 """Set a token to compute $LINENO
1340
1341 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1342 case, etc. -- anything that evaluates a word. Example: there was a bug
1343 with 'case $LINENO'
1344
1345 This token also used as a "least-specific" / fallback location for
1346 errors in ExecuteAndCatch().
1347
1348 Although most of that should be taken over by 'with ui.ctx_Location()`,
1349 for the errfmt.
1350 """
1351 if self.running_debug_trap or self.running_err_trap:
1352 return
1353
1354 #if tok.span_id == runtime.NO_SPID:
1355 # NOTE: This happened in the osh-runtime benchmark for yash.
1356 #log('Warning: span_id undefined in SetTokenForLine')
1357
1358 #import traceback
1359 #traceback.print_stack()
1360 #return
1361
1362 # Reset the expression fallback location on every line. It REFINES the
1363 # line-based fallback location.
1364 self.loc_for_expr = loc.Missing
1365
1366 self.token_for_line = tok
1367
1368 def SetLocationForExpr(self, blame_loc):
1369 # type: (loc_t) -> None
1370 """
1371 A more specific fallback location, like the $[ in
1372
1373 echo $[len(42)]
1374 """
1375 self.loc_for_expr = blame_loc
1376
1377 def GetFallbackLocation(self):
1378 # type: () -> loc_t
1379
1380 if self.loc_for_expr != loc.Missing: # more specific
1381 return self.loc_for_expr
1382
1383 if self.token_for_line: # less specific
1384 return self.token_for_line
1385
1386 return loc.Missing
1387
1388 #
1389 # Status Variable Stack (for isolating $PS1 and $PS4)
1390 #
1391
1392 def LastStatus(self):
1393 # type: () -> int
1394 return self.last_status[-1]
1395
1396 def TryStatus(self):
1397 # type: () -> int
1398 return self.try_status[-1]
1399
1400 def TryError(self):
1401 # type: () -> value.Dict
1402 return self.try_error[-1]
1403
1404 def PipeStatus(self):
1405 # type: () -> List[int]
1406 return self.pipe_status[-1]
1407
1408 def SetLastStatus(self, x):
1409 # type: (int) -> None
1410 self.last_status[-1] = x
1411
1412 def SetTryStatus(self, x):
1413 # type: (int) -> None
1414 self.try_status[-1] = x
1415
1416 def SetTryError(self, x):
1417 # type: (value.Dict) -> None
1418 self.try_error[-1] = x
1419
1420 def SetPipeStatus(self, x):
1421 # type: (List[int]) -> None
1422 self.pipe_status[-1] = x
1423
1424 def SetSimplePipeStatus(self, status):
1425 # type: (int) -> None
1426
1427 # Optimization to avoid allocations
1428 top = self.pipe_status[-1]
1429 if len(top) == 1:
1430 top[0] = status
1431 else:
1432 self.pipe_status[-1] = [status]
1433
1434 def SetProcessSubStatus(self, x):
1435 # type: (List[int]) -> None
1436 self.process_sub_status[-1] = x
1437
1438 #
1439 # Call Stack
1440 #
1441
1442 def PushCall(self, func_name, def_tok):
1443 # type: (str, Token) -> None
1444 """Push argv, var, and debug stack frames.
1445
1446 Currently used for proc and func calls. TODO: New func evaluator may
1447 not use it.
1448
1449 Args:
1450 def_tok: Token where proc or func was defined, used to compute
1451 BASH_SOURCE.
1452 """
1453 # self.token_for_line can be None?
1454 self.debug_stack.append(
1455 debug_frame.Call(self.token_for_line, def_tok, func_name))
1456
1457 def PopCall(self):
1458 # type: () -> None
1459 """
1460 Args:
1461 should_pop_argv_stack: Pass False if PushCall was given None for argv
1462 True for proc, False for func
1463 """
1464 self.debug_stack.pop()
1465
1466 def ShouldRunDebugTrap(self):
1467 # type: () -> bool
1468
1469 # TODO: RunLastPart of pipeline can disable this
1470
1471 # Don't recursively run DEBUG trap
1472 if self.running_debug_trap:
1473 return False
1474
1475 # Don't run it inside functions
1476 if len(self.var_stack) > 1:
1477 return False
1478
1479 return True
1480
1481 def IsGlobalScope(self):
1482 # type: () -> bool
1483 """
1484 local -g uses this, probably because bash does the wrong thing and
1485 prints LOCALS, not globals.
1486 """
1487 return len(self.var_stack) == 1
1488
1489 def InsideFunction(self):
1490 # type: () -> bool
1491 """For the ERR trap, and use builtin"""
1492
1493 # TODO: Should this be unified with ParsingChangesAllowed()? Slightly
1494 # different logic.
1495
1496 # Don't run it inside functions
1497 return len(self.var_stack) > 1
1498
1499 def GlobalFrame(self):
1500 # type: () -> Dict[str, Cell]
1501 """For defining the global scope of modules.
1502
1503 It's affected by ctx_ModuleEval()
1504 """
1505 return self.var_stack[0]
1506
1507 def CurrentFrame(self):
1508 # type: () -> Dict[str, Cell]
1509 """For attaching a stack frame to a value.Block"""
1510 return self.var_stack[-1]
1511
1512 def PushSource(self, source_name, argv):
1513 # type: (str, List[str]) -> None
1514 """ For 'source foo.sh 1 2 3' """
1515 if len(argv):
1516 self.argv_stack.append(_ArgFrame(argv))
1517
1518 # self.token_for_line can be None?
1519 self.debug_stack.append(
1520 debug_frame.Source(self.token_for_line, source_name))
1521
1522 def PopSource(self, argv):
1523 # type: (List[str]) -> None
1524 self.debug_stack.pop()
1525
1526 if len(argv):
1527 self.argv_stack.pop()
1528
1529 def PushTemp(self):
1530 # type: () -> None
1531 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1532
1533 Also for PS4 evaluation with more variables.
1534 """
1535 # We don't want the 'read' builtin to write to this frame!
1536 frame = NewDict() # type: Dict[str, Cell]
1537 self.var_stack.append(frame)
1538
1539 def PopTemp(self):
1540 # type: () -> None
1541 self.var_stack.pop()
1542
1543 def _BindEnvObj(self):
1544 # type: () -> None
1545 self.SetNamed(location.LName('ENV'), self.env_object,
1546 scope_e.GlobalOnly)
1547
1548 def MaybeInitEnvDict(self, environ):
1549 # type: (Dict[str, str]) -> None
1550 if self.did_ysh_env:
1551 return
1552
1553 for name, s in iteritems(environ):
1554 self.env_dict[name] = value.Str(s)
1555
1556 self._BindEnvObj()
1557 self.did_ysh_env = True
1558
1559 def PushEnvObj(self, bindings):
1560 # type: (Dict[str, value_t]) -> None
1561 """Push "bindings" as the MOST visible part of the ENV Obj
1562
1563 i.e. first() / propView()
1564 """
1565 self.env_object = Obj(self.env_object, bindings)
1566 self._BindEnvObj()
1567
1568 def PopEnvObj(self):
1569 # type: () -> None
1570 """Pop a Dict of bindings."""
1571 self.env_object = self.env_object.prototype
1572 if self.env_object is None:
1573 # Note: there isn't a way to hit this now, but let's be defensive.
1574 # See test case in spec/ysh-env.test.sh.
1575 e_die('PopEnvObj: env.prototype is null', loc.Missing)
1576
1577 self._BindEnvObj()
1578
1579 #
1580 # Argv
1581 #
1582
1583 def Shift(self, n):
1584 # type: (int) -> int
1585 frame = self.argv_stack[-1]
1586 num_args = len(frame.argv)
1587
1588 if (frame.num_shifted + n) <= num_args:
1589 frame.num_shifted += n
1590 return 0 # success
1591 else:
1592 return 1 # silent error
1593
1594 def GetArg0(self):
1595 # type: () -> value.Str
1596 """Like GetArgNum(0) but with a more specific type."""
1597 return value.Str(self.dollar0)
1598
1599 def GetArgNum(self, arg_num):
1600 # type: (int) -> value_t
1601 if arg_num == 0:
1602 # Disabled
1603 if 0:
1604 # Problem: Doesn't obey enclosing frame?
1605 # Yeah it needs FrameLookup
1606 cell, _ = _FrameLookup(self.var_stack[-1], '0')
1607 if cell is not None:
1608 val = cell.val
1609 if val.tag() != value_e.Undef:
1610 return val
1611
1612 return value.Str(self.dollar0)
1613
1614 return self.argv_stack[-1].GetArgNum(arg_num)
1615
1616 def GetArgv(self):
1617 # type: () -> List[str]
1618 """For $* and $@."""
1619 return self.argv_stack[-1].GetArgv()
1620
1621 def SetArgv(self, argv):
1622 # type: (List[str]) -> None
1623 """For set -- 1 2 3."""
1624 # from set -- 1 2 3
1625 self.argv_stack[-1].SetArgv(argv)
1626
1627 #
1628 # Special Vars
1629 #
1630
1631 def GetSpecialVar(self, op_id):
1632 # type: (int) -> value_t
1633 if op_id == Id.VSub_Bang: # $!
1634 n = self.last_bg_pid
1635 if n == -1:
1636 return value.Undef # could be an error
1637
1638 elif op_id == Id.VSub_QMark: # $?
1639 # External commands need WIFEXITED test. What about subshells?
1640 n = self.last_status[-1]
1641
1642 elif op_id == Id.VSub_Pound: # $#
1643 n = self.argv_stack[-1].GetNumArgs()
1644
1645 elif op_id == Id.VSub_Dollar: # $$
1646 n = self.root_pid
1647
1648 else:
1649 raise NotImplementedError(op_id)
1650
1651 return value.Str(str(n))
1652
1653 #
1654 # Named Vars
1655 #
1656
1657 def _ResolveNameOnly(self, name, which_scopes):
1658 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1659 """Helper for getting and setting variable.
1660
1661 Returns:
1662 cell: The cell corresponding to looking up 'name' with the given mode, or
1663 None if it's not found.
1664 var_frame: The frame it should be set to or deleted from.
1665 """
1666 if which_scopes == scope_e.Dynamic:
1667 for i in xrange(len(self.var_stack) - 1, -1, -1):
1668 var_frame = self.var_stack[i]
1669 cell, result_frame = _FrameLookup(var_frame, name)
1670 if cell:
1671 return cell, result_frame
1672 return None, self.var_stack[0] # set in global var_frame
1673
1674 if which_scopes == scope_e.LocalOnly:
1675 var_frame = self.var_stack[-1]
1676 cell, result_frame = _FrameLookup(var_frame, name)
1677 if cell:
1678 return cell, result_frame
1679 return None, var_frame
1680
1681 if which_scopes == scope_e.GlobalOnly:
1682 var_frame = self.var_stack[0]
1683 cell, result_frame = _FrameLookup(var_frame, name)
1684 if cell:
1685 return cell, result_frame
1686
1687 return None, var_frame
1688
1689 if which_scopes == scope_e.LocalOrGlobal:
1690 # Local
1691 var_frame = self.var_stack[-1]
1692 cell, result_frame = _FrameLookup(var_frame, name)
1693 if cell:
1694 return cell, result_frame
1695
1696 # Global
1697 var_frame = self.var_stack[0]
1698 cell, result_frame = _FrameLookup(var_frame, name)
1699 if cell:
1700 return cell, result_frame
1701
1702 return None, var_frame
1703
1704 raise AssertionError()
1705
1706 def _ResolveNameOrRef(
1707 self,
1708 name, # type: str
1709 which_scopes, # type: scope_t
1710 ref_trail=None, # type: Optional[List[str]]
1711 ):
1712 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1713 """Look up a cell and namespace, but respect the nameref flag.
1714
1715 Resolving namerefs does RECURSIVE calls.
1716 """
1717 cell, var_frame = self._ResolveNameOnly(name, which_scopes)
1718
1719 if cell is None or not cell.nameref:
1720 return cell, var_frame, name # not a nameref
1721
1722 val = cell.val
1723 UP_val = val
1724 with tagswitch(val) as case:
1725 if case(value_e.Undef):
1726 # This is 'local -n undef_ref', which is kind of useless, because the
1727 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1728 # references themselves with local -n ref=new.
1729 if self.exec_opts.strict_nameref():
1730 e_die('nameref %r is undefined' % name)
1731 else:
1732 return cell, var_frame, name # fallback
1733
1734 elif case(value_e.Str):
1735 val = cast(value.Str, UP_val)
1736 new_name = val.s
1737
1738 else:
1739 # SetValue() protects the invariant that nameref is Undef or Str
1740 raise AssertionError(val.tag())
1741
1742 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1743 # 'printf -v' with MakeArithParser
1744 if not match.IsValidVarName(new_name):
1745 # e.g. '#' or '1' or ''
1746 if self.exec_opts.strict_nameref():
1747 e_die('nameref %r contains invalid variable name %r' %
1748 (name, new_name))
1749 else:
1750 # Bash has this odd behavior of clearing the nameref bit when
1751 # ref=#invalid#. strict_nameref avoids it.
1752 cell.nameref = False
1753 return cell, var_frame, name # fallback
1754
1755 # Check for circular namerefs.
1756 if ref_trail is None:
1757 ref_trail = [name]
1758 else:
1759 if new_name in ref_trail:
1760 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1761 ref_trail.append(new_name)
1762
1763 # 'declare -n' uses dynamic scope.
1764 cell, var_frame, cell_name = self._ResolveNameOrRef(
1765 new_name, scope_e.Dynamic, ref_trail=ref_trail)
1766 return cell, var_frame, cell_name
1767
1768 def IsBashAssoc(self, name):
1769 # type: (str) -> bool
1770 """Returns whether a name resolve to a cell with an associative array.
1771
1772 We need to know this to evaluate the index expression properly
1773 -- should it be coerced to an integer or not?
1774 """
1775 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1776 # foo=([key]=value)
1777 return cell is not None and cell.val.tag() == value_e.BashAssoc
1778
1779 def SetPlace(self, place, val, blame_loc):
1780 # type: (value.Place, value_t, loc_t) -> None
1781
1782 yval = place.lval
1783 UP_yval = yval
1784 with tagswitch(yval) as case:
1785 if case(y_lvalue_e.Local):
1786 yval = cast(LeftName, UP_yval)
1787
1788 if 0:
1789 # Check that the frame is still alive
1790 # Note: Disabled because it doesn't work with modules. the
1791 # Place captures a frame in def-test.ysh, which we want to
1792 # mutate while Dict is executing in the module_frame for
1793 # def.ysh. See ctx_ModuleEval
1794 found = False
1795 for i in xrange(len(self.var_stack) - 1, -1, -1):
1796 frame = self.var_stack[i]
1797 if frame is place.frame:
1798 found = True
1799 #log('FOUND %s', found)
1800 break
1801 if not found:
1802 e_die(
1803 "Can't assign to place that's no longer on the call stack.",
1804 blame_loc)
1805
1806 frame = place.frame
1807 cell = frame.get(yval.name)
1808 if cell is None:
1809 cell = Cell(False, False, False, val)
1810 frame[yval.name] = cell
1811 else:
1812 cell.val = val
1813
1814 elif case(y_lvalue_e.Container):
1815 e_die('Container place not implemented', blame_loc)
1816
1817 else:
1818 raise AssertionError()
1819
1820 def SetLocalName(self, lval, val):
1821 # type: (LeftName, value_t) -> None
1822 """
1823 Set a name in the local scope - used for func/proc param binding, etc.
1824 """
1825
1826 # Equivalent to
1827 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1828 var_frame = self.var_stack[-1]
1829 cell = var_frame.get(lval.name)
1830
1831 if cell:
1832 if cell.readonly:
1833 e_die("Can't assign to readonly value %r" % lval.name,
1834 lval.blame_loc)
1835 cell.val = val # Mutate value_t
1836 else:
1837 cell = Cell(False, False, False, val)
1838 var_frame[lval.name] = cell
1839
1840 def SetNamed(self, lval, val, which_scopes, flags=0):
1841 # type: (LeftName, value_t, scope_t, int) -> None
1842 if flags & SetNameref or flags & ClearNameref:
1843 # declare -n ref=x # refers to the ref itself
1844 cell, var_frame = self._ResolveNameOnly(lval.name, which_scopes)
1845 cell_name = lval.name
1846 else:
1847 # ref=x # mutates THROUGH the reference
1848
1849 # Note on how to implement declare -n ref='a[42]'
1850 # 1. Call _ResolveNameOnly()
1851 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1852 # BracedVarSub
1853 # 3. Turn BracedVarSub into an sh_lvalue, and call
1854 # self.unsafe_arith.SetValue() wrapper with ref_trail
1855 cell, var_frame, cell_name = self._ResolveNameOrRef(
1856 lval.name, which_scopes)
1857
1858 if cell:
1859 # Clear before checking readonly bit.
1860 # NOTE: Could be cell.flags &= flag_clear_mask
1861 if flags & ClearExport:
1862 cell.exported = False
1863 if flags & ClearReadOnly:
1864 cell.readonly = False
1865 if flags & ClearNameref:
1866 cell.nameref = False
1867
1868 if val is not None: # e.g. declare -rx existing
1869 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1870 # But that's true for 'readonly' too, and hoisting it makes more
1871 # sense anyway.
1872 if cell.readonly:
1873 e_die("Can't assign to readonly value %r" % lval.name,
1874 lval.blame_loc)
1875 cell.val = val # CHANGE VAL
1876
1877 # NOTE: Could be cell.flags |= flag_set_mask
1878 if flags & SetExport:
1879 cell.exported = True
1880 if flags & SetReadOnly:
1881 cell.readonly = True
1882 if flags & SetNameref:
1883 cell.nameref = True
1884
1885 else:
1886 if val is None: # declare -rx nonexistent
1887 # set -o nounset; local foo; echo $foo # It's still undefined!
1888 val = value.Undef # export foo, readonly foo
1889
1890 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1891 bool(flags & SetNameref), val)
1892 var_frame[cell_name] = cell
1893
1894 # Maintain invariant that only strings and undefined cells can be
1895 # exported.
1896 assert cell.val is not None, cell
1897
1898 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1899 if cell.exported:
1900 if self.exec_opts.strict_array():
1901 e_die("Only strings can be exported (strict_array)",
1902 lval.blame_loc)
1903 if cell.nameref:
1904 e_die("nameref must be a string", lval.blame_loc)
1905
1906 def SetValue(self, lval, val, which_scopes, flags=0):
1907 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1908 """
1909 Args:
1910 lval: sh_lvalue
1911 val: value, or None if only changing flags
1912 which_scopes:
1913 Local | Global | Dynamic - for builtins, PWD, etc.
1914 flags: packed pair (keyword_id, bit mask of set/clear flags)
1915
1916 Note: in bash, PWD=/ changes the directory. But not in dash.
1917 """
1918 # STRICTNESS / SANENESS:
1919 #
1920 # 1) Don't create arrays automatically, e.g. a[1000]=x
1921 # 2) Never change types? yeah I think that's a good idea, at least for YSH
1922 # (not sh, for compatibility). set -o strict_types or something. That
1923 # means arrays have to be initialized with let arr = [], which is fine.
1924 # This helps with stuff like IFS. It starts off as a string, and assigning
1925 # it to a list is an error. I guess you will have to turn this no for
1926 # bash?
1927 #
1928 # TODO:
1929 # - COMPUTED vars can't be set
1930 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1931 # readonly.
1932 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1933 # - Other validity: $HOME could be checked for existence
1934
1935 UP_lval = lval
1936 with tagswitch(lval) as case:
1937 if case(sh_lvalue_e.Var):
1938 lval = cast(LeftName, UP_lval)
1939
1940 self.SetNamed(lval, val, which_scopes, flags=flags)
1941
1942 elif case(sh_lvalue_e.Indexed):
1943 lval = cast(sh_lvalue.Indexed, UP_lval)
1944
1945 # There is no syntax 'declare a[x]'
1946 assert val is not None, val
1947
1948 # TODO: relax this for YSH
1949 assert val.tag() == value_e.Str, val
1950 rval = cast(value.Str, val)
1951
1952 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1953 left_loc = lval.blame_loc
1954
1955 # bash/mksh have annoying behavior of letting you do LHS assignment to
1956 # Undef, which then turns into an INDEXED array. (Undef means that set
1957 # -o nounset fails.)
1958 cell, var_frame, _ = self._ResolveNameOrRef(
1959 lval.name, which_scopes)
1960 if not cell:
1961 self._BindNewArrayWithEntry(var_frame, lval, rval, flags)
1962 return
1963
1964 if cell.readonly:
1965 e_die("Can't assign to readonly array", left_loc)
1966
1967 UP_cell_val = cell.val
1968 # undef[0]=y is allowed
1969 with tagswitch(UP_cell_val) as case2:
1970 if case2(value_e.Undef):
1971 self._BindNewArrayWithEntry(var_frame, lval, rval,
1972 flags)
1973 return
1974
1975 elif case2(value_e.Str):
1976 # s=x
1977 # s[1]=y # invalid
1978 e_die("Can't assign to items in a string", left_loc)
1979
1980 elif case2(value_e.BashArray):
1981 cell_val = cast(value.BashArray, UP_cell_val)
1982 error_code = bash_impl.BashArray_SetElement(
1983 cell_val, lval.index, rval.s)
1984 if error_code == error_code_e.IndexOutOfRange:
1985 n = bash_impl.BashArray_Length(cell_val)
1986 e_die(
1987 "Index %d is out of bounds for array of length %d"
1988 % (lval.index, n), left_loc)
1989 return
1990
1991 elif case2(value_e.SparseArray):
1992 lhs_sp = cast(value.SparseArray, UP_cell_val)
1993 error_code = bash_impl.SparseArray_SetElement(
1994 lhs_sp, mops.IntWiden(lval.index), rval.s)
1995 if error_code == error_code_e.IndexOutOfRange:
1996 n_big = bash_impl.SparseArray_Length(lhs_sp)
1997 e_die(
1998 "Index %d is out of bounds for array of length %s"
1999 % (lval.index, mops.ToStr(n_big)), left_loc)
2000 return
2001
2002 # This could be an object, eggex object, etc. It won't be
2003 # BashAssoc shouldn because we query IsBashAssoc before evaluating
2004 # sh_lhs. Could conslidate with s[i] case above
2005 e_die(
2006 "Value of type %s can't be indexed" % ui.ValType(cell.val),
2007 left_loc)
2008
2009 elif case(sh_lvalue_e.Keyed):
2010 lval = cast(sh_lvalue.Keyed, UP_lval)
2011
2012 # There is no syntax 'declare A["x"]'
2013 assert val is not None, val
2014 assert val.tag() == value_e.Str, val
2015 rval = cast(value.Str, val)
2016
2017 left_loc = lval.blame_loc
2018
2019 cell, var_frame, _ = self._ResolveNameOrRef(
2020 lval.name, which_scopes)
2021 if cell.readonly:
2022 e_die("Can't assign to readonly associative array",
2023 left_loc)
2024
2025 # We already looked it up before making the sh_lvalue
2026 assert cell.val.tag() == value_e.BashAssoc, cell
2027 cell_val2 = cast(value.BashAssoc, cell.val)
2028 bash_impl.BashAssoc_SetElement(cell_val2, lval.key, rval.s)
2029
2030 else:
2031 raise AssertionError(lval.tag())
2032
2033 def _BindNewArrayWithEntry(self, var_frame, lval, val, flags):
2034 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
2035 """Fill 'var_frame' with a new indexed array entry."""
2036 no_str = None # type: Optional[str]
2037 items = [no_str] * lval.index
2038 items.append(val.s)
2039 new_value = value.BashArray(items)
2040
2041 # arrays can't be exported; can't have BashAssoc flag
2042 readonly = bool(flags & SetReadOnly)
2043 var_frame[lval.name] = Cell(False, readonly, False, new_value)
2044
2045 def InternalSetGlobal(self, name, new_val):
2046 # type: (str, value_t) -> None
2047 """For setting read-only globals internally.
2048
2049 Args:
2050 name: string (not Lhs)
2051 new_val: value
2052
2053 The variable must already exist.
2054
2055 Use case: SHELLOPTS.
2056 """
2057 cell = self.var_stack[0][name]
2058 cell.val = new_val
2059
2060 def GetValue(self, name, which_scopes=scope_e.Shopt):
2061 # type: (str, scope_t) -> value_t
2062 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
2063 assert isinstance(name, str), name
2064
2065 if which_scopes == scope_e.Shopt:
2066 which_scopes = self.ScopesForReading()
2067
2068 with str_switch(name) as case:
2069 # "Registers"
2070 if case('_status'): # deprecated in favor of _error.code
2071 return num.ToBig(self.TryStatus())
2072
2073 elif case('_error'):
2074 return self.TryError()
2075
2076 elif case('_this_dir'):
2077 if len(self.this_dir) == 0:
2078 # e.g. osh -c '' doesn't have it set
2079 # Should we give a custom error here?
2080 # If you're at the interactive shell, 'source mymodule.ysh' will still
2081 # work because 'source' sets it.
2082 return value.Undef
2083 else:
2084 return value.Str(self.this_dir[-1]) # top of stack
2085
2086 elif case('PIPESTATUS'):
2087 strs2 = [str(i)
2088 for i in self.pipe_status[-1]] # type: List[str]
2089 return value.BashArray(strs2)
2090
2091 elif case('_pipeline_status'):
2092 items = [num.ToBig(i)
2093 for i in self.pipe_status[-1]] # type: List[value_t]
2094 return value.List(items)
2095
2096 elif case('_process_sub_status'): # YSH naming convention
2097 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
2098 return value.List(items)
2099
2100 elif case('BASH_REMATCH'):
2101 top_match = self.regex_match[-1]
2102 with tagswitch(top_match) as case2:
2103 if case2(regex_match_e.No):
2104 groups = [] # type: List[str]
2105 elif case2(regex_match_e.Yes):
2106 m = cast(RegexMatch, top_match)
2107 groups = util.RegexGroupStrings(m.s, m.indices)
2108 return value.BashArray(groups)
2109
2110 # Do lookup of system globals before looking at user variables. Note: we
2111 # could optimize this at compile-time like $?. That would break
2112 # ${!varref}, but it's already broken for $?.
2113
2114 elif case('FUNCNAME'):
2115 # bash wants it in reverse order. This is a little inefficient but we're
2116 # not depending on deque().
2117 strs = [] # type: List[str]
2118 for frame in reversed(self.debug_stack):
2119 UP_frame = frame
2120 with tagswitch(frame) as case2:
2121 if case2(debug_frame_e.Call):
2122 frame = cast(debug_frame.Call, UP_frame)
2123 strs.append(frame.func_name)
2124
2125 elif case2(debug_frame_e.Source):
2126 # bash doesn't tell you the filename sourced
2127 strs.append('source')
2128
2129 elif case2(debug_frame_e.Main):
2130 strs.append('main') # also bash behavior
2131
2132 return value.BashArray(strs) # TODO: Reuse this object too?
2133
2134 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2135 #
2136 # ${BASH_LINENO[$i]} is the line number in the source file
2137 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2138 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2139 #
2140 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2141
2142 elif case('BASH_SOURCE'):
2143 strs = []
2144 for frame in reversed(self.debug_stack):
2145 UP_frame = frame
2146 with tagswitch(frame) as case2:
2147 if case2(debug_frame_e.Call):
2148 frame = cast(debug_frame.Call, UP_frame)
2149
2150 # Weird bash behavior
2151 assert frame.def_tok.line is not None
2152 source_str = ui.GetLineSourceString(
2153 frame.def_tok.line)
2154 strs.append(source_str)
2155
2156 elif case2(debug_frame_e.Source):
2157 frame = cast(debug_frame.Source, UP_frame)
2158 # Is this right?
2159 strs.append(frame.source_name)
2160
2161 elif case2(debug_frame_e.Main):
2162 frame = cast(debug_frame.Main, UP_frame)
2163 strs.append(frame.dollar0)
2164
2165 return value.BashArray(strs) # TODO: Reuse this object too?
2166
2167 elif case('BASH_LINENO'):
2168 strs = []
2169 for frame in reversed(self.debug_stack):
2170 UP_frame = frame
2171 with tagswitch(frame) as case2:
2172 if case2(debug_frame_e.Call):
2173 frame = cast(debug_frame.Call, UP_frame)
2174 strs.append(_LineNumber(frame.call_tok))
2175
2176 elif case2(debug_frame_e.Source):
2177 frame = cast(debug_frame.Source, UP_frame)
2178 strs.append(_LineNumber(frame.call_tok))
2179
2180 elif case2(debug_frame_e.Main):
2181 # Bash does this to line up with 'main'
2182 strs.append('0')
2183
2184 return value.BashArray(strs) # TODO: Reuse this object too?
2185
2186 elif case('LINENO'):
2187 assert self.token_for_line is not None
2188 # Reuse object with mutation
2189 # TODO: maybe use interned GetLineNumStr?
2190 self.line_num.s = str(self.token_for_line.line.line_num)
2191 return self.line_num
2192
2193 elif case('BASHPID'): # TODO: YSH io->getpid()
2194 return value.Str(str(posix.getpid()))
2195
2196 elif case('_'):
2197 return value.Str(self.last_arg)
2198
2199 elif case('SECONDS'):
2200 f = time_.time() - self.seconds_start
2201 ok, big_int = mops.FromFloat(f)
2202 assert ok, f # should never be NAN or INFINITY
2203 return value.Int(big_int)
2204
2205 else:
2206 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2207 # fix this:
2208 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2209 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2210 # We still need a ref_trail to detect cycles.
2211 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2212 if cell:
2213 return cell.val
2214
2215 builtin_val = self.builtins.get(name)
2216 if builtin_val:
2217 return builtin_val
2218
2219 # TODO: Can look in the builtins module, which is a value.Obj
2220 return value.Undef
2221
2222 def GetCell(self, name, which_scopes=scope_e.Shopt):
2223 # type: (str, scope_t) -> Cell
2224 """Get both the value and flags.
2225
2226 Usages:
2227 - the 'pp' builtin.
2228 - declare -p
2229 - ${x@a}
2230 - to test of 'TZ' is exported in printf? Why?
2231
2232 Note: consulting __builtins__ doesn't see necessary for any of these
2233 """
2234 if which_scopes == scope_e.Shopt:
2235 which_scopes = self.ScopesForReading()
2236
2237 cell, _ = self._ResolveNameOnly(name, which_scopes)
2238 return cell
2239
2240 def Unset(self, lval, which_scopes):
2241 # type: (sh_lvalue_t, scope_t) -> bool
2242 """
2243 Returns:
2244 Whether the cell was found.
2245 """
2246 # TODO: Refactor sh_lvalue type to avoid this
2247 UP_lval = lval
2248
2249 with tagswitch(lval) as case:
2250 if case(sh_lvalue_e.Var): # unset x
2251 lval = cast(LeftName, UP_lval)
2252 var_name = lval.name
2253 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2254 lval = cast(sh_lvalue.Indexed, UP_lval)
2255 var_name = lval.name
2256 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2257 lval = cast(sh_lvalue.Keyed, UP_lval)
2258 var_name = lval.name
2259 else:
2260 raise AssertionError()
2261
2262 if which_scopes == scope_e.Shopt:
2263 which_scopes = self.ScopesForWriting()
2264
2265 cell, var_frame, cell_name = self._ResolveNameOrRef(
2266 var_name, which_scopes)
2267 if not cell:
2268 return False # 'unset' builtin falls back on functions
2269 if cell.readonly:
2270 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2271
2272 with tagswitch(lval) as case:
2273 if case(sh_lvalue_e.Var): # unset x
2274 # Make variables in higher scopes visible.
2275 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2276 mylib.dict_erase(var_frame, cell_name)
2277
2278 # alternative that some shells use:
2279 # var_frame[cell_name].val = value.Undef
2280 # cell.exported = False
2281
2282 # This should never happen because we do recursive lookups of namerefs.
2283 assert not cell.nameref, cell
2284
2285 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2286 lval = cast(sh_lvalue.Indexed, UP_lval)
2287 # Note: Setting an entry to None and shifting entries are pretty
2288 # much the same in shell.
2289
2290 val = cell.val
2291 UP_val = val
2292 if val.tag() == value_e.BashArray:
2293 val = cast(value.BashArray, UP_val)
2294 error_code = bash_impl.BashArray_UnsetElement(
2295 val, lval.index)
2296 if error_code == error_code_e.IndexOutOfRange:
2297 n = bash_impl.BashArray_Length(val)
2298 raise error.Runtime(
2299 "%s[%d]: Index is out of bounds for array of length %d"
2300 % (var_name, lval.index, n))
2301 elif val.tag() == value_e.SparseArray:
2302 val = cast(value.SparseArray, UP_val)
2303 error_code = bash_impl.SparseArray_UnsetElement(
2304 val, mops.IntWiden(lval.index))
2305 if error_code == error_code_e.IndexOutOfRange:
2306 big_length = bash_impl.SparseArray_Length(val)
2307 raise error.Runtime(
2308 "%s[%d]: Index is out of bounds for array of length %s"
2309 % (var_name, lval.index, mops.ToStr(big_length)))
2310 else:
2311 raise error.Runtime("%r isn't an array" % var_name)
2312
2313 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2314 lval = cast(sh_lvalue.Keyed, UP_lval)
2315
2316 val = cell.val
2317 UP_val = val
2318
2319 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2320 #if val.tag() != value_e.BashAssoc:
2321 # raise error.Runtime("%r isn't an associative array" % lval.name)
2322
2323 val = cast(value.BashAssoc, UP_val)
2324 bash_impl.BashAssoc_UnsetElement(val, lval.key)
2325
2326 else:
2327 raise AssertionError(lval)
2328
2329 return True
2330
2331 def ScopesForReading(self):
2332 # type: () -> scope_t
2333 """Read scope."""
2334 return (scope_e.Dynamic
2335 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2336
2337 def ScopesForWriting(self):
2338 # type: () -> scope_t
2339 """Write scope."""
2340 return (scope_e.Dynamic
2341 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2342
2343 def ClearFlag(self, name, flag):
2344 # type: (str, int) -> bool
2345 """Used for export -n.
2346
2347 We don't use SetValue() because even if rval is None, it will make an
2348 Undef value in a scope.
2349 """
2350 cell, var_frame = self._ResolveNameOnly(name, self.ScopesForReading())
2351 if cell:
2352 if flag & ClearExport:
2353 cell.exported = False
2354 if flag & ClearNameref:
2355 cell.nameref = False
2356 return True
2357 else:
2358 return False
2359
2360 def _FillWithExported(self, new_env):
2361 # type: (Dict[str, str]) -> None
2362
2363 # Search from globals up. Names higher on the stack will overwrite
2364 # names lower on the stack.
2365 for scope in self.var_stack:
2366 for name, cell in iteritems(scope):
2367 if cell.exported and cell.val.tag() == value_e.Str:
2368 val = cast(value.Str, cell.val)
2369 new_env[name] = val.s
2370
2371 def _FillEnvObj(self, new_env, env_object):
2372 # type: (Dict[str, str], Obj) -> None
2373
2374 # Do the LEAST visible parts first
2375 if env_object.prototype is not None:
2376 self._FillEnvObj(new_env, env_object.prototype)
2377
2378 # Overwrite with MOST visible parts
2379 for name, val in iteritems(env_object.d):
2380 if val.tag() != value_e.Str:
2381 continue
2382 new_env[name] = cast(value.Str, val).s
2383
2384 def GetEnv(self):
2385 # type: () -> Dict[str, str]
2386 """
2387 Get the environment that should be used for launching processes.
2388
2389 Note: This is run on every SimpleCommand. Should we have a dirty
2390 flag? We could notice these things:
2391
2392 - If an exported variable is changed
2393 - If the set of exported variables changes.
2394 """
2395 new_env = {} # type: Dict[str, str]
2396
2397 # Note: ysh:upgrade has both of these behaviors
2398
2399 # OSH: Consult exported vars
2400 if not self.exec_opts.no_exported():
2401 self._FillWithExported(new_env)
2402
2403 # YSH: Consult the ENV dict
2404 if self.exec_opts.env_obj():
2405 self._FillEnvObj(new_env, self.env_object)
2406
2407 return new_env
2408
2409 def VarNames(self):
2410 # type: () -> List[str]
2411 """For internal OSH completion and compgen -A variable.
2412
2413 NOTE: We could also add $? $$ etc.?
2414 """
2415 ret = [] # type: List[str]
2416 # Look up the stack, yielding all variables. Bash seems to do this.
2417 for scope in self.var_stack:
2418 for name in scope:
2419 ret.append(name)
2420 return ret
2421
2422 def VarNamesStartingWith(self, prefix):
2423 # type: (str) -> List[str]
2424 """For ${!prefix@}"""
2425 # Look up the stack, yielding all variables. Bash seems to do this.
2426 names = [] # type: List[str]
2427 for scope in self.var_stack:
2428 for name in scope:
2429 if name.startswith(prefix):
2430 names.append(name)
2431 return names
2432
2433 def GetAllVars(self):
2434 # type: () -> Dict[str, str]
2435 """Get all variables and their values, for 'set' builtin."""
2436 result = {} # type: Dict[str, str]
2437 for scope in self.var_stack:
2438 for name, cell in iteritems(scope):
2439 # TODO: Show other types?
2440 val = cell.val
2441 if val.tag() == value_e.Str:
2442 str_val = cast(value.Str, val)
2443 result[name] = str_val.s
2444 return result
2445
2446 def GetAllCells(self, which_scopes):
2447 # type: (scope_t) -> Dict[str, Cell]
2448 """Get all variables and their values, for 'set' builtin."""
2449 result = {} # type: Dict[str, Cell]
2450
2451 if which_scopes == scope_e.Dynamic:
2452 scopes = self.var_stack
2453 elif which_scopes == scope_e.LocalOnly:
2454 scopes = self.var_stack[-1:]
2455 elif which_scopes == scope_e.GlobalOnly:
2456 scopes = self.var_stack[0:1]
2457 elif which_scopes == scope_e.LocalOrGlobal:
2458 scopes = [self.var_stack[0]]
2459 if len(self.var_stack) > 1:
2460 scopes.append(self.var_stack[-1])
2461 else:
2462 raise AssertionError()
2463
2464 for scope in scopes:
2465 for name, cell in iteritems(scope):
2466 result[name] = cell
2467 return result
2468
2469 def SetRegexMatch(self, match):
2470 # type: (regex_match_t) -> None
2471 self.regex_match[-1] = match
2472
2473 def GetRegexMatch(self):
2474 # type: () -> regex_match_t
2475 return self.regex_match[-1]
2476
2477 def PushContextStack(self, context):
2478 # type: (Dict[str, value_t]) -> None
2479 self.ctx_stack.append(context)
2480
2481 def GetContext(self):
2482 # type: () -> Optional[Dict[str, value_t]]
2483 if len(self.ctx_stack):
2484 return self.ctx_stack[-1]
2485 return None
2486
2487 def PopContextStack(self):
2488 # type: () -> Dict[str, value_t]
2489 assert self.ctx_stack, "Empty context stack"
2490 return self.ctx_stack.pop()
2491
2492
2493def ValueIsInvokableObj(val):
2494 # type: (value_t) -> Tuple[Optional[value_t], Optional[Obj]]
2495 """
2496 Returns:
2497 (__invoke__ Proc or BuiltinProc, self Obj) if the value is invokable
2498 (None, None) otherwise
2499 """
2500 if val.tag() != value_e.Obj:
2501 return None, None
2502
2503 obj = cast(Obj, val)
2504 if not obj.prototype:
2505 return None, None
2506
2507 invoke_val = obj.prototype.d.get('__invoke__')
2508 if invoke_val is None:
2509 return None, None
2510
2511 # TODO: __invoke__ of wrong type could be fatal error?
2512 if invoke_val.tag() in (value_e.Proc, value_e.BuiltinProc):
2513 return invoke_val, obj
2514
2515 return None, None
2516
2517
2518def _AddNames(unique, frame):
2519 # type: (Dict[str, bool], Dict[str, Cell]) -> None
2520 for name in frame:
2521 val = frame[name].val
2522 if val.tag() == value_e.Proc:
2523 unique[name] = True
2524 proc, _ = ValueIsInvokableObj(val)
2525 if proc is not None:
2526 unique[name] = True
2527
2528
2529class Procs(object):
2530 """
2531 Terminology:
2532
2533 - invokable - these are INTERIOR
2534 - value.Proc - which can be shell function in __sh_funcs__ namespace, or
2535 YSH proc
2536 - value.Obj with __invoke__
2537 - exterior - external commands, extern builtin
2538
2539 Note: the YSH 'invoke' builtin can generalize YSH 'runproc' builtin, shell command/builtin,
2540 and also type / type -a
2541 """
2542
2543 def __init__(self, mem):
2544 # type: (Mem) -> None
2545 self.mem = mem
2546 self.sh_funcs = {} # type: Dict[str, value.Proc]
2547
2548 def DefineShellFunc(self, name, proc):
2549 # type: (str, value.Proc) -> None
2550 self.sh_funcs[name] = proc
2551
2552 def IsShellFunc(self, name):
2553 # type: (str) -> bool
2554 return name in self.sh_funcs
2555
2556 def GetShellFunc(self, name):
2557 # type: (str) -> Optional[value.Proc]
2558 return self.sh_funcs.get(name)
2559
2560 def EraseShellFunc(self, to_del):
2561 # type: (str) -> None
2562 """Undefine a sh-func with name `to_del`, if it exists."""
2563 mylib.dict_erase(self.sh_funcs, to_del)
2564
2565 def ShellFuncNames(self):
2566 # type: () -> List[str]
2567 """Returns a *sorted* list of all shell function names
2568
2569 Callers:
2570 declare -f -F
2571 """
2572 names = self.sh_funcs.keys()
2573 names.sort()
2574 return names
2575
2576 def DefineProc(self, name, proc):
2577 # type: (str, value.Proc) -> None
2578 """
2579 procs are defined in the local scope.
2580 """
2581 self.mem.var_stack[-1][name] = Cell(False, False, False, proc)
2582
2583 def IsProc(self, name):
2584 # type: (str) -> bool
2585
2586 maybe_proc = self.mem.GetValue(name)
2587 # Could be Undef
2588 return maybe_proc.tag() == value_e.Proc
2589
2590 def IsInvokableObj(self, name):
2591 # type: (str) -> bool
2592
2593 val = self.mem.GetValue(name)
2594 proc, _ = ValueIsInvokableObj(val)
2595 return proc is not None
2596
2597 def InvokableNames(self):
2598 # type: () -> List[str]
2599 """Returns a *sorted* list of all invokable names
2600
2601 Callers:
2602 complete -A function
2603 pp proc - should deprecate this
2604 """
2605 unique = NewDict() # type: Dict[str, bool]
2606 for name in self.sh_funcs:
2607 unique[name] = True
2608
2609 top_frame = self.mem.var_stack[-1]
2610 _AddNames(unique, top_frame)
2611
2612 global_frame = self.mem.var_stack[0]
2613 #log('%d %d', id(top_frame), id(global_frame))
2614 if global_frame is not top_frame:
2615 _AddNames(unique, global_frame)
2616
2617 #log('%s', unique)
2618
2619 names = unique.keys()
2620 names.sort()
2621
2622 return names
2623
2624 def GetInvokable(self, name):
2625 # type: (str) -> Tuple[Optional[value_t], Optional[Obj]]
2626 """Find a proc, invokable Obj, or sh-func, in that order
2627
2628 Callers:
2629 executor.py: to actually run
2630 meta_oils.py runproc lookup - this is not 'invoke', because it is
2631 INTERIOR shell functions, procs, invokable Obj
2632 """
2633 val = self.mem.GetValue(name)
2634
2635 if val.tag() == value_e.Proc:
2636 return cast(value.Proc, val), None
2637
2638 proc, self_val = ValueIsInvokableObj(val)
2639 if proc:
2640 return proc, self_val
2641
2642 if name in self.sh_funcs:
2643 return self.sh_funcs[name], None
2644
2645 return None, None
2646
2647
2648#
2649# Wrappers to Set Variables
2650#
2651
2652
2653def OshLanguageSetValue(mem, lval, val, flags=0):
2654 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2655 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2656
2657 That is, it respects shopt --unset dynamic_scope.
2658
2659 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2660 """
2661 which_scopes = mem.ScopesForWriting()
2662 mem.SetValue(lval, val, which_scopes, flags=flags)
2663
2664
2665def BuiltinSetValue(mem, lval, val):
2666 # type: (Mem, sh_lvalue_t, value_t) -> None
2667 """Equivalent of x=$y
2668
2669 Called by BuiltinSetString and BuiltinSetArray Used directly by
2670 printf -v because it can mutate an array
2671 """
2672 mem.SetValue(lval, val, mem.ScopesForWriting())
2673
2674
2675def BuiltinSetString(mem, name, s):
2676 # type: (Mem, str, str) -> None
2677 """Set a string by looking up the stack.
2678
2679 Used for 'read', 'getopts', completion builtins, etc.
2680 """
2681 assert isinstance(s, str)
2682 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2683
2684
2685def BuiltinSetArray(mem, name, a):
2686 # type: (Mem, str, List[str]) -> None
2687 """Set an array by looking up the stack.
2688
2689 Used by compadjust, read -a, etc.
2690 """
2691 assert isinstance(a, list)
2692 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2693
2694
2695def SetGlobalString(mem, name, s):
2696 # type: (Mem, str, str) -> None
2697 """Helper for completion, etc."""
2698 assert isinstance(s, str)
2699 val = value.Str(s)
2700 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2701
2702
2703def SetGlobalArray(mem, name, a):
2704 # type: (Mem, str, List[str]) -> None
2705 """Used by completion, shell initialization, etc."""
2706 assert isinstance(a, list)
2707 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2708
2709
2710def SetGlobalValue(mem, name, val):
2711 # type: (Mem, str, value_t) -> None
2712 """Helper for completion, etc."""
2713 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2714
2715
2716def SetLocalValue(mem, name, val):
2717 # type: (Mem, str, value_t) -> None
2718 """For 'use' builtin."""
2719 mem.SetNamed(location.LName(name), val, scope_e.LocalOnly)
2720
2721
2722def ExportGlobalString(mem, name, s):
2723 # type: (Mem, str, str) -> None
2724 """Helper for completion, $PWD, $OLDPWD, etc."""
2725 assert isinstance(s, str)
2726 val = value.Str(s)
2727 mem.SetNamed(location.LName(name),
2728 val,
2729 scope_e.GlobalOnly,
2730 flags=SetExport)
2731
2732
2733# TODO: remove in favor of EnvConfig
2734def SetStringInEnv(mem, var_name, s):
2735 # type: (Mem, str, str) -> None
2736 if mem.exec_opts.env_obj(): # e.g. ENV.YSH_HISTFILE
2737 mem.env_dict[var_name] = value.Str(s)
2738 else: # e.g. $YSH_HISTFILE
2739 SetGlobalString(mem, var_name, s)
2740
2741
2742#
2743# Wrappers to Get Variables
2744#
2745
2746
2747def DynamicGetVar(mem, name, which_scopes):
2748 # type: (Mem, str, scope_t) -> value_t
2749 """
2750 For getVar() and shvarGet()
2751 """
2752 val = mem.GetValue(name, which_scopes=which_scopes)
2753
2754 # Undef is not a user-visible value!
2755 # There's no way to distinguish null from undefined.
2756 if val.tag() == value_e.Undef:
2757 return value.Null
2758
2759 return val
2760
2761
2762def GetString(mem, name):
2763 # type: (Mem, str) -> str
2764 """Wrapper around GetValue().
2765
2766 Check that HOME, PWD, OLDPWD, etc. are strings. bash doesn't have these
2767 errors because ${array} is ${array[0]}.
2768
2769 TODO: We could also check this when you're storing variables?
2770 """
2771 val = mem.GetValue(name)
2772 UP_val = val
2773 with tagswitch(val) as case:
2774 if case(value_e.Undef):
2775 raise error.Runtime("$%s isn't defined" % name)
2776 elif case(value_e.Str):
2777 return cast(value.Str, UP_val).s
2778 else:
2779 # User would have to 'unset HOME' to get rid of exported flag
2780 raise error.Runtime("$%s should be a string" % name)
2781
2782
2783def MaybeString(mem, name):
2784 # type: (Mem, str) -> Optional[str]
2785 """Like GetString(), but doesn't throw an exception."""
2786 try:
2787 return GetString(mem, name)
2788 except error.Runtime:
2789 return None
2790
2791
2792def GetInteger(mem, name):
2793 # type: (Mem, str) -> int
2794 """For OPTIND variable used in getopts builtin.
2795
2796 TODO: it could be value.Int() ?
2797 """
2798 val = mem.GetValue(name)
2799 if val.tag() != value_e.Str:
2800 raise error.Runtime('$%s should be a string, got %s' %
2801 (name, ui.ValType(val)))
2802 s = cast(value.Str, val).s
2803 try:
2804 i = int(s)
2805 except ValueError:
2806 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2807 (name, s))
2808 return i
2809
2810
2811# vim: sw=4