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

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