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

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