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

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