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 0',
121        res)
122enddef
123
124def s:YankRange()
125  norm! m[jjm]
126  :'[,']yank
127enddef
128
129def Test_disassemble_yank_range()
130  var res = execute('disass s:YankRange')
131  assert_match('<SNR>\d*_YankRange.*' ..
132        ' norm! m\[jjm\]\_s*' ..
133        '\d EXEC   norm! m\[jjm\]\_s*' ..
134        '  :''\[,''\]yank\_s*' ..
135        '\d EXEC   :''\[,''\]yank\_s*' ..
136        '\d RETURN 0',
137        res)
138enddef
139
140def s:PutExpr()
141  :3put ="text"
142enddef
143
144def Test_disassemble_put_expr()
145  var res = execute('disass s:PutExpr')
146  assert_match('<SNR>\d*_PutExpr.*' ..
147        ' :3put ="text"\_s*' ..
148        '\d PUSHS "text"\_s*' ..
149        '\d PUT = 3\_s*' ..
150        '\d RETURN 0',
151        res)
152enddef
153
154def s:PutRange()
155  :$-2put a
156enddef
157
158def Test_disassemble_put_range()
159  var res = execute('disass s:PutRange')
160  assert_match('<SNR>\d*_PutRange.*' ..
161        ' :$-2put a\_s*' ..
162        '\d RANGE $-2\_s*' ..
163        '\d PUT a range\_s*' ..
164        '\d RETURN 0',
165        res)
166enddef
167
168def s:ScriptFuncPush()
169  var localbool = true
170  var localspec = v:none
171  var localblob = 0z1234
172  if has('float')
173    var localfloat = 1.234
174  endif
175enddef
176
177def Test_disassemble_push()
178  var res = execute('disass s:ScriptFuncPush')
179  assert_match('<SNR>\d*_ScriptFuncPush.*' ..
180        'localbool = true.*' ..
181        ' PUSH true.*' ..
182        'localspec = v:none.*' ..
183        ' PUSH v:none.*' ..
184        'localblob = 0z1234.*' ..
185        ' PUSHBLOB 0z1234.*',
186        res)
187  if has('float')
188    assert_match('<SNR>\d*_ScriptFuncPush.*' ..
189          'localfloat = 1.234.*' ..
190          ' PUSHF 1.234.*',
191          res)
192  endif
193enddef
194
195def s:ScriptFuncStore()
196  var localnr = 1
197  localnr = 2
198  var localstr = 'abc'
199  localstr = 'xyz'
200  v:char = 'abc'
201  s:scriptvar = 'sv'
202  g:globalvar = 'gv'
203  g:auto#var = 'av'
204  b:buffervar = 'bv'
205  w:windowvar = 'wv'
206  t:tabpagevar = 'tv'
207  &tabstop = 8
208  $ENVVAR = 'ev'
209  @z = 'rv'
210enddef
211
212def Test_disassemble_store()
213  var res = execute('disass s:ScriptFuncStore')
214  assert_match('<SNR>\d*_ScriptFuncStore.*' ..
215        'var localnr = 1.*' ..
216        'localnr = 2.*' ..
217        ' STORE 2 in $0.*' ..
218        'var localstr = ''abc''.*' ..
219        'localstr = ''xyz''.*' ..
220        ' STORE $1.*' ..
221        'v:char = ''abc''.*' ..
222        'STOREV v:char.*' ..
223        's:scriptvar = ''sv''.*' ..
224        ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' ..
225        'g:globalvar = ''gv''.*' ..
226        ' STOREG g:globalvar.*' ..
227        'g:auto#var = ''av''.*' ..
228        ' STOREAUTO g:auto#var.*' ..
229        'b:buffervar = ''bv''.*' ..
230        ' STOREB b:buffervar.*' ..
231        'w:windowvar = ''wv''.*' ..
232        ' STOREW w:windowvar.*' ..
233        't:tabpagevar = ''tv''.*' ..
234        ' STORET t:tabpagevar.*' ..
235        '&tabstop = 8.*' ..
236        ' STOREOPT &tabstop.*' ..
237        '$ENVVAR = ''ev''.*' ..
238        ' STOREENV $ENVVAR.*' ..
239        '@z = ''rv''.*' ..
240        ' STOREREG @z.*',
241        res)
242enddef
243
244def s:ScriptFuncStoreMember()
245  var locallist: list<number> = []
246  locallist[0] = 123
247  var localdict: dict<number> = {}
248  localdict["a"] = 456
249enddef
250
251def Test_disassemble_store_member()
252  var res = execute('disass s:ScriptFuncStoreMember')
253  assert_match('<SNR>\d*_ScriptFuncStoreMember\_s*' ..
254        'var locallist: list<number> = []\_s*' ..
255        '\d NEWLIST size 0\_s*' ..
256        '\d SETTYPE list<number>\_s*' ..
257        '\d STORE $0\_s*' ..
258        'locallist\[0\] = 123\_s*' ..
259        '\d PUSHNR 123\_s*' ..
260        '\d PUSHNR 0\_s*' ..
261        '\d LOAD $0\_s*' ..
262        '\d STORELIST\_s*' ..
263        'var localdict: dict<number> = {}\_s*' ..
264        '\d NEWDICT size 0\_s*' ..
265        '\d SETTYPE dict<number>\_s*' ..
266        '\d STORE $1\_s*' ..
267        'localdict\["a"\] = 456\_s*' ..
268        '\d\+ PUSHNR 456\_s*' ..
269        '\d\+ PUSHS "a"\_s*' ..
270        '\d\+ LOAD $1\_s*' ..
271        '\d\+ STOREDICT\_s*' ..
272        '\d\+ RETURN 0',
273        res)
274enddef
275
276def s:ScriptFuncStoreIndex()
277  var d = {dd: {}}
278  d.dd[0] = 0
279enddef
280
281def Test_disassemble_store_index()
282  var res = execute('disass s:ScriptFuncStoreIndex')
283  assert_match('<SNR>\d*_ScriptFuncStoreIndex\_s*' ..
284        'var d = {dd: {}}\_s*' ..
285        '\d PUSHS "dd"\_s*' ..
286        '\d NEWDICT size 0\_s*' ..
287        '\d NEWDICT size 1\_s*' ..
288        '\d STORE $0\_s*' ..
289        'd.dd\[0\] = 0\_s*' ..
290        '\d PUSHNR 0\_s*' ..
291        '\d PUSHNR 0\_s*' ..
292        '\d LOAD $0\_s*' ..
293        '\d MEMBER dd\_s*' ..
294        '\d STOREINDEX\_s*' ..
295        '\d\+ RETURN 0',
296        res)
297enddef
298
299def s:ListAssign()
300  var x: string
301  var y: string
302  var l: list<any>
303  [x, y; l] = g:stringlist
304enddef
305
306def Test_disassemble_list_assign()
307  var res = execute('disass s:ListAssign')
308  assert_match('<SNR>\d*_ListAssign\_s*' ..
309        'var x: string\_s*' ..
310        '\d PUSHS "\[NULL\]"\_s*' ..
311        '\d STORE $0\_s*' ..
312        'var y: string\_s*' ..
313        '\d PUSHS "\[NULL\]"\_s*' ..
314        '\d STORE $1\_s*' ..
315        'var l: list<any>\_s*' ..
316        '\d NEWLIST size 0\_s*' ..
317        '\d STORE $2\_s*' ..
318        '\[x, y; l\] = g:stringlist\_s*' ..
319        '\d LOADG g:stringlist\_s*' ..
320        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
321        '\d CHECKLEN >= 2\_s*' ..
322        '\d\+ ITEM 0\_s*' ..
323        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
324        '\d\+ STORE $0\_s*' ..
325        '\d\+ ITEM 1\_s*' ..
326        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
327        '\d\+ STORE $1\_s*' ..
328        '\d\+ SLICE 2\_s*' ..
329        '\d\+ STORE $2\_s*' ..
330        '\d\+ RETURN 0',
331        res)
332enddef
333
334def s:ListAdd()
335  var l: list<number> = []
336  add(l, 123)
337  add(l, g:aNumber)
338enddef
339
340def Test_disassemble_list_add()
341  var res = execute('disass s:ListAdd')
342  assert_match('<SNR>\d*_ListAdd\_s*' ..
343        'var l: list<number> = []\_s*' ..
344        '\d NEWLIST size 0\_s*' ..
345        '\d SETTYPE list<number>\_s*' ..
346        '\d STORE $0\_s*' ..
347        'add(l, 123)\_s*' ..
348        '\d LOAD $0\_s*' ..
349        '\d PUSHNR 123\_s*' ..
350        '\d LISTAPPEND\_s*' ..
351        '\d DROP\_s*' ..
352        'add(l, g:aNumber)\_s*' ..
353        '\d LOAD $0\_s*' ..
354        '\d\+ LOADG g:aNumber\_s*' ..
355        '\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
356        '\d\+ LISTAPPEND\_s*' ..
357        '\d\+ DROP\_s*' ..
358        '\d\+ RETURN 0',
359        res)
360enddef
361
362def s:BlobAdd()
363  var b: blob = 0z
364  add(b, 123)
365  add(b, g:aNumber)
366enddef
367
368def Test_disassemble_blob_add()
369  var res = execute('disass s:BlobAdd')
370  assert_match('<SNR>\d*_BlobAdd\_s*' ..
371        'var b: blob = 0z\_s*' ..
372        '\d PUSHBLOB 0z\_s*' ..
373        '\d STORE $0\_s*' ..
374        'add(b, 123)\_s*' ..
375        '\d LOAD $0\_s*' ..
376        '\d PUSHNR 123\_s*' ..
377        '\d BLOBAPPEND\_s*' ..
378        '\d DROP\_s*' ..
379        'add(b, g:aNumber)\_s*' ..
380        '\d LOAD $0\_s*' ..
381        '\d\+ LOADG g:aNumber\_s*' ..
382        '\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
383        '\d\+ BLOBAPPEND\_s*' ..
384        '\d\+ DROP\_s*' ..
385        '\d\+ RETURN 0',
386        res)
387enddef
388
389def s:ScriptFuncUnlet()
390  g:somevar = "value"
391  unlet g:somevar
392  unlet! g:somevar
393  unlet $SOMEVAR
394enddef
395
396def Test_disassemble_unlet()
397  var res = execute('disass s:ScriptFuncUnlet')
398  assert_match('<SNR>\d*_ScriptFuncUnlet\_s*' ..
399        'g:somevar = "value"\_s*' ..
400        '\d PUSHS "value"\_s*' ..
401        '\d STOREG g:somevar\_s*' ..
402        'unlet g:somevar\_s*' ..
403        '\d UNLET g:somevar\_s*' ..
404        'unlet! g:somevar\_s*' ..
405        '\d UNLET! g:somevar\_s*' ..
406        'unlet $SOMEVAR\_s*' ..
407        '\d UNLETENV $SOMEVAR\_s*',
408        res)
409enddef
410
411def s:ScriptFuncTry()
412  try
413    echo "yes"
414  catch /fail/
415    echo "no"
416  finally
417    throw "end"
418  endtry
419enddef
420
421def Test_disassemble_try()
422  var res = execute('disass s:ScriptFuncTry')
423  assert_match('<SNR>\d*_ScriptFuncTry\_s*' ..
424        'try\_s*' ..
425        '\d TRY catch -> \d\+, finally -> \d\+, endtry -> \d\+\_s*' ..
426        'echo "yes"\_s*' ..
427        '\d PUSHS "yes"\_s*' ..
428        '\d ECHO 1\_s*' ..
429        'catch /fail/\_s*' ..
430        '\d JUMP -> \d\+\_s*' ..
431        '\d PUSH v:exception\_s*' ..
432        '\d PUSHS "fail"\_s*' ..
433        '\d COMPARESTRING =\~\_s*' ..
434        '\d JUMP_IF_FALSE -> \d\+\_s*' ..
435        '\d CATCH\_s*' ..
436        'echo "no"\_s*' ..
437        '\d\+ PUSHS "no"\_s*' ..
438        '\d\+ ECHO 1\_s*' ..
439        'finally\_s*' ..
440        '\d\+ FINALLY\_s*' ..
441        'throw "end"\_s*' ..
442        '\d\+ PUSHS "end"\_s*' ..
443        '\d\+ THROW\_s*' ..
444        'endtry\_s*' ..
445        '\d\+ ENDTRY',
446        res)
447enddef
448
449def s:ScriptFuncNew()
450  var ll = [1, "two", 333]
451  var dd = {one: 1, two: "val"}
452enddef
453
454def Test_disassemble_new()
455  var res = execute('disass s:ScriptFuncNew')
456  assert_match('<SNR>\d*_ScriptFuncNew\_s*' ..
457        'var ll = \[1, "two", 333\]\_s*' ..
458        '\d PUSHNR 1\_s*' ..
459        '\d PUSHS "two"\_s*' ..
460        '\d PUSHNR 333\_s*' ..
461        '\d NEWLIST size 3\_s*' ..
462        '\d STORE $0\_s*' ..
463        'var dd = {one: 1, two: "val"}\_s*' ..
464        '\d PUSHS "one"\_s*' ..
465        '\d PUSHNR 1\_s*' ..
466        '\d PUSHS "two"\_s*' ..
467        '\d PUSHS "val"\_s*' ..
468        '\d NEWDICT size 2\_s*',
469        res)
470enddef
471
472def FuncWithArg(arg: any)
473  echo arg
474enddef
475
476func UserFunc()
477  echo 'nothing'
478endfunc
479
480func UserFuncWithArg(arg)
481  echo a:arg
482endfunc
483
484def s:ScriptFuncCall(): string
485  changenr()
486  char2nr("abc")
487  Test_disassemble_new()
488  FuncWithArg(343)
489  ScriptFuncNew()
490  s:ScriptFuncNew()
491  UserFunc()
492  UserFuncWithArg("foo")
493  var FuncRef = function("UserFunc")
494  FuncRef()
495  var FuncRefWithArg = function("UserFuncWithArg")
496  FuncRefWithArg("bar")
497  return "yes"
498enddef
499
500def Test_disassemble_call()
501  var res = execute('disass s:ScriptFuncCall')
502  assert_match('<SNR>\d\+_ScriptFuncCall\_s*' ..
503        'changenr()\_s*' ..
504        '\d BCALL changenr(argc 0)\_s*' ..
505        '\d DROP\_s*' ..
506        'char2nr("abc")\_s*' ..
507        '\d PUSHS "abc"\_s*' ..
508        '\d BCALL char2nr(argc 1)\_s*' ..
509        '\d DROP\_s*' ..
510        'Test_disassemble_new()\_s*' ..
511        '\d DCALL Test_disassemble_new(argc 0)\_s*' ..
512        '\d DROP\_s*' ..
513        'FuncWithArg(343)\_s*' ..
514        '\d\+ PUSHNR 343\_s*' ..
515        '\d\+ DCALL FuncWithArg(argc 1)\_s*' ..
516        '\d\+ DROP\_s*' ..
517        'ScriptFuncNew()\_s*' ..
518        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
519        '\d\+ DROP\_s*' ..
520        's:ScriptFuncNew()\_s*' ..
521        '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' ..
522        '\d\+ DROP\_s*' ..
523        'UserFunc()\_s*' ..
524        '\d\+ UCALL UserFunc(argc 0)\_s*' ..
525        '\d\+ DROP\_s*' ..
526        'UserFuncWithArg("foo")\_s*' ..
527        '\d\+ PUSHS "foo"\_s*' ..
528        '\d\+ UCALL UserFuncWithArg(argc 1)\_s*' ..
529        '\d\+ DROP\_s*' ..
530        'var FuncRef = function("UserFunc")\_s*' ..
531        '\d\+ PUSHS "UserFunc"\_s*' ..
532        '\d\+ BCALL function(argc 1)\_s*' ..
533        '\d\+ STORE $0\_s*' ..
534        'FuncRef()\_s*' ..
535        '\d\+ LOAD $\d\_s*' ..
536        '\d\+ PCALL (argc 0)\_s*' ..
537        '\d\+ DROP\_s*' ..
538        'var FuncRefWithArg = function("UserFuncWithArg")\_s*' ..
539        '\d\+ PUSHS "UserFuncWithArg"\_s*' ..
540        '\d\+ BCALL function(argc 1)\_s*' ..
541        '\d\+ STORE $1\_s*' ..
542        'FuncRefWithArg("bar")\_s*' ..
543        '\d\+ PUSHS "bar"\_s*' ..
544        '\d\+ LOAD $\d\_s*' ..
545        '\d\+ PCALL (argc 1)\_s*' ..
546        '\d\+ DROP\_s*' ..
547        'return "yes"\_s*' ..
548        '\d\+ PUSHS "yes"\_s*' ..
549        '\d\+ RETURN',
550        res)
551enddef
552
553
554def s:CreateRefs()
555  var local = 'a'
556  def Append(arg: string)
557    local ..= arg
558  enddef
559  g:Append = Append
560  def Get(): string
561    return local
562  enddef
563  g:Get = Get
564enddef
565
566def Test_disassemble_closure()
567  CreateRefs()
568  var res = execute('disass g:Append')
569  assert_match('<lambda>\d\_s*' ..
570        'local ..= arg\_s*' ..
571        '\d LOADOUTER level 1 $0\_s*' ..
572        '\d LOAD arg\[-1\]\_s*' ..
573        '\d CONCAT\_s*' ..
574        '\d STOREOUTER level 1 $0\_s*' ..
575        '\d RETURN 0',
576        res)
577
578  res = execute('disass g:Get')
579  assert_match('<lambda>\d\_s*' ..
580        'return local\_s*' ..
581        '\d LOADOUTER level 1 $0\_s*' ..
582        '\d RETURN',
583        res)
584
585  unlet g:Append
586  unlet g:Get
587enddef
588
589
590def EchoArg(arg: string): string
591  return arg
592enddef
593def RefThis(): func
594  return function('EchoArg')
595enddef
596def s:ScriptPCall()
597  RefThis()("text")
598enddef
599
600def Test_disassemble_pcall()
601  var res = execute('disass s:ScriptPCall')
602  assert_match('<SNR>\d\+_ScriptPCall\_s*' ..
603        'RefThis()("text")\_s*' ..
604        '\d DCALL RefThis(argc 0)\_s*' ..
605        '\d PUSHS "text"\_s*' ..
606        '\d PCALL top (argc 1)\_s*' ..
607        '\d PCALL end\_s*' ..
608        '\d DROP\_s*' ..
609        '\d RETURN 0',
610        res)
611enddef
612
613
614def s:FuncWithForwardCall(): string
615  return g:DefinedLater("yes")
616enddef
617
618def DefinedLater(arg: string): string
619  return arg
620enddef
621
622def Test_disassemble_update_instr()
623  var res = execute('disass s:FuncWithForwardCall')
624  assert_match('FuncWithForwardCall\_s*' ..
625        'return g:DefinedLater("yes")\_s*' ..
626        '\d PUSHS "yes"\_s*' ..
627        '\d DCALL DefinedLater(argc 1)\_s*' ..
628        '\d RETURN',
629        res)
630
631  # Calling the function will change UCALL into the faster DCALL
632  assert_equal('yes', FuncWithForwardCall())
633
634  res = execute('disass s:FuncWithForwardCall')
635  assert_match('FuncWithForwardCall\_s*' ..
636        'return g:DefinedLater("yes")\_s*' ..
637        '\d PUSHS "yes"\_s*' ..
638        '\d DCALL DefinedLater(argc 1)\_s*' ..
639        '\d RETURN',
640        res)
641enddef
642
643
644def FuncWithDefault(arg: string = 'default'): string
645  return arg
646enddef
647
648def Test_disassemble_call_default()
649  var res = execute('disass FuncWithDefault')
650  assert_match('FuncWithDefault\_s*' ..
651        '\d PUSHS "default"\_s*' ..
652        '\d STORE arg\[-1]\_s*' ..
653        'return arg\_s*' ..
654        '\d LOAD arg\[-1]\_s*' ..
655        '\d RETURN',
656        res)
657enddef
658
659
660def HasEval()
661  if has("eval")
662    echo "yes"
663  else
664    echo "no"
665  endif
666enddef
667
668def HasNothing()
669  if has("nothing")
670    echo "yes"
671  else
672    echo "no"
673  endif
674enddef
675
676def HasSomething()
677  if has("nothing")
678    echo "nothing"
679  elseif has("something")
680    echo "something"
681  elseif has("eval")
682    echo "eval"
683  elseif has("less")
684    echo "less"
685  endif
686enddef
687
688def HasGuiRunning()
689  if has("gui_running")
690    echo "yes"
691  else
692    echo "no"
693  endif
694enddef
695
696def Test_disassemble_const_expr()
697  assert_equal("\nyes", execute('HasEval()'))
698  var instr = execute('disassemble HasEval')
699  assert_match('HasEval\_s*' ..
700        'if has("eval")\_s*' ..
701        'echo "yes"\_s*' ..
702        '\d PUSHS "yes"\_s*' ..
703        '\d ECHO 1\_s*' ..
704        'else\_s*' ..
705        'echo "no"\_s*' ..
706        'endif\_s*',
707        instr)
708  assert_notmatch('JUMP', instr)
709
710  assert_equal("\nno", execute('HasNothing()'))
711  instr = execute('disassemble HasNothing')
712  assert_match('HasNothing\_s*' ..
713        'if has("nothing")\_s*' ..
714        'echo "yes"\_s*' ..
715        'else\_s*' ..
716        'echo "no"\_s*' ..
717        '\d PUSHS "no"\_s*' ..
718        '\d ECHO 1\_s*' ..
719        'endif',
720        instr)
721  assert_notmatch('PUSHS "yes"', instr)
722  assert_notmatch('JUMP', instr)
723
724  assert_equal("\neval", execute('HasSomething()'))
725  instr = execute('disassemble HasSomething')
726  assert_match('HasSomething.*' ..
727        'if has("nothing")\_s*' ..
728        'echo "nothing"\_s*' ..
729        'elseif has("something")\_s*' ..
730        'echo "something"\_s*' ..
731        'elseif has("eval")\_s*' ..
732        'echo "eval"\_s*' ..
733        '\d PUSHS "eval"\_s*' ..
734        '\d ECHO 1\_s*' ..
735        'elseif has("less").*' ..
736        'echo "less"\_s*' ..
737        'endif',
738        instr)
739  assert_notmatch('PUSHS "nothing"', instr)
740  assert_notmatch('PUSHS "something"', instr)
741  assert_notmatch('PUSHS "less"', instr)
742  assert_notmatch('JUMP', instr)
743
744  var result: string
745  var instr_expected: string
746  if has('gui')
747    if has('gui_running')
748      # GUI already running, always returns "yes"
749      result = "\nyes"
750      instr_expected = 'HasGuiRunning.*' ..
751          'if has("gui_running")\_s*' ..
752          '  echo "yes"\_s*' ..
753          '\d PUSHS "yes"\_s*' ..
754          '\d ECHO 1\_s*' ..
755          'else\_s*' ..
756          '  echo "no"\_s*' ..
757          'endif'
758    else
759      result = "\nno"
760      if has('unix')
761        # GUI not running but can start later, call has()
762        instr_expected = 'HasGuiRunning.*' ..
763            'if has("gui_running")\_s*' ..
764            '\d PUSHS "gui_running"\_s*' ..
765            '\d BCALL has(argc 1)\_s*' ..
766            '\d 2BOOL (!!val)\_s*' ..
767            '\d JUMP_IF_FALSE -> \d\_s*' ..
768            '  echo "yes"\_s*' ..
769            '\d PUSHS "yes"\_s*' ..
770            '\d ECHO 1\_s*' ..
771            'else\_s*' ..
772            '\d JUMP -> \d\_s*' ..
773            '  echo "no"\_s*' ..
774            '\d PUSHS "no"\_s*' ..
775            '\d ECHO 1\_s*' ..
776            'endif'
777      else
778        # GUI not running, always return "no"
779        instr_expected = 'HasGuiRunning.*' ..
780            'if has("gui_running")\_s*' ..
781            '  echo "yes"\_s*' ..
782            'else\_s*' ..
783            '  echo "no"\_s*' ..
784            '\d PUSHS "no"\_s*' ..
785            '\d ECHO 1\_s*' ..
786            'endif'
787      endif
788    endif
789  else
790    # GUI not supported, always return "no"
791    result = "\nno"
792    instr_expected = 'HasGuiRunning.*' ..
793        'if has("gui_running")\_s*' ..
794        '  echo "yes"\_s*' ..
795        'else\_s*' ..
796        '  echo "no"\_s*' ..
797        '\d PUSHS "no"\_s*' ..
798        '\d ECHO 1\_s*' ..
799        'endif'
800  endif
801
802  assert_equal(result, execute('HasGuiRunning()'))
803  instr = execute('disassemble HasGuiRunning')
804  assert_match(instr_expected, instr)
805enddef
806
807def ReturnInIf(): string
808  if 1 < 0
809    return "maybe"
810  endif
811  if g:cond
812    return "yes"
813  else
814    return "no"
815  endif
816enddef
817
818def Test_disassemble_return_in_if()
819  var instr = execute('disassemble ReturnInIf')
820  assert_match('ReturnInIf\_s*' ..
821        'if 1 < 0\_s*' ..
822        '  return "maybe"\_s*' ..
823        'endif\_s*' ..
824        'if g:cond\_s*' ..
825        '0 LOADG g:cond\_s*' ..
826        '1 COND2BOOL\_s*' ..
827        '2 JUMP_IF_FALSE -> 5\_s*' ..
828        'return "yes"\_s*' ..
829        '3 PUSHS "yes"\_s*' ..
830        '4 RETURN\_s*' ..
831        'else\_s*' ..
832        ' return "no"\_s*' ..
833        '5 PUSHS "no"\_s*' ..
834        '6 RETURN$',
835        instr)
836enddef
837
838def WithFunc()
839  var Funky1: func
840  var Funky2: func = function("len")
841  var Party2: func = funcref("UserFunc")
842enddef
843
844def Test_disassemble_function()
845  var instr = execute('disassemble WithFunc')
846  assert_match('WithFunc\_s*' ..
847        'var Funky1: func\_s*' ..
848        '0 PUSHFUNC "\[none]"\_s*' ..
849        '1 STORE $0\_s*' ..
850        'var Funky2: func = function("len")\_s*' ..
851        '2 PUSHS "len"\_s*' ..
852        '3 BCALL function(argc 1)\_s*' ..
853        '4 STORE $1\_s*' ..
854        'var Party2: func = funcref("UserFunc")\_s*' ..
855        '\d PUSHS "UserFunc"\_s*' ..
856        '\d BCALL funcref(argc 1)\_s*' ..
857        '\d STORE $2\_s*' ..
858        '\d RETURN 0',
859        instr)
860enddef
861
862if has('channel')
863  def WithChannel()
864    var job1: job
865    var job2: job = job_start("donothing")
866    var chan1: channel
867  enddef
868endif
869
870def Test_disassemble_channel()
871  CheckFeature channel
872
873  var instr = execute('disassemble WithChannel')
874  assert_match('WithChannel\_s*' ..
875        'var job1: job\_s*' ..
876        '\d PUSHJOB "no process"\_s*' ..
877        '\d STORE $0\_s*' ..
878        'var job2: job = job_start("donothing")\_s*' ..
879        '\d PUSHS "donothing"\_s*' ..
880        '\d BCALL job_start(argc 1)\_s*' ..
881        '\d STORE $1\_s*' ..
882        'var chan1: channel\_s*' ..
883        '\d PUSHCHANNEL 0\_s*' ..
884        '\d STORE $2\_s*' ..
885        '\d RETURN 0',
886        instr)
887enddef
888
889def WithLambda(): string
890  var F = (a) => "X" .. a .. "X"
891  return F("x")
892enddef
893
894def Test_disassemble_lambda()
895  assert_equal("XxX", WithLambda())
896  var instr = execute('disassemble WithLambda')
897  assert_match('WithLambda\_s*' ..
898        'var F = (a) => "X" .. a .. "X"\_s*' ..
899        '\d FUNCREF <lambda>\d\+\_s*' ..
900        '\d STORE $0\_s*' ..
901        'return F("x")\_s*' ..
902        '\d PUSHS "x"\_s*' ..
903        '\d LOAD $0\_s*' ..
904        '\d PCALL (argc 1)\_s*' ..
905        '\d RETURN',
906        instr)
907
908   var name = substitute(instr, '.*\(<lambda>\d\+\).*', '\1', '')
909   instr = execute('disassemble ' .. name)
910   assert_match('<lambda>\d\+\_s*' ..
911        'return "X" .. a .. "X"\_s*' ..
912        '\d PUSHS "X"\_s*' ..
913        '\d LOAD arg\[-1\]\_s*' ..
914        '\d 2STRING_ANY stack\[-1\]\_s*' ..
915        '\d CONCAT\_s*' ..
916        '\d PUSHS "X"\_s*' ..
917        '\d CONCAT\_s*' ..
918        '\d RETURN',
919        instr)
920enddef
921
922def LambdaWithType(): number
923  var Ref = (a: number) => a + 10
924  return Ref(g:value)
925enddef
926
927def Test_disassemble_lambda_with_type()
928  g:value = 5
929  assert_equal(15, LambdaWithType())
930  var instr = execute('disassemble LambdaWithType')
931  assert_match('LambdaWithType\_s*' ..
932        'var Ref = (a: number) => a + 10\_s*' ..
933        '\d FUNCREF <lambda>\d\+\_s*' ..
934        '\d STORE $0\_s*' ..
935        'return Ref(g:value)\_s*' ..
936        '\d LOADG g:value\_s*' ..
937        '\d LOAD $0\_s*' ..
938        '\d CHECKTYPE number stack\[-2\] arg 1\_s*' ..
939        '\d PCALL (argc 1)\_s*' ..
940        '\d RETURN',
941        instr)
942enddef
943
944def NestedOuter()
945  def g:Inner()
946    echomsg "inner"
947  enddef
948enddef
949
950def Test_nested_func()
951   var instr = execute('disassemble NestedOuter')
952   assert_match('NestedOuter\_s*' ..
953        'def g:Inner()\_s*' ..
954        'echomsg "inner"\_s*' ..
955        'enddef\_s*' ..
956        '\d NEWFUNC <lambda>\d\+ Inner\_s*' ..
957        '\d RETURN 0',
958        instr)
959enddef
960
961def NestedDefList()
962  def
963  def Info
964  def /Info
965  def /Info/
966enddef
967
968def Test_nested_def_list()
969   var instr = execute('disassemble NestedDefList')
970   assert_match('NestedDefList\_s*' ..
971        'def\_s*' ..
972        '\d DEF \_s*' ..
973        'def Info\_s*' ..
974        '\d DEF Info\_s*' ..
975        'def /Info\_s*' ..
976        '\d DEF /Info\_s*' ..
977        'def /Info/\_s*' ..
978        '\d DEF /Info/\_s*' ..
979        '\d RETURN 0',
980        instr)
981enddef
982
983def AndOr(arg: any): string
984  if arg == 1 && arg != 2 || arg == 4
985    return 'yes'
986  endif
987  return 'no'
988enddef
989
990def Test_disassemble_and_or()
991  assert_equal("yes", AndOr(1))
992  assert_equal("no", AndOr(2))
993  assert_equal("yes", AndOr(4))
994  var instr = execute('disassemble AndOr')
995  assert_match('AndOr\_s*' ..
996        'if arg == 1 && arg != 2 || arg == 4\_s*' ..
997        '\d LOAD arg\[-1]\_s*' ..
998        '\d PUSHNR 1\_s*' ..
999        '\d COMPAREANY ==\_s*' ..
1000        '\d JUMP_IF_COND_FALSE -> \d\+\_s*' ..
1001        '\d LOAD arg\[-1]\_s*' ..
1002        '\d PUSHNR 2\_s*' ..
1003        '\d COMPAREANY !=\_s*' ..
1004        '\d JUMP_IF_COND_TRUE -> \d\+\_s*' ..
1005        '\d LOAD arg\[-1]\_s*' ..
1006        '\d\+ PUSHNR 4\_s*' ..
1007        '\d\+ COMPAREANY ==\_s*' ..
1008        '\d\+ JUMP_IF_FALSE -> \d\+',
1009        instr)
1010enddef
1011
1012def ForLoop(): list<number>
1013  var res: list<number>
1014  for i in range(3)
1015    res->add(i)
1016  endfor
1017  return res
1018enddef
1019
1020def Test_disassemble_for_loop()
1021  assert_equal([0, 1, 2], ForLoop())
1022  var instr = execute('disassemble ForLoop')
1023  assert_match('ForLoop\_s*' ..
1024        'var res: list<number>\_s*' ..
1025        '\d NEWLIST size 0\_s*' ..
1026        '\d SETTYPE list<number>\_s*' ..
1027        '\d STORE $0\_s*' ..
1028        'for i in range(3)\_s*' ..
1029        '\d STORE -1 in $1\_s*' ..
1030        '\d PUSHNR 3\_s*' ..
1031        '\d BCALL range(argc 1)\_s*' ..
1032        '\d FOR $1 -> \d\+\_s*' ..
1033        '\d STORE $2\_s*' ..
1034        'res->add(i)\_s*' ..
1035        '\d LOAD $0\_s*' ..
1036        '\d LOAD $2\_s*' ..
1037        '\d\+ LISTAPPEND\_s*' ..
1038        '\d\+ DROP\_s*' ..
1039        'endfor\_s*' ..
1040        '\d\+ JUMP -> \d\+\_s*' ..
1041        '\d\+ DROP',
1042        instr)
1043enddef
1044
1045def ForLoopEval(): string
1046  var res = ""
1047  for str in eval('["one", "two"]')
1048    res ..= str
1049  endfor
1050  return res
1051enddef
1052
1053def Test_disassemble_for_loop_eval()
1054  assert_equal('onetwo', ForLoopEval())
1055  var instr = execute('disassemble ForLoopEval')
1056  assert_match('ForLoopEval\_s*' ..
1057        'var res = ""\_s*' ..
1058        '\d PUSHS ""\_s*' ..
1059        '\d STORE $0\_s*' ..
1060        'for str in eval(''\["one", "two"\]'')\_s*' ..
1061        '\d STORE -1 in $1\_s*' ..
1062        '\d PUSHS "\["one", "two"\]"\_s*' ..
1063        '\d BCALL eval(argc 1)\_s*' ..
1064        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
1065        '\d FOR $1 -> \d\+\_s*' ..
1066        '\d STORE $2\_s*' ..
1067        'res ..= str\_s*' ..
1068        '\d\+ LOAD $0\_s*' ..
1069        '\d\+ LOAD $2\_s*' ..
1070        '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
1071        '\d\+ CONCAT\_s*' ..
1072        '\d\+ STORE $0\_s*' ..
1073        'endfor\_s*' ..
1074        '\d\+ JUMP -> 6\_s*' ..
1075        '\d\+ DROP\_s*' ..
1076        'return res\_s*' ..
1077        '\d\+ LOAD $0\_s*' ..
1078        '\d\+ RETURN',
1079        instr)
1080enddef
1081
1082def ForLoopUnpack()
1083  for [x1, x2] in [[1, 2], [3, 4]]
1084    echo x1 x2
1085  endfor
1086enddef
1087
1088def Test_disassemble_for_loop_unpack()
1089  var instr = execute('disassemble ForLoopUnpack')
1090  assert_match('ForLoopUnpack\_s*' ..
1091        'for \[x1, x2\] in \[\[1, 2\], \[3, 4\]\]\_s*' ..
1092        '\d\+ STORE -1 in $0\_s*' ..
1093        '\d\+ PUSHNR 1\_s*' ..
1094        '\d\+ PUSHNR 2\_s*' ..
1095        '\d\+ NEWLIST size 2\_s*' ..
1096        '\d\+ PUSHNR 3\_s*' ..
1097        '\d\+ PUSHNR 4\_s*' ..
1098        '\d\+ NEWLIST size 2\_s*' ..
1099        '\d\+ NEWLIST size 2\_s*' ..
1100        '\d\+ FOR $0 -> 16\_s*' ..
1101        '\d\+ UNPACK 2\_s*' ..
1102        '\d\+ STORE $1\_s*' ..
1103        '\d\+ STORE $2\_s*' ..
1104        'echo x1 x2\_s*' ..
1105        '\d\+ LOAD $1\_s*' ..
1106        '\d\+ LOAD $2\_s*' ..
1107        '\d\+ ECHO 2\_s*' ..
1108        'endfor\_s*' ..
1109        '\d\+ JUMP -> 8\_s*' ..
1110        '\d\+ DROP\_s*' ..
1111        '\d\+ RETURN 0',
1112        instr)
1113enddef
1114
1115def ForLoopContinue()
1116  for nr in [1, 2]
1117    try
1118      echo "ok"
1119      try
1120        echo "deeper"
1121      catch
1122        continue
1123      endtry
1124    catch
1125      echo "not ok"
1126    endtry
1127  endfor
1128enddef
1129
1130def Test_disassemble_for_loop_continue()
1131  var instr = execute('disassemble ForLoopContinue')
1132  assert_match('ForLoopContinue\_s*' ..
1133        'for nr in \[1, 2]\_s*' ..
1134        '0 STORE -1 in $0\_s*' ..
1135        '1 PUSHNR 1\_s*' ..
1136        '2 PUSHNR 2\_s*' ..
1137        '3 NEWLIST size 2\_s*' ..
1138        '4 FOR $0 -> 22\_s*' ..
1139        '5 STORE $1\_s*' ..
1140        'try\_s*' ..
1141        '6 TRY catch -> 17, endtry -> 20\_s*' ..
1142        'echo "ok"\_s*' ..
1143        '7 PUSHS "ok"\_s*' ..
1144        '8 ECHO 1\_s*' ..
1145        'try\_s*' ..
1146        '9 TRY catch -> 13, endtry -> 15\_s*' ..
1147        'echo "deeper"\_s*' ..
1148        '10 PUSHS "deeper"\_s*' ..
1149        '11 ECHO 1\_s*' ..
1150        'catch\_s*' ..
1151        '12 JUMP -> 15\_s*' ..
1152        '13 CATCH\_s*' ..
1153        'continue\_s*' ..
1154        '14 TRY-CONTINUE 2 levels -> 4\_s*' ..
1155        'endtry\_s*' ..
1156        '15 ENDTRY\_s*' ..
1157        'catch\_s*' ..
1158        '16 JUMP -> 20\_s*' ..
1159        '17 CATCH\_s*' ..
1160        'echo "not ok"\_s*' ..
1161        '18 PUSHS "not ok"\_s*' ..
1162        '19 ECHO 1\_s*' ..
1163        'endtry\_s*' ..
1164        '20 ENDTRY\_s*' ..
1165        'endfor\_s*' ..
1166        '21 JUMP -> 4\_s*' ..
1167        '\d\+ DROP\_s*' ..
1168        '\d\+ RETURN 0',
1169        instr)
1170enddef
1171
1172let g:number = 42
1173
1174def TypeCast()
1175  var l: list<number> = [23, <number>g:number]
1176enddef
1177
1178def Test_disassemble_typecast()
1179  var instr = execute('disassemble TypeCast')
1180  assert_match('TypeCast.*' ..
1181        'var l: list<number> = \[23, <number>g:number\].*' ..
1182        '\d PUSHNR 23\_s*' ..
1183        '\d LOADG g:number\_s*' ..
1184        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1185        '\d NEWLIST size 2\_s*' ..
1186        '\d SETTYPE list<number>\_s*' ..
1187        '\d STORE $0\_s*' ..
1188        '\d RETURN 0\_s*',
1189        instr)
1190enddef
1191
1192def Computing()
1193  var nr = 3
1194  var nrres = nr + 7
1195  nrres = nr - 7
1196  nrres = nr * 7
1197  nrres = nr / 7
1198  nrres = nr % 7
1199
1200  var anyres = g:number + 7
1201  anyres = g:number - 7
1202  anyres = g:number * 7
1203  anyres = g:number / 7
1204  anyres = g:number % 7
1205
1206  if has('float')
1207    var fl = 3.0
1208    var flres = fl + 7.0
1209    flres = fl - 7.0
1210    flres = fl * 7.0
1211    flres = fl / 7.0
1212  endif
1213enddef
1214
1215def Test_disassemble_computing()
1216  var instr = execute('disassemble Computing')
1217  assert_match('Computing.*' ..
1218        'var nr = 3.*' ..
1219        '\d STORE 3 in $0.*' ..
1220        'var nrres = nr + 7.*' ..
1221        '\d LOAD $0.*' ..
1222        '\d PUSHNR 7.*' ..
1223        '\d OPNR +.*' ..
1224        '\d STORE $1.*' ..
1225        'nrres = nr - 7.*' ..
1226        '\d OPNR -.*' ..
1227        'nrres = nr \* 7.*' ..
1228        '\d OPNR \*.*' ..
1229        'nrres = nr / 7.*' ..
1230        '\d OPNR /.*' ..
1231        'nrres = nr % 7.*' ..
1232        '\d OPNR %.*' ..
1233        'var anyres = g:number + 7.*' ..
1234        '\d LOADG g:number.*' ..
1235        '\d PUSHNR 7.*' ..
1236        '\d OPANY +.*' ..
1237        '\d STORE $2.*' ..
1238        'anyres = g:number - 7.*' ..
1239        '\d OPANY -.*' ..
1240        'anyres = g:number \* 7.*' ..
1241        '\d OPANY \*.*' ..
1242        'anyres = g:number / 7.*' ..
1243        '\d OPANY /.*' ..
1244        'anyres = g:number % 7.*' ..
1245        '\d OPANY %.*',
1246        instr)
1247  if has('float')
1248    assert_match('Computing.*' ..
1249        'var fl = 3.0.*' ..
1250        '\d PUSHF 3.0.*' ..
1251        '\d STORE $3.*' ..
1252        'var flres = fl + 7.0.*' ..
1253        '\d LOAD $3.*' ..
1254        '\d PUSHF 7.0.*' ..
1255        '\d OPFLOAT +.*' ..
1256        '\d STORE $4.*' ..
1257        'flres = fl - 7.0.*' ..
1258        '\d OPFLOAT -.*' ..
1259        'flres = fl \* 7.0.*' ..
1260        '\d OPFLOAT \*.*' ..
1261        'flres = fl / 7.0.*' ..
1262        '\d OPFLOAT /.*',
1263        instr)
1264  endif
1265enddef
1266
1267def AddListBlob()
1268  var reslist = [1, 2] + [3, 4]
1269  var resblob = 0z1122 + 0z3344
1270enddef
1271
1272def Test_disassemble_add_list_blob()
1273  var instr = execute('disassemble AddListBlob')
1274  assert_match('AddListBlob.*' ..
1275        'var reslist = \[1, 2] + \[3, 4].*' ..
1276        '\d PUSHNR 1.*' ..
1277        '\d PUSHNR 2.*' ..
1278        '\d NEWLIST size 2.*' ..
1279        '\d PUSHNR 3.*' ..
1280        '\d PUSHNR 4.*' ..
1281        '\d NEWLIST size 2.*' ..
1282        '\d ADDLIST.*' ..
1283        '\d STORE $.*.*' ..
1284        'var resblob = 0z1122 + 0z3344.*' ..
1285        '\d PUSHBLOB 0z1122.*' ..
1286        '\d PUSHBLOB 0z3344.*' ..
1287        '\d ADDBLOB.*' ..
1288        '\d STORE $.*',
1289        instr)
1290enddef
1291
1292let g:aa = 'aa'
1293def ConcatString(): string
1294  var res = g:aa .. "bb"
1295  return res
1296enddef
1297
1298def Test_disassemble_concat()
1299  var instr = execute('disassemble ConcatString')
1300  assert_match('ConcatString.*' ..
1301        'var res = g:aa .. "bb".*' ..
1302        '\d LOADG g:aa.*' ..
1303        '\d PUSHS "bb".*' ..
1304        '\d 2STRING_ANY stack\[-2].*' ..
1305        '\d CONCAT.*' ..
1306        '\d STORE $.*',
1307        instr)
1308  assert_equal('aabb', ConcatString())
1309enddef
1310
1311def StringIndex(): string
1312  var s = "abcd"
1313  var res = s[1]
1314  return res
1315enddef
1316
1317def Test_disassemble_string_index()
1318  var instr = execute('disassemble StringIndex')
1319  assert_match('StringIndex\_s*' ..
1320        'var s = "abcd"\_s*' ..
1321        '\d PUSHS "abcd"\_s*' ..
1322        '\d STORE $0\_s*' ..
1323        'var res = s\[1]\_s*' ..
1324        '\d LOAD $0\_s*' ..
1325        '\d PUSHNR 1\_s*' ..
1326        '\d STRINDEX\_s*' ..
1327        '\d STORE $1\_s*',
1328        instr)
1329  assert_equal('b', StringIndex())
1330enddef
1331
1332def StringSlice(): string
1333  var s = "abcd"
1334  var res = s[1 : 8]
1335  return res
1336enddef
1337
1338def Test_disassemble_string_slice()
1339  var instr = execute('disassemble StringSlice')
1340  assert_match('StringSlice\_s*' ..
1341        'var s = "abcd"\_s*' ..
1342        '\d PUSHS "abcd"\_s*' ..
1343        '\d STORE $0\_s*' ..
1344        'var res = s\[1 : 8]\_s*' ..
1345        '\d LOAD $0\_s*' ..
1346        '\d PUSHNR 1\_s*' ..
1347        '\d PUSHNR 8\_s*' ..
1348        '\d STRSLICE\_s*' ..
1349        '\d STORE $1\_s*',
1350        instr)
1351  assert_equal('bcd', StringSlice())
1352enddef
1353
1354def ListIndex(): number
1355  var l = [1, 2, 3]
1356  var res = l[1]
1357  return res
1358enddef
1359
1360def Test_disassemble_list_index()
1361  var instr = execute('disassemble ListIndex')
1362  assert_match('ListIndex\_s*' ..
1363        'var l = \[1, 2, 3]\_s*' ..
1364        '\d PUSHNR 1\_s*' ..
1365        '\d PUSHNR 2\_s*' ..
1366        '\d PUSHNR 3\_s*' ..
1367        '\d NEWLIST size 3\_s*' ..
1368        '\d STORE $0\_s*' ..
1369        'var res = l\[1]\_s*' ..
1370        '\d LOAD $0\_s*' ..
1371        '\d PUSHNR 1\_s*' ..
1372        '\d LISTINDEX\_s*' ..
1373        '\d STORE $1\_s*',
1374        instr)
1375  assert_equal(2, ListIndex())
1376enddef
1377
1378def ListSlice(): list<number>
1379  var l = [1, 2, 3]
1380  var res = l[1 : 8]
1381  return res
1382enddef
1383
1384def Test_disassemble_list_slice()
1385  var instr = execute('disassemble ListSlice')
1386  assert_match('ListSlice\_s*' ..
1387        'var l = \[1, 2, 3]\_s*' ..
1388        '\d PUSHNR 1\_s*' ..
1389        '\d PUSHNR 2\_s*' ..
1390        '\d PUSHNR 3\_s*' ..
1391        '\d NEWLIST size 3\_s*' ..
1392        '\d STORE $0\_s*' ..
1393        'var res = l\[1 : 8]\_s*' ..
1394        '\d LOAD $0\_s*' ..
1395        '\d PUSHNR 1\_s*' ..
1396        '\d PUSHNR 8\_s*' ..
1397        '\d LISTSLICE\_s*' ..
1398        '\d STORE $1\_s*',
1399        instr)
1400  assert_equal([2, 3], ListSlice())
1401enddef
1402
1403def DictMember(): number
1404  var d = {item: 1}
1405  var res = d.item
1406  res = d["item"]
1407  return res
1408enddef
1409
1410def Test_disassemble_dict_member()
1411  var instr = execute('disassemble DictMember')
1412  assert_match('DictMember\_s*' ..
1413        'var d = {item: 1}\_s*' ..
1414        '\d PUSHS "item"\_s*' ..
1415        '\d PUSHNR 1\_s*' ..
1416        '\d NEWDICT size 1\_s*' ..
1417        '\d STORE $0\_s*' ..
1418        'var res = d.item\_s*' ..
1419        '\d\+ LOAD $0\_s*' ..
1420        '\d\+ MEMBER item\_s*' ..
1421        '\d\+ STORE $1\_s*' ..
1422        'res = d\["item"\]\_s*' ..
1423        '\d\+ LOAD $0\_s*' ..
1424        '\d\+ PUSHS "item"\_s*' ..
1425        '\d\+ MEMBER\_s*' ..
1426        '\d\+ STORE $1\_s*',
1427        instr)
1428  assert_equal(1, DictMember())
1429enddef
1430
1431let somelist = [1, 2, 3, 4, 5]
1432def AnyIndex(): number
1433  var res = g:somelist[2]
1434  return res
1435enddef
1436
1437def Test_disassemble_any_index()
1438  var instr = execute('disassemble AnyIndex')
1439  assert_match('AnyIndex\_s*' ..
1440        'var res = g:somelist\[2\]\_s*' ..
1441        '\d LOADG g:somelist\_s*' ..
1442        '\d PUSHNR 2\_s*' ..
1443        '\d ANYINDEX\_s*' ..
1444        '\d STORE $0\_s*' ..
1445        'return res\_s*' ..
1446        '\d LOAD $0\_s*' ..
1447        '\d CHECKTYPE number stack\[-1\]\_s*' ..
1448        '\d RETURN',
1449        instr)
1450  assert_equal(3, AnyIndex())
1451enddef
1452
1453def AnySlice(): list<number>
1454  var res = g:somelist[1 : 3]
1455  return res
1456enddef
1457
1458def Test_disassemble_any_slice()
1459  var instr = execute('disassemble AnySlice')
1460  assert_match('AnySlice\_s*' ..
1461        'var res = g:somelist\[1 : 3\]\_s*' ..
1462        '\d LOADG g:somelist\_s*' ..
1463        '\d PUSHNR 1\_s*' ..
1464        '\d PUSHNR 3\_s*' ..
1465        '\d ANYSLICE\_s*' ..
1466        '\d STORE $0\_s*' ..
1467        'return res\_s*' ..
1468        '\d LOAD $0\_s*' ..
1469        '\d CHECKTYPE list<number> stack\[-1\]\_s*' ..
1470        '\d RETURN',
1471        instr)
1472  assert_equal([2, 3, 4], AnySlice())
1473enddef
1474
1475def NegateNumber(): number
1476  var nr = 9
1477  var plus = +nr
1478  var res = -nr
1479  return res
1480enddef
1481
1482def Test_disassemble_negate_number()
1483  var instr = execute('disassemble NegateNumber')
1484  assert_match('NegateNumber\_s*' ..
1485        'var nr = 9\_s*' ..
1486        '\d STORE 9 in $0\_s*' ..
1487        'var plus = +nr\_s*' ..
1488        '\d LOAD $0\_s*' ..
1489        '\d CHECKNR\_s*' ..
1490        '\d STORE $1\_s*' ..
1491        'var res = -nr\_s*' ..
1492        '\d LOAD $0\_s*' ..
1493        '\d NEGATENR\_s*' ..
1494        '\d STORE $2\_s*',
1495        instr)
1496  assert_equal(-9, NegateNumber())
1497enddef
1498
1499def InvertBool(): bool
1500  var flag = true
1501  var invert = !flag
1502  var res = !!flag
1503  return res
1504enddef
1505
1506def Test_disassemble_invert_bool()
1507  var instr = execute('disassemble InvertBool')
1508  assert_match('InvertBool\_s*' ..
1509        'var flag = true\_s*' ..
1510        '\d PUSH true\_s*' ..
1511        '\d STORE $0\_s*' ..
1512        'var invert = !flag\_s*' ..
1513        '\d LOAD $0\_s*' ..
1514        '\d INVERT (!val)\_s*' ..
1515        '\d STORE $1\_s*' ..
1516        'var res = !!flag\_s*' ..
1517        '\d LOAD $0\_s*' ..
1518        '\d 2BOOL (!!val)\_s*' ..
1519        '\d STORE $2\_s*',
1520        instr)
1521  assert_equal(true, InvertBool())
1522enddef
1523
1524def ReturnBool(): bool
1525  var name: bool = 1 && 0 || 1
1526  return name
1527enddef
1528
1529def Test_disassemble_return_bool()
1530  var instr = execute('disassemble ReturnBool')
1531  assert_match('ReturnBool\_s*' ..
1532        'var name: bool = 1 && 0 || 1\_s*' ..
1533        '0 PUSHNR 1\_s*' ..
1534        '1 2BOOL (!!val)\_s*' ..
1535        '2 JUMP_IF_COND_FALSE -> 5\_s*' ..
1536        '3 PUSHNR 0\_s*' ..
1537        '4 2BOOL (!!val)\_s*' ..
1538        '5 JUMP_IF_COND_TRUE -> 8\_s*' ..
1539        '6 PUSHNR 1\_s*' ..
1540        '7 2BOOL (!!val)\_s*' ..
1541        '\d STORE $0\_s*' ..
1542        'return name\_s*' ..
1543        '\d\+ LOAD $0\_s*' ..
1544        '\d\+ RETURN',
1545        instr)
1546  assert_equal(true, InvertBool())
1547enddef
1548
1549def Test_disassemble_compare()
1550  var cases = [
1551        ['true == isFalse', 'COMPAREBOOL =='],
1552        ['true != isFalse', 'COMPAREBOOL !='],
1553        ['v:none == isNull', 'COMPARESPECIAL =='],
1554        ['v:none != isNull', 'COMPARESPECIAL !='],
1555
1556        ['111 == aNumber', 'COMPARENR =='],
1557        ['111 != aNumber', 'COMPARENR !='],
1558        ['111 > aNumber', 'COMPARENR >'],
1559        ['111 < aNumber', 'COMPARENR <'],
1560        ['111 >= aNumber', 'COMPARENR >='],
1561        ['111 <= aNumber', 'COMPARENR <='],
1562        ['111 =~ aNumber', 'COMPARENR =\~'],
1563        ['111 !~ aNumber', 'COMPARENR !\~'],
1564
1565        ['"xx" != aString', 'COMPARESTRING !='],
1566        ['"xx" > aString', 'COMPARESTRING >'],
1567        ['"xx" < aString', 'COMPARESTRING <'],
1568        ['"xx" >= aString', 'COMPARESTRING >='],
1569        ['"xx" <= aString', 'COMPARESTRING <='],
1570        ['"xx" =~ aString', 'COMPARESTRING =\~'],
1571        ['"xx" !~ aString', 'COMPARESTRING !\~'],
1572        ['"xx" is aString', 'COMPARESTRING is'],
1573        ['"xx" isnot aString', 'COMPARESTRING isnot'],
1574
1575        ['0z11 == aBlob', 'COMPAREBLOB =='],
1576        ['0z11 != aBlob', 'COMPAREBLOB !='],
1577        ['0z11 is aBlob', 'COMPAREBLOB is'],
1578        ['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
1579
1580        ['[1, 2] == aList', 'COMPARELIST =='],
1581        ['[1, 2] != aList', 'COMPARELIST !='],
1582        ['[1, 2] is aList', 'COMPARELIST is'],
1583        ['[1, 2] isnot aList', 'COMPARELIST isnot'],
1584
1585        ['{a: 1} == aDict', 'COMPAREDICT =='],
1586        ['{a: 1} != aDict', 'COMPAREDICT !='],
1587        ['{a: 1} is aDict', 'COMPAREDICT is'],
1588        ['{a: 1} isnot aDict', 'COMPAREDICT isnot'],
1589
1590        ['(() => 33) == (() => 44)', 'COMPAREFUNC =='],
1591        ['(() => 33) != (() => 44)', 'COMPAREFUNC !='],
1592        ['(() => 33) is (() => 44)', 'COMPAREFUNC is'],
1593        ['(() => 33) isnot (() => 44)', 'COMPAREFUNC isnot'],
1594
1595        ['77 == g:xx', 'COMPAREANY =='],
1596        ['77 != g:xx', 'COMPAREANY !='],
1597        ['77 > g:xx', 'COMPAREANY >'],
1598        ['77 < g:xx', 'COMPAREANY <'],
1599        ['77 >= g:xx', 'COMPAREANY >='],
1600        ['77 <= g:xx', 'COMPAREANY <='],
1601        ['77 =~ g:xx', 'COMPAREANY =\~'],
1602        ['77 !~ g:xx', 'COMPAREANY !\~'],
1603        ['77 is g:xx', 'COMPAREANY is'],
1604        ['77 isnot g:xx', 'COMPAREANY isnot'],
1605        ]
1606  var floatDecl = ''
1607  if has('float')
1608    cases->extend([
1609        ['1.1 == aFloat', 'COMPAREFLOAT =='],
1610        ['1.1 != aFloat', 'COMPAREFLOAT !='],
1611        ['1.1 > aFloat', 'COMPAREFLOAT >'],
1612        ['1.1 < aFloat', 'COMPAREFLOAT <'],
1613        ['1.1 >= aFloat', 'COMPAREFLOAT >='],
1614        ['1.1 <= aFloat', 'COMPAREFLOAT <='],
1615        ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
1616        ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
1617        ])
1618    floatDecl = 'var aFloat = 2.2'
1619  endif
1620
1621  var nr = 1
1622  for case in cases
1623    # declare local variables to get a non-constant with the right type
1624    writefile(['def TestCase' .. nr .. '()',
1625             '  var isFalse = false',
1626             '  var isNull = v:null',
1627             '  var aNumber = 222',
1628             '  var aString = "yy"',
1629             '  var aBlob = 0z22',
1630             '  var aList = [3, 4]',
1631             '  var aDict = {x: 2}',
1632             floatDecl,
1633             '  if ' .. case[0],
1634             '    echo 42'
1635             '  endif',
1636             'enddef'], 'Xdisassemble')
1637    source Xdisassemble
1638    var instr = execute('disassemble TestCase' .. nr)
1639    assert_match('TestCase' .. nr .. '.*' ..
1640        'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1641        '\d \(PUSH\|FUNCREF\).*' ..
1642        '\d \(PUSH\|FUNCREF\|LOAD\).*' ..
1643        '\d ' .. case[1] .. '.*' ..
1644        '\d JUMP_IF_FALSE -> \d\+.*',
1645        instr)
1646
1647    nr += 1
1648  endfor
1649
1650  delete('Xdisassemble')
1651enddef
1652
1653def s:FalsyOp()
1654  echo g:flag ?? "yes"
1655  echo [] ?? "empty list"
1656  echo "" ?? "empty string"
1657enddef
1658
1659def Test_dsassemble_falsy_op()
1660  var res = execute('disass s:FalsyOp')
1661  assert_match('\<SNR>\d*_FalsyOp\_s*' ..
1662      'echo g:flag ?? "yes"\_s*' ..
1663      '0 LOADG g:flag\_s*' ..
1664      '1 JUMP_AND_KEEP_IF_TRUE -> 3\_s*' ..
1665      '2 PUSHS "yes"\_s*' ..
1666      '3 ECHO 1\_s*' ..
1667      'echo \[\] ?? "empty list"\_s*' ..
1668      '4 NEWLIST size 0\_s*' ..
1669      '5 JUMP_AND_KEEP_IF_TRUE -> 7\_s*' ..
1670      '6 PUSHS "empty list"\_s*' ..
1671      '7 ECHO 1\_s*' ..
1672      'echo "" ?? "empty string"\_s*' ..
1673      '\d\+ PUSHS "empty string"\_s*' ..
1674      '\d\+ ECHO 1\_s*' ..
1675      '\d\+ RETURN 0',
1676      res)
1677enddef
1678
1679def Test_disassemble_compare_const()
1680  var cases = [
1681        ['"xx" == "yy"', false],
1682        ['"aa" == "aa"', true],
1683        ['has("eval") ? true : false', true],
1684        ['has("asdf") ? true : false', false],
1685        ]
1686
1687  var nr = 1
1688  for case in cases
1689    writefile(['def TestCase' .. nr .. '()',
1690             '  if ' .. case[0],
1691             '    echo 42'
1692             '  endif',
1693             'enddef'], 'Xdisassemble')
1694    source Xdisassemble
1695    var instr = execute('disassemble TestCase' .. nr)
1696    if case[1]
1697      # condition true, "echo 42" executed
1698      assert_match('TestCase' .. nr .. '.*' ..
1699          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
1700          '\d PUSHNR 42.*' ..
1701          '\d ECHO 1.*' ..
1702          '\d RETURN 0',
1703          instr)
1704    else
1705      # condition false, function just returns
1706      assert_match('TestCase' .. nr .. '.*' ..
1707          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' ..
1708          'echo 42[ \n]*' ..
1709          'endif[ \n]*' ..
1710          '\d RETURN 0',
1711          instr)
1712    endif
1713
1714    nr += 1
1715  endfor
1716
1717  delete('Xdisassemble')
1718enddef
1719
1720def s:Execute()
1721  execute 'help vim9.txt'
1722  var cmd = 'help vim9.txt'
1723  execute cmd
1724  var tag = 'vim9.txt'
1725  execute 'help ' .. tag
1726enddef
1727
1728def Test_disassemble_execute()
1729  var res = execute('disass s:Execute')
1730  assert_match('\<SNR>\d*_Execute\_s*' ..
1731        "execute 'help vim9.txt'\\_s*" ..
1732        '\d PUSHS "help vim9.txt"\_s*' ..
1733        '\d EXECUTE 1\_s*' ..
1734        "var cmd = 'help vim9.txt'\\_s*" ..
1735        '\d PUSHS "help vim9.txt"\_s*' ..
1736        '\d STORE $0\_s*' ..
1737        'execute cmd\_s*' ..
1738        '\d LOAD $0\_s*' ..
1739        '\d EXECUTE 1\_s*' ..
1740        "var tag = 'vim9.txt'\\_s*" ..
1741        '\d PUSHS "vim9.txt"\_s*' ..
1742        '\d STORE $1\_s*' ..
1743        "execute 'help ' .. tag\\_s*" ..
1744        '\d\+ PUSHS "help "\_s*' ..
1745        '\d\+ LOAD $1\_s*' ..
1746        '\d\+ CONCAT\_s*' ..
1747        '\d\+ EXECUTE 1\_s*' ..
1748        '\d\+ RETURN 0',
1749        res)
1750enddef
1751
1752def s:Echomsg()
1753  echomsg 'some' 'message'
1754  echoerr 'went' .. 'wrong'
1755enddef
1756
1757def Test_disassemble_echomsg()
1758  var res = execute('disass s:Echomsg')
1759  assert_match('\<SNR>\d*_Echomsg\_s*' ..
1760        "echomsg 'some' 'message'\\_s*" ..
1761        '\d PUSHS "some"\_s*' ..
1762        '\d PUSHS "message"\_s*' ..
1763        '\d ECHOMSG 2\_s*' ..
1764        "echoerr 'went' .. 'wrong'\\_s*" ..
1765        '\d PUSHS "wentwrong"\_s*' ..
1766        '\d ECHOERR 1\_s*' ..
1767        '\d RETURN 0',
1768        res)
1769enddef
1770
1771def SomeStringArg(arg: string)
1772  echo arg
1773enddef
1774
1775def SomeAnyArg(arg: any)
1776  echo arg
1777enddef
1778
1779def SomeStringArgAndReturn(arg: string): string
1780  return arg
1781enddef
1782
1783def Test_display_func()
1784  var res1 = execute('function SomeStringArg')
1785  assert_match('.* def SomeStringArg(arg: string)\_s*' ..
1786        '\d *echo arg.*' ..
1787        ' *enddef',
1788        res1)
1789
1790  var res2 = execute('function SomeAnyArg')
1791  assert_match('.* def SomeAnyArg(arg: any)\_s*' ..
1792        '\d *echo arg\_s*' ..
1793        ' *enddef',
1794        res2)
1795
1796  var res3 = execute('function SomeStringArgAndReturn')
1797  assert_match('.* def SomeStringArgAndReturn(arg: string): string\_s*' ..
1798        '\d *return arg\_s*' ..
1799        ' *enddef',
1800        res3)
1801enddef
1802
1803def Test_vim9script_forward_func()
1804  var lines =<< trim END
1805    vim9script
1806    def FuncOne(): string
1807      return FuncTwo()
1808    enddef
1809    def FuncTwo(): string
1810      return 'two'
1811    enddef
1812    g:res_FuncOne = execute('disass FuncOne')
1813  END
1814  writefile(lines, 'Xdisassemble')
1815  source Xdisassemble
1816
1817  # check that the first function calls the second with DCALL
1818  assert_match('\<SNR>\d*_FuncOne\_s*' ..
1819        'return FuncTwo()\_s*' ..
1820        '\d DCALL <SNR>\d\+_FuncTwo(argc 0)\_s*' ..
1821        '\d RETURN',
1822        g:res_FuncOne)
1823
1824  delete('Xdisassemble')
1825  unlet g:res_FuncOne
1826enddef
1827
1828def s:ConcatStrings(): string
1829  return 'one' .. 'two' .. 'three'
1830enddef
1831
1832def s:ComputeConst(): number
1833  return 2 + 3 * 4 / 6 + 7
1834enddef
1835
1836def s:ComputeConstParen(): number
1837  return ((2 + 4) * (8 / 2)) / (3 + 4)
1838enddef
1839
1840def Test_simplify_const_expr()
1841  var res = execute('disass s:ConcatStrings')
1842  assert_match('<SNR>\d*_ConcatStrings\_s*' ..
1843        "return 'one' .. 'two' .. 'three'\\_s*" ..
1844        '\d PUSHS "onetwothree"\_s*' ..
1845        '\d RETURN',
1846        res)
1847
1848  res = execute('disass s:ComputeConst')
1849  assert_match('<SNR>\d*_ComputeConst\_s*' ..
1850        'return 2 + 3 \* 4 / 6 + 7\_s*' ..
1851        '\d PUSHNR 11\_s*' ..
1852        '\d RETURN',
1853        res)
1854
1855  res = execute('disass s:ComputeConstParen')
1856  assert_match('<SNR>\d*_ComputeConstParen\_s*' ..
1857        'return ((2 + 4) \* (8 / 2)) / (3 + 4)\_s*' ..
1858        '\d PUSHNR 3\>\_s*' ..
1859        '\d RETURN',
1860        res)
1861enddef
1862
1863def s:CallAppend()
1864  eval "some text"->append(2)
1865enddef
1866
1867def Test_shuffle()
1868  var res = execute('disass s:CallAppend')
1869  assert_match('<SNR>\d*_CallAppend\_s*' ..
1870        'eval "some text"->append(2)\_s*' ..
1871        '\d PUSHS "some text"\_s*' ..
1872        '\d PUSHNR 2\_s*' ..
1873        '\d SHUFFLE 2 up 1\_s*' ..
1874        '\d BCALL append(argc 2)\_s*' ..
1875        '\d DROP\_s*' ..
1876        '\d RETURN 0',
1877        res)
1878enddef
1879
1880
1881def s:SilentMessage()
1882  silent echomsg "text"
1883  silent! echoerr "error"
1884enddef
1885
1886def Test_silent()
1887  var res = execute('disass s:SilentMessage')
1888  assert_match('<SNR>\d*_SilentMessage\_s*' ..
1889        'silent echomsg "text"\_s*' ..
1890        '\d CMDMOD silent\_s*' ..
1891        '\d PUSHS "text"\_s*' ..
1892        '\d ECHOMSG 1\_s*' ..
1893        '\d CMDMOD_REV\_s*' ..
1894        'silent! echoerr "error"\_s*' ..
1895        '\d CMDMOD silent!\_s*' ..
1896        '\d PUSHS "error"\_s*' ..
1897        '\d ECHOERR 1\_s*' ..
1898        '\d CMDMOD_REV\_s*' ..
1899        '\d RETURN 0',
1900        res)
1901enddef
1902
1903def s:Profiled(): string
1904  echo "profiled"
1905  return "done"
1906enddef
1907
1908def Test_profiled()
1909  if !has('profile')
1910    MissingFeature 'profile'
1911  endif
1912  var res = execute('disass! s:Profiled')
1913  assert_match('<SNR>\d*_Profiled\_s*' ..
1914        'echo "profiled"\_s*' ..
1915        '\d PROFILE START line 1\_s*' ..
1916        '\d PUSHS "profiled"\_s*' ..
1917        '\d ECHO 1\_s*' ..
1918        'return "done"\_s*' ..
1919        '\d PROFILE END\_s*' ..
1920        '\d PROFILE START line 2\_s*' ..
1921        '\d PUSHS "done"\_s*' ..
1922        '\d RETURN\_s*' ..
1923        '\d PROFILE END',
1924        res)
1925enddef
1926
1927def s:SilentReturn(): string
1928  silent return "done"
1929enddef
1930
1931def Test_silent_return()
1932  var res = execute('disass s:SilentReturn')
1933  assert_match('<SNR>\d*_SilentReturn\_s*' ..
1934        'silent return "done"\_s*' ..
1935        '\d CMDMOD silent\_s*' ..
1936        '\d PUSHS "done"\_s*' ..
1937        '\d CMDMOD_REV\_s*' ..
1938        '\d RETURN',
1939        res)
1940enddef
1941
1942" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
1943