Skip to content

Commit

Permalink
global: add workarounds for "localvar_inherit"
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed May 6, 2021
1 parent 24ea379 commit 7b63c60
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 95 deletions.
1 change: 0 additions & 1 deletion ble.pp
Expand Up @@ -217,7 +217,6 @@ function ble/base/adjust-builtin-wrappers-1 {
# Note: set -o posix にしても read, type, builtin, local 等は上書き
# された儘なので難しい。unset -f builtin さえすれば色々動く様になる
# ので builtin は unset -f builtin してしまう。
# return 0
unset -f builtin
builtin local POSIXLY_CORRECT=y builtins1 keywords1
builtins1=(builtin unset enable unalias return break continue declare local typeset readonly eval exec)
Expand Down
11 changes: 5 additions & 6 deletions lib/core-complete.sh
Expand Up @@ -2343,8 +2343,8 @@ function ble/complete/progcomp/.compgen {
compcmd=${comp_words[0]}
fi

local -a compargs compoptions flag_noquote=
local ret iarg=1
local -a compargs compoptions=()
local ret iarg=1 flag_noquote=
if [[ $is_special_completion ]]; then
ble/util/assign ret 'builtin complete -p "$compcmd" 2>/dev/null'
else
Expand Down Expand Up @@ -5338,7 +5338,7 @@ function ble/complete/auto-complete/.search-history-light {
if [[ $text != [-0-9#?!]* ]]; then
word=${text%%[$wordbreaks]*}
local expand
BASH_COMMAND='!'$word ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1
command='!'$word ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1
if [[ $expand == "$text"* ]]; then
ret=$expand
return 0
Expand All @@ -5358,7 +5358,7 @@ function ble/complete/auto-complete/.search-history-light {
done

for frag in "${longest_fragments[@]}"; do
BASH_COMMAND='!?'$frag ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1
command='!?'$frag ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1
[[ $expand == "$text"* ]] || continue
ret=$expand
return 0
Expand Down Expand Up @@ -5459,8 +5459,7 @@ function ble/complete/auto-complete/.check-context {
local bleopt_complete_polling_cycle=25
local COMP1 COMP2 COMPS COMPV
local comps_flags comps_fixed
local cand_count
local -a cand_cand cand_word cand_pack
local cand_count cand_cand cand_word cand_pack
local cand_limit_reached=
ble/complete/candidates/generate; local ext=$?
[[ $COMPV ]] || return 1
Expand Down
2 changes: 1 addition & 1 deletion lib/core-syntax.sh
Expand Up @@ -4962,7 +4962,7 @@ function ble/syntax/parse/check-end {
## 今回の呼出によって文法的な解釈の変更が行われた範囲を更新します。
##
function ble/syntax/parse {
local -r text=$1 iN=${#1}
local text=$1 iN=${#1}
local opts=$2
local beg=${3:-0} end=${4:-$iN} end0=${5:-0}
((end==beg&&end0==beg&&_ble_syntax_dbeg<0)) && return 0
Expand Down
12 changes: 7 additions & 5 deletions make_command.sh
Expand Up @@ -262,18 +262,18 @@ function sub:scan/check-todo-mark {
function sub:scan/a.txt {
echo "--- $FUNCNAME ---"
grc --color --exclude=./{test,ext} --exclude=./lib/test-*.sh --exclude=./make_command.sh --exclude=\*.md 'a\.txt|/dev/(pts/|pty)[0-9]*' |
grep -Ev "$rex_grep_head#|[[:space:]]#"
grep -Ev "$rex_grep_head#|[[:space:]]#|DEBUG_LEAKVAR"
}

function sub:scan/bash300bug {
echo "--- $FUNCNAME ---"
# bash-3.0 では local arr=(1 2 3) とすると
# local arr='(1 2 3)' と解釈されてしまう。
grc 'local [a-zA-Z_]+=\(' --exclude=./test --exclude=./make_command.sh --exclude=ChangeLog.md
grc 'local [a-zA-Z_]+=\(' --exclude=./{test,ext} --exclude=./make_command.sh --exclude=ChangeLog.md

# bash-3.0 では local -a arr=("$hello") とすると
# クォートしているにも拘らず $hello の中身が単語分割されてしまう。
grc 'local -a [[:alnum:]_]+=\([^)]*[\"'\''`]' --exclude=./test --exclude=./make_command.sh
grc 'local -a [[:alnum:]_]+=\([^)]*[\"'\''`]' --exclude=./{test,ext} --exclude=./make_command.sh
}

function sub:scan/bash301bug-array-element-length {
Expand Down Expand Up @@ -386,20 +386,22 @@ function sub:scan {
sub:scan/builtin 'eval' |
sed -E 'h;s/'"$esc"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
\Z\('\''eval'\''\)Zd
\Zbuiltins2=\(.* eval\)Zd
\Zbuiltins1=\(.* eval .*\)Zd
\Z\^eval --Zd
\Zt = "eval -- \$"Zd
\Ztext = "eval -- \$'\''Zd
\Zcmd '\''eval -- %q'\''Zd
\Z\$\(eval \$\(call .*\)\)Zd
g'
sub:scan/builtin 'unset' |
sed -E 'h;s/'"$esc"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
\Zunset _ble_init_(version|arg|exit|test)\bZd
\Zreadonly -f unsetZd
\Zunset -f builtinZd
g'
sub:scan/builtin 'unalias' |
sed -E 'h;s/'"$esc"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
\Zbuiltins1=\(.* unalias\)Zd
\Zbuiltins1=\(.* unalias .*\)Zd
g'

#sub:scan/assign
Expand Down
1 change: 1 addition & 0 deletions memo/ChangeLog.md
Expand Up @@ -83,6 +83,7 @@
- canvas: fix a glitch that SGR at the end of command line is applied to new lines `#D1498` 4bdfdbf
- syntax: fix a bug that `eval() { :; }`, `declare() { :; }` are not treated as function definition `#D1529` 0000000
- decode: fix a hang on attach failure by cache corruption `#D1531` 0000000
- edit, etc: add workarounds for `localvar_inherit` `#D1532` 0000000

## Compatibility

Expand Down
223 changes: 191 additions & 32 deletions note.txt
Expand Up @@ -1253,6 +1253,106 @@ bash_tips
これは少なくとも 1 文字 i を進めてからでないと tree-append を呼び出せないという事である。
nest-pop も内部的にそのまま tree-append を呼び出しているので同じ制限がある。


*******************************************************************************
bug-bash
-------------------------------------------------------------------------------

2021-05-06

* bug-bash localvar_inherit: dynamic variables の性質も継承されるのは意図的か。
Ref #D1532

例:

shopt -s localvar_inherit
local BASH_COMMAND='xxxx'
local LINENO='xxxx'
local RANDOM='xxxx'

* PS1 を評価する為に BASH_COMMAND を一時的に別の物に置き換えたい。
localvar_inherit を一時的に off にしたり或いは tempvar を通して
BASH_COMMAND を変更する等すれば一応 BASH_COMMAND を置き換える事は可能だが
非自明である。

* localvar_inherit は local variable という形で初期化を行わなかった時の振る
舞いを制御する物と思っていたが、実際には上記の様にした場合に影響が出てく
る。つまり、localvar_inherit の下で上を実行すると set/get がコピーされた
上で代入が行われる様で、動的変数としての性質が継承される。振る舞いが変わっ
てしまって困る。特に代入した値が消滅してしまう。

* また、マニュアルを見ても attr 及び value が継承されるとは書かれているが
dynamic variable としての性質が継承されるとまでは書かれていない (?)。

> info bash より (man bash は本質的に同じ)
>
> localvar_inherit
>
> If set, local variables inherit the value and attributes of a
> variable of the same name that exists at a previous scope before
> any new value is assigned. The 'nameref' attribute is not
> inherited.
>
> declare
>
> The '-I' option causes local variables to inherit the attributes
> (except the 'nameref' attribute) and value of any existing variable
> with the same NAME at a surrounding scope. If there is no existing
> variable, the local variable is initially unset.
>
> declare --help より
>
> -I if creating a local variable, inherit the attributes and value of a
> variable with the same name at a previous scope

* 他の実装はどうだろうかと思って zsh で RANDOM, LINENO を試してみたが、
RANDOM に関しては local にしても何も振る舞いの変化は見られず、LINENO に関
してはそもそも代入不可能だった。そもそも zsh は local var; とすると前のス
コープの値は保持される単に unset になる。

* 実際にこの継承を行っているのは variables.c:2738 の以下の部分である

variables.c L2738
> if (localvar_inherit || (flags & MKLOC_INHERIT))
> {
> /* It doesn't make sense to inherit the nameref attribute */
> new_var->attributes = old_var->attributes & ~att_nameref;
> new_var->dynamic_value = old_var->dynamic_value;
> new_var->assign_func = old_var->assign_func;
> }

2021-05-04

* mapfile -d "" が buffered になったら
ble/syntax:bash/simple-word/eval/.print-result (lib/core-syntax.sh) を修正

* 2021-03-21 gitstatus.plugin.sh の alias builtin で builtin eval 他が破壊されている
https://github.com/akinomyoga/ble.sh/issues/93

* 取り敢えず暫定的な対策として source ble.sh の時に unalias builtin 等を実
行する事にした。

* 他に source ble.sh した後に gitstatus.plugin.sh が実行されて、更にその後
になって ble-attach した時の事も考える必要がある。

gitstatus.plugin.sh の側で対策できないのか。
34e21707

https://github.com/romkatv/gitstatus/issues/154
ここで本人が builtin を置き換えるのは良くない事だという事を述べている。

> It's generally not a good idea to redefine builtins. If you also redefine
> things like true, local, unset, etc., gitstatus won't work. Please don't
> file a bug in this case but rather fix your config.

eval ... pos parameters
source ... pos parameters
source ... function scope or not (declare var の振る舞い)
unset ... previous-scope dynamic-unset or value-unset
exec ... persistent redirections
これは実際に問題になるという事を確認した。


@todo
*******************************************************************************
ToDo
Expand All @@ -1275,9 +1375,6 @@ bash_tips
* robustness: ble.sh では exit を上書きしているが set -o posix の時にはそれが
無効になる。

* mapfile -d "" が buffered になったら
ble/syntax:bash/simple-word/eval/.print-result (lib/core-syntax.sh) を修正

* util: builtins 復元、function#advice, etc. において functrace 属性は復元し
なくて良いのだろうか。復元という事を考えるとやはり declare -pf を使う必要が
あるのかもしれない。
Expand Down Expand Up @@ -1366,14 +1463,6 @@ bash_tips

2021-04-06

* localvar_inherit で動かなくなっている。或いは元から動いた事はなかった可能性も?

* localvar_inherit をしていると local -r a した変数に対して、
内側の関数で同名の変数を定義できなくなってしまう。

うーん。それでも未だ色々動かない。というか、バグというかちゃんと書
かれていないコードを洗い出すのに使えるかもしれないとは思う。

* logout も exit と同様に置き換えるべきなのではないか。

* prompt を評価する時に $var が local 変数に被覆されている。
Expand Down Expand Up @@ -4252,33 +4341,103 @@ bash_tips
Done (実装ログ)
-------------------------------------------------------------------------------

* 2021-03-21 gitstatus.plugin.sh の alias builtin で builtin eval 他が破壊されている
https://github.com/akinomyoga/ble.sh/issues/93
2021-05-06

* 取り敢えず暫定的な対策として source ble.sh の時に unalias builtin 等を実
行する事にした。
* 2021-04-06 localvar_inherit で動かなくなっている [#D1532]

* 他に source ble.sh した後に gitstatus.plugin.sh が実行されて、更にその後
になって ble-attach した時の事も考える必要がある。
或いは元から動いた事はなかった可能性も?

gitstatus.plugin.sh の側で対策できないのか。
34e21707

https://github.com/romkatv/gitstatus/issues/154
ここで本人が builtin を置き換えるのは良くない事だという事を述べている。
* localvar_inherit をしていると local -r a した変数に対して、
内側の関数で同名の変数を定義できなくなってしまう。

> It's generally not a good idea to redefine builtins. If you also redefine
> things like true, local, unset, etc., gitstatus won't work. Please don't
> file a bug in this case but rather fix your config.
うーん。それでも未だ色々動かない。というか、バグというかちゃんと書
かれていないコードを洗い出すのに使えるかもしれないとは思う。

eval ... pos parameters
source ... pos parameters
source ... function scope or not (declare var の振る舞い)
unset ... previous-scope dynamic-unset or value-unset
exec ... persistent redirections
これは実際に問題になるという事を確認した。
* どうやら localvar_inherit になっていると local BASH_COMMAND としても
BASH_COMMAND の値が変化する性質は受け継がれてしまう様である。

2021-05-05
| RANDOM でも同様の現象が発生している。
|
| $ f1() { local RANDOM=hello; echo $RANDOM; RANDOM=world; echo $RANDOM; }
| $ shopt -u localvar_inherit; f1
| hello
| world
| $ shopt -s localvar_inherit; f1
| 20034
| 20034
|
| 何が起こっているのだろう。元の変数がそのまま見えているという事だろうか。
| と思ったがそうでもない。unset RANDOM して見たが元の変数が消えるという事は
| なかった。恐らく localvar_inherit の時には値が受け継がれるのではなくて、
| setter/getter が引き継がれてしまっているのが原因なのだろうと思われる。
|
| bash を確認すると subst.c (expand_declaration_argument) では
| localvar_inherit が設定されている時には declare は declare -I と同様の振
| る舞いになる。 declare -I の説明を declare --help で確認すると値と属性を
| 継承すると書かれている。
|
| if creating a local variable, inherit the attributes and value of a
| variable with the same name at a previous scope
|
| と思ったが実装をよく見ても inheriting の値が一切使われていない気がする。
| 不思議である。未実装なのだろうか。或いは何かを見落としている? どうもこの
| 関数は arr=() の形式の引数を処理する時にだけ使われているという事だろうか。
|
| 実際に localvar_inherit の処理を具体的に処理しているのは variables.c の方
| である様に思われる。特に以下の部分 (variables.c:2738) が setter/getter を
| 継承するコードである。
|
| if (localvar_inherit || (flags & MKLOC_INHERIT))
| {
| /* It doesn't make sense to inherit the nameref attribute */
| new_var->attributes = old_var->attributes & ~att_nameref;
| new_var->dynamic_value = old_var->dynamic_value;
| new_var->assign_func = old_var->assign_func;
| }
|
| 明示的にコピーしているという事を考えるとこれを覆すのは難しいかも知れない。
| 具体的に変な振る舞いをする例を作ってそれを呈示すれば何か考えて貰えるかも
| しれない。getter/setter が関わっている関数は他にあるだろうか。
|
| INIT_DYNAMIC_VAR で検索するとそういった関数の一覧が得られる。
|
| - "SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds
| - "FUNCNAME", (char *)NULL, get_funcname, null_assign
| - "BASH_ARGV0", (char *)NULL, get_bash_argv0, assign_bash_argv0
| - "BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL
| - "BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell
| - "RANDOM", (char *)NULL, get_random, assign_random
| - "SRANDOM", (char *)NULL, get_urandom, (sh_var_assign_func_t *)NULL
| - "LINENO", (char *)NULL, get_lineno, assign_lineno
| - "BASHPID", (char *)NULL, get_bashpid, null_assign
| - "EPOCHSECONDS", (char *)NULL, get_epochseconds, null_assign
| - "EPOCHREALTIME", (char *)NULL, get_epochrealtime, null_assign
| - "HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL
| - "COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks
|
| TERM の場合はどうなっているのだろうか。これは special_vars という事になっ
| ていて、値が変更した時に特別の関数が呼び出される様になっている。他に
| TERM* や LANG, LC_* 等様々な変数がこれに相当する。これらは local に関係なく
| 常に値が変更したら何か処理する事になっているので今回の振る舞いには関係ない。
|
| やはり勝手に上書きするのが悪いという結論になりそうな変数しか存在しない。
| 敢えて言うならば RANDOM か LINENO ばかりだろうか。LINENO の方も確認したが、
| やはり LINENO に対する代入は無視されて現在の行数が常に表示される様になっ
| ている。因みに LINENO=1234 eval 'echo $LINENO' に関しては
| localvar_inherit を設定しても特に変わりなく同名の tempvar が作成される。

bash のバグか或いは confusing behavior かどうかは別として、古い version
に対しては取り敢えず修正しなければならないので、対策は行う事にする。時間
があれば bash に報告・相談もする事にする。

* もう一つの問題は local -a buff とした後に ble/array#push としていると、前
の配列の内容が既にあった場合にそれの続きとして値を push してしまうという
事。こう言った使い方をしている箇所はないと思っていたが、確かめてみると古
いコードを中心にして沢山あった。と思ったがそんなには無かった。合計で4箇所
だけだった。

上記の事を直したら取り敢えず動いている様に見える。残りは具体的に問題が生じ
てから対処する事にする。

* 2021-04-28 keymap 初期化に失敗した時にそれを検出しているのにも拘らず操作不能になる [#D1531]

Expand Down

0 comments on commit 7b63c60

Please sign in to comment.