OILS / spec / nameref.test.sh View on Github | oilshell.org

638 lines, 345 significant
1# Run with
2#
3# $ test/spec.sh nameref
4
5#### pass array by reference
6show_value() {
7 local -n array_name=$1
8 local idx=$2
9 echo "${array_name[$idx]}"
10}
11shadock=(ga bu zo meu)
12show_value shadock 2
13## stdout: zo
14
15#### mutate array by reference
16set1() {
17 local -n array_name=$1
18 local val=$2
19 array_name[1]=$val
20}
21shadock=(a b c d)
22set1 shadock ZZZ
23echo ${shadock[@]}
24## STDOUT:
25a ZZZ c d
26## END
27
28#### pass assoc array by reference
29show_value() {
30 local -n array_name=$1
31 local idx=$2
32 echo "${array_name[$idx]}"
33}
34days=([monday]=eggs [tuesday]=bread [sunday]=jam)
35show_value days sunday
36## stdout: jam
37## BUG mksh stdout: [monday]=eggs
38# mksh note: it coerces "days" to 0? Horrible.
39
40#### pass local array by reference, relying on DYNAMIC SCOPING
41show_value() {
42 local -n array_name=$1
43 local idx=$2
44 echo "${array_name[$idx]}"
45}
46caller() {
47 local shadock=(ga bu zo meu)
48 show_value shadock 2
49}
50caller
51## stdout: zo
52# mksh appears not to have local arrays!
53## BUG mksh stdout-json: ""
54## BUG mksh status: 1
55
56
57#### flag -n and +n
58x=foo
59
60ref=x
61
62echo ref=$ref
63
64typeset -n ref
65echo ref=$ref
66
67# mutate underlying var
68x=bar
69echo ref=$ref
70
71typeset +n ref
72echo ref=$ref
73
74## STDOUT:
75ref=x
76ref=foo
77ref=bar
78ref=x
79## END
80
81#### mutating through nameref: ref=
82x=XX
83y=YY
84
85ref=x
86ref=y
87echo 1 ref=$ref
88
89# now it's a reference
90typeset -n ref
91
92echo 2 ref=$ref # prints YY
93
94ref=XXXX
95echo 3 ref=$ref # it actually prints y, which is XXXX
96
97# now Y is mutated!
98echo 4 y=$y
99
100## STDOUT:
1011 ref=y
1022 ref=YY
1033 ref=XXXX
1044 y=XXXX
105## END
106
107
108#### flag -n combined ${!ref} -- bash INVERTS
109foo=FOO # should NOT use this
110
111x=foo
112ref=x
113
114echo ref=$ref
115echo "!ref=${!ref}"
116
117echo 'NOW A NAMEREF'
118
119typeset -n ref
120echo ref=$ref
121echo "!ref=${!ref}"
122
123## STDOUT:
124ref=x
125!ref=foo
126NOW A NAMEREF
127ref=foo
128!ref=x
129## END
130## N-I mksh STDOUT:
131ref=x
132!ref=ref
133NOW A NAMEREF
134ref=foo
135!ref=x
136## END
137
138#### named ref with $# doesn't work
139set -- one two three
140
141ref='#'
142echo ref=$ref
143typeset -n ref
144echo ref=$ref
145
146## STDOUT:
147ref=#
148ref=#
149## END
150
151# mksh does respect it!! Gah.
152## OK mksh STDOUT:
153ref=#
154ref=3
155## END
156
157
158#### named ref with $# and shopt -s strict_nameref
159shopt -s strict_nameref
160
161ref='#'
162echo ref=$ref
163typeset -n ref
164echo ref=$ref
165## STDOUT:
166ref=#
167## END
168## status: 1
169## N-I bash status: 0
170## N-I bash STDOUT:
171ref=#
172ref=#
173## END
174## N-I mksh status: 0
175## N-I mksh STDOUT:
176ref=#
177ref=0
178## END
179
180#### named ref with 1 $1 etc.
181set -- one two three
182
183x=X
184
185ref='1'
186echo ref=$ref
187typeset -n ref
188echo ref=$ref
189
190# BUG: This is really assigning '1', which is INVALID
191# with strict_nameref that degrades!!!
192ref2='$1'
193echo ref2=$ref2
194typeset -n ref2
195echo ref2=$ref2
196
197x=foo
198
199ref3='x'
200echo ref3=$ref3
201typeset -n ref3
202echo ref3=$ref3
203
204## STDOUT:
205ref=1
206ref=1
207ref2=$1
208ref2=$1
209ref3=x
210ref3=foo
211## END
212## BUG mksh status: 1
213## BUG mksh STDOUT:
214ref=1
215ref=one
216ref2=$1
217## END
218
219#### assign to invalid ref
220ref=1 # mksh makes this READ-ONLY! Because it's not valid.
221
222echo ref=$ref
223typeset -n ref
224echo ref=$ref
225
226ref=foo
227echo ref=$ref
228## STDOUT:
229ref=1
230ref=1
231ref=foo
232## END
233## OK mksh status: 2
234## OK mksh STDOUT:
235ref=1
236ref=
237## END
238
239#### assign to invalid ref with strict_nameref
240case $SH in *bash|*mksh) exit ;; esac
241
242shopt -s strict_nameref
243
244ref=1
245
246echo ref=$ref
247typeset -n ref
248echo ref=$ref
249
250ref=foo
251echo ref=$ref
252## status: 1
253## STDOUT:
254ref=1
255## END
256## N-I bash/mksh status: 0
257## N-I bash/mksh stdout-json: ""
258
259#### name ref on Undef cell
260typeset -n ref
261
262# This is technically incorrect: an undefined name shouldn't evaluate to empty
263# string. mksh doesn't allow it.
264echo ref=$ref
265
266echo nounset
267set -o nounset
268echo ref=$ref
269## status: 1
270## STDOUT:
271ref=
272nounset
273## END
274## OK mksh stdout-json: ""
275
276#### assign to empty nameref and invalid nameref
277typeset -n ref
278echo ref=$ref
279
280# this is a no-op in bash, should be stricter
281ref=x
282echo ref=$ref
283
284typeset -n ref2=undef
285echo ref2=$ref2
286ref2=x
287echo ref2=$ref2
288
289## STDOUT:
290ref=
291ref=
292ref2=
293ref2=x
294## END
295
296# mksh gives a good error: empty nameref target
297## OK mksh status: 1
298## OK mksh stdout-json: ""
299
300#### -n attribute before it has a value
301typeset -n ref
302
303echo ref=$ref
304
305# Now that it's a string, it still has the -n attribute
306x=XX
307ref=x
308echo ref=$ref
309
310## STDOUT:
311ref=
312ref=XX
313## END
314## N-I mksh status: 1
315## N-I mksh stdout-json: ""
316
317#### -n attribute on array is hard error, not a warning
318x=X
319typeset -n ref #=x
320echo hi
321
322# bash prints warning: REMOVES the nameref attribute here!
323ref=(x y)
324echo ref=$ref
325
326## status: 1
327## STDOUT:
328hi
329## END
330## N-I mksh status: 1
331## N-I mksh stdout-json: ""
332## BUG bash status: 0
333## BUG bash STDOUT:
334hi
335ref=x
336## END
337
338#### exported nameref
339x=foo
340typeset -n -x ref=x
341
342# hm bash ignores it but mksh doesn't. maybe disallow it.
343printenv.py x ref
344echo ---
345export x
346printenv.py x ref
347## STDOUT:
348None
349x
350---
351foo
352x
353## END
354## OK mksh STDOUT:
355None
356None
357---
358foo
359None
360## END
361
362
363#### readonly nameref doesn't prevent assigning through it
364
365# hm bash also ignores -r when -n is set
366
367x=XX
368typeset -n -r ref=x
369
370echo ref=$ref
371
372# it feels like I shouldn't be able to mutate this?
373ref=XXXX
374echo ref=$ref
375
376x=X
377echo x=$x
378
379## STDOUT:
380ref=XX
381ref=XXXX
382x=X
383## END
384
385#### readonly var can't be assigned through nameref
386
387x=X
388typeset -n -r ref=x
389
390echo ref=$ref
391
392# it feels like I shouldn't be able to mutate this?
393ref=XX
394echo ref=$ref
395
396# now the underling variable is immutable
397typeset -r x
398
399ref=XXX
400echo ref=$ref
401echo x=$x
402
403## status: 1
404## OK mksh status: 2
405## STDOUT:
406ref=X
407ref=XX
408## END
409
410## OK bash status: 0
411## OK bash STDOUT:
412ref=X
413ref=XX
414ref=XX
415x=XX
416## END
417
418#### unset nameref
419x=X
420typeset -n ref=x
421echo ref=$ref
422
423# this works
424unset ref
425echo ref=$ref
426echo x=$x
427
428## STDOUT:
429ref=X
430ref=
431x=
432## END
433
434#### Chain of namerefs
435x=foo
436typeset -n ref=x
437typeset -n ref_to_ref=ref
438echo ref_to_ref=$ref_to_ref
439echo ref=$ref
440## STDOUT:
441ref_to_ref=foo
442ref=foo
443## END
444
445#### Mutually recursive namerefs detected on READ
446typeset -n ref1=ref2
447typeset -n ref2=ref1
448echo defined
449echo ref1=$ref1
450echo ref2=$ref1
451## status: 1
452## STDOUT:
453defined
454## END
455## OK mksh stdout-json: ""
456## BUG bash status: 0
457## BUG bash STDOUT:
458defined
459ref1=
460ref2=
461## END
462
463#### Mutually recursive namerefs detected on WRITE
464typeset -n ref1=ref2
465typeset -n ref2=ref1 # not detected here
466echo defined $?
467ref1=z # detected here
468echo mutated $?
469## status: 1
470## STDOUT:
471defined 0
472## END
473## OK mksh stdout-json: ""
474## BUG bash status: 0
475## BUG bash STDOUT:
476defined 0
477mutated 1
478## END
479
480#### Dynamic scope with namerefs
481
482f3() {
483 local -n ref=$1
484 ref=x
485}
486
487f2() {
488 f3 "$@"
489}
490
491f1() {
492 local F1=F1
493 echo F1=$F1
494 f2 F1
495 echo F1=$F1
496}
497f1
498
499## STDOUT:
500F1=F1
501F1=x
502## END
503
504
505#### change reference itself
506x=XX
507y=YY
508typeset -n ref=x
509echo ref=$ref
510echo x=$x
511echo y=$y
512
513echo ----
514typeset -n ref=y
515echo ref=$ref
516echo x=$x
517echo y=$y
518echo ----
519ref=z
520echo ref=$ref
521echo x=$x
522echo y=$y
523
524## STDOUT:
525ref=XX
526x=XX
527y=YY
528----
529ref=YY
530x=XX
531y=YY
532----
533ref=z
534x=XX
535y=z
536## END
537
538#### a[2] in nameref
539
540typeset -n ref='a[2]'
541a=(zero one two three)
542echo ref=$ref
543## STDOUT:
544ref=two
545## END
546
547#### a[expr] in nameref
548
549# this confuses code and data
550typeset -n ref='a[$(echo 2) + 1]'
551a=(zero one two three)
552echo ref=$ref
553## STDOUT:
554ref=three
555## END
556
557#### a[@] in nameref
558
559# this confuses code and data
560typeset -n ref='a[@]'
561a=('A B' C)
562argv.py ref "$ref" # READ through ref works
563ref=(X Y Z) # WRITE through doesn't work
564echo status=$?
565argv.py 'ref[@]' "${ref[@]}"
566argv.py ref "$ref" # JOINING mangles the array?
567argv.py 'a[@]' "${a[@]}"
568## STDOUT:
569['ref', 'A B C']
570status=1
571['ref[@]']
572['ref', 'A B C']
573['a[@]', 'A B', 'C']
574## END
575## OK mksh status: 1
576## OK mksh stdout-json: ""
577
578#### mutate through nameref: ref[0]=
579
580# This is DIFFERENT than the nameref itself being 'array[0]' !
581
582array=(X Y Z)
583typeset -n ref=array
584ref[0]=xx
585echo ${array[@]}
586## STDOUT:
587xx Y Z
588## END
589
590#### bad mutation through nameref: ref[0]= where ref is array[0]
591array=(X Y Z)
592typeset -n ref='array[0]'
593ref[0]=foo # error in bash: 'array[0]': not a valid identifier
594echo status=$?
595echo ${array[@]}
596## STDOUT:
597status=1
598X Y Z
599## END
600## BUG mksh STDOUT:
601status=0
602foo Y Z
603## END
604
605#### @ in nameref isn't supported, unlike in ${!ref}
606
607set -- A B
608typeset -n ref='@' # bash gives an error here
609echo status=$?
610
611echo ref=$ref # bash doesn't give an error here
612echo status=$?
613## status: 1
614## stdout-json: ""
615## OK bash status: 0
616## OK bash STDOUT:
617status=1
618ref=
619status=0
620## END
621
622#### Unquoted assoc reference on RHS
623typeset -A bashup_ev_r
624bashup_ev_r['foo']=bar
625
626p() {
627 local s=foo
628 local -n e=bashup_ev["$s"] f=bashup_ev_r["$s"]
629 # Different!
630 #local e=bashup_ev["$s"] f=bashup_ev_r["$s"]
631 argv.py "$f"
632}
633p
634## STDOUT:
635['bar']
636## END
637## N-I mksh stdout-json: ""
638## N-I mksh status: 1