1" Test the :disassemble command, and compilation as a side effect
2
3source check.vim
4
5func NotCompiled()
6  echo "not"
7endfunc
8
9let s:scriptvar = 4
10let g:globalvar = 'g'
11let b:buffervar = 'b'
12let w:windowvar = 'w'
13let t:tabpagevar = 't'
14
15def s:ScriptFuncLoad(arg: string)
16  let local = 1
17  buffers
18  echo arg
19  echo local
20  echo &lines
21  echo v:version
22  echo s:scriptvar
23  echo g:globalvar
24  echo get(g:, "global")
25  echo b:buffervar
26  echo get(b:, "buffer")
27  echo w:windowvar
28  echo get(w:, "window")
29  echo t:tabpagevar
30  echo get(t:, "tab")
31  echo &tabstop
32  echo $ENVVAR
33  echo @z
34enddef
35
36def Test_disassemble_load()
37  assert_fails('disass NoFunc', 'E1061:')
38  assert_fails('disass NotCompiled', 'E1062:')
39  assert_fails('disass', 'E471:')
40  assert_fails('disass [', 'E475:')
41  assert_fails('disass 234', 'E129:')
42  assert_fails('disass <XX>foo', 'E129:')
43
44  let res = execute('disass s:ScriptFuncLoad')
45  assert_match('<SNR>\d*_ScriptFuncLoad.*' ..
46        'buffers.*' ..
47        ' EXEC \+buffers.*' ..
48        ' LOAD arg\[-1\].*' ..
49        ' LOAD $0.*' ..
50        ' LOADOPT &lines.*' ..
51        ' LOADV v:version.*' ..
52        ' LOADS s:scriptvar from .*test_vim9_disassemble.vim.*' ..
53        ' LOADG g:globalvar.*' ..
54        'echo get(g:, "global")\_s*' ..
55        '\d\+ LOAD g:\_s*' ..
56        '\d\+ PUSHS "global"\_s*' ..
57        '\d\+ BCALL get(argc 2).*' ..
58        ' LOADB b:buffervar.*' ..
59        'echo get(b:, "buffer")\_s*' ..
60        '\d\+ LOAD b:\_s*' ..
61        '\d\+ PUSHS "buffer"\_s*' ..
62        '\d\+ BCALL get(argc 2).*' ..
63        ' LOADW w:windowvar.*' ..
64        'echo get(w:, "window")\_s*' ..
65        '\d\+ LOAD w:\_s*' ..
66        '\d\+ PUSHS "window"\_s*' ..
67        '\d\+ BCALL get(argc 2).*' ..
68        ' LOADT t:tabpagevar.*' ..
69        'echo get(t:, "tab")\_s*' ..
70        '\d\+ LOAD t:\_s*' ..
71        '\d\+ PUSHS "tab"\_s*' ..
72        '\d\+ BCALL get(argc 2).*' ..
73        ' LOADENV $ENVVAR.*' ..
74        ' LOADREG @z.*',
75        res)
76enddef
77
78def s:EditExpand()
79  let filename = "file"
80  let filenr = 123
81  edit the`=filename``=filenr`.txt
82enddef
83
84def Test_disassemble_exec_expr()
85  let res = execute('disass s:EditExpand')
86  assert_match('<SNR>\d*_EditExpand.*' ..
87        ' let filename = "file".*' ..
88        '\d PUSHS "file".*' ..
89        '\d STORE $0.*' ..
90        ' let filenr = 123.*' ..
91        '\d STORE 123 in $1.*' ..
92        ' edit the`=filename``=filenr`.txt.*' ..
93        '\d PUSHS "edit the".*' ..
94        '\d LOAD $0.*' ..
95        '\d LOAD $1.*' ..
96        '\d 2STRING stack\[-1\].*' ..
97        '\d PUSHS ".txt".*' ..
98        '\d EXECCONCAT 4.*' ..
99        '\d PUSHNR 0.*' ..
100        '\d RETURN',
101        res)
102enddef
103
104def s:ScriptFuncPush()
105  let localbool = true
106  let localspec = v:none
107  let localblob = 0z1234
108  if has('float')
109    let localfloat = 1.234
110  endif
111enddef
112
113def Test_disassemble_push()
114  let res = execute('disass s:ScriptFuncPush')
115  assert_match('<SNR>\d*_ScriptFuncPush.*' ..
116        'localbool = true.*' ..
117        ' PUSH v:true.*' ..
118        'localspec = v:none.*' ..
119        ' PUSH v:none.*' ..
120        'localblob = 0z1234.*' ..
121        ' PUSHBLOB 0z1234.*',
122        res)
123  if has('float')
124    assert_match('<SNR>\d*_ScriptFuncPush.*' ..
125          'localfloat = 1.234.*' ..
126          ' PUSHF 1.234.*',
127          res)
128  endif
129enddef
130
131def s:ScriptFuncStore()
132  let localnr = 1
133  localnr = 2
134  let localstr = 'abc'
135  localstr = 'xyz'
136  v:char = 'abc'
137  s:scriptvar = 'sv'
138  g:globalvar = 'gv'
139  b:buffervar = 'bv'
140  w:windowvar = 'wv'
141  t:tabpagevar = 'tv'
142  &tabstop = 8
143  $ENVVAR = 'ev'
144  @z = 'rv'
145enddef
146
147def Test_disassemble_store()
148  let res = execute('disass s:ScriptFuncStore')
149  assert_match('<SNR>\d*_ScriptFuncStore.*' ..
150        'let localnr = 1.*' ..
151        'localnr = 2.*' ..
152        ' STORE 2 in $0.*' ..
153        'let localstr = ''abc''.*' ..
154        'localstr = ''xyz''.*' ..
155        ' STORE $1.*' ..
156        'v:char = ''abc''.*' ..
157        'STOREV v:char.*' ..
158        's:scriptvar = ''sv''.*' ..
159        ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' ..
160        'g:globalvar = ''gv''.*' ..
161        ' STOREG g:globalvar.*' ..
162        'b:buffervar = ''bv''.*' ..
163        ' STOREB b:buffervar.*' ..
164        'w:windowvar = ''wv''.*' ..
165        ' STOREW w:windowvar.*' ..
166        't:tabpagevar = ''tv''.*' ..
167        ' STORET t:tabpagevar.*' ..
168        '&tabstop = 8.*' ..
169        ' STOREOPT &tabstop.*' ..
170        '$ENVVAR = ''ev''.*' ..
171        ' STOREENV $ENVVAR.*' ..
172        '@z = ''rv''.*' ..
173        ' STOREREG @z.*',
174        res)
175enddef
176
177def s:ScriptFuncStoreMember()
178  let locallist: list<number> = []
179  locallist[0] = 123
180  let localdict: dict<number> = {}
181  localdict["a"] = 456
182enddef
183
184def Test_disassemble_store_member()
185  let res = execute('disass s:ScriptFuncStoreMember')
186  assert_match('<SNR>\d*_ScriptFuncStoreMember\_s*' ..
187        'let locallist: list<number> = []\_s*' ..
188        '\d NEWLIST size 0\_s*' ..
189        '\d STORE $0\_s*' ..
190        'locallist\[0\] = 123\_s*' ..
191        '\d PUSHNR 123\_s*' ..
192        '\d PUSHNR 0\_s*' ..
193        '\d LOAD $0\_s*' ..
194        '\d STORELIST\_s*' ..
195        'let localdict: dict<number> = {}\_s*' ..
196        '\d NEWDICT size 0\_s*' ..
197        '\d STORE $1\_s*' ..
198        'localdict\["a"\] = 456\_s*' ..
199        '\d\+ PUSHNR 456\_s*' ..
200        '\d\+ PUSHS "a"\_s*' ..
201        '\d\+ LOAD $1\_s*' ..
202        '\d\+ STOREDICT\_s*' ..
203        '\d\+ PUSHNR 0\_s*' ..
204        '\d\+ RETURN',
205        res)
206enddef
207
208def s:ListAssign()
209  let x: string
210  let y: string
211  let l: list<any>
212  [x, y; l] = g:stringlist
213enddef
214
215def Test_disassemble_list_assign()
216  let res = execute('disass s:ListAssign')
217  assert_match('<SNR>\d*_ListAssign\_s*' ..
218        'let x: string\_s*' ..
219        '\d PUSHS "\[NULL\]"\_s*' ..
220        '\d STORE $0\_s*' ..
221        'let y: string\_s*' ..
222        '\d PUSHS "\[NULL\]"\_s*' ..
223        '\d STORE $1\_s*' ..
224        'let l: list<any>\_s*' ..
225        '\d NEWLIST size 0\_s*' ..
226        '\d STORE $2\_s*' ..
227        '\[x, y; l\] = g:stringlist\_s*' ..
228        '\d LOADG g:stringlist\_s*' ..
229        '\d CHECKTYPE list stack\[-1\]\_s*' ..
230        '\d CHECKLEN >= 2\_s*' ..
231        '\d\+ ITEM 0\_s*' ..
232        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
233        '\d\+ STORE $0\_s*' ..
234        '\d\+ ITEM 1\_s*' ..
235        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
236        '\d\+ STORE $1\_s*' ..
237        '\d\+ SLICE 2\_s*' ..
238        '\d\+ STORE $2\_s*' ..
239        '\d\+ PUSHNR 0\_s*' ..
240        '\d\+ RETURN',
241        res)
242enddef
243
244def s:ScriptFuncUnlet()
245  g:somevar = "value"
246  unlet g:somevar
247  unlet! g:somevar
248  unlet $SOMEVAR
249enddef
250
251def Test_disassemble_unlet()
252  let res = execute('disass s:ScriptFuncUnlet')
253  assert_match('<SNR>\d*_ScriptFuncUnlet\_s*' ..
254        'g:somevar = "value"\_s*' ..
255        '\d PUSHS "value"\_s*' ..
256        '\d STOREG g:somevar\_s*' ..
257        'unlet g:somevar\_s*' ..
258        '\d UNLET g:somevar\_s*' ..
259        'unlet! g:somevar\_s*' ..
260        '\d UNLET! g:somevar\_s*' ..
261        'unlet $SOMEVAR\_s*' ..
262        '\d UNLETENV $SOMEVAR\_s*',
263        res)
264enddef
265
266def s:ScriptFuncTry()
267  try
268    echo "yes"
269  catch /fail/
270    echo "no"
271  finally
272    throw "end"
273  endtry
274enddef
275
276def Test_disassemble_try()
277  let res = execute('disass s:ScriptFuncTry')
278  assert_match('<SNR>\d*_ScriptFuncTry\_s*' ..
279        'try\_s*' ..
280        '\d TRY catch -> \d\+, finally -> \d\+\_s*' ..
281        'echo "yes"\_s*' ..
282        '\d PUSHS "yes"\_s*' ..
283        '\d ECHO 1\_s*' ..
284        'catch /fail/\_s*' ..
285        '\d JUMP -> \d\+\_s*' ..
286        '\d PUSH v:exception\_s*' ..
287        '\d PUSHS "fail"\_s*' ..
288        '\d COMPARESTRING =\~\_s*' ..
289        '\d JUMP_IF_FALSE -> \d\+\_s*' ..
290        '\d CATCH\_s*' ..
291        'echo "no"\_s*' ..
292        '\d\+ PUSHS "no"\_s*' ..
293        '\d\+ ECHO 1\_s*' ..
294        'finally\_s*' ..
295        'throw "end"\_s*' ..
296        '\d\+ PUSHS "end"\_s*' ..
297        '\d\+ THROW\_s*' ..
298        'endtry\_s*' ..
299        '\d\+ ENDTRY',
300        res)
301enddef
302
303def s:ScriptFuncNew()
304  let ll = [1, "two", 333]
305  let dd = #{one: 1, two: "val"}
306enddef
307
308def Test_disassemble_new()
309  let res = execute('disass s:ScriptFuncNew')
310  assert_match('<SNR>\d*_ScriptFuncNew\_s*' ..
311        'let ll = \[1, "two", 333\]\_s*' ..
312        '\d PUSHNR 1\_s*' ..
313        '\d PUSHS "two"\_s*' ..
314        '\d PUSHNR 333\_s*' ..
315        '\d NEWLIST size 3\_s*' ..
316        '\d STORE $0\_s*' ..
317        'let dd = #{one: 1, two: "val"}\_s*' ..
318        '\d PUSHS "one"\_s*' ..
319        '\d PUSHNR 1\_s*' ..
320        '\d PUSHS "two"\_s*' ..
321        '\d PUSHS "val"\_s*' ..
322        '\d NEWDICT size 2\_s*',
323        res)
324enddef
325
326def FuncWithArg(arg: any)
327  echo arg
328enddef
329
330func UserFunc()
331  echo 'nothing'
332endfunc
333
334func UserFuncWithArg(arg)
335  echo a:arg
336endfunc
337
338def s:ScriptFuncCall(): string
339  changenr()
340  char2nr("abc")
341  Test_disassemble_new()
342  FuncWithArg(343)
343  ScriptFuncNew()
344  s:ScriptFuncNew()
345  UserFunc()
346  UserFuncWithArg("foo")
347  let FuncRef = function("UserFunc")
348  FuncRef()
349  let FuncRefWithArg = function("UserFuncWithArg")
350  FuncRefWithArg("bar")
351  return "yes"
352enddef
353
354def Test_disassemble_call()
355  let res = execute('disass s:ScriptFuncCall')
356  assert_match('<SNR>\d\+_ScriptFuncCall\_s*' ..
357        'changenr()\_s*' ..
358        '\d BCALL changenr(argc 0)\_s*' ..
359        '\d DROP\_s*' ..
360        'char2nr("abc")\_s*' ..
361        '\d PUSHS "abc"\_s*' ..
362        '\d BCALL char2nr(argc 1)\_s*' ..
363        '\d DROP\_s*' ..
364        'Test_disassemble_new()\_s*' ..
365        '\d DCALL Test_disassemble_new(argc 0)\_s*' ..
366        '\d DROP\_s*' ..
367        'FuncWithArg(343)\_s*' ..
368        '\d\+ PUSHNR 343\_s*' ..
369        '\d\+ DCALL FuncWithArg(argc 1)\_s*' ..
370        '\d\+ DROP\_s*' ..
371        'ScriptFuncNew()\_s*' ..
372        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
373        '\d\+ DROP\_s*' ..
374        's:ScriptFuncNew()\_s*' ..
375        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
376        '\d\+ DROP\_s*' ..
377        'UserFunc()\_s*' ..
378        '\d\+ UCALL UserFunc(argc 0)\_s*' ..
379        '\d\+ DROP\_s*' ..
380        'UserFuncWithArg("foo")\_s*' ..
381        '\d\+ PUSHS "foo"\_s*' ..
382        '\d\+ UCALL UserFuncWithArg(argc 1)\_s*' ..
383        '\d\+ DROP\_s*' ..
384        'let FuncRef = function("UserFunc")\_s*' ..
385        '\d\+ PUSHS "UserFunc"\_s*' ..
386        '\d\+ BCALL function(argc 1)\_s*' ..
387        '\d\+ STORE $0\_s*' ..
388        'FuncRef()\_s*' ..
389        '\d\+ LOAD $\d\_s*' ..
390        '\d\+ PCALL (argc 0)\_s*' ..
391        '\d\+ DROP\_s*' ..
392        'let FuncRefWithArg = function("UserFuncWithArg")\_s*' ..
393        '\d\+ PUSHS "UserFuncWithArg"\_s*' ..
394        '\d\+ BCALL function(argc 1)\_s*' ..
395        '\d\+ STORE $1\_s*' ..
396        'FuncRefWithArg("bar")\_s*' ..
397        '\d\+ PUSHS "bar"\_s*' ..
398        '\d\+ LOAD $\d\_s*' ..
399        '\d\+ PCALL (argc 1)\_s*' ..
400        '\d\+ DROP\_s*' ..
401        'return "yes"\_s*' ..
402        '\d\+ PUSHS "yes"\_s*' ..
403        '\d\+ RETURN',
404        res)
405enddef
406
407def s:CreateRefs()
408  let local = 'a'
409  def Append(arg: string)
410    local ..= arg
411  enddef
412  g:Append = Append
413  def Get(): string
414    return local
415  enddef
416  g:Get = Get
417enddef
418
419def Test_disassemble_closure()
420  CreateRefs()
421  let res = execute('disass g:Append')
422  assert_match('<lambda>\d\_s*' ..
423        'local ..= arg\_s*' ..
424        '\d LOADOUTER $0\_s*' ..
425        '\d LOAD arg\[-1\]\_s*' ..
426        '\d CONCAT\_s*' ..
427        '\d STOREOUTER $0\_s*' ..
428        '\d PUSHNR 0\_s*' ..
429        '\d RETURN',
430        res)
431
432  res = execute('disass g:Get')
433  assert_match('<lambda>\d\_s*' ..
434        'return local\_s*' ..
435        '\d LOADOUTER $0\_s*' ..
436        '\d RETURN',
437        res)
438
439  unlet g:Append
440  unlet g:Get
441enddef
442
443
444def EchoArg(arg: string): string
445  return arg
446enddef
447def RefThis(): func
448  return function('EchoArg')
449enddef
450def s:ScriptPCall()
451  RefThis()("text")
452enddef
453
454def Test_disassemble_pcall()
455  let res = execute('disass s:ScriptPCall')
456  assert_match('<SNR>\d\+_ScriptPCall\_s*' ..
457        'RefThis()("text")\_s*' ..
458        '\d DCALL RefThis(argc 0)\_s*' ..
459        '\d PUSHS "text"\_s*' ..
460        '\d PCALL top (argc 1)\_s*' ..
461        '\d PCALL end\_s*' ..
462        '\d DROP\_s*' ..
463        '\d PUSHNR 0\_s*' ..
464        '\d RETURN',
465        res)
466enddef
467
468
469def s:FuncWithForwardCall(): string
470  return g:DefinedLater("yes")
471enddef
472
473def DefinedLater(arg: string): string
474  return arg
475enddef
476
477def Test_disassemble_update_instr()
478  let res = execute('disass s:FuncWithForwardCall')
479  assert_match('FuncWithForwardCall\_s*' ..
480        'return g:DefinedLater("yes")\_s*' ..
481        '\d PUSHS "yes"\_s*' ..
482        '\d DCALL DefinedLater(argc 1)\_s*' ..
483        '\d RETURN',
484        res)
485
486  # Calling the function will change UCALL into the faster DCALL
487  assert_equal('yes', FuncWithForwardCall())
488
489  res = execute('disass s:FuncWithForwardCall')
490  assert_match('FuncWithForwardCall\_s*' ..
491        'return g:DefinedLater("yes")\_s*' ..
492        '\d PUSHS "yes"\_s*' ..
493        '\d DCALL DefinedLater(argc 1)\_s*' ..
494        '\d RETURN',
495        res)
496enddef
497
498
499def FuncWithDefault(arg: string = 'default'): string
500  return arg
501enddef
502
503def Test_disassemble_call_default()
504  let res = execute('disass FuncWithDefault')
505  assert_match('FuncWithDefault\_s*' ..
506        '\d PUSHS "default"\_s*' ..
507        '\d STORE arg\[-1]\_s*' ..
508        'return arg\_s*' ..
509        '\d LOAD arg\[-1]\_s*' ..
510        '\d RETURN',
511        res)
512enddef
513
514
515def HasEval()
516  if has("eval")
517    echo "yes"
518  else
519    echo "no"
520  endif
521enddef
522
523def HasNothing()
524  if has("nothing")
525    echo "yes"
526  else
527    echo "no"
528  endif
529enddef
530
531def HasSomething()
532  if has("nothing")
533    echo "nothing"
534  elseif has("something")
535    echo "something"
536  elseif has("eval")
537    echo "eval"
538  elseif has("less")
539    echo "less"
540  endif
541enddef
542
543def Test_disassemble_const_expr()
544  assert_equal("\nyes", execute('call HasEval()'))
545  let instr = execute('disassemble HasEval')
546  assert_match('HasEval\_s*' ..
547        'if has("eval")\_s*' ..
548        'echo "yes"\_s*' ..
549        '\d PUSHS "yes"\_s*' ..
550        '\d ECHO 1\_s*' ..
551        'else\_s*' ..
552        'echo "no"\_s*' ..
553        'endif\_s*',
554        instr)
555  assert_notmatch('JUMP', instr)
556
557  assert_equal("\nno", execute('call HasNothing()'))
558  instr = execute('disassemble HasNothing')
559  assert_match('HasNothing\_s*' ..
560        'if has("nothing")\_s*' ..
561        'echo "yes"\_s*' ..
562        'else\_s*' ..
563        'echo "no"\_s*' ..
564        '\d PUSHS "no"\_s*' ..
565        '\d ECHO 1\_s*' ..
566        'endif',
567        instr)
568  assert_notmatch('PUSHS "yes"', instr)
569  assert_notmatch('JUMP', instr)
570
571  assert_equal("\neval", execute('call HasSomething()'))
572  instr = execute('disassemble HasSomething')
573  assert_match('HasSomething.*' ..
574        'if has("nothing")\_s*' ..
575        'echo "nothing"\_s*' ..
576        'elseif has("something")\_s*' ..
577        'echo "something"\_s*' ..
578        'elseif has("eval")\_s*' ..
579        'echo "eval"\_s*' ..
580        '\d PUSHS "eval"\_s*' ..
581        '\d ECHO 1\_s*' ..
582        'elseif has("less").*' ..
583        'echo "less"\_s*' ..
584        'endif',
585        instr)
586  assert_notmatch('PUSHS "nothing"', instr)
587  assert_notmatch('PUSHS "something"', instr)
588  assert_notmatch('PUSHS "less"', instr)
589  assert_notmatch('JUMP', instr)
590enddef
591
592def ReturnInIf(): string
593  if g:cond
594    return "yes"
595  else
596    return "no"
597  endif
598enddef
599
600def Test_disassemble_return_in_if()
601  let instr = execute('disassemble ReturnInIf')
602  assert_match('ReturnInIf\_s*' ..
603        'if g:cond\_s*' ..
604        '0 LOADG g:cond\_s*' ..
605        '1 JUMP_IF_FALSE -> 4\_s*' ..
606        'return "yes"\_s*' ..
607        '2 PUSHS "yes"\_s*' ..
608        '3 RETURN\_s*' ..
609        'else\_s*' ..
610        ' return "no"\_s*' ..
611        '4 PUSHS "no"\_s*' ..
612        '5 RETURN$',
613        instr)
614enddef
615
616def WithFunc()
617  let Funky1: func
618  let Funky2: func = function("len")
619  let Party2: func = funcref("UserFunc")
620enddef
621
622def Test_disassemble_function()
623  let instr = execute('disassemble WithFunc')
624  assert_match('WithFunc\_s*' ..
625        'let Funky1: func\_s*' ..
626        '0 PUSHFUNC "\[none]"\_s*' ..
627        '1 STORE $0\_s*' ..
628        'let Funky2: func = function("len")\_s*' ..
629        '2 PUSHS "len"\_s*' ..
630        '3 BCALL function(argc 1)\_s*' ..
631        '4 STORE $1\_s*' ..
632        'let Party2: func = funcref("UserFunc")\_s*' ..
633        '\d PUSHS "UserFunc"\_s*' ..
634        '\d BCALL funcref(argc 1)\_s*' ..
635        '\d STORE $2\_s*' ..
636        '\d PUSHNR 0\_s*' ..
637        '\d RETURN',
638        instr)
639enddef
640
641if has('channel')
642  def WithChannel()
643    let job1: job
644    let job2: job = job_start("donothing")
645    let chan1: channel
646  enddef
647endif
648
649def Test_disassemble_channel()
650  CheckFeature channel
651
652  let instr = execute('disassemble WithChannel')
653  assert_match('WithChannel\_s*' ..
654        'let job1: job\_s*' ..
655        '\d PUSHJOB "no process"\_s*' ..
656        '\d STORE $0\_s*' ..
657        'let job2: job = job_start("donothing")\_s*' ..
658        '\d PUSHS "donothing"\_s*' ..
659        '\d BCALL job_start(argc 1)\_s*' ..
660        '\d STORE $1\_s*' ..
661        'let chan1: channel\_s*' ..
662        '\d PUSHCHANNEL 0\_s*' ..
663        '\d STORE $2\_s*' ..
664        '\d PUSHNR 0\_s*' ..
665        '\d RETURN',
666        instr)
667enddef
668
669def WithLambda(): string
670  let F = {a -> "X" .. a .. "X"}
671  return F("x")
672enddef
673
674def Test_disassemble_lambda()
675  assert_equal("XxX", WithLambda())
676  let instr = execute('disassemble WithLambda')
677  assert_match('WithLambda\_s*' ..
678        'let F = {a -> "X" .. a .. "X"}\_s*' ..
679        '\d FUNCREF <lambda>\d\+ $1\_s*' ..
680        '\d STORE $0\_s*' ..
681        'return F("x")\_s*' ..
682        '\d PUSHS "x"\_s*' ..
683        '\d LOAD $0\_s*' ..
684        '\d PCALL (argc 1)\_s*' ..
685        '\d RETURN',
686        instr)
687
688   let name = substitute(instr, '.*\(<lambda>\d\+\).*', '\1', '')
689   instr = execute('disassemble ' .. name)
690   assert_match('<lambda>\d\+\_s*' ..
691        'return "X" .. a .. "X"\_s*' ..
692        '\d PUSHS "X"\_s*' ..
693        '\d LOAD arg\[-1\]\_s*' ..
694        '\d 2STRING stack\[-1\]\_s*' ..
695        '\d CONCAT\_s*' ..
696        '\d PUSHS "X"\_s*' ..
697        '\d CONCAT\_s*' ..
698        '\d RETURN',
699        instr)
700enddef
701
702def NestedOuter()
703  def g:Inner()
704    echomsg "inner"
705  enddef
706enddef
707
708def Test_nested_func()
709   let instr = execute('disassemble NestedOuter')
710   assert_match('NestedOuter\_s*' ..
711        'def g:Inner()\_s*' ..
712        'echomsg "inner"\_s*' ..
713        'enddef\_s*' ..
714        '\d NEWFUNC <lambda>\d\+ Inner\_s*' ..
715        '\d PUSHNR 0\_s*' ..
716        '\d RETURN',
717        instr)
718enddef
719
720def AndOr(arg: any): string
721  if arg == 1 && arg != 2 || arg == 4
722    return 'yes'
723  endif
724  return 'no'
725enddef
726
727def Test_disassemble_and_or()
728  assert_equal("yes", AndOr(1))
729  assert_equal("no", AndOr(2))
730  assert_equal("yes", AndOr(4))
731  let instr = execute('disassemble AndOr')
732  assert_match('AndOr\_s*' ..
733        'if arg == 1 && arg != 2 || arg == 4\_s*' ..
734        '\d LOAD arg\[-1]\_s*' ..
735        '\d PUSHNR 1\_s*' ..
736        '\d COMPAREANY ==\_s*' ..
737        '\d JUMP_AND_KEEP_IF_FALSE -> \d\+\_s*' ..
738        '\d LOAD arg\[-1]\_s*' ..
739        '\d PUSHNR 2\_s*' ..
740        '\d COMPAREANY !=\_s*' ..
741        '\d JUMP_AND_KEEP_IF_TRUE -> \d\+\_s*' ..
742        '\d LOAD arg\[-1]\_s*' ..
743        '\d\+ PUSHNR 4\_s*' ..
744        '\d\+ COMPAREANY ==\_s*' ..
745        '\d\+ JUMP_IF_FALSE -> \d\+',
746        instr)
747enddef
748
749def ForLoop(): list<number>
750  let res: list<number>
751  for i in range(3)
752    res->add(i)
753  endfor
754  return res
755enddef
756
757def Test_disassemble_for_loop()
758  assert_equal([0, 1, 2], ForLoop())
759  let instr = execute('disassemble ForLoop')
760  assert_match('ForLoop\_s*' ..
761        'let res: list<number>\_s*' ..
762        '\d NEWLIST size 0\_s*' ..
763        '\d STORE $0\_s*' ..
764        'for i in range(3)\_s*' ..
765        '\d STORE -1 in $1\_s*' ..
766        '\d PUSHNR 3\_s*' ..
767        '\d BCALL range(argc 1)\_s*' ..
768        '\d FOR $1 -> \d\+\_s*' ..
769        '\d STORE $2\_s*' ..
770        'res->add(i)\_s*' ..
771        '\d LOAD $0\_s*' ..
772        '\d LOAD $2\_s*' ..
773        '\d\+ BCALL add(argc 2)\_s*' ..
774        '\d\+ DROP\_s*' ..
775        'endfor\_s*' ..
776        '\d\+ JUMP -> \d\+\_s*' ..
777        '\d\+ DROP',
778        instr)
779enddef
780
781def ForLoopEval(): string
782  let res = ""
783  for str in eval('["one", "two"]')
784    res ..= str
785  endfor
786  return res
787enddef
788
789def Test_disassemble_for_loop_eval()
790  assert_equal('onetwo', ForLoopEval())
791  let instr = execute('disassemble ForLoopEval')
792  assert_match('ForLoopEval\_s*' ..
793        'let res = ""\_s*' ..
794        '\d PUSHS ""\_s*' ..
795        '\d STORE $0\_s*' ..
796        'for str in eval(''\["one", "two"\]'')\_s*' ..
797        '\d STORE -1 in $1\_s*' ..
798        '\d PUSHS "\["one", "two"\]"\_s*' ..
799        '\d BCALL eval(argc 1)\_s*' ..
800        '\d CHECKTYPE list stack\[-1\]\_s*' ..
801        '\d FOR $1 -> \d\+\_s*' ..
802        '\d STORE $2\_s*' ..
803        'res ..= str\_s*' ..
804        '\d\+ LOAD $0\_s*' ..
805        '\d\+ LOAD $2\_s*' ..
806        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
807        '\d\+ CONCAT\_s*' ..
808        '\d\+ STORE $0\_s*' ..
809        'endfor\_s*' ..
810        '\d\+ JUMP -> 6\_s*' ..
811        '\d\+ DROP\_s*' ..
812        'return res\_s*' ..
813        '\d\+ LOAD $0\_s*' ..
814        '\d\+ RETURN',
815        instr)
816enddef
817
818let g:number = 42
819
820def Computing()
821  let nr = 3
822  let nrres = nr + 7
823  nrres = nr - 7
824  nrres = nr * 7
825  nrres = nr / 7
826  nrres = nr % 7
827
828  let anyres = g:number + 7
829  anyres = g:number - 7
830  anyres = g:number * 7
831  anyres = g:number / 7
832  anyres = g:number % 7
833
834  if has('float')
835    let fl = 3.0
836    let flres = fl + 7.0
837    flres = fl - 7.0
838    flres = fl * 7.0
839    flres = fl / 7.0
840  endif
841enddef
842
843def Test_disassemble_computing()
844  let instr = execute('disassemble Computing')
845  assert_match('Computing.*' ..
846        'let nr = 3.*' ..
847        '\d STORE 3 in $0.*' ..
848        'let nrres = nr + 7.*' ..
849        '\d LOAD $0.*' ..
850        '\d PUSHNR 7.*' ..
851        '\d OPNR +.*' ..
852        '\d STORE $1.*' ..
853        'nrres = nr - 7.*' ..
854        '\d OPNR -.*' ..
855        'nrres = nr \* 7.*' ..
856        '\d OPNR \*.*' ..
857        'nrres = nr / 7.*' ..
858        '\d OPNR /.*' ..
859        'nrres = nr % 7.*' ..
860        '\d OPNR %.*' ..
861        'let anyres = g:number + 7.*' ..
862        '\d LOADG g:number.*' ..
863        '\d PUSHNR 7.*' ..
864        '\d OPANY +.*' ..
865        '\d STORE $2.*' ..
866        'anyres = g:number - 7.*' ..
867        '\d OPANY -.*' ..
868        'anyres = g:number \* 7.*' ..
869        '\d OPANY \*.*' ..
870        'anyres = g:number / 7.*' ..
871        '\d OPANY /.*' ..
872        'anyres = g:number % 7.*' ..
873        '\d OPANY %.*',
874        instr)
875  if has('float')
876    assert_match('Computing.*' ..
877        'let fl = 3.0.*' ..
878        '\d PUSHF 3.0.*' ..
879        '\d STORE $3.*' ..
880        'let flres = fl + 7.0.*' ..
881        '\d LOAD $3.*' ..
882        '\d PUSHF 7.0.*' ..
883        '\d OPFLOAT +.*' ..
884        '\d STORE $4.*' ..
885        'flres = fl - 7.0.*' ..
886        '\d OPFLOAT -.*' ..
887        'flres = fl \* 7.0.*' ..
888        '\d OPFLOAT \*.*' ..
889        'flres = fl / 7.0.*' ..
890        '\d OPFLOAT /.*',
891        instr)
892  endif
893enddef
894
895def AddListBlob()
896  let reslist = [1, 2] + [3, 4]
897  let resblob = 0z1122 + 0z3344
898enddef
899
900def Test_disassemble_add_list_blob()
901  let instr = execute('disassemble AddListBlob')
902  assert_match('AddListBlob.*' ..
903        'let reslist = \[1, 2] + \[3, 4].*' ..
904        '\d PUSHNR 1.*' ..
905        '\d PUSHNR 2.*' ..
906        '\d NEWLIST size 2.*' ..
907        '\d PUSHNR 3.*' ..
908        '\d PUSHNR 4.*' ..
909        '\d NEWLIST size 2.*' ..
910        '\d ADDLIST.*' ..
911        '\d STORE $.*.*' ..
912        'let resblob = 0z1122 + 0z3344.*' ..
913        '\d PUSHBLOB 0z1122.*' ..
914        '\d PUSHBLOB 0z3344.*' ..
915        '\d ADDBLOB.*' ..
916        '\d STORE $.*',
917        instr)
918enddef
919
920let g:aa = 'aa'
921def ConcatString(): string
922  let res = g:aa .. "bb"
923  return res
924enddef
925
926def Test_disassemble_concat()
927  let instr = execute('disassemble ConcatString')
928  assert_match('ConcatString.*' ..
929        'let res = g:aa .. "bb".*' ..
930        '\d LOADG g:aa.*' ..
931        '\d PUSHS "bb".*' ..
932        '\d 2STRING stack\[-2].*' ..
933        '\d CONCAT.*' ..
934        '\d STORE $.*',
935        instr)
936  assert_equal('aabb', ConcatString())
937enddef
938
939def StringIndex(): number
940  let s = "abcd"
941  let res = s[1]
942  return res
943enddef
944
945def Test_disassemble_string_index()
946  let instr = execute('disassemble StringIndex')
947  assert_match('StringIndex\_s*' ..
948        'let s = "abcd"\_s*' ..
949        '\d PUSHS "abcd"\_s*' ..
950        '\d STORE $0\_s*' ..
951        'let res = s\[1]\_s*' ..
952        '\d LOAD $0\_s*' ..
953        '\d PUSHNR 1\_s*' ..
954        '\d STRINDEX\_s*' ..
955        '\d STORE $1\_s*',
956        instr)
957  assert_equal('b', StringIndex())
958enddef
959
960def ListIndex(): number
961  let l = [1, 2, 3]
962  let res = l[1]
963  return res
964enddef
965
966def Test_disassemble_list_index()
967  let instr = execute('disassemble ListIndex')
968  assert_match('ListIndex\_s*' ..
969        'let l = \[1, 2, 3]\_s*' ..
970        '\d PUSHNR 1\_s*' ..
971        '\d PUSHNR 2\_s*' ..
972        '\d PUSHNR 3\_s*' ..
973        '\d NEWLIST size 3\_s*' ..
974        '\d STORE $0\_s*' ..
975        'let res = l\[1]\_s*' ..
976        '\d LOAD $0\_s*' ..
977        '\d PUSHNR 1\_s*' ..
978        '\d LISTINDEX\_s*' ..
979        '\d STORE $1\_s*',
980        instr)
981  assert_equal(2, ListIndex())
982enddef
983
984def DictMember(): number
985  let d = #{item: 1}
986  let res = d.item
987  res = d["item"]
988  return res
989enddef
990
991def Test_disassemble_dict_member()
992  let instr = execute('disassemble DictMember')
993  assert_match('DictMember\_s*' ..
994        'let d = #{item: 1}\_s*' ..
995        '\d PUSHS "item"\_s*' ..
996        '\d PUSHNR 1\_s*' ..
997        '\d NEWDICT size 1\_s*' ..
998        '\d STORE $0\_s*' ..
999        'let res = d.item\_s*' ..
1000        '\d\+ LOAD $0\_s*' ..
1001        '\d\+ MEMBER item\_s*' ..
1002        '\d\+ STORE $1\_s*' ..
1003        'res = d\["item"\]\_s*' ..
1004        '\d\+ LOAD $0\_s*' ..
1005        '\d\+ PUSHS "item"\_s*' ..
1006        '\d\+ MEMBER\_s*' ..
1007        '\d\+ STORE $1\_s*',
1008        instr)
1009  call assert_equal(1, DictMember())
1010enddef
1011
1012def NegateNumber(): number
1013  let nr = 9
1014  let plus = +nr
1015  let res = -nr
1016  return res
1017enddef
1018
1019def Test_disassemble_negate_number()
1020  let instr = execute('disassemble NegateNumber')
1021  assert_match('NegateNumber\_s*' ..
1022        'let nr = 9\_s*' ..
1023        '\d STORE 9 in $0\_s*' ..
1024        'let plus = +nr\_s*' ..
1025        '\d LOAD $0\_s*' ..
1026        '\d CHECKNR\_s*' ..
1027        '\d STORE $1\_s*' ..
1028        'let res = -nr\_s*' ..
1029        '\d LOAD $0\_s*' ..
1030        '\d NEGATENR\_s*' ..
1031        '\d STORE $2\_s*',
1032        instr)
1033  call assert_equal(-9, NegateNumber())
1034enddef
1035
1036def InvertBool(): bool
1037  let flag = true
1038  let invert = !flag
1039  let res = !!flag
1040  return res
1041enddef
1042
1043def Test_disassemble_invert_bool()
1044  let instr = execute('disassemble InvertBool')
1045  assert_match('InvertBool\_s*' ..
1046        'let flag = true\_s*' ..
1047        '\d PUSH v:true\_s*' ..
1048        '\d STORE $0\_s*' ..
1049        'let invert = !flag\_s*' ..
1050        '\d LOAD $0\_s*' ..
1051        '\d INVERT (!val)\_s*' ..
1052        '\d STORE $1\_s*' ..
1053        'let res = !!flag\_s*' ..
1054        '\d LOAD $0\_s*' ..
1055        '\d 2BOOL (!!val)\_s*' ..
1056        '\d STORE $2\_s*',
1057        instr)
1058  call assert_equal(true, InvertBool())
1059enddef
1060
1061def Test_disassemble_compare()
1062  let cases = [
1063        ['true == isFalse', 'COMPAREBOOL =='],
1064        ['true != isFalse', 'COMPAREBOOL !='],
1065        ['v:none == isNull', 'COMPARESPECIAL =='],
1066        ['v:none != isNull', 'COMPARESPECIAL !='],
1067
1068        ['111 == aNumber', 'COMPARENR =='],
1069        ['111 != aNumber', 'COMPARENR !='],
1070        ['111 > aNumber', 'COMPARENR >'],
1071        ['111 < aNumber', 'COMPARENR <'],
1072        ['111 >= aNumber', 'COMPARENR >='],
1073        ['111 <= aNumber', 'COMPARENR <='],
1074        ['111 =~ aNumber', 'COMPARENR =\~'],
1075        ['111 !~ aNumber', 'COMPARENR !\~'],
1076
1077        ['"xx" != aString', 'COMPARESTRING !='],
1078        ['"xx" > aString', 'COMPARESTRING >'],
1079        ['"xx" < aString', 'COMPARESTRING <'],
1080        ['"xx" >= aString', 'COMPARESTRING >='],
1081        ['"xx" <= aString', 'COMPARESTRING <='],
1082        ['"xx" =~ aString', 'COMPARESTRING =\~'],
1083        ['"xx" !~ aString', 'COMPARESTRING !\~'],
1084        ['"xx" is aString', 'COMPARESTRING is'],
1085        ['"xx" isnot aString', 'COMPARESTRING isnot'],
1086
1087        ['0z11 == aBlob', 'COMPAREBLOB =='],
1088        ['0z11 != aBlob', 'COMPAREBLOB !='],
1089        ['0z11 is aBlob', 'COMPAREBLOB is'],
1090        ['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
1091
1092        ['[1, 2] == aList', 'COMPARELIST =='],
1093        ['[1, 2] != aList', 'COMPARELIST !='],
1094        ['[1, 2] is aList', 'COMPARELIST is'],
1095        ['[1, 2] isnot aList', 'COMPARELIST isnot'],
1096
1097        ['#{a: 1} == aDict', 'COMPAREDICT =='],
1098        ['#{a: 1} != aDict', 'COMPAREDICT !='],
1099        ['#{a: 1} is aDict', 'COMPAREDICT is'],
1100        ['#{a: 1} isnot aDict', 'COMPAREDICT isnot'],
1101
1102        ['{->33} == {->44}', 'COMPAREFUNC =='],
1103        ['{->33} != {->44}', 'COMPAREFUNC !='],
1104        ['{->33} is {->44}', 'COMPAREFUNC is'],
1105        ['{->33} isnot {->44}', 'COMPAREFUNC isnot'],
1106
1107        ['77 == g:xx', 'COMPAREANY =='],
1108        ['77 != g:xx', 'COMPAREANY !='],
1109        ['77 > g:xx', 'COMPAREANY >'],
1110        ['77 < g:xx', 'COMPAREANY <'],
1111        ['77 >= g:xx', 'COMPAREANY >='],
1112        ['77 <= g:xx', 'COMPAREANY <='],
1113        ['77 =~ g:xx', 'COMPAREANY =\~'],
1114        ['77 !~ g:xx', 'COMPAREANY !\~'],
1115        ['77 is g:xx', 'COMPAREANY is'],
1116        ['77 isnot g:xx', 'COMPAREANY isnot'],
1117        ]
1118  let floatDecl = ''
1119  if has('float')
1120    cases->extend([
1121        ['1.1 == aFloat', 'COMPAREFLOAT =='],
1122        ['1.1 != aFloat', 'COMPAREFLOAT !='],
1123        ['1.1 > aFloat', 'COMPAREFLOAT >'],
1124        ['1.1 < aFloat', 'COMPAREFLOAT <'],
1125        ['1.1 >= aFloat', 'COMPAREFLOAT >='],
1126        ['1.1 <= aFloat', 'COMPAREFLOAT <='],
1127        ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
1128        ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
1129        ])
1130    floatDecl = 'let aFloat = 2.2'
1131  endif
1132
1133  let nr = 1
1134  for case in cases
1135    # declare local variables to get a non-constant with the right type
1136    writefile(['def TestCase' .. nr .. '()',
1137             '  let isFalse = false',
1138             '  let isNull = v:null',
1139             '  let aNumber = 222',
1140             '  let aString = "yy"',
1141             '  let aBlob = 0z22',
1142             '  let aList = [3, 4]',
1143             '  let aDict = #{x: 2}',
1144             floatDecl,
1145             '  if ' .. case[0],
1146             '    echo 42'
1147             '  endif',
1148             'enddef'], 'Xdisassemble')
1149    source Xdisassemble
1150    let instr = execute('disassemble TestCase' .. nr)
1151    assert_match('TestCase' .. nr .. '.*' ..
1152        'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1153        '\d \(PUSH\|FUNCREF\).*' ..
1154        '\d \(PUSH\|FUNCREF\|LOAD\).*' ..
1155        '\d ' .. case[1] .. '.*' ..
1156        '\d JUMP_IF_FALSE -> \d\+.*',
1157        instr)
1158
1159    nr += 1
1160  endfor
1161
1162  delete('Xdisassemble')
1163enddef
1164
1165def Test_disassemble_compare_const()
1166  let cases = [
1167        ['"xx" == "yy"', false],
1168        ['"aa" == "aa"', true],
1169        ['has("eval") ? true : false', true],
1170        ['has("asdf") ? true : false', false],
1171        ]
1172
1173  let nr = 1
1174  for case in cases
1175    writefile(['def TestCase' .. nr .. '()',
1176             '  if ' .. case[0],
1177             '    echo 42'
1178             '  endif',
1179             'enddef'], 'Xdisassemble')
1180    source Xdisassemble
1181    let instr = execute('disassemble TestCase' .. nr)
1182    if case[1]
1183      # condition true, "echo 42" executed
1184      assert_match('TestCase' .. nr .. '.*' ..
1185          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1186          '\d PUSHNR 42.*' ..
1187          '\d ECHO 1.*' ..
1188          '\d PUSHNR 0.*' ..
1189          '\d RETURN.*',
1190          instr)
1191    else
1192      # condition false, function just returns
1193      assert_match('TestCase' .. nr .. '.*' ..
1194          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' ..
1195          'echo 42[ \n]*' ..
1196          'endif[ \n]*' ..
1197          '\s*\d PUSHNR 0.*' ..
1198          '\d RETURN.*',
1199          instr)
1200    endif
1201
1202    nr += 1
1203  endfor
1204
1205  delete('Xdisassemble')
1206enddef
1207
1208def s:Execute()
1209  execute 'help vim9.txt'
1210  let cmd = 'help vim9.txt'
1211  execute cmd
1212  let tag = 'vim9.txt'
1213  execute 'help ' .. tag
1214enddef
1215
1216def Test_disassemble_execute()
1217  let res = execute('disass s:Execute')
1218  assert_match('\<SNR>\d*_Execute\_s*' ..
1219        "execute 'help vim9.txt'\\_s*" ..
1220        '\d PUSHS "help vim9.txt"\_s*' ..
1221        '\d EXECUTE 1\_s*' ..
1222        "let cmd = 'help vim9.txt'\\_s*" ..
1223        '\d PUSHS "help vim9.txt"\_s*' ..
1224        '\d STORE $0\_s*' ..
1225        'execute cmd\_s*' ..
1226        '\d LOAD $0\_s*' ..
1227        '\d EXECUTE 1\_s*' ..
1228        "let tag = 'vim9.txt'\\_s*" ..
1229        '\d PUSHS "vim9.txt"\_s*' ..
1230        '\d STORE $1\_s*' ..
1231        "execute 'help ' .. tag\\_s*" ..
1232        '\d\+ PUSHS "help "\_s*' ..
1233        '\d\+ LOAD $1\_s*' ..
1234        '\d\+ CONCAT\_s*' ..
1235        '\d\+ EXECUTE 1\_s*' ..
1236        '\d\+ PUSHNR 0\_s*' ..
1237        '\d\+ RETURN',
1238        res)
1239enddef
1240
1241def s:Echomsg()
1242  echomsg 'some' 'message'
1243  echoerr 'went' .. 'wrong'
1244enddef
1245
1246def Test_disassemble_echomsg()
1247  let res = execute('disass s:Echomsg')
1248  assert_match('\<SNR>\d*_Echomsg\_s*' ..
1249        "echomsg 'some' 'message'\\_s*" ..
1250        '\d PUSHS "some"\_s*' ..
1251        '\d PUSHS "message"\_s*' ..
1252        '\d ECHOMSG 2\_s*' ..
1253        "echoerr 'went' .. 'wrong'\\_s*" ..
1254        '\d PUSHS "wentwrong"\_s*' ..
1255        '\d ECHOERR 1\_s*' ..
1256        '\d PUSHNR 0\_s*' ..
1257        '\d RETURN',
1258        res)
1259enddef
1260
1261def SomeStringArg(arg: string)
1262  echo arg
1263enddef
1264
1265def SomeAnyArg(arg: any)
1266  echo arg
1267enddef
1268
1269def SomeStringArgAndReturn(arg: string): string
1270  return arg
1271enddef
1272
1273def Test_display_func()
1274  let res1 = execute('function SomeStringArg')
1275  assert_match('.* def SomeStringArg(arg: string)\_s*' ..
1276        '\d *echo arg.*' ..
1277        ' *enddef',
1278        res1)
1279
1280  let res2 = execute('function SomeAnyArg')
1281  assert_match('.* def SomeAnyArg(arg: any)\_s*' ..
1282        '\d *echo arg\_s*' ..
1283        ' *enddef',
1284        res2)
1285
1286  let res3 = execute('function SomeStringArgAndReturn')
1287  assert_match('.* def SomeStringArgAndReturn(arg: string): string\_s*' ..
1288        '\d *return arg\_s*' ..
1289        ' *enddef',
1290        res3)
1291enddef
1292
1293def Test_vim9script_forward_func()
1294  let lines =<< trim END
1295    vim9script
1296    def FuncOne(): string
1297      return FuncTwo()
1298    enddef
1299    def FuncTwo(): string
1300      return 'two'
1301    enddef
1302    g:res_FuncOne = execute('disass FuncOne')
1303  END
1304  writefile(lines, 'Xdisassemble')
1305  source Xdisassemble
1306
1307  # check that the first function calls the second with DCALL
1308  assert_match('\<SNR>\d*_FuncOne\_s*' ..
1309        'return FuncTwo()\_s*' ..
1310        '\d DCALL <SNR>\d\+_FuncTwo(argc 0)\_s*' ..
1311        '\d RETURN',
1312        g:res_FuncOne)
1313
1314  delete('Xdisassemble')
1315  unlet g:res_FuncOne
1316enddef
1317
1318def s:ConcatStrings(): string
1319  return 'one' .. 'two' .. 'three'
1320enddef
1321
1322def s:ComputeConst(): number
1323  return 2 + 3 * 4 / 6 + 7
1324enddef
1325
1326def s:ComputeConstParen(): number
1327  return ((2 + 4) * (8 / 2)) / (3 + 4)
1328enddef
1329
1330def Test_simplify_const_expr()
1331  let res = execute('disass s:ConcatStrings')
1332  assert_match('<SNR>\d*_ConcatStrings\_s*' ..
1333        "return 'one' .. 'two' .. 'three'\\_s*" ..
1334        '\d PUSHS "onetwothree"\_s*' ..
1335        '\d RETURN',
1336        res)
1337
1338  res = execute('disass s:ComputeConst')
1339  assert_match('<SNR>\d*_ComputeConst\_s*' ..
1340        'return 2 + 3 \* 4 / 6 + 7\_s*' ..
1341        '\d PUSHNR 11\_s*' ..
1342        '\d RETURN',
1343        res)
1344
1345  res = execute('disass s:ComputeConstParen')
1346  assert_match('<SNR>\d*_ComputeConstParen\_s*' ..
1347        'return ((2 + 4) \* (8 / 2)) / (3 + 4)\_s*' ..
1348        '\d PUSHNR 3\>\_s*' ..
1349        '\d RETURN',
1350        res)
1351enddef
1352
1353def s:CallAppend()
1354  eval "some text"->append(2)
1355enddef
1356
1357def Test_shuffle()
1358  let res = execute('disass s:CallAppend')
1359  assert_match('<SNR>\d*_CallAppend\_s*' ..
1360        'eval "some text"->append(2)\_s*' ..
1361        '\d PUSHS "some text"\_s*' ..
1362        '\d PUSHNR 2\_s*' ..
1363        '\d SHUFFLE 2 up 1\_s*' ..
1364        '\d BCALL append(argc 2)\_s*' ..
1365        '\d DROP\_s*' ..
1366        '\d PUSHNR 0\_s*' ..
1367        '\d RETURN',
1368        res)
1369enddef
1370
1371" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
1372