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