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