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

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