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  var local = 1
17  buffers
18  echo
19  echo arg
20  echo local
21  echo &lines
22  echo v:version
23  echo s:scriptvar
24  echo g:globalvar
25  echo get(g:, "global")
26  echo g:auto#var
27  echo b:buffervar
28  echo get(b:, "buffer")
29  echo w:windowvar
30  echo get(w:, "window")
31  echo t:tabpagevar
32  echo get(t:, "tab")
33  echo &tabstop
34  echo $ENVVAR
35  echo @z
36enddef
37
38def Test_disassemble_load()
39  assert_fails('disass NoFunc', 'E1061:')
40  assert_fails('disass NotCompiled', 'E1091:')
41  assert_fails('disass', 'E471:')
42  assert_fails('disass [', 'E475:')
43  assert_fails('disass 234', 'E129:')
44  assert_fails('disass <XX>foo', 'E129:')
45
46  var res = execute('disass s:ScriptFuncLoad')
47  assert_match('<SNR>\d*_ScriptFuncLoad.*' ..
48        'buffers\_s*' ..
49        '\d\+ EXEC \+buffers\_s*' ..
50        'echo\_s*' ..
51        'echo arg\_s*' ..
52        '\d\+ LOAD arg\[-1\]\_s*' ..
53        '\d\+ ECHO 1\_s*' ..
54        'echo local\_s*' ..
55        '\d\+ LOAD $0\_s*' ..
56        '\d\+ ECHO 1\_s*' ..
57        'echo &lines\_s*' ..
58        '\d\+ LOADOPT &lines\_s*' ..
59        '\d\+ ECHO 1\_s*' ..
60        'echo v:version\_s*' ..
61        '\d\+ LOADV v:version\_s*' ..
62        '\d\+ ECHO 1\_s*' ..
63        'echo s:scriptvar\_s*' ..
64        '\d\+ LOADS s:scriptvar from .*test_vim9_disassemble.vim\_s*' ..
65        '\d\+ ECHO 1\_s*' ..
66        'echo g:globalvar\_s*' ..
67        '\d\+ LOADG g:globalvar\_s*' ..
68        '\d\+ ECHO 1\_s*' ..
69        'echo get(g:, "global")\_s*' ..
70        '\d\+ LOAD g:\_s*' ..
71        '\d\+ PUSHS "global"\_s*' ..
72        '\d\+ BCALL get(argc 2)\_s*' ..
73        '\d\+ ECHO 1\_s*' ..
74        'echo g:auto#var\_s*' ..
75        '\d\+ LOADAUTO g:auto#var\_s*' ..
76        '\d\+ ECHO 1\_s*' ..
77        'echo b:buffervar\_s*' ..
78        '\d\+ LOADB b:buffervar\_s*' ..
79        '\d\+ ECHO 1\_s*' ..
80        'echo get(b:, "buffer")\_s*' ..
81        '\d\+ LOAD b:\_s*' ..
82        '\d\+ PUSHS "buffer"\_s*' ..
83        '\d\+ BCALL get(argc 2).*' ..
84        ' LOADW w:windowvar.*' ..
85        'echo get(w:, "window")\_s*' ..
86        '\d\+ LOAD w:\_s*' ..
87        '\d\+ PUSHS "window"\_s*' ..
88        '\d\+ BCALL get(argc 2).*' ..
89        ' LOADT t:tabpagevar.*' ..
90        'echo get(t:, "tab")\_s*' ..
91        '\d\+ LOAD t:\_s*' ..
92        '\d\+ PUSHS "tab"\_s*' ..
93        '\d\+ BCALL get(argc 2).*' ..
94        ' LOADENV $ENVVAR.*' ..
95        ' LOADREG @z.*',
96        res)
97enddef
98
99def s:EditExpand()
100  var filename = "file"
101  var filenr = 123
102  edit the`=filename``=filenr`.txt
103enddef
104
105def Test_disassemble_exec_expr()
106  var res = execute('disass s:EditExpand')
107  assert_match('<SNR>\d*_EditExpand\_s*' ..
108        ' var filename = "file"\_s*' ..
109        '\d PUSHS "file"\_s*' ..
110        '\d STORE $0\_s*' ..
111        ' var filenr = 123\_s*' ..
112        '\d STORE 123 in $1\_s*' ..
113        ' edit the`=filename``=filenr`.txt\_s*' ..
114        '\d PUSHS "edit the"\_s*' ..
115        '\d LOAD $0\_s*' ..
116        '\d LOAD $1\_s*' ..
117        '\d 2STRING stack\[-1\]\_s*' ..
118        '\d\+ PUSHS ".txt"\_s*' ..
119        '\d\+ EXECCONCAT 4\_s*' ..
120        '\d\+ RETURN void',
121        res)
122enddef
123
124if has('python3')
125  def s:PyHeredoc()
126    python3 << EOF
127      print('hello')
128EOF
129  enddef
130
131  def Test_disassemble_python_heredoc()
132    var res = execute('disass s:PyHeredoc')
133    assert_match('<SNR>\d*_PyHeredoc.*' ..
134          "    python3 << EOF^@      print('hello')^@EOF\\_s*" ..
135          '\d EXEC_SPLIT     python3 << EOF^@      print(''hello'')^@EOF\_s*' ..
136          '\d RETURN void',
137          res)
138  enddef
139endif
140
141def s:Substitute()
142  var expr = "abc"
143  :%s/a/\=expr/&g#c
144enddef
145
146def Test_disassemble_substitute()
147  var res = execute('disass s:Substitute')
148  assert_match('<SNR>\d*_Substitute.*' ..
149        ' var expr = "abc"\_s*' ..
150        '\d PUSHS "abc"\_s*' ..
151        '\d STORE $0\_s*' ..
152        ' :%s/a/\\=expr/&g#c\_s*' ..
153        '\d SUBSTITUTE   :%s/a/\\=expr/&g#c\_s*' ..
154        '    0 LOAD $0\_s*' ..
155        '    -------------\_s*' ..
156        '\d RETURN void',
157        res)
158enddef
159
160
161def s:SearchPair()
162  var col = 8
163  searchpair("{", "", "}", "", "col('.') > col")
164enddef
165
166def Test_disassemble_seachpair()
167  var res = execute('disass s:SearchPair')
168  assert_match('<SNR>\d*_SearchPair.*' ..
169        ' var col = 8\_s*' ..
170        '\d STORE 8 in $0\_s*' ..
171        ' searchpair("{", "", "}", "", "col(''.'') > col")\_s*' ..
172        '\d PUSHS "{"\_s*' ..
173        '\d PUSHS ""\_s*' ..
174        '\d PUSHS "}"\_s*' ..
175        '\d PUSHS ""\_s*' ..
176        '\d INSTR\_s*' ..
177        '  0 PUSHS "."\_s*' ..
178        '  1 BCALL col(argc 1)\_s*' ..
179        '  2 LOAD $0\_s*' ..
180        '  3 COMPARENR >\_s*' ..
181        ' -------------\_s*' ..
182        '\d BCALL searchpair(argc 5)\_s*' ..
183        '\d DROP\_s*' ..
184        '\d RETURN void',
185        res)
186enddef
187
188
189def s:RedirVar()
190  var result: string
191  redir =>> result
192    echo "text"
193  redir END
194enddef
195
196def Test_disassemble_redir_var()
197  var res = execute('disass s:RedirVar')
198  assert_match('<SNR>\d*_RedirVar.*' ..
199        ' var result: string\_s*' ..
200        '\d PUSHS "\[NULL\]"\_s*' ..
201        '\d STORE $0\_s*' ..
202        ' redir =>> result\_s*' ..
203        '\d REDIR\_s*' ..
204        ' echo "text"\_s*' ..
205        '\d PUSHS "text"\_s*' ..
206        '\d ECHO 1\_s*' ..
207        ' redir END\_s*' ..
208        '\d LOAD $0\_s*' ..
209        '\d REDIR END\_s*' ..
210        '\d CONCAT\_s*' ..
211        '\d STORE $0\_s*' ..
212        '\d RETURN void',
213        res)
214enddef
215
216def s:Cexpr()
217  var errors = "list of errors"
218  cexpr errors
219enddef
220
221def Test_disassemble_cexpr()
222  var res = execute('disass s:Cexpr')
223  assert_match('<SNR>\d*_Cexpr.*' ..
224        ' var errors = "list of errors"\_s*' ..
225        '\d PUSHS "list of errors"\_s*' ..
226        '\d STORE $0\_s*' ..
227        ' cexpr errors\_s*' ..
228        '\d CEXPR pre cexpr\_s*' ..
229        '\d LOAD $0\_s*' ..
230        '\d CEXPR core cexpr "cexpr errors"\_s*' ..
231        '\d RETURN void',
232        res)
233enddef
234
235def s:YankRange()
236  norm! m[jjm]
237  :'[,']yank
238enddef
239
240def Test_disassemble_yank_range()
241  var res = execute('disass s:YankRange')
242  assert_match('<SNR>\d*_YankRange.*' ..
243        ' norm! m\[jjm\]\_s*' ..
244        '\d EXEC   norm! m\[jjm\]\_s*' ..
245        '  :''\[,''\]yank\_s*' ..
246        '\d EXEC   :''\[,''\]yank\_s*' ..
247        '\d RETURN void',
248        res)
249enddef
250
251def s:PutExpr()
252  :3put ="text"
253enddef
254
255def Test_disassemble_put_expr()
256  var res = execute('disass s:PutExpr')
257  assert_match('<SNR>\d*_PutExpr.*' ..
258        ' :3put ="text"\_s*' ..
259        '\d PUSHS "text"\_s*' ..
260        '\d PUT = 3\_s*' ..
261        '\d RETURN void',
262        res)
263enddef
264
265def s:PutRange()
266  :$-2put a
267enddef
268
269def Test_disassemble_put_range()
270  var res = execute('disass s:PutRange')
271  assert_match('<SNR>\d*_PutRange.*' ..
272        ' :$-2put a\_s*' ..
273        '\d RANGE $-2\_s*' ..
274        '\d PUT a range\_s*' ..
275        '\d RETURN void',
276        res)
277enddef
278
279def s:ScriptFuncPush()
280  var localbool = true
281  var localspec = v:none
282  var localblob = 0z1234
283  if has('float')
284    var localfloat = 1.234
285  endif
286enddef
287
288def Test_disassemble_push()
289  var res = execute('disass s:ScriptFuncPush')
290  assert_match('<SNR>\d*_ScriptFuncPush.*' ..
291        'localbool = true.*' ..
292        ' PUSH true.*' ..
293        'localspec = v:none.*' ..
294        ' PUSH v:none.*' ..
295        'localblob = 0z1234.*' ..
296        ' PUSHBLOB 0z1234.*',
297        res)
298  if has('float')
299    assert_match('<SNR>\d*_ScriptFuncPush.*' ..
300          'localfloat = 1.234.*' ..
301          ' PUSHF 1.234.*',
302          res)
303  endif
304enddef
305
306def s:ScriptFuncStore()
307  var localnr = 1
308  localnr = 2
309  var localstr = 'abc'
310  localstr = 'xyz'
311  v:char = 'abc'
312  s:scriptvar = 'sv'
313  g:globalvar = 'gv'
314  g:auto#var = 'av'
315  b:buffervar = 'bv'
316  w:windowvar = 'wv'
317  t:tabpagevar = 'tv'
318  &tabstop = 8
319  $ENVVAR = 'ev'
320  @z = 'rv'
321enddef
322
323def Test_disassemble_store()
324  var res = execute('disass s:ScriptFuncStore')
325  assert_match('<SNR>\d*_ScriptFuncStore.*' ..
326        'var localnr = 1.*' ..
327        'localnr = 2.*' ..
328        ' STORE 2 in $0.*' ..
329        'var localstr = ''abc''.*' ..
330        'localstr = ''xyz''.*' ..
331        ' STORE $1.*' ..
332        'v:char = ''abc''.*' ..
333        'STOREV v:char.*' ..
334        's:scriptvar = ''sv''.*' ..
335        ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' ..
336        'g:globalvar = ''gv''.*' ..
337        ' STOREG g:globalvar.*' ..
338        'g:auto#var = ''av''.*' ..
339        ' STOREAUTO g:auto#var.*' ..
340        'b:buffervar = ''bv''.*' ..
341        ' STOREB b:buffervar.*' ..
342        'w:windowvar = ''wv''.*' ..
343        ' STOREW w:windowvar.*' ..
344        't:tabpagevar = ''tv''.*' ..
345        ' STORET t:tabpagevar.*' ..
346        '&tabstop = 8.*' ..
347        ' STOREOPT &tabstop.*' ..
348        '$ENVVAR = ''ev''.*' ..
349        ' STOREENV $ENVVAR.*' ..
350        '@z = ''rv''.*' ..
351        ' STOREREG @z.*',
352        res)
353enddef
354
355def s:ScriptFuncStoreMember()
356  var locallist: list<number> = []
357  locallist[0] = 123
358  var localdict: dict<number> = {}
359  localdict["a"] = 456
360  var localblob: blob = 0z1122
361  localblob[1] = 33
362enddef
363
364def Test_disassemble_store_member()
365  var res = execute('disass s:ScriptFuncStoreMember')
366  assert_match('<SNR>\d*_ScriptFuncStoreMember\_s*' ..
367        'var locallist: list<number> = []\_s*' ..
368        '\d NEWLIST size 0\_s*' ..
369        '\d SETTYPE list<number>\_s*' ..
370        '\d STORE $0\_s*' ..
371        'locallist\[0\] = 123\_s*' ..
372        '\d PUSHNR 123\_s*' ..
373        '\d PUSHNR 0\_s*' ..
374        '\d LOAD $0\_s*' ..
375        '\d STOREINDEX list\_s*' ..
376        'var localdict: dict<number> = {}\_s*' ..
377        '\d NEWDICT size 0\_s*' ..
378        '\d SETTYPE dict<number>\_s*' ..
379        '\d STORE $1\_s*' ..
380        'localdict\["a"\] = 456\_s*' ..
381        '\d\+ PUSHNR 456\_s*' ..
382        '\d\+ PUSHS "a"\_s*' ..
383        '\d\+ LOAD $1\_s*' ..
384        '\d\+ STOREINDEX dict\_s*' ..
385        'var localblob: blob = 0z1122\_s*' ..
386        '\d\+ PUSHBLOB 0z1122\_s*' ..
387        '\d\+ STORE $2\_s*' ..
388        'localblob\[1\] = 33\_s*' ..
389        '\d\+ PUSHNR 33\_s*' ..
390        '\d\+ PUSHNR 1\_s*' ..
391        '\d\+ LOAD $2\_s*' ..
392        '\d\+ STOREINDEX blob\_s*' ..
393        '\d\+ RETURN void',
394        res)
395enddef
396
397def s:ScriptFuncStoreIndex()
398  var d = {dd: {}}
399  d.dd[0] = 0
400enddef
401
402def Test_disassemble_store_index()
403  var res = execute('disass s:ScriptFuncStoreIndex')
404  assert_match('<SNR>\d*_ScriptFuncStoreIndex\_s*' ..
405        'var d = {dd: {}}\_s*' ..
406        '\d PUSHS "dd"\_s*' ..
407        '\d NEWDICT size 0\_s*' ..
408        '\d NEWDICT size 1\_s*' ..
409        '\d STORE $0\_s*' ..
410        'd.dd\[0\] = 0\_s*' ..
411        '\d PUSHNR 0\_s*' ..
412        '\d PUSHNR 0\_s*' ..
413        '\d LOAD $0\_s*' ..
414        '\d MEMBER dd\_s*' ..
415        '\d\+ USEDICT\_s*' ..
416        '\d\+ STOREINDEX any\_s*' ..
417        '\d\+ RETURN void',
418        res)
419enddef
420
421def s:ListAssign()
422  var x: string
423  var y: string
424  var l: list<any>
425  [x, y; l] = g:stringlist
426enddef
427
428def Test_disassemble_list_assign()
429  var res = execute('disass s:ListAssign')
430  assert_match('<SNR>\d*_ListAssign\_s*' ..
431        'var x: string\_s*' ..
432        '\d PUSHS "\[NULL\]"\_s*' ..
433        '\d STORE $0\_s*' ..
434        'var y: string\_s*' ..
435        '\d PUSHS "\[NULL\]"\_s*' ..
436        '\d STORE $1\_s*' ..
437        'var l: list<any>\_s*' ..
438        '\d NEWLIST size 0\_s*' ..
439        '\d SETTYPE list<any>\_s*' ..
440        '\d STORE $2\_s*' ..
441        '\[x, y; l\] = g:stringlist\_s*' ..
442        '\d LOADG g:stringlist\_s*' ..
443        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
444        '\d CHECKLEN >= 2\_s*' ..
445        '\d\+ ITEM 0\_s*' ..
446        '\d\+ CHECKTYPE string stack\[-1\] arg 1\_s*' ..
447        '\d\+ STORE $0\_s*' ..
448        '\d\+ ITEM 1\_s*' ..
449        '\d\+ CHECKTYPE string stack\[-1\] arg 2\_s*' ..
450        '\d\+ STORE $1\_s*' ..
451        '\d\+ SLICE 2\_s*' ..
452        '\d\+ STORE $2\_s*' ..
453        '\d\+ RETURN void',
454        res)
455enddef
456
457def s:ListAssignWithOp()
458  var a = 2
459  var b = 3
460  [a, b] += [4, 5]
461enddef
462
463def Test_disassemble_list_assign_with_op()
464  var res = execute('disass s:ListAssignWithOp')
465  assert_match('<SNR>\d*_ListAssignWithOp\_s*' ..
466        'var a = 2\_s*' ..
467        '\d STORE 2 in $0\_s*' ..
468        'var b = 3\_s*' ..
469        '\d STORE 3 in $1\_s*' ..
470        '\[a, b\] += \[4, 5\]\_s*' ..
471        '\d\+ PUSHNR 4\_s*' ..
472        '\d\+ PUSHNR 5\_s*' ..
473        '\d\+ NEWLIST size 2\_s*' ..
474        '\d\+ CHECKLEN 2\_s*' ..
475        '\d\+ LOAD $0\_s*' ..
476        '\d\+ ITEM 0 with op\_s*' ..
477        '\d\+ OPNR +\_s*' ..
478        '\d\+ STORE $0\_s*' ..
479        '\d\+ LOAD $1\_s*' ..
480        '\d\+ ITEM 1 with op\_s*' ..
481        '\d\+ OPNR +\_s*' ..
482        '\d\+ STORE $1\_s*' ..
483        '\d\+ DROP\_s*' ..
484        '\d\+ RETURN void',
485        res)
486enddef
487
488def s:ListAdd()
489  var l: list<number> = []
490  add(l, 123)
491  add(l, g:aNumber)
492enddef
493
494def Test_disassemble_list_add()
495  var res = execute('disass s:ListAdd')
496  assert_match('<SNR>\d*_ListAdd\_s*' ..
497        'var l: list<number> = []\_s*' ..
498        '\d NEWLIST size 0\_s*' ..
499        '\d SETTYPE list<number>\_s*' ..
500        '\d STORE $0\_s*' ..
501        'add(l, 123)\_s*' ..
502        '\d LOAD $0\_s*' ..
503        '\d PUSHNR 123\_s*' ..
504        '\d LISTAPPEND\_s*' ..
505        '\d DROP\_s*' ..
506        'add(l, g:aNumber)\_s*' ..
507        '\d LOAD $0\_s*' ..
508        '\d\+ LOADG g:aNumber\_s*' ..
509        '\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
510        '\d\+ LISTAPPEND\_s*' ..
511        '\d\+ DROP\_s*' ..
512        '\d\+ RETURN void',
513        res)
514enddef
515
516def s:BlobAdd()
517  var b: blob = 0z
518  add(b, 123)
519  add(b, g:aNumber)
520enddef
521
522def Test_disassemble_blob_add()
523  var res = execute('disass s:BlobAdd')
524  assert_match('<SNR>\d*_BlobAdd\_s*' ..
525        'var b: blob = 0z\_s*' ..
526        '\d PUSHBLOB 0z\_s*' ..
527        '\d STORE $0\_s*' ..
528        'add(b, 123)\_s*' ..
529        '\d LOAD $0\_s*' ..
530        '\d PUSHNR 123\_s*' ..
531        '\d BLOBAPPEND\_s*' ..
532        '\d DROP\_s*' ..
533        'add(b, g:aNumber)\_s*' ..
534        '\d LOAD $0\_s*' ..
535        '\d\+ LOADG g:aNumber\_s*' ..
536        '\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
537        '\d\+ BLOBAPPEND\_s*' ..
538        '\d\+ DROP\_s*' ..
539        '\d\+ RETURN void',
540        res)
541enddef
542
543def s:BlobIndexSlice()
544  var b: blob = 0z112233
545  echo b[1]
546  echo b[1 : 2]
547enddef
548
549def Test_disassemble_blob_index_slice()
550  var res = execute('disass s:BlobIndexSlice')
551  assert_match('<SNR>\d*_BlobIndexSlice\_s*' ..
552        'var b: blob = 0z112233\_s*' ..
553        '\d PUSHBLOB 0z112233\_s*' ..
554        '\d STORE $0\_s*' ..
555        'echo b\[1\]\_s*' ..
556        '\d LOAD $0\_s*' ..
557        '\d PUSHNR 1\_s*' ..
558        '\d BLOBINDEX\_s*' ..
559        '\d ECHO 1\_s*' ..
560        'echo b\[1 : 2\]\_s*' ..
561        '\d LOAD $0\_s*' ..
562        '\d PUSHNR 1\_s*' ..
563        '\d\+ PUSHNR 2\_s*' ..
564        '\d\+ BLOBSLICE\_s*' ..
565        '\d\+ ECHO 1\_s*' ..
566        '\d\+ RETURN void',
567        res)
568enddef
569
570def s:ScriptFuncUnlet()
571  g:somevar = "value"
572  unlet g:somevar
573  unlet! g:somevar
574  unlet $SOMEVAR
575enddef
576
577def Test_disassemble_unlet()
578  var res = execute('disass s:ScriptFuncUnlet')
579  assert_match('<SNR>\d*_ScriptFuncUnlet\_s*' ..
580        'g:somevar = "value"\_s*' ..
581        '\d PUSHS "value"\_s*' ..
582        '\d STOREG g:somevar\_s*' ..
583        'unlet g:somevar\_s*' ..
584        '\d UNLET g:somevar\_s*' ..
585        'unlet! g:somevar\_s*' ..
586        '\d UNLET! g:somevar\_s*' ..
587        'unlet $SOMEVAR\_s*' ..
588        '\d UNLETENV $SOMEVAR\_s*',
589        res)
590enddef
591
592def s:LockLocal()
593  var d = {a: 1}
594  lockvar d.a
595enddef
596
597def Test_disassemble_locl_local()
598  var res = execute('disass s:LockLocal')
599  assert_match('<SNR>\d*_LockLocal\_s*' ..
600        'var d = {a: 1}\_s*' ..
601        '\d PUSHS "a"\_s*' ..
602        '\d PUSHNR 1\_s*' ..
603        '\d NEWDICT size 1\_s*' ..
604        '\d STORE $0\_s*' ..
605        'lockvar d.a\_s*' ..
606        '\d LOAD $0\_s*' ..
607        '\d LOCKUNLOCK lockvar d.a\_s*',
608        res)
609enddef
610
611def s:ScriptFuncTry()
612  try
613    echo "yes"
614  catch /fail/
615    echo "no"
616  finally
617    throw "end"
618  endtry
619enddef
620
621def Test_disassemble_try()
622  var res = execute('disass s:ScriptFuncTry')
623  assert_match('<SNR>\d*_ScriptFuncTry\_s*' ..
624        'try\_s*' ..
625        '\d TRY catch -> \d\+, finally -> \d\+, endtry -> \d\+\_s*' ..
626        'echo "yes"\_s*' ..
627        '\d PUSHS "yes"\_s*' ..
628        '\d ECHO 1\_s*' ..
629        'catch /fail/\_s*' ..
630        '\d JUMP -> \d\+\_s*' ..
631        '\d PUSH v:exception\_s*' ..
632        '\d PUSHS "fail"\_s*' ..
633        '\d COMPARESTRING =\~\_s*' ..
634        '\d JUMP_IF_FALSE -> \d\+\_s*' ..
635        '\d CATCH\_s*' ..
636        'echo "no"\_s*' ..
637        '\d\+ PUSHS "no"\_s*' ..
638        '\d\+ ECHO 1\_s*' ..
639        'finally\_s*' ..
640        '\d\+ FINALLY\_s*' ..
641        'throw "end"\_s*' ..
642        '\d\+ PUSHS "end"\_s*' ..
643        '\d\+ THROW\_s*' ..
644        'endtry\_s*' ..
645        '\d\+ ENDTRY',
646        res)
647enddef
648
649def s:ScriptFuncNew()
650  var ll = [1, "two", 333]
651  var dd = {one: 1, two: "val"}
652enddef
653
654def Test_disassemble_new()
655  var res = execute('disass s:ScriptFuncNew')
656  assert_match('<SNR>\d*_ScriptFuncNew\_s*' ..
657        'var ll = \[1, "two", 333\]\_s*' ..
658        '\d PUSHNR 1\_s*' ..
659        '\d PUSHS "two"\_s*' ..
660        '\d PUSHNR 333\_s*' ..
661        '\d NEWLIST size 3\_s*' ..
662        '\d STORE $0\_s*' ..
663        'var dd = {one: 1, two: "val"}\_s*' ..
664        '\d PUSHS "one"\_s*' ..
665        '\d PUSHNR 1\_s*' ..
666        '\d PUSHS "two"\_s*' ..
667        '\d PUSHS "val"\_s*' ..
668        '\d NEWDICT size 2\_s*',
669        res)
670enddef
671
672def FuncWithArg(arg: any)
673  echo arg
674enddef
675
676func UserFunc()
677  echo 'nothing'
678endfunc
679
680func UserFuncWithArg(arg)
681  echo a:arg
682endfunc
683
684def s:ScriptFuncCall(): string
685  changenr()
686  char2nr("abc")
687  Test_disassemble_new()
688  FuncWithArg(343)
689  ScriptFuncNew()
690  s:ScriptFuncNew()
691  UserFunc()
692  UserFuncWithArg("foo")
693  var FuncRef = function("UserFunc")
694  FuncRef()
695  var FuncRefWithArg = function("UserFuncWithArg")
696  FuncRefWithArg("bar")
697  return "yes"
698enddef
699
700def Test_disassemble_call()
701  var res = execute('disass s:ScriptFuncCall')
702  assert_match('<SNR>\d\+_ScriptFuncCall\_s*' ..
703        'changenr()\_s*' ..
704        '\d BCALL changenr(argc 0)\_s*' ..
705        '\d DROP\_s*' ..
706        'char2nr("abc")\_s*' ..
707        '\d PUSHS "abc"\_s*' ..
708        '\d BCALL char2nr(argc 1)\_s*' ..
709        '\d DROP\_s*' ..
710        'Test_disassemble_new()\_s*' ..
711        '\d DCALL Test_disassemble_new(argc 0)\_s*' ..
712        '\d DROP\_s*' ..
713        'FuncWithArg(343)\_s*' ..
714        '\d\+ PUSHNR 343\_s*' ..
715        '\d\+ DCALL FuncWithArg(argc 1)\_s*' ..
716        '\d\+ DROP\_s*' ..
717        'ScriptFuncNew()\_s*' ..
718        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
719        '\d\+ DROP\_s*' ..
720        's:ScriptFuncNew()\_s*' ..
721        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
722        '\d\+ DROP\_s*' ..
723        'UserFunc()\_s*' ..
724        '\d\+ UCALL UserFunc(argc 0)\_s*' ..
725        '\d\+ DROP\_s*' ..
726        'UserFuncWithArg("foo")\_s*' ..
727        '\d\+ PUSHS "foo"\_s*' ..
728        '\d\+ UCALL UserFuncWithArg(argc 1)\_s*' ..
729        '\d\+ DROP\_s*' ..
730        'var FuncRef = function("UserFunc")\_s*' ..
731        '\d\+ PUSHS "UserFunc"\_s*' ..
732        '\d\+ BCALL function(argc 1)\_s*' ..
733        '\d\+ STORE $0\_s*' ..
734        'FuncRef()\_s*' ..
735        '\d\+ LOAD $\d\_s*' ..
736        '\d\+ PCALL (argc 0)\_s*' ..
737        '\d\+ DROP\_s*' ..
738        'var FuncRefWithArg = function("UserFuncWithArg")\_s*' ..
739        '\d\+ PUSHS "UserFuncWithArg"\_s*' ..
740        '\d\+ BCALL function(argc 1)\_s*' ..
741        '\d\+ STORE $1\_s*' ..
742        'FuncRefWithArg("bar")\_s*' ..
743        '\d\+ PUSHS "bar"\_s*' ..
744        '\d\+ LOAD $\d\_s*' ..
745        '\d\+ PCALL (argc 1)\_s*' ..
746        '\d\+ DROP\_s*' ..
747        'return "yes"\_s*' ..
748        '\d\+ PUSHS "yes"\_s*' ..
749        '\d\+ RETURN',
750        res)
751enddef
752
753
754def s:CreateRefs()
755  var local = 'a'
756  def Append(arg: string)
757    local ..= arg
758  enddef
759  g:Append = Append
760  def Get(): string
761    return local
762  enddef
763  g:Get = Get
764enddef
765
766def Test_disassemble_closure()
767  CreateRefs()
768  var res = execute('disass g:Append')
769  assert_match('<lambda>\d\_s*' ..
770        'local ..= arg\_s*' ..
771        '\d LOADOUTER level 1 $0\_s*' ..
772        '\d LOAD arg\[-1\]\_s*' ..
773        '\d CONCAT\_s*' ..
774        '\d STOREOUTER level 1 $0\_s*' ..
775        '\d RETURN void',
776        res)
777
778  res = execute('disass g:Get')
779  assert_match('<lambda>\d\_s*' ..
780        'return local\_s*' ..
781        '\d LOADOUTER level 1 $0\_s*' ..
782        '\d RETURN',
783        res)
784
785  unlet g:Append
786  unlet g:Get
787enddef
788
789
790def EchoArg(arg: string): string
791  return arg
792enddef
793def RefThis(): func
794  return function('EchoArg')
795enddef
796def s:ScriptPCall()
797  RefThis()("text")
798enddef
799
800def Test_disassemble_pcall()
801  var res = execute('disass s:ScriptPCall')
802  assert_match('<SNR>\d\+_ScriptPCall\_s*' ..
803        'RefThis()("text")\_s*' ..
804        '\d DCALL RefThis(argc 0)\_s*' ..
805        '\d PUSHS "text"\_s*' ..
806        '\d PCALL top (argc 1)\_s*' ..
807        '\d PCALL end\_s*' ..
808        '\d DROP\_s*' ..
809        '\d RETURN void',
810        res)
811enddef
812
813
814def s:FuncWithForwardCall(): string
815  return g:DefinedLater("yes")
816enddef
817
818def DefinedLater(arg: string): string
819  return arg
820enddef
821
822def Test_disassemble_update_instr()
823  var res = execute('disass s:FuncWithForwardCall')
824  assert_match('FuncWithForwardCall\_s*' ..
825        'return g:DefinedLater("yes")\_s*' ..
826        '\d PUSHS "yes"\_s*' ..
827        '\d DCALL DefinedLater(argc 1)\_s*' ..
828        '\d RETURN',
829        res)
830
831  # Calling the function will change UCALL into the faster DCALL
832  assert_equal('yes', FuncWithForwardCall())
833
834  res = execute('disass s:FuncWithForwardCall')
835  assert_match('FuncWithForwardCall\_s*' ..
836        'return g:DefinedLater("yes")\_s*' ..
837        '\d PUSHS "yes"\_s*' ..
838        '\d DCALL DefinedLater(argc 1)\_s*' ..
839        '\d RETURN',
840        res)
841enddef
842
843
844def FuncWithDefault(l: number, arg: string = "default", nr = 77): string
845  return arg .. nr
846enddef
847
848def Test_disassemble_call_default()
849  var res = execute('disass FuncWithDefault')
850  assert_match('FuncWithDefault\_s*' ..
851        '  arg = "default"\_s*' ..
852        '\d JUMP_IF_ARG_SET arg\[-2\] -> 3\_s*' ..
853        '\d PUSHS "default"\_s*' ..
854        '\d STORE arg\[-2]\_s*' ..
855        '  nr = 77\_s*' ..
856        '3 JUMP_IF_ARG_SET arg\[-1\] -> 6\_s*' ..
857        '\d PUSHNR 77\_s*' ..
858        '\d STORE arg\[-1]\_s*' ..
859        '  return arg .. nr\_s*' ..
860        '6 LOAD arg\[-2]\_s*' ..
861        '\d LOAD arg\[-1]\_s*' ..
862        '\d 2STRING stack\[-1]\_s*' ..
863        '\d\+ CONCAT\_s*' ..
864        '\d\+ RETURN',
865        res)
866enddef
867
868
869def HasEval()
870  if has("eval")
871    echo "yes"
872  else
873    echo "no"
874  endif
875enddef
876
877def HasNothing()
878  if has("nothing")
879    echo "yes"
880  else
881    echo "no"
882  endif
883enddef
884
885def HasSomething()
886  if has("nothing")
887    echo "nothing"
888  elseif has("something")
889    echo "something"
890  elseif has("eval")
891    echo "eval"
892  elseif has("less")
893    echo "less"
894  endif
895enddef
896
897def HasGuiRunning()
898  if has("gui_running")
899    echo "yes"
900  else
901    echo "no"
902  endif
903enddef
904
905def Test_disassemble_const_expr()
906  assert_equal("\nyes", execute('HasEval()'))
907  var instr = execute('disassemble HasEval')
908  assert_match('HasEval\_s*' ..
909        'if has("eval")\_s*' ..
910        'echo "yes"\_s*' ..
911        '\d PUSHS "yes"\_s*' ..
912        '\d ECHO 1\_s*' ..
913        'else\_s*' ..
914        'echo "no"\_s*' ..
915        'endif\_s*',
916        instr)
917  assert_notmatch('JUMP', instr)
918
919  assert_equal("\nno", execute('HasNothing()'))
920  instr = execute('disassemble HasNothing')
921  assert_match('HasNothing\_s*' ..
922        'if has("nothing")\_s*' ..
923        'echo "yes"\_s*' ..
924        'else\_s*' ..
925        'echo "no"\_s*' ..
926        '\d PUSHS "no"\_s*' ..
927        '\d ECHO 1\_s*' ..
928        'endif',
929        instr)
930  assert_notmatch('PUSHS "yes"', instr)
931  assert_notmatch('JUMP', instr)
932
933  assert_equal("\neval", execute('HasSomething()'))
934  instr = execute('disassemble HasSomething')
935  assert_match('HasSomething.*' ..
936        'if has("nothing")\_s*' ..
937        'echo "nothing"\_s*' ..
938        'elseif has("something")\_s*' ..
939        'echo "something"\_s*' ..
940        'elseif has("eval")\_s*' ..
941        'echo "eval"\_s*' ..
942        '\d PUSHS "eval"\_s*' ..
943        '\d ECHO 1\_s*' ..
944        'elseif has("less").*' ..
945        'echo "less"\_s*' ..
946        'endif',
947        instr)
948  assert_notmatch('PUSHS "nothing"', instr)
949  assert_notmatch('PUSHS "something"', instr)
950  assert_notmatch('PUSHS "less"', instr)
951  assert_notmatch('JUMP', instr)
952
953  var result: string
954  var instr_expected: string
955  if has('gui')
956    if has('gui_running')
957      # GUI already running, always returns "yes"
958      result = "\nyes"
959      instr_expected = 'HasGuiRunning.*' ..
960          'if has("gui_running")\_s*' ..
961          '  echo "yes"\_s*' ..
962          '\d PUSHS "yes"\_s*' ..
963          '\d ECHO 1\_s*' ..
964          'else\_s*' ..
965          '  echo "no"\_s*' ..
966          'endif'
967    else
968      result = "\nno"
969      if has('unix')
970        # GUI not running but can start later, call has()
971        instr_expected = 'HasGuiRunning.*' ..
972            'if has("gui_running")\_s*' ..
973            '\d PUSHS "gui_running"\_s*' ..
974            '\d BCALL has(argc 1)\_s*' ..
975            '\d COND2BOOL\_s*' ..
976            '\d JUMP_IF_FALSE -> \d\_s*' ..
977            '  echo "yes"\_s*' ..
978            '\d PUSHS "yes"\_s*' ..
979            '\d ECHO 1\_s*' ..
980            'else\_s*' ..
981            '\d JUMP -> \d\_s*' ..
982            '  echo "no"\_s*' ..
983            '\d PUSHS "no"\_s*' ..
984            '\d ECHO 1\_s*' ..
985            'endif'
986      else
987        # GUI not running, always return "no"
988        instr_expected = 'HasGuiRunning.*' ..
989            'if has("gui_running")\_s*' ..
990            '  echo "yes"\_s*' ..
991            'else\_s*' ..
992            '  echo "no"\_s*' ..
993            '\d PUSHS "no"\_s*' ..
994            '\d ECHO 1\_s*' ..
995            'endif'
996      endif
997    endif
998  else
999    # GUI not supported, always return "no"
1000    result = "\nno"
1001    instr_expected = 'HasGuiRunning.*' ..
1002        'if has("gui_running")\_s*' ..
1003        '  echo "yes"\_s*' ..
1004        'else\_s*' ..
1005        '  echo "no"\_s*' ..
1006        '\d PUSHS "no"\_s*' ..
1007        '\d ECHO 1\_s*' ..
1008        'endif'
1009  endif
1010
1011  assert_equal(result, execute('HasGuiRunning()'))
1012  instr = execute('disassemble HasGuiRunning')
1013  assert_match(instr_expected, instr)
1014enddef
1015
1016def ReturnInIf(): string
1017  if 1 < 0
1018    return "maybe"
1019  endif
1020  if g:cond
1021    return "yes"
1022  else
1023    return "no"
1024  endif
1025enddef
1026
1027def Test_disassemble_return_in_if()
1028  var instr = execute('disassemble ReturnInIf')
1029  assert_match('ReturnInIf\_s*' ..
1030        'if 1 < 0\_s*' ..
1031        '  return "maybe"\_s*' ..
1032        'endif\_s*' ..
1033        'if g:cond\_s*' ..
1034        '0 LOADG g:cond\_s*' ..
1035        '1 COND2BOOL\_s*' ..
1036        '2 JUMP_IF_FALSE -> 5\_s*' ..
1037        'return "yes"\_s*' ..
1038        '3 PUSHS "yes"\_s*' ..
1039        '4 RETURN\_s*' ..
1040        'else\_s*' ..
1041        ' return "no"\_s*' ..
1042        '5 PUSHS "no"\_s*' ..
1043        '6 RETURN$',
1044        instr)
1045enddef
1046
1047def WithFunc()
1048  var Funky1: func
1049  var Funky2: func = function("len")
1050  var Party2: func = funcref("UserFunc")
1051enddef
1052
1053def Test_disassemble_function()
1054  var instr = execute('disassemble WithFunc')
1055  assert_match('WithFunc\_s*' ..
1056        'var Funky1: func\_s*' ..
1057        '0 PUSHFUNC "\[none]"\_s*' ..
1058        '1 STORE $0\_s*' ..
1059        'var Funky2: func = function("len")\_s*' ..
1060        '2 PUSHS "len"\_s*' ..
1061        '3 BCALL function(argc 1)\_s*' ..
1062        '4 STORE $1\_s*' ..
1063        'var Party2: func = funcref("UserFunc")\_s*' ..
1064        '\d PUSHS "UserFunc"\_s*' ..
1065        '\d BCALL funcref(argc 1)\_s*' ..
1066        '\d STORE $2\_s*' ..
1067        '\d RETURN void',
1068        instr)
1069enddef
1070
1071if has('channel')
1072  def WithChannel()
1073    var job1: job
1074    var job2: job = job_start("donothing")
1075    var chan1: channel
1076  enddef
1077endif
1078
1079def Test_disassemble_channel()
1080  CheckFeature channel
1081
1082  var instr = execute('disassemble WithChannel')
1083  assert_match('WithChannel\_s*' ..
1084        'var job1: job\_s*' ..
1085        '\d PUSHJOB "no process"\_s*' ..
1086        '\d STORE $0\_s*' ..
1087        'var job2: job = job_start("donothing")\_s*' ..
1088        '\d PUSHS "donothing"\_s*' ..
1089        '\d BCALL job_start(argc 1)\_s*' ..
1090        '\d STORE $1\_s*' ..
1091        'var chan1: channel\_s*' ..
1092        '\d PUSHCHANNEL 0\_s*' ..
1093        '\d STORE $2\_s*' ..
1094        '\d RETURN void',
1095        instr)
1096enddef
1097
1098def WithLambda(): string
1099  var F = (a) => "X" .. a .. "X"
1100  return F("x")
1101enddef
1102
1103def Test_disassemble_lambda()
1104  assert_equal("XxX", WithLambda())
1105  var instr = execute('disassemble WithLambda')
1106  assert_match('WithLambda\_s*' ..
1107        'var F = (a) => "X" .. a .. "X"\_s*' ..
1108        '\d FUNCREF <lambda>\d\+\_s*' ..
1109        '\d STORE $0\_s*' ..
1110        'return F("x")\_s*' ..
1111        '\d PUSHS "x"\_s*' ..
1112        '\d LOAD $0\_s*' ..
1113        '\d PCALL (argc 1)\_s*' ..
1114        '\d RETURN',
1115        instr)
1116
1117   var name = substitute(instr, '.*\(<lambda>\d\+\).*', '\1', '')
1118   instr = execute('disassemble ' .. name)
1119   assert_match('<lambda>\d\+\_s*' ..
1120        'return "X" .. a .. "X"\_s*' ..
1121        '\d PUSHS "X"\_s*' ..
1122        '\d LOAD arg\[-1\]\_s*' ..
1123        '\d 2STRING_ANY stack\[-1\]\_s*' ..
1124        '\d CONCAT\_s*' ..
1125        '\d PUSHS "X"\_s*' ..
1126        '\d CONCAT\_s*' ..
1127        '\d RETURN',
1128        instr)
1129enddef
1130
1131def LambdaWithType(): number
1132  var Ref = (a: number) => a + 10
1133  return Ref(g:value)
1134enddef
1135
1136def Test_disassemble_lambda_with_type()
1137  g:value = 5
1138  assert_equal(15, LambdaWithType())
1139  var instr = execute('disassemble LambdaWithType')
1140  assert_match('LambdaWithType\_s*' ..
1141        'var Ref = (a: number) => a + 10\_s*' ..
1142        '\d FUNCREF <lambda>\d\+\_s*' ..
1143        '\d STORE $0\_s*' ..
1144        'return Ref(g:value)\_s*' ..
1145        '\d LOADG g:value\_s*' ..
1146        '\d LOAD $0\_s*' ..
1147        '\d CHECKTYPE number stack\[-2\] arg 1\_s*' ..
1148        '\d PCALL (argc 1)\_s*' ..
1149        '\d RETURN',
1150        instr)
1151enddef
1152
1153def NestedOuter()
1154  def g:Inner()
1155    echomsg "inner"
1156  enddef
1157enddef
1158
1159def Test_disassemble_nested_func()
1160   var instr = execute('disassemble NestedOuter')
1161   assert_match('NestedOuter\_s*' ..
1162        'def g:Inner()\_s*' ..
1163        'echomsg "inner"\_s*' ..
1164        'enddef\_s*' ..
1165        '\d NEWFUNC <lambda>\d\+ Inner\_s*' ..
1166        '\d RETURN void',
1167        instr)
1168enddef
1169
1170def NestedDefList()
1171  def
1172  def Info
1173  def /Info
1174  def /Info/
1175enddef
1176
1177def Test_disassemble_nested_def_list()
1178   var instr = execute('disassemble NestedDefList')
1179   assert_match('NestedDefList\_s*' ..
1180        'def\_s*' ..
1181        '\d DEF \_s*' ..
1182        'def Info\_s*' ..
1183        '\d DEF Info\_s*' ..
1184        'def /Info\_s*' ..
1185        '\d DEF /Info\_s*' ..
1186        'def /Info/\_s*' ..
1187        '\d DEF /Info/\_s*' ..
1188        '\d RETURN void',
1189        instr)
1190enddef
1191
1192def AndOr(arg: any): string
1193  if arg == 1 && arg != 2 || arg == 4
1194    return 'yes'
1195  endif
1196  return 'no'
1197enddef
1198
1199def Test_disassemble_and_or()
1200  assert_equal("yes", AndOr(1))
1201  assert_equal("no", AndOr(2))
1202  assert_equal("yes", AndOr(4))
1203  var instr = execute('disassemble AndOr')
1204  assert_match('AndOr\_s*' ..
1205        'if arg == 1 && arg != 2 || arg == 4\_s*' ..
1206        '\d LOAD arg\[-1]\_s*' ..
1207        '\d PUSHNR 1\_s*' ..
1208        '\d COMPAREANY ==\_s*' ..
1209        '\d JUMP_IF_COND_FALSE -> \d\+\_s*' ..
1210        '\d LOAD arg\[-1]\_s*' ..
1211        '\d PUSHNR 2\_s*' ..
1212        '\d COMPAREANY !=\_s*' ..
1213        '\d JUMP_IF_COND_TRUE -> \d\+\_s*' ..
1214        '\d LOAD arg\[-1]\_s*' ..
1215        '\d\+ PUSHNR 4\_s*' ..
1216        '\d\+ COMPAREANY ==\_s*' ..
1217        '\d\+ JUMP_IF_FALSE -> \d\+',
1218        instr)
1219enddef
1220
1221def AndConstant(arg: any): string
1222  if true && arg
1223    return "yes"
1224  endif
1225  if false && arg
1226    return "never"
1227  endif
1228  return "no"
1229enddef
1230
1231def Test_disassemble_and_constant()
1232  assert_equal("yes", AndConstant(1))
1233  assert_equal("no", AndConstant(false))
1234  var instr = execute('disassemble AndConstant')
1235  assert_match('AndConstant\_s*' ..
1236      'if true && arg\_s*' ..
1237      '0 LOAD arg\[-1\]\_s*' ..
1238      '1 COND2BOOL\_s*' ..
1239      '2 JUMP_IF_FALSE -> 5\_s*' ..
1240      'return "yes"\_s*' ..
1241      '3 PUSHS "yes"\_s*' ..
1242      '4 RETURN\_s*' ..
1243      'endif\_s*' ..
1244      'if false && arg\_s*' ..
1245      'return "never"\_s*' ..
1246      'endif\_s*' ..
1247      'return "no"\_s*' ..
1248      '5 PUSHS "no"\_s*' ..
1249      '6 RETURN',
1250      instr)
1251enddef
1252
1253def ForLoop(): list<number>
1254  var res: list<number>
1255  for i in range(3)
1256    res->add(i)
1257  endfor
1258  return res
1259enddef
1260
1261def Test_disassemble_for_loop()
1262  assert_equal([0, 1, 2], ForLoop())
1263  var instr = execute('disassemble ForLoop')
1264  assert_match('ForLoop\_s*' ..
1265        'var res: list<number>\_s*' ..
1266        '\d NEWLIST size 0\_s*' ..
1267        '\d SETTYPE list<number>\_s*' ..
1268        '\d STORE $0\_s*' ..
1269        'for i in range(3)\_s*' ..
1270        '\d STORE -1 in $1\_s*' ..
1271        '\d PUSHNR 3\_s*' ..
1272        '\d BCALL range(argc 1)\_s*' ..
1273        '\d FOR $1 -> \d\+\_s*' ..
1274        '\d STORE $2\_s*' ..
1275        'res->add(i)\_s*' ..
1276        '\d LOAD $0\_s*' ..
1277        '\d LOAD $2\_s*' ..
1278        '\d\+ LISTAPPEND\_s*' ..
1279        '\d\+ DROP\_s*' ..
1280        'endfor\_s*' ..
1281        '\d\+ JUMP -> \d\+\_s*' ..
1282        '\d\+ DROP',
1283        instr)
1284enddef
1285
1286def ForLoopEval(): string
1287  var res = ""
1288  for str in eval('["one", "two"]')
1289    res ..= str
1290  endfor
1291  return res
1292enddef
1293
1294def Test_disassemble_for_loop_eval()
1295  assert_equal('onetwo', ForLoopEval())
1296  var instr = execute('disassemble ForLoopEval')
1297  assert_match('ForLoopEval\_s*' ..
1298        'var res = ""\_s*' ..
1299        '\d PUSHS ""\_s*' ..
1300        '\d STORE $0\_s*' ..
1301        'for str in eval(''\["one", "two"\]'')\_s*' ..
1302        '\d STORE -1 in $1\_s*' ..
1303        '\d PUSHS "\["one", "two"\]"\_s*' ..
1304        '\d BCALL eval(argc 1)\_s*' ..
1305        '\d FOR $1 -> \d\+\_s*' ..
1306        '\d STORE $2\_s*' ..
1307        'res ..= str\_s*' ..
1308        '\d\+ LOAD $0\_s*' ..
1309        '\d\+ LOAD $2\_s*' ..
1310        '\d 2STRING_ANY stack\[-1\]\_s*' ..
1311        '\d\+ CONCAT\_s*' ..
1312        '\d\+ STORE $0\_s*' ..
1313        'endfor\_s*' ..
1314        '\d\+ JUMP -> 5\_s*' ..
1315        '\d\+ DROP\_s*' ..
1316        'return res\_s*' ..
1317        '\d\+ LOAD $0\_s*' ..
1318        '\d\+ RETURN',
1319        instr)
1320enddef
1321
1322def ForLoopUnpack()
1323  for [x1, x2] in [[1, 2], [3, 4]]
1324    echo x1 x2
1325  endfor
1326enddef
1327
1328def Test_disassemble_for_loop_unpack()
1329  var instr = execute('disassemble ForLoopUnpack')
1330  assert_match('ForLoopUnpack\_s*' ..
1331        'for \[x1, x2\] in \[\[1, 2\], \[3, 4\]\]\_s*' ..
1332        '\d\+ STORE -1 in $0\_s*' ..
1333        '\d\+ PUSHNR 1\_s*' ..
1334        '\d\+ PUSHNR 2\_s*' ..
1335        '\d\+ NEWLIST size 2\_s*' ..
1336        '\d\+ PUSHNR 3\_s*' ..
1337        '\d\+ PUSHNR 4\_s*' ..
1338        '\d\+ NEWLIST size 2\_s*' ..
1339        '\d\+ NEWLIST size 2\_s*' ..
1340        '\d\+ FOR $0 -> 16\_s*' ..
1341        '\d\+ UNPACK 2\_s*' ..
1342        '\d\+ STORE $1\_s*' ..
1343        '\d\+ STORE $2\_s*' ..
1344        'echo x1 x2\_s*' ..
1345        '\d\+ LOAD $1\_s*' ..
1346        '\d\+ LOAD $2\_s*' ..
1347        '\d\+ ECHO 2\_s*' ..
1348        'endfor\_s*' ..
1349        '\d\+ JUMP -> 8\_s*' ..
1350        '\d\+ DROP\_s*' ..
1351        '\d\+ RETURN void',
1352        instr)
1353enddef
1354
1355def ForLoopContinue()
1356  for nr in [1, 2]
1357    try
1358      echo "ok"
1359      try
1360        echo "deeper"
1361      catch
1362        continue
1363      endtry
1364    catch
1365      echo "not ok"
1366    endtry
1367  endfor
1368enddef
1369
1370def Test_disassemble_for_loop_continue()
1371  var instr = execute('disassemble ForLoopContinue')
1372  assert_match('ForLoopContinue\_s*' ..
1373        'for nr in \[1, 2]\_s*' ..
1374        '0 STORE -1 in $0\_s*' ..
1375        '1 PUSHNR 1\_s*' ..
1376        '2 PUSHNR 2\_s*' ..
1377        '3 NEWLIST size 2\_s*' ..
1378        '4 FOR $0 -> 22\_s*' ..
1379        '5 STORE $1\_s*' ..
1380        'try\_s*' ..
1381        '6 TRY catch -> 17, endtry -> 20\_s*' ..
1382        'echo "ok"\_s*' ..
1383        '7 PUSHS "ok"\_s*' ..
1384        '8 ECHO 1\_s*' ..
1385        'try\_s*' ..
1386        '9 TRY catch -> 13, endtry -> 15\_s*' ..
1387        'echo "deeper"\_s*' ..
1388        '10 PUSHS "deeper"\_s*' ..
1389        '11 ECHO 1\_s*' ..
1390        'catch\_s*' ..
1391        '12 JUMP -> 15\_s*' ..
1392        '13 CATCH\_s*' ..
1393        'continue\_s*' ..
1394        '14 TRY-CONTINUE 2 levels -> 4\_s*' ..
1395        'endtry\_s*' ..
1396        '15 ENDTRY\_s*' ..
1397        'catch\_s*' ..
1398        '16 JUMP -> 20\_s*' ..
1399        '17 CATCH\_s*' ..
1400        'echo "not ok"\_s*' ..
1401        '18 PUSHS "not ok"\_s*' ..
1402        '19 ECHO 1\_s*' ..
1403        'endtry\_s*' ..
1404        '20 ENDTRY\_s*' ..
1405        'endfor\_s*' ..
1406        '21 JUMP -> 4\_s*' ..
1407        '\d\+ DROP\_s*' ..
1408        '\d\+ RETURN void',
1409        instr)
1410enddef
1411
1412let g:number = 42
1413
1414def TypeCast()
1415  var l: list<number> = [23, <number>g:number]
1416enddef
1417
1418def Test_disassemble_typecast()
1419  var instr = execute('disassemble TypeCast')
1420  assert_match('TypeCast.*' ..
1421        'var l: list<number> = \[23, <number>g:number\].*' ..
1422        '\d PUSHNR 23\_s*' ..
1423        '\d LOADG g:number\_s*' ..
1424        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1425        '\d NEWLIST size 2\_s*' ..
1426        '\d SETTYPE list<number>\_s*' ..
1427        '\d STORE $0\_s*' ..
1428        '\d RETURN void\_s*',
1429        instr)
1430enddef
1431
1432def Computing()
1433  var nr = 3
1434  var nrres = nr + 7
1435  nrres = nr - 7
1436  nrres = nr * 7
1437  nrres = nr / 7
1438  nrres = nr % 7
1439
1440  var anyres = g:number + 7
1441  anyres = g:number - 7
1442  anyres = g:number * 7
1443  anyres = g:number / 7
1444  anyres = g:number % 7
1445
1446  if has('float')
1447    var fl = 3.0
1448    var flres = fl + 7.0
1449    flres = fl - 7.0
1450    flres = fl * 7.0
1451    flres = fl / 7.0
1452  endif
1453enddef
1454
1455def Test_disassemble_computing()
1456  var instr = execute('disassemble Computing')
1457  assert_match('Computing.*' ..
1458        'var nr = 3.*' ..
1459        '\d STORE 3 in $0.*' ..
1460        'var nrres = nr + 7.*' ..
1461        '\d LOAD $0.*' ..
1462        '\d PUSHNR 7.*' ..
1463        '\d OPNR +.*' ..
1464        '\d STORE $1.*' ..
1465        'nrres = nr - 7.*' ..
1466        '\d OPNR -.*' ..
1467        'nrres = nr \* 7.*' ..
1468        '\d OPNR \*.*' ..
1469        'nrres = nr / 7.*' ..
1470        '\d OPNR /.*' ..
1471        'nrres = nr % 7.*' ..
1472        '\d OPNR %.*' ..
1473        'var anyres = g:number + 7.*' ..
1474        '\d LOADG g:number.*' ..
1475        '\d PUSHNR 7.*' ..
1476        '\d OPANY +.*' ..
1477        '\d STORE $2.*' ..
1478        'anyres = g:number - 7.*' ..
1479        '\d OPANY -.*' ..
1480        'anyres = g:number \* 7.*' ..
1481        '\d OPANY \*.*' ..
1482        'anyres = g:number / 7.*' ..
1483        '\d OPANY /.*' ..
1484        'anyres = g:number % 7.*' ..
1485        '\d OPANY %.*',
1486        instr)
1487  if has('float')
1488    assert_match('Computing.*' ..
1489        'var fl = 3.0.*' ..
1490        '\d PUSHF 3.0.*' ..
1491        '\d STORE $3.*' ..
1492        'var flres = fl + 7.0.*' ..
1493        '\d LOAD $3.*' ..
1494        '\d PUSHF 7.0.*' ..
1495        '\d OPFLOAT +.*' ..
1496        '\d STORE $4.*' ..
1497        'flres = fl - 7.0.*' ..
1498        '\d OPFLOAT -.*' ..
1499        'flres = fl \* 7.0.*' ..
1500        '\d OPFLOAT \*.*' ..
1501        'flres = fl / 7.0.*' ..
1502        '\d OPFLOAT /.*',
1503        instr)
1504  endif
1505enddef
1506
1507def AddListBlob()
1508  var reslist = [1, 2] + [3, 4]
1509  var resblob = 0z1122 + 0z3344
1510enddef
1511
1512def Test_disassemble_add_list_blob()
1513  var instr = execute('disassemble AddListBlob')
1514  assert_match('AddListBlob.*' ..
1515        'var reslist = \[1, 2] + \[3, 4].*' ..
1516        '\d PUSHNR 1.*' ..
1517        '\d PUSHNR 2.*' ..
1518        '\d NEWLIST size 2.*' ..
1519        '\d PUSHNR 3.*' ..
1520        '\d PUSHNR 4.*' ..
1521        '\d NEWLIST size 2.*' ..
1522        '\d ADDLIST.*' ..
1523        '\d STORE $.*.*' ..
1524        'var resblob = 0z1122 + 0z3344.*' ..
1525        '\d PUSHBLOB 0z1122.*' ..
1526        '\d PUSHBLOB 0z3344.*' ..
1527        '\d ADDBLOB.*' ..
1528        '\d STORE $.*',
1529        instr)
1530enddef
1531
1532let g:aa = 'aa'
1533def ConcatString(): string
1534  var res = g:aa .. "bb"
1535  return res
1536enddef
1537
1538def Test_disassemble_concat()
1539  var instr = execute('disassemble ConcatString')
1540  assert_match('ConcatString.*' ..
1541        'var res = g:aa .. "bb".*' ..
1542        '\d LOADG g:aa.*' ..
1543        '\d PUSHS "bb".*' ..
1544        '\d 2STRING_ANY stack\[-2].*' ..
1545        '\d CONCAT.*' ..
1546        '\d STORE $.*',
1547        instr)
1548  assert_equal('aabb', ConcatString())
1549enddef
1550
1551def StringIndex(): string
1552  var s = "abcd"
1553  var res = s[1]
1554  return res
1555enddef
1556
1557def Test_disassemble_string_index()
1558  var instr = execute('disassemble StringIndex')
1559  assert_match('StringIndex\_s*' ..
1560        'var s = "abcd"\_s*' ..
1561        '\d PUSHS "abcd"\_s*' ..
1562        '\d STORE $0\_s*' ..
1563        'var res = s\[1]\_s*' ..
1564        '\d LOAD $0\_s*' ..
1565        '\d PUSHNR 1\_s*' ..
1566        '\d STRINDEX\_s*' ..
1567        '\d STORE $1\_s*',
1568        instr)
1569  assert_equal('b', StringIndex())
1570enddef
1571
1572def StringSlice(): string
1573  var s = "abcd"
1574  var res = s[1 : 8]
1575  return res
1576enddef
1577
1578def Test_disassemble_string_slice()
1579  var instr = execute('disassemble StringSlice')
1580  assert_match('StringSlice\_s*' ..
1581        'var s = "abcd"\_s*' ..
1582        '\d PUSHS "abcd"\_s*' ..
1583        '\d STORE $0\_s*' ..
1584        'var res = s\[1 : 8]\_s*' ..
1585        '\d LOAD $0\_s*' ..
1586        '\d PUSHNR 1\_s*' ..
1587        '\d PUSHNR 8\_s*' ..
1588        '\d STRSLICE\_s*' ..
1589        '\d STORE $1\_s*',
1590        instr)
1591  assert_equal('bcd', StringSlice())
1592enddef
1593
1594def ListIndex(): number
1595  var l = [1, 2, 3]
1596  var res = l[1]
1597  return res
1598enddef
1599
1600def Test_disassemble_list_index()
1601  var instr = execute('disassemble ListIndex')
1602  assert_match('ListIndex\_s*' ..
1603        'var l = \[1, 2, 3]\_s*' ..
1604        '\d PUSHNR 1\_s*' ..
1605        '\d PUSHNR 2\_s*' ..
1606        '\d PUSHNR 3\_s*' ..
1607        '\d NEWLIST size 3\_s*' ..
1608        '\d STORE $0\_s*' ..
1609        'var res = l\[1]\_s*' ..
1610        '\d LOAD $0\_s*' ..
1611        '\d PUSHNR 1\_s*' ..
1612        '\d LISTINDEX\_s*' ..
1613        '\d STORE $1\_s*',
1614        instr)
1615  assert_equal(2, ListIndex())
1616enddef
1617
1618def ListSlice(): list<number>
1619  var l = [1, 2, 3]
1620  var res = l[1 : 8]
1621  return res
1622enddef
1623
1624def Test_disassemble_list_slice()
1625  var instr = execute('disassemble ListSlice')
1626  assert_match('ListSlice\_s*' ..
1627        'var l = \[1, 2, 3]\_s*' ..
1628        '\d PUSHNR 1\_s*' ..
1629        '\d PUSHNR 2\_s*' ..
1630        '\d PUSHNR 3\_s*' ..
1631        '\d NEWLIST size 3\_s*' ..
1632        '\d STORE $0\_s*' ..
1633        'var res = l\[1 : 8]\_s*' ..
1634        '\d LOAD $0\_s*' ..
1635        '\d PUSHNR 1\_s*' ..
1636        '\d PUSHNR 8\_s*' ..
1637        '\d LISTSLICE\_s*' ..
1638        '\d STORE $1\_s*',
1639        instr)
1640  assert_equal([2, 3], ListSlice())
1641enddef
1642
1643def DictMember(): number
1644  var d = {item: 1}
1645  var res = d.item
1646  res = d["item"]
1647  return res
1648enddef
1649
1650def Test_disassemble_dict_member()
1651  var instr = execute('disassemble DictMember')
1652  assert_match('DictMember\_s*' ..
1653        'var d = {item: 1}\_s*' ..
1654        '\d PUSHS "item"\_s*' ..
1655        '\d PUSHNR 1\_s*' ..
1656        '\d NEWDICT size 1\_s*' ..
1657        '\d STORE $0\_s*' ..
1658        'var res = d.item\_s*' ..
1659        '\d\+ LOAD $0\_s*' ..
1660        '\d\+ MEMBER item\_s*' ..
1661        '\d\+ USEDICT\_s*' ..
1662        '\d\+ STORE $1\_s*' ..
1663        'res = d\["item"\]\_s*' ..
1664        '\d\+ LOAD $0\_s*' ..
1665        '\d\+ PUSHS "item"\_s*' ..
1666        '\d\+ MEMBER\_s*' ..
1667        '\d\+ USEDICT\_s*' ..
1668        '\d\+ STORE $1\_s*',
1669        instr)
1670  assert_equal(1, DictMember())
1671enddef
1672
1673let somelist = [1, 2, 3, 4, 5]
1674def AnyIndex(): number
1675  var res = g:somelist[2]
1676  return res
1677enddef
1678
1679def Test_disassemble_any_index()
1680  var instr = execute('disassemble AnyIndex')
1681  assert_match('AnyIndex\_s*' ..
1682        'var res = g:somelist\[2\]\_s*' ..
1683        '\d LOADG g:somelist\_s*' ..
1684        '\d PUSHNR 2\_s*' ..
1685        '\d ANYINDEX\_s*' ..
1686        '\d STORE $0\_s*' ..
1687        'return res\_s*' ..
1688        '\d LOAD $0\_s*' ..
1689        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1690        '\d RETURN',
1691        instr)
1692  assert_equal(3, AnyIndex())
1693enddef
1694
1695def AnySlice(): list<number>
1696  var res = g:somelist[1 : 3]
1697  return res
1698enddef
1699
1700def Test_disassemble_any_slice()
1701  var instr = execute('disassemble AnySlice')
1702  assert_match('AnySlice\_s*' ..
1703        'var res = g:somelist\[1 : 3\]\_s*' ..
1704        '\d LOADG g:somelist\_s*' ..
1705        '\d PUSHNR 1\_s*' ..
1706        '\d PUSHNR 3\_s*' ..
1707        '\d ANYSLICE\_s*' ..
1708        '\d STORE $0\_s*' ..
1709        'return res\_s*' ..
1710        '\d LOAD $0\_s*' ..
1711        '\d CHECKTYPE list<number> stack\[-1\]\_s*' ..
1712        '\d RETURN',
1713        instr)
1714  assert_equal([2, 3, 4], AnySlice())
1715enddef
1716
1717def NegateNumber(): number
1718  g:nr = 9
1719  var plus = +g:nr
1720  var minus = -g:nr
1721  return minus
1722enddef
1723
1724def Test_disassemble_negate_number()
1725  var instr = execute('disassemble NegateNumber')
1726  assert_match('NegateNumber\_s*' ..
1727        'g:nr = 9\_s*' ..
1728        '\d PUSHNR 9\_s*' ..
1729        '\d STOREG g:nr\_s*' ..
1730        'var plus = +g:nr\_s*' ..
1731        '\d LOADG g:nr\_s*' ..
1732        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1733        '\d STORE $0\_s*' ..
1734        'var minus = -g:nr\_s*' ..
1735        '\d LOADG g:nr\_s*' ..
1736        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1737        '\d NEGATENR\_s*' ..
1738        '\d STORE $1\_s*',
1739        instr)
1740  assert_equal(-9, NegateNumber())
1741enddef
1742
1743def InvertBool(): bool
1744  var flag = true
1745  var invert = !flag
1746  var res = !!flag
1747  return res
1748enddef
1749
1750def Test_disassemble_invert_bool()
1751  var instr = execute('disassemble InvertBool')
1752  assert_match('InvertBool\_s*' ..
1753        'var flag = true\_s*' ..
1754        '\d PUSH true\_s*' ..
1755        '\d STORE $0\_s*' ..
1756        'var invert = !flag\_s*' ..
1757        '\d LOAD $0\_s*' ..
1758        '\d INVERT -1 (!val)\_s*' ..
1759        '\d STORE $1\_s*' ..
1760        'var res = !!flag\_s*' ..
1761        '\d LOAD $0\_s*' ..
1762        '\d 2BOOL -1 (!!val)\_s*' ..
1763        '\d STORE $2\_s*',
1764        instr)
1765  assert_equal(true, InvertBool())
1766enddef
1767
1768def ReturnBool(): bool
1769  var one = 1
1770  var zero = 0
1771  var name: bool = one && zero || one
1772  return name
1773enddef
1774
1775def Test_disassemble_return_bool()
1776  var instr = execute('disassemble ReturnBool')
1777  assert_match('ReturnBool\_s*' ..
1778        'var one = 1\_s*' ..
1779        '0 STORE 1 in $0\_s*' ..
1780        'var zero = 0\_s*' ..
1781        '1 STORE 0 in $1\_s*' ..
1782        'var name: bool = one && zero || one\_s*' ..
1783        '2 LOAD $0\_s*' ..
1784        '3 COND2BOOL\_s*' ..
1785        '4 JUMP_IF_COND_FALSE -> 7\_s*' ..
1786        '5 LOAD $1\_s*' ..
1787        '6 COND2BOOL\_s*' ..
1788        '7 JUMP_IF_COND_TRUE -> 10\_s*' ..
1789        '8 LOAD $0\_s*' ..
1790        '9 COND2BOOL\_s*' ..
1791        '10 STORE $2\_s*' ..
1792        'return name\_s*' ..
1793        '\d\+ LOAD $2\_s*' ..
1794        '\d\+ RETURN',
1795        instr)
1796  assert_equal(true, InvertBool())
1797enddef
1798
1799def Test_disassemble_compare()
1800  var cases = [
1801        ['true == isFalse', 'COMPAREBOOL =='],
1802        ['true != isFalse', 'COMPAREBOOL !='],
1803        ['v:none == isNull', 'COMPARESPECIAL =='],
1804        ['v:none != isNull', 'COMPARESPECIAL !='],
1805
1806        ['111 == aNumber', 'COMPARENR =='],
1807        ['111 != aNumber', 'COMPARENR !='],
1808        ['111 > aNumber', 'COMPARENR >'],
1809        ['111 < aNumber', 'COMPARENR <'],
1810        ['111 >= aNumber', 'COMPARENR >='],
1811        ['111 <= aNumber', 'COMPARENR <='],
1812        ['111 =~ aNumber', 'COMPARENR =\~'],
1813        ['111 !~ aNumber', 'COMPARENR !\~'],
1814
1815        ['"xx" != aString', 'COMPARESTRING !='],
1816        ['"xx" > aString', 'COMPARESTRING >'],
1817        ['"xx" < aString', 'COMPARESTRING <'],
1818        ['"xx" >= aString', 'COMPARESTRING >='],
1819        ['"xx" <= aString', 'COMPARESTRING <='],
1820        ['"xx" =~ aString', 'COMPARESTRING =\~'],
1821        ['"xx" !~ aString', 'COMPARESTRING !\~'],
1822        ['"xx" is aString', 'COMPARESTRING is'],
1823        ['"xx" isnot aString', 'COMPARESTRING isnot'],
1824
1825        ['0z11 == aBlob', 'COMPAREBLOB =='],
1826        ['0z11 != aBlob', 'COMPAREBLOB !='],
1827        ['0z11 is aBlob', 'COMPAREBLOB is'],
1828        ['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
1829
1830        ['[1, 2] == aList', 'COMPARELIST =='],
1831        ['[1, 2] != aList', 'COMPARELIST !='],
1832        ['[1, 2] is aList', 'COMPARELIST is'],
1833        ['[1, 2] isnot aList', 'COMPARELIST isnot'],
1834
1835        ['{a: 1} == aDict', 'COMPAREDICT =='],
1836        ['{a: 1} != aDict', 'COMPAREDICT !='],
1837        ['{a: 1} is aDict', 'COMPAREDICT is'],
1838        ['{a: 1} isnot aDict', 'COMPAREDICT isnot'],
1839
1840        ['(() => 33) == (() => 44)', 'COMPAREFUNC =='],
1841        ['(() => 33) != (() => 44)', 'COMPAREFUNC !='],
1842        ['(() => 33) is (() => 44)', 'COMPAREFUNC is'],
1843        ['(() => 33) isnot (() => 44)', 'COMPAREFUNC isnot'],
1844
1845        ['77 == g:xx', 'COMPAREANY =='],
1846        ['77 != g:xx', 'COMPAREANY !='],
1847        ['77 > g:xx', 'COMPAREANY >'],
1848        ['77 < g:xx', 'COMPAREANY <'],
1849        ['77 >= g:xx', 'COMPAREANY >='],
1850        ['77 <= g:xx', 'COMPAREANY <='],
1851        ['77 =~ g:xx', 'COMPAREANY =\~'],
1852        ['77 !~ g:xx', 'COMPAREANY !\~'],
1853        ['77 is g:xx', 'COMPAREANY is'],
1854        ['77 isnot g:xx', 'COMPAREANY isnot'],
1855        ]
1856  var floatDecl = ''
1857  if has('float')
1858    cases->extend([
1859        ['1.1 == aFloat', 'COMPAREFLOAT =='],
1860        ['1.1 != aFloat', 'COMPAREFLOAT !='],
1861        ['1.1 > aFloat', 'COMPAREFLOAT >'],
1862        ['1.1 < aFloat', 'COMPAREFLOAT <'],
1863        ['1.1 >= aFloat', 'COMPAREFLOAT >='],
1864        ['1.1 <= aFloat', 'COMPAREFLOAT <='],
1865        ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
1866        ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
1867        ])
1868    floatDecl = 'var aFloat = 2.2'
1869  endif
1870
1871  var nr = 1
1872  for case in cases
1873    # declare local variables to get a non-constant with the right type
1874    writefile(['def TestCase' .. nr .. '()',
1875             '  var isFalse = false',
1876             '  var isNull = v:null',
1877             '  var aNumber = 222',
1878             '  var aString = "yy"',
1879             '  var aBlob = 0z22',
1880             '  var aList = [3, 4]',
1881             '  var aDict = {x: 2}',
1882             floatDecl,
1883             '  if ' .. case[0],
1884             '    echo 42'
1885             '  endif',
1886             'enddef'], 'Xdisassemble')
1887    source Xdisassemble
1888    var instr = execute('disassemble TestCase' .. nr)
1889    assert_match('TestCase' .. nr .. '.*' ..
1890        'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1891        '\d \(PUSH\|FUNCREF\).*' ..
1892        '\d \(PUSH\|FUNCREF\|LOAD\).*' ..
1893        '\d ' .. case[1] .. '.*' ..
1894        '\d JUMP_IF_FALSE -> \d\+.*',
1895        instr)
1896
1897    nr += 1
1898  endfor
1899
1900  delete('Xdisassemble')
1901enddef
1902
1903def s:FalsyOp()
1904  echo g:flag ?? "yes"
1905  echo [] ?? "empty list"
1906  echo "" ?? "empty string"
1907enddef
1908
1909def Test_dsassemble_falsy_op()
1910  var res = execute('disass s:FalsyOp')
1911  assert_match('\<SNR>\d*_FalsyOp\_s*' ..
1912      'echo g:flag ?? "yes"\_s*' ..
1913      '0 LOADG g:flag\_s*' ..
1914      '1 JUMP_AND_KEEP_IF_TRUE -> 3\_s*' ..
1915      '2 PUSHS "yes"\_s*' ..
1916      '3 ECHO 1\_s*' ..
1917      'echo \[\] ?? "empty list"\_s*' ..
1918      '4 NEWLIST size 0\_s*' ..
1919      '5 JUMP_AND_KEEP_IF_TRUE -> 7\_s*' ..
1920      '6 PUSHS "empty list"\_s*' ..
1921      '7 ECHO 1\_s*' ..
1922      'echo "" ?? "empty string"\_s*' ..
1923      '\d\+ PUSHS "empty string"\_s*' ..
1924      '\d\+ ECHO 1\_s*' ..
1925      '\d\+ RETURN void',
1926      res)
1927enddef
1928
1929def Test_disassemble_compare_const()
1930  var cases = [
1931        ['"xx" == "yy"', false],
1932        ['"aa" == "aa"', true],
1933        ['has("eval") ? true : false', true],
1934        ['has("asdf") ? true : false', false],
1935        ]
1936
1937  var nr = 1
1938  for case in cases
1939    writefile(['def TestCase' .. nr .. '()',
1940             '  if ' .. case[0],
1941             '    echo 42'
1942             '  endif',
1943             'enddef'], 'Xdisassemble')
1944    source Xdisassemble
1945    var instr = execute('disassemble TestCase' .. nr)
1946    if case[1]
1947      # condition true, "echo 42" executed
1948      assert_match('TestCase' .. nr .. '.*' ..
1949          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1950          '\d PUSHNR 42.*' ..
1951          '\d ECHO 1.*' ..
1952          '\d RETURN void',
1953          instr)
1954    else
1955      # condition false, function just returns
1956      assert_match('TestCase' .. nr .. '.*' ..
1957          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' ..
1958          'echo 42[ \n]*' ..
1959          'endif[ \n]*' ..
1960          '\d RETURN void',
1961          instr)
1962    endif
1963
1964    nr += 1
1965  endfor
1966
1967  delete('Xdisassemble')
1968enddef
1969
1970def s:Execute()
1971  execute 'help vim9.txt'
1972  var cmd = 'help vim9.txt'
1973  execute cmd
1974  var tag = 'vim9.txt'
1975  execute 'help ' .. tag
1976enddef
1977
1978def Test_disassemble_execute()
1979  var res = execute('disass s:Execute')
1980  assert_match('\<SNR>\d*_Execute\_s*' ..
1981        "execute 'help vim9.txt'\\_s*" ..
1982        '\d PUSHS "help vim9.txt"\_s*' ..
1983        '\d EXECUTE 1\_s*' ..
1984        "var cmd = 'help vim9.txt'\\_s*" ..
1985        '\d PUSHS "help vim9.txt"\_s*' ..
1986        '\d STORE $0\_s*' ..
1987        'execute cmd\_s*' ..
1988        '\d LOAD $0\_s*' ..
1989        '\d EXECUTE 1\_s*' ..
1990        "var tag = 'vim9.txt'\\_s*" ..
1991        '\d PUSHS "vim9.txt"\_s*' ..
1992        '\d STORE $1\_s*' ..
1993        "execute 'help ' .. tag\\_s*" ..
1994        '\d\+ PUSHS "help "\_s*' ..
1995        '\d\+ LOAD $1\_s*' ..
1996        '\d\+ CONCAT\_s*' ..
1997        '\d\+ EXECUTE 1\_s*' ..
1998        '\d\+ RETURN void',
1999        res)
2000enddef
2001
2002def s:Echomsg()
2003  echomsg 'some' 'message'
2004  echoconsole 'nothing'
2005  echoerr 'went' .. 'wrong'
2006enddef
2007
2008def Test_disassemble_echomsg()
2009  var res = execute('disass s:Echomsg')
2010  assert_match('\<SNR>\d*_Echomsg\_s*' ..
2011        "echomsg 'some' 'message'\\_s*" ..
2012        '\d PUSHS "some"\_s*' ..
2013        '\d PUSHS "message"\_s*' ..
2014        '\d ECHOMSG 2\_s*' ..
2015        "echoconsole 'nothing'\\_s*" ..
2016        '\d PUSHS "nothing"\_s*' ..
2017        '\d ECHOCONSOLE 1\_s*' ..
2018        "echoerr 'went' .. 'wrong'\\_s*" ..
2019        '\d PUSHS "wentwrong"\_s*' ..
2020        '\d ECHOERR 1\_s*' ..
2021        '\d RETURN void',
2022        res)
2023enddef
2024
2025def SomeStringArg(arg: string)
2026  echo arg
2027enddef
2028
2029def SomeAnyArg(arg: any)
2030  echo arg
2031enddef
2032
2033def SomeStringArgAndReturn(arg: string): string
2034  return arg
2035enddef
2036
2037def Test_display_func()
2038  var res1 = execute('function SomeStringArg')
2039  assert_match('.* def SomeStringArg(arg: string)\_s*' ..
2040        '\d *echo arg.*' ..
2041        ' *enddef',
2042        res1)
2043
2044  var res2 = execute('function SomeAnyArg')
2045  assert_match('.* def SomeAnyArg(arg: any)\_s*' ..
2046        '\d *echo arg\_s*' ..
2047        ' *enddef',
2048        res2)
2049
2050  var res3 = execute('function SomeStringArgAndReturn')
2051  assert_match('.* def SomeStringArgAndReturn(arg: string): string\_s*' ..
2052        '\d *return arg\_s*' ..
2053        ' *enddef',
2054        res3)
2055enddef
2056
2057def Test_vim9script_forward_func()
2058  var lines =<< trim END
2059    vim9script
2060    def FuncOne(): string
2061      return FuncTwo()
2062    enddef
2063    def FuncTwo(): string
2064      return 'two'
2065    enddef
2066    g:res_FuncOne = execute('disass FuncOne')
2067  END
2068  writefile(lines, 'Xdisassemble')
2069  source Xdisassemble
2070
2071  # check that the first function calls the second with DCALL
2072  assert_match('\<SNR>\d*_FuncOne\_s*' ..
2073        'return FuncTwo()\_s*' ..
2074        '\d DCALL <SNR>\d\+_FuncTwo(argc 0)\_s*' ..
2075        '\d RETURN',
2076        g:res_FuncOne)
2077
2078  delete('Xdisassemble')
2079  unlet g:res_FuncOne
2080enddef
2081
2082def s:ConcatStrings(): string
2083  return 'one' .. 'two' .. 'three'
2084enddef
2085
2086def s:ComputeConst(): number
2087  return 2 + 3 * 4 / 6 + 7
2088enddef
2089
2090def s:ComputeConstParen(): number
2091  return ((2 + 4) * (8 / 2)) / (3 + 4)
2092enddef
2093
2094def Test_simplify_const_expr()
2095  var res = execute('disass s:ConcatStrings')
2096  assert_match('<SNR>\d*_ConcatStrings\_s*' ..
2097        "return 'one' .. 'two' .. 'three'\\_s*" ..
2098        '\d PUSHS "onetwothree"\_s*' ..
2099        '\d RETURN',
2100        res)
2101
2102  res = execute('disass s:ComputeConst')
2103  assert_match('<SNR>\d*_ComputeConst\_s*' ..
2104        'return 2 + 3 \* 4 / 6 + 7\_s*' ..
2105        '\d PUSHNR 11\_s*' ..
2106        '\d RETURN',
2107        res)
2108
2109  res = execute('disass s:ComputeConstParen')
2110  assert_match('<SNR>\d*_ComputeConstParen\_s*' ..
2111        'return ((2 + 4) \* (8 / 2)) / (3 + 4)\_s*' ..
2112        '\d PUSHNR 3\>\_s*' ..
2113        '\d RETURN',
2114        res)
2115enddef
2116
2117def s:CallAppend()
2118  eval "some text"->append(2)
2119enddef
2120
2121def Test_shuffle()
2122  var res = execute('disass s:CallAppend')
2123  assert_match('<SNR>\d*_CallAppend\_s*' ..
2124        'eval "some text"->append(2)\_s*' ..
2125        '\d PUSHS "some text"\_s*' ..
2126        '\d PUSHNR 2\_s*' ..
2127        '\d SHUFFLE 2 up 1\_s*' ..
2128        '\d BCALL append(argc 2)\_s*' ..
2129        '\d DROP\_s*' ..
2130        '\d RETURN void',
2131        res)
2132enddef
2133
2134
2135def s:SilentMessage()
2136  silent echomsg "text"
2137  silent! echoerr "error"
2138enddef
2139
2140def Test_silent()
2141  var res = execute('disass s:SilentMessage')
2142  assert_match('<SNR>\d*_SilentMessage\_s*' ..
2143        'silent echomsg "text"\_s*' ..
2144        '\d CMDMOD silent\_s*' ..
2145        '\d PUSHS "text"\_s*' ..
2146        '\d ECHOMSG 1\_s*' ..
2147        '\d CMDMOD_REV\_s*' ..
2148        'silent! echoerr "error"\_s*' ..
2149        '\d CMDMOD silent!\_s*' ..
2150        '\d PUSHS "error"\_s*' ..
2151        '\d ECHOERR 1\_s*' ..
2152        '\d CMDMOD_REV\_s*' ..
2153        '\d\+ RETURN void',
2154        res)
2155enddef
2156
2157def s:SilentIf()
2158  silent if 4 == g:five
2159  silent elseif 4 == g:five
2160  endif
2161enddef
2162
2163def Test_silent_if()
2164  var res = execute('disass s:SilentIf')
2165  assert_match('<SNR>\d*_SilentIf\_s*' ..
2166        'silent if 4 == g:five\_s*' ..
2167        '\d\+ CMDMOD silent\_s*' ..
2168        '\d\+ PUSHNR 4\_s*' ..
2169        '\d\+ LOADG g:five\_s*' ..
2170        '\d\+ COMPAREANY ==\_s*' ..
2171        '\d\+ CMDMOD_REV\_s*' ..
2172        '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
2173        'silent elseif 4 == g:five\_s*' ..
2174        '\d\+ JUMP -> \d\+\_s*' ..
2175        '\d\+ CMDMOD silent\_s*' ..
2176        '\d\+ PUSHNR 4\_s*' ..
2177        '\d\+ LOADG g:five\_s*' ..
2178        '\d\+ COMPAREANY ==\_s*' ..
2179        '\d\+ CMDMOD_REV\_s*' ..
2180        '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
2181        'endif\_s*' ..
2182        '\d\+ RETURN void',
2183        res)
2184enddef
2185
2186def s:SilentFor()
2187  silent for i in [0]
2188  endfor
2189enddef
2190
2191def Test_silent_for()
2192  var res = execute('disass s:SilentFor')
2193  assert_match('<SNR>\d*_SilentFor\_s*' ..
2194        'silent for i in \[0\]\_s*' ..
2195        '\d CMDMOD silent\_s*' ..
2196        '\d STORE -1 in $0\_s*' ..
2197        '\d PUSHNR 0\_s*' ..
2198        '\d NEWLIST size 1\_s*' ..
2199        '\d CMDMOD_REV\_s*' ..
2200        '5 FOR $0 -> 8\_s*' ..
2201        '\d STORE $1\_s*' ..
2202        'endfor\_s*' ..
2203        '\d JUMP -> 5\_s*' ..
2204        '8 DROP\_s*' ..
2205        '\d RETURN void\_s*',
2206        res)
2207enddef
2208
2209def s:SilentWhile()
2210  silent while g:not
2211  endwhile
2212enddef
2213
2214def Test_silent_while()
2215  var res = execute('disass s:SilentWhile')
2216  assert_match('<SNR>\d*_SilentWhile\_s*' ..
2217        'silent while g:not\_s*' ..
2218        '0 CMDMOD silent\_s*' ..
2219        '\d LOADG g:not\_s*' ..
2220        '\d COND2BOOL\_s*' ..
2221        '\d CMDMOD_REV\_s*' ..
2222        '\d JUMP_IF_FALSE -> 6\_s*' ..
2223
2224        'endwhile\_s*' ..
2225        '\d JUMP -> 0\_s*' ..
2226        '6 RETURN void\_s*',
2227         res)
2228enddef
2229
2230def s:SilentReturn(): string
2231  silent return "done"
2232enddef
2233
2234def Test_silent_return()
2235  var res = execute('disass s:SilentReturn')
2236  assert_match('<SNR>\d*_SilentReturn\_s*' ..
2237        'silent return "done"\_s*' ..
2238        '\d CMDMOD silent\_s*' ..
2239        '\d PUSHS "done"\_s*' ..
2240        '\d CMDMOD_REV\_s*' ..
2241        '\d RETURN',
2242        res)
2243enddef
2244
2245def s:Profiled(): string
2246  # comment
2247  echo "profiled"
2248  # comment
2249  var some = "some text"
2250  return "done"
2251enddef
2252
2253def Test_profiled()
2254  if !has('profile')
2255    MissingFeature 'profile'
2256  endif
2257  var res = execute('disass profile s:Profiled')
2258  assert_match('<SNR>\d*_Profiled\_s*' ..
2259        '# comment\_s*' ..
2260        'echo "profiled"\_s*' ..
2261        '\d PROFILE START line 2\_s*' ..
2262        '\d PUSHS "profiled"\_s*' ..
2263        '\d ECHO 1\_s*' ..
2264        '# comment\_s*' ..
2265        'var some = "some text"\_s*' ..
2266        '\d PROFILE END\_s*' ..
2267        '\d PROFILE START line 4\_s*' ..
2268        '\d PUSHS "some text"\_s*' ..
2269        '\d STORE $0\_s*' ..
2270        'return "done"\_s*' ..
2271        '\d PROFILE END\_s*' ..
2272        '\d PROFILE START line 5\_s*' ..
2273        '\d PUSHS "done"\_s*' ..
2274        '\d\+ RETURN\_s*' ..
2275        '\d\+ PROFILE END',
2276        res)
2277enddef
2278
2279def Test_debugged()
2280  var res = execute('disass debug s:Profiled')
2281  assert_match('<SNR>\d*_Profiled\_s*' ..
2282        '# comment\_s*' ..
2283        'echo "profiled"\_s*' ..
2284        '\d DEBUG line 1-2 varcount 0\_s*' ..
2285        '\d PUSHS "profiled"\_s*' ..
2286        '\d ECHO 1\_s*' ..
2287        '# comment\_s*' ..
2288        'var some = "some text"\_s*' ..
2289        '\d DEBUG line 3-4 varcount 0\_s*' ..
2290        '\d PUSHS "some text"\_s*' ..
2291        '\d STORE $0\_s*' ..
2292        'return "done"\_s*' ..
2293        '\d DEBUG line 5-5 varcount 1\_s*' ..
2294        '\d PUSHS "done"\_s*' ..
2295        '\d RETURN\_s*',
2296        res)
2297enddef
2298
2299def s:DebugElseif()
2300  var b = false
2301  if b
2302    eval 1 + 0
2303  silent elseif !b
2304    eval 2 + 0
2305  endif
2306enddef
2307
2308def Test_debug_elseif()
2309  var res = execute('disass debug s:DebugElseif')
2310  assert_match('<SNR>\d*_DebugElseif\_s*' ..
2311          'var b = false\_s*' ..
2312          '0 DEBUG line 1-1 varcount 0\_s*' ..
2313          '1 PUSH false\_s*' ..
2314          '2 STORE $0\_s*' ..
2315
2316          'if b\_s*' ..
2317          '3 DEBUG line 2-2 varcount 1\_s*' ..
2318          '4 LOAD $0\_s*' ..
2319          '5 JUMP_IF_FALSE -> 10\_s*' ..
2320
2321          'eval 1 + 0\_s*' ..
2322          '6 DEBUG line 3-3 varcount 1\_s*' ..
2323          '7 PUSHNR 1\_s*' ..
2324          '8 DROP\_s*' ..
2325
2326          'silent elseif !b\_s*' ..
2327          '9 JUMP -> 20\_s*' ..
2328          '10 CMDMOD silent\_s*' ..
2329          '11 DEBUG line 4-4 varcount 1\_s*' ..
2330          '12 LOAD $0\_s*' ..
2331          '13 INVERT -1 (!val)\_s*' ..
2332          '14 CMDMOD_REV\_s*' ..
2333          '15 JUMP_IF_FALSE -> 20\_s*' ..
2334
2335          'eval 2 + 0\_s*' ..
2336          '16 DEBUG line 5-5 varcount 1\_s*' ..
2337          '17 PUSHNR 2\_s*' ..
2338          '18 DROP\_s*' ..
2339
2340          'endif\_s*' ..
2341          '19 DEBUG line 6-6 varcount 1\_s*' ..
2342          '20 RETURN void*',
2343        res)
2344enddef
2345
2346func Legacy() dict
2347  echo 'legacy'
2348endfunc
2349
2350def s:UseMember()
2351  var d = {func: Legacy}
2352  var v = d.func()
2353enddef
2354
2355def Test_disassemble_dict_stack()
2356  var res = execute('disass s:UseMember')
2357  assert_match('<SNR>\d*_UseMember\_s*' ..
2358          'var d = {func: Legacy}\_s*' ..
2359          '\d PUSHS "func"\_s*' ..
2360          '\d PUSHFUNC "Legacy"\_s*' ..
2361          '\d NEWDICT size 1\_s*' ..
2362          '\d STORE $0\_s*' ..
2363
2364          'var v = d.func()\_s*' ..
2365          '\d LOAD $0\_s*' ..
2366          '\d MEMBER func\_s*' ..
2367          '\d PCALL top (argc 0)\_s*' ..
2368          '\d PCALL end\_s*' ..
2369          '\d CLEARDICT\_s*' ..
2370          '\d\+ STORE $1\_s*' ..
2371          '\d\+ RETURN void*',
2372        res)
2373enddef
2374
2375def s:EchoMessages()
2376  echohl ErrorMsg | echom v:exception | echohl NONE
2377enddef
2378
2379def Test_disassemble_nextcmd()
2380  # splitting commands and removing trailing blanks should not change the line
2381  var res = execute('disass s:EchoMessages')
2382  assert_match('<SNR>\d*_EchoMessages\_s*' ..
2383        'echohl ErrorMsg | echom v:exception | echohl NONE',
2384        res)
2385enddef
2386
2387def Test_disassemble_after_reload()
2388    var lines =<< trim END
2389        vim9script
2390        if exists('g:ThisFunc')
2391          finish
2392        endif
2393        var name: any
2394        def g:ThisFunc(): number
2395          g:name = name
2396          return 0
2397        enddef
2398        def g:ThatFunc(): number
2399          name = g:name
2400          return 0
2401        enddef
2402    END
2403    lines->writefile('Xreload.vim')
2404
2405    source Xreload.vim
2406    g:ThisFunc()
2407    g:ThatFunc()
2408
2409    source Xreload.vim
2410    var res = execute('disass g:ThisFunc')
2411    assert_match('ThisFunc\_s*' ..
2412          'g:name = name\_s*' ..
2413          '\d LOADSCRIPT \[deleted\] from .*/Xreload.vim\_s*' ..
2414          '\d STOREG g:name\_s*' ..
2415          'return 0\_s*' ..
2416          '\d PUSHNR 0\_s*' ..
2417          '\d RETURN\_s*',
2418          res)
2419
2420    res = execute('disass g:ThatFunc')
2421    assert_match('ThatFunc\_s*' ..
2422          'name = g:name\_s*' ..
2423          '\d LOADG g:name\_s*' ..
2424          '\d STORESCRIPT \[deleted\] in .*/Xreload.vim\_s*' ..
2425          'return 0\_s*' ..
2426          '\d PUSHNR 0\_s*' ..
2427          '\d RETURN\_s*',
2428          res)
2429
2430    delete('Xreload.vim')
2431    delfunc g:ThisFunc
2432    delfunc g:ThatFunc
2433enddef
2434
2435
2436
2437" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
2438