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

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