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

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