diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 08:50:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 08:50:31 +0000 |
commit | aed8ce9da277f5ecffe968b324f242c41c3b752a (patch) | |
tree | d2e538394cb7a8a7c42a4aac6ccf1a8e3256999b /src/testdir/test_vim9_disassemble.vim | |
parent | Initial commit. (diff) | |
download | vim-aed8ce9da277f5ecffe968b324f242c41c3b752a.tar.xz vim-aed8ce9da277f5ecffe968b324f242c41c3b752a.zip |
Adding upstream version 2:9.0.1378.upstream/2%9.0.1378upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/testdir/test_vim9_disassemble.vim')
-rw-r--r-- | src/testdir/test_vim9_disassemble.vim | 3049 |
1 files changed, 3049 insertions, 0 deletions
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim new file mode 100644 index 0000000..6f68798 --- /dev/null +++ b/src/testdir/test_vim9_disassemble.vim @@ -0,0 +1,3049 @@ +" Test the :disassemble command, and compilation as a side effect + +source check.vim +import './vim9.vim' as v9 + +func s:NotCompiled() + echo "not" +endfunc + +let s:scriptvar = 4 +let g:globalvar = 'g' +let b:buffervar = 'b' +let w:windowvar = 'w' +let t:tabpagevar = 't' + +def s:ScriptFuncLoad(arg: string) + var local = 1 + buffers + echo + echo arg + echo local + echo &lines + echo v:version + echo s:scriptvar + echo g:globalvar + echo get(g:, "global") + echo g:auto#var + echo b:buffervar + echo get(b:, "buffer") + echo w:windowvar + echo get(w:, "window") + echo t:tabpagevar + echo get(t:, "tab") + echo &tabstop + echo $ENVVAR + echo @z +enddef + +def Test_disassemble_load() + assert_fails('disass NoFunc', 'E1061:') + assert_fails('disass NotCompiled', 'E1091:') + assert_fails('disass', 'E471:') + assert_fails('disass [', 'E475:') + assert_fails('disass 234', 'E129:') + assert_fails('disass <XX>foo', 'E129:') + assert_fails('disass Test_disassemble_load burp', 'E488:') + assert_fails('disass debug debug Test_disassemble_load', 'E488:') + assert_fails('disass profile profile Test_disassemble_load', 'E488:') + + var res = execute('disass s:ScriptFuncLoad') + assert_match('<SNR>\d*_ScriptFuncLoad.*' .. + 'buffers\_s*' .. + '\d\+ EXEC \+buffers\_s*' .. + 'echo\_s*' .. + 'echo arg\_s*' .. + '\d\+ LOAD arg\[-1\]\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo local\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo &lines\_s*' .. + '\d\+ LOADOPT &lines\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo v:version\_s*' .. + '\d\+ LOADV v:version\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo s:scriptvar\_s*' .. + '\d\+ LOADS s:scriptvar from .*test_vim9_disassemble.vim\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo g:globalvar\_s*' .. + '\d\+ LOADG g:globalvar\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo get(g:, "global")\_s*' .. + '\d\+ LOAD g:\_s*' .. + '\d\+ PUSHS "global"\_s*' .. + '\d\+ BCALL get(argc 2)\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo g:auto#var\_s*' .. + '\d\+ LOADAUTO g:auto#var\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo b:buffervar\_s*' .. + '\d\+ LOADB b:buffervar\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'echo get(b:, "buffer")\_s*' .. + '\d\+ LOAD b:\_s*' .. + '\d\+ PUSHS "buffer"\_s*' .. + '\d\+ BCALL get(argc 2).*' .. + ' LOADW w:windowvar.*' .. + 'echo get(w:, "window")\_s*' .. + '\d\+ LOAD w:\_s*' .. + '\d\+ PUSHS "window"\_s*' .. + '\d\+ BCALL get(argc 2).*' .. + ' LOADT t:tabpagevar.*' .. + 'echo get(t:, "tab")\_s*' .. + '\d\+ LOAD t:\_s*' .. + '\d\+ PUSHS "tab"\_s*' .. + '\d\+ BCALL get(argc 2).*' .. + ' LOADENV $ENVVAR.*' .. + ' LOADREG @z.*', + res) +enddef + +def s:EditExpand() + var filename = "file" + var filenr = 123 + edit the`=filename``=filenr`.txt +enddef + +def Test_disassemble_exec_expr() + var res = execute('disass s:EditExpand') + assert_match('<SNR>\d*_EditExpand\_s*' .. + ' var filename = "file"\_s*' .. + '\d PUSHS "file"\_s*' .. + '\d STORE $0\_s*' .. + ' var filenr = 123\_s*' .. + '\d STORE 123 in $1\_s*' .. + ' edit the`=filename``=filenr`.txt\_s*' .. + '\d PUSHS "edit the"\_s*' .. + '\d LOAD $0\_s*' .. + '\d LOAD $1\_s*' .. + '\d 2STRING stack\[-1\]\_s*' .. + '\d\+ PUSHS ".txt"\_s*' .. + '\d\+ EXECCONCAT 4\_s*' .. + '\d\+ RETURN void', + res) +enddef + +if has('python3') + def s:PyHeredoc() + python3 << EOF + print('hello') +EOF + enddef + + def Test_disassemble_python_heredoc() + var res = execute('disass s:PyHeredoc') + assert_match('<SNR>\d*_PyHeredoc.*' .. + " python3 << EOF^@ print('hello')^@EOF\\_s*" .. + '\d EXEC_SPLIT python3 << EOF^@ print(''hello'')^@EOF\_s*' .. + '\d RETURN void', + res) + enddef +endif + +def s:Substitute() + var expr = "abc" + :%s/a/\=expr/&g#c +enddef + +def Test_disassemble_substitute() + var res = execute('disass s:Substitute') + assert_match('<SNR>\d*_Substitute.*' .. + ' var expr = "abc"\_s*' .. + '\d PUSHS "abc"\_s*' .. + '\d STORE $0\_s*' .. + ' :%s/a/\\=expr/&g#c\_s*' .. + '\d SUBSTITUTE :%s/a/\\=expr/&g#c\_s*' .. + ' 0 LOAD $0\_s*' .. + ' -------------\_s*' .. + '\d RETURN void', + res) +enddef + + +def s:SearchPair() + var col = 8 + searchpair("{", "", "}", "", "col('.') > col") +enddef + +def Test_disassemble_seachpair() + var res = execute('disass s:SearchPair') + assert_match('<SNR>\d*_SearchPair.*' .. + ' var col = 8\_s*' .. + '\d STORE 8 in $0\_s*' .. + ' searchpair("{", "", "}", "", "col(''.'') > col")\_s*' .. + '\d PUSHS "{"\_s*' .. + '\d PUSHS ""\_s*' .. + '\d PUSHS "}"\_s*' .. + '\d PUSHS ""\_s*' .. + '\d INSTR\_s*' .. + ' 0 PUSHS "."\_s*' .. + ' 1 BCALL col(argc 1)\_s*' .. + ' 2 LOAD $0\_s*' .. + ' 3 COMPARENR >\_s*' .. + ' -------------\_s*' .. + '\d BCALL searchpair(argc 5)\_s*' .. + '\d DROP\_s*' .. + '\d RETURN void', + res) +enddef + + +def s:SubstituteExpr() + substitute('a', 'b', '\=123', 'g') +enddef + +def Test_disassemble_substitute_expr() + var res = execute('disass s:SubstituteExpr') + assert_match('<SNR>\d*_SubstituteExpr.*' .. + 'substitute(''a'', ''b'', ''\\=123'', ''g'')\_s*' .. + '\d PUSHS "a"\_s*' .. + '\d PUSHS "b"\_s*' .. + '\d INSTR\_s*' .. + ' 0 PUSHNR 123\_s*' .. + ' -------------\_s*' .. + '\d PUSHS "g"\_s*' .. + '\d BCALL substitute(argc 4)\_s*' .. + '\d DROP\_s*' .. + '\d RETURN void', + res) +enddef + +def s:RedirVar() + var result: string + redir =>> result + echo "text" + redir END +enddef + +def Test_disassemble_redir_var() + var res = execute('disass s:RedirVar') + assert_match('<SNR>\d*_RedirVar.*' .. + ' var result: string\_s*' .. + '\d PUSHS "\[NULL\]"\_s*' .. + '\d STORE $0\_s*' .. + ' redir =>> result\_s*' .. + '\d REDIR\_s*' .. + ' echo "text"\_s*' .. + '\d PUSHS "text"\_s*' .. + '\d ECHO 1\_s*' .. + ' redir END\_s*' .. + '\d LOAD $0\_s*' .. + '\d REDIR END\_s*' .. + '\d CONCAT size 2\_s*' .. + '\d STORE $0\_s*' .. + '\d RETURN void', + res) +enddef + +def s:Cexpr() + var errors = "list of errors" + cexpr errors +enddef + +def Test_disassemble_cexpr() + var res = execute('disass s:Cexpr') + assert_match('<SNR>\d*_Cexpr.*' .. + ' var errors = "list of errors"\_s*' .. + '\d PUSHS "list of errors"\_s*' .. + '\d STORE $0\_s*' .. + ' cexpr errors\_s*' .. + '\d CEXPR pre cexpr\_s*' .. + '\d LOAD $0\_s*' .. + '\d CEXPR core cexpr "cexpr errors"\_s*' .. + '\d RETURN void', + res) +enddef + +def s:YankRange() + norm! m[jjm] + :'[,']yank +enddef + +def Test_disassemble_yank_range() + var res = execute('disass s:YankRange') + assert_match('<SNR>\d*_YankRange.*' .. + ' norm! m\[jjm\]\_s*' .. + '\d EXEC norm! m\[jjm\]\_s*' .. + ' :''\[,''\]yank\_s*' .. + '\d EXEC :''\[,''\]yank\_s*' .. + '\d RETURN void', + res) +enddef + +def s:PutExpr() + :3put ="text" +enddef + +def Test_disassemble_put_expr() + var res = execute('disass s:PutExpr') + assert_match('<SNR>\d*_PutExpr.*' .. + ' :3put ="text"\_s*' .. + '\d PUSHS "text"\_s*' .. + '\d PUT = 3\_s*' .. + '\d RETURN void', + res) +enddef + +def s:PutRange() + :$-2put a + :$-3put! b +enddef + +def Test_disassemble_put_range() + var res = execute('disass s:PutRange') + assert_match('<SNR>\d*_PutRange.*' .. + ' :$-2put a\_s*' .. + '\d RANGE $-2\_s*' .. + '\d PUT a range\_s*' .. + + ' :$-3put! b\_s*' .. + '\d RANGE $-3\_s*' .. + '\d PUT b above range\_s*' .. + '\d RETURN void', + res) +enddef + +def s:ScriptFuncPush() + var localbool = true + var localspec = v:none + var localblob = 0z1234 + var localfloat = 1.234 +enddef + +def Test_disassemble_push() + mkdir('Xdisdir/autoload', 'pR') + var save_rtp = &rtp + exe 'set rtp^=' .. getcwd() .. '/Xdisdir' + + var lines =<< trim END + vim9script + END + writefile(lines, 'Xdisdir/autoload/autoscript.vim') + + lines =<< trim END + vim9script + import autoload 'autoscript.vim' + + def AutoloadFunc() + &operatorfunc = autoscript.Opfunc + enddef + + var res = execute('disass AutoloadFunc') + assert_match('<SNR>\d*_AutoloadFunc.*' .. + '&operatorfunc = autoscript.Opfunc\_s*' .. + '0 AUTOLOAD autoscript#Opfunc\_s*' .. + '1 STOREFUNCOPT &operatorfunc\_s*' .. + '2 RETURN void', + res) + END + v9.CheckScriptSuccess(lines) + + &rtp = save_rtp +enddef + +def Test_disassemble_import_autoload() + writefile(['vim9script'], 'XimportAL.vim', 'D') + + var lines =<< trim END + vim9script + import autoload './XimportAL.vim' + + def AutoloadFunc() + echo XimportAL.SomeFunc() + echo XimportAL.someVar + XimportAL.someVar = "yes" + enddef + + var res = execute('disass AutoloadFunc') + assert_match('<SNR>\d*_AutoloadFunc.*' .. + 'echo XimportAL.SomeFunc()\_s*' .. + '\d SOURCE .*/testdir/XimportAL.vim\_s*' .. + '\d PUSHFUNC "<80><fd>R\d\+_SomeFunc"\_s*' .. + '\d PCALL top (argc 0)\_s*' .. + '\d PCALL end\_s*' .. + '\d ECHO 1\_s*' .. + + 'echo XimportAL.someVar\_s*' .. + '\d SOURCE .*/testdir/XimportAL.vim\_s*' .. + '\d LOADEXPORT s:someVar from .*/testdir/XimportAL.vim\_s*' .. + '\d ECHO 1\_s*' .. + + 'XimportAL.someVar = "yes"\_s*' .. + '\d\+ PUSHS "yes"\_s*' .. + '\d\+ SOURCE .*/testdir/XimportAL.vim\_s*' .. + '\d\+ STOREEXPORT someVar in .*/testdir/XimportAL.vim\_s*' .. + + '\d\+ RETURN void', + res) + END + v9.CheckScriptSuccess(lines) +enddef + +def s:ScriptFuncStore() + var localnr = 1 + localnr = 2 + var localstr = 'abc' + localstr = 'xyz' + v:char = 'abc' + s:scriptvar = 'sv' + g:globalvar = 'gv' + g:auto#var = 'av' + b:buffervar = 'bv' + w:windowvar = 'wv' + t:tabpagevar = 'tv' + &tabstop = 8 + &opfunc = (t) => len(t) + $ENVVAR = 'ev' + @z = 'rv' +enddef + +def Test_disassemble_store() + var res = execute('disass s:ScriptFuncStore') + assert_match('<SNR>\d*_ScriptFuncStore.*' .. + 'var localnr = 1.*' .. + 'localnr = 2.*' .. + ' STORE 2 in $0.*' .. + 'var localstr = ''abc''.*' .. + 'localstr = ''xyz''.*' .. + ' STORE $1.*' .. + 'v:char = ''abc''.*' .. + 'STOREV v:char.*' .. + 's:scriptvar = ''sv''.*' .. + ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' .. + 'g:globalvar = ''gv''.*' .. + ' STOREG g:globalvar.*' .. + 'g:auto#var = ''av''.*' .. + ' STOREAUTO g:auto#var.*' .. + 'b:buffervar = ''bv''.*' .. + ' STOREB b:buffervar.*' .. + 'w:windowvar = ''wv''.*' .. + ' STOREW w:windowvar.*' .. + 't:tabpagevar = ''tv''.*' .. + ' STORET t:tabpagevar.*' .. + '&tabstop = 8\_s*' .. + '\d\+ PUSHNR 8\_s*' .. + '\d\+ STOREOPT &tabstop\_s*' .. + '&opfunc = (t) => len(t)\_s*' .. + '\d\+ FUNCREF <lambda>\d\+\_s*' .. + '\d\+ STOREFUNCOPT &opfunc\_s*' .. + '$ENVVAR = ''ev''\_s*' .. + '\d\+ PUSHS "ev"\_s*' .. + '\d\+ STOREENV $ENVVAR\_s*' .. + '@z = ''rv''.*' .. + '\d\+ STOREREG @z.*', + res) +enddef + +def s:ScriptFuncStoreMember() + var locallist: list<number> = [] + locallist[0] = 123 + var localdict: dict<number> = {} + localdict["a"] = 456 + var localblob: blob = 0z1122 + localblob[1] = 33 +enddef + +def Test_disassemble_store_member() + var res = execute('disass s:ScriptFuncStoreMember') + assert_match('<SNR>\d*_ScriptFuncStoreMember\_s*' .. + 'var locallist: list<number> = []\_s*' .. + '\d NEWLIST size 0\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + 'locallist\[0\] = 123\_s*' .. + '\d PUSHNR 123\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d LOAD $0\_s*' .. + '\d STOREINDEX list\_s*' .. + 'var localdict: dict<number> = {}\_s*' .. + '\d NEWDICT size 0\_s*' .. + '\d SETTYPE dict<number>\_s*' .. + '\d STORE $1\_s*' .. + 'localdict\["a"\] = 456\_s*' .. + '\d\+ PUSHNR 456\_s*' .. + '\d\+ PUSHS "a"\_s*' .. + '\d\+ LOAD $1\_s*' .. + '\d\+ STOREINDEX dict\_s*' .. + 'var localblob: blob = 0z1122\_s*' .. + '\d\+ PUSHBLOB 0z1122\_s*' .. + '\d\+ STORE $2\_s*' .. + 'localblob\[1\] = 33\_s*' .. + '\d\+ PUSHNR 33\_s*' .. + '\d\+ PUSHNR 1\_s*' .. + '\d\+ LOAD $2\_s*' .. + '\d\+ STOREINDEX blob\_s*' .. + '\d\+ RETURN void', + res) +enddef + +if has('job') + def s:StoreNull() + var ss = null_string + var bb = null_blob + var dd = null_dict + var ll = null_list + var Ff = null_function + var Pp = null_partial + var jj = null_job + var cc = null_channel + enddef + + def Test_disassemble_assign_null() + var res = execute('disass s:StoreNull') + assert_match('<SNR>\d*_StoreNull\_s*' .. + 'var ss = null_string\_s*' .. + '\d\+ PUSHS "\[NULL\]"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var bb = null_blob\_s*' .. + '\d\+ PUSHBLOB 0z\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var dd = null_dict\_s*' .. + '\d\+ NEWDICT size -1\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var ll = null_list\_s*' .. + '\d\+ NEWLIST size -1\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var Ff = null_function\_s*' .. + '\d\+ PUSHFUNC "\[none\]"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var Pp = null_partial\_s*' .. + '\d\+ NEWPARTIAL\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var jj = null_job\_s*' .. + '\d\+ PUSHJOB "no process"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var cc = null_channel\_s*' .. + '\d\+ PUSHCHANNEL 0\_s*' .. + '\d\+ STORE $\d\_s*' .. + + '\d\+ RETURN void', + res) + enddef +endif + +def s:ScriptFuncStoreIndex() + var d = {dd: {}} + d.dd[0] = 0 +enddef + +def Test_disassemble_store_index() + var res = execute('disass s:ScriptFuncStoreIndex') + assert_match('<SNR>\d*_ScriptFuncStoreIndex\_s*' .. + 'var d = {dd: {}}\_s*' .. + '\d PUSHS "dd"\_s*' .. + '\d NEWDICT size 0\_s*' .. + '\d NEWDICT size 1\_s*' .. + '\d SETTYPE dict<dict<unknown>>\_s*' .. + '\d STORE $0\_s*' .. + 'd.dd\[0\] = 0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d LOAD $0\_s*' .. + '\d MEMBER dd\_s*' .. + '\d\+ USEDICT\_s*' .. + '\d\+ STOREINDEX any\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:ListAssign() + var x: string + var y: string + var l: list<any> + [x, y; l] = g:stringlist +enddef + +def Test_disassemble_list_assign() + var res = execute('disass s:ListAssign') + assert_match('<SNR>\d*_ListAssign\_s*' .. + 'var x: string\_s*' .. + '\d PUSHS "\[NULL\]"\_s*' .. + '\d STORE $0\_s*' .. + 'var y: string\_s*' .. + '\d PUSHS "\[NULL\]"\_s*' .. + '\d STORE $1\_s*' .. + 'var l: list<any>\_s*' .. + '\d NEWLIST size 0\_s*' .. + '\d STORE $2\_s*' .. + '\[x, y; l\] = g:stringlist\_s*' .. + '\d LOADG g:stringlist\_s*' .. + '\d CHECKTYPE list<any> stack\[-1\]\_s*' .. + '\d CHECKLEN >= 2\_s*' .. + '\d\+ ITEM 0\_s*' .. + '\d\+ CHECKTYPE string stack\[-1\] var 1\_s*' .. + '\d\+ STORE $0\_s*' .. + '\d\+ ITEM 1\_s*' .. + '\d\+ CHECKTYPE string stack\[-1\] var 2\_s*' .. + '\d\+ STORE $1\_s*' .. + '\d\+ SLICE 2\_s*' .. + '\d\+ STORE $2\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:ListAssignWithOp() + var a = 2 + var b = 3 + [a, b] += [4, 5] +enddef + +def Test_disassemble_list_assign_with_op() + var res = execute('disass s:ListAssignWithOp') + assert_match('<SNR>\d*_ListAssignWithOp\_s*' .. + 'var a = 2\_s*' .. + '\d STORE 2 in $0\_s*' .. + 'var b = 3\_s*' .. + '\d STORE 3 in $1\_s*' .. + '\[a, b\] += \[4, 5\]\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ PUSHNR 5\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ ITEM 0 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $0\_s*' .. + '\d\+ LOAD $1\_s*' .. + '\d\+ ITEM 1 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $1\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:ListAdd() + var l: list<number> = [] + add(l, 123) + add(l, g:aNumber) +enddef + +def Test_disassemble_list_add() + var res = execute('disass s:ListAdd') + assert_match('<SNR>\d*_ListAdd\_s*' .. + 'var l: list<number> = []\_s*' .. + '\d NEWLIST size 0\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + 'add(l, 123)\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 123\_s*' .. + '\d LISTAPPEND\_s*' .. + '\d DROP\_s*' .. + 'add(l, g:aNumber)\_s*' .. + '\d LOAD $0\_s*' .. + '\d\+ LOADG g:aNumber\_s*' .. + '\d\+ CHECKTYPE number stack\[-1\]\_s*' .. + '\d\+ LISTAPPEND\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:BlobAdd() + var b: blob = 0z + add(b, 123) + add(b, g:aNumber) +enddef + +def Test_disassemble_blob_add() + var res = execute('disass s:BlobAdd') + assert_match('<SNR>\d*_BlobAdd\_s*' .. + 'var b: blob = 0z\_s*' .. + '\d PUSHBLOB 0z\_s*' .. + '\d STORE $0\_s*' .. + 'add(b, 123)\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 123\_s*' .. + '\d BLOBAPPEND\_s*' .. + '\d DROP\_s*' .. + 'add(b, g:aNumber)\_s*' .. + '\d LOAD $0\_s*' .. + '\d\+ LOADG g:aNumber\_s*' .. + '\d\+ CHECKTYPE number stack\[-1\]\_s*' .. + '\d\+ BLOBAPPEND\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:BlobIndexSlice() + var b: blob = 0z112233 + echo b[1] + echo b[1 : 2] +enddef + +def Test_disassemble_blob_index_slice() + var res = execute('disass s:BlobIndexSlice') + assert_match('<SNR>\d*_BlobIndexSlice\_s*' .. + 'var b: blob = 0z112233\_s*' .. + '\d PUSHBLOB 0z112233\_s*' .. + '\d STORE $0\_s*' .. + 'echo b\[1\]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d BLOBINDEX\_s*' .. + '\d ECHO 1\_s*' .. + 'echo b\[1 : 2\]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d\+ PUSHNR 2\_s*' .. + '\d\+ BLOBSLICE\_s*' .. + '\d\+ ECHO 1\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:ScriptFuncUnlet() + g:somevar = "value" + unlet g:somevar + unlet! g:somevar + unlet $SOMEVAR + + var l = [1, 2, 3] + unlet l[2] + unlet l[0 : 1] +enddef + +def Test_disassemble_unlet() + var res = execute('disass s:ScriptFuncUnlet') + assert_match('<SNR>\d*_ScriptFuncUnlet\_s*' .. + 'g:somevar = "value"\_s*' .. + '\d PUSHS "value"\_s*' .. + '\d STOREG g:somevar\_s*' .. + 'unlet g:somevar\_s*' .. + '\d UNLET g:somevar\_s*' .. + 'unlet! g:somevar\_s*' .. + '\d UNLET! g:somevar\_s*' .. + 'unlet $SOMEVAR\_s*' .. + '\d UNLETENV $SOMEVAR\_s*' .. + + 'var l = \[1, 2, 3]\_s*' .. + '\d\+ PUSHNR 1\_s*' .. + '\d\+ PUSHNR 2\_s*' .. + '\d\+ PUSHNR 3\_s*' .. + '\d\+ NEWLIST size 3\_s*' .. + '\d\+ SETTYPE list<number>\_s*' .. + '\d\+ STORE $0\_s*' .. + + 'unlet l\[2]\_s*' .. + '\d\+ PUSHNR 2\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ UNLETINDEX\_s*' .. + + 'unlet l\[0 : 1]\_s*' .. + '\d\+ PUSHNR 0\_s*' .. + '\d\+ PUSHNR 1\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ UNLETRANGE\_s*', + res) +enddef + +def s:LockLocal() + var d = {a: 1} + lockvar d.a + const nr = 22 +enddef + +def Test_disassemble_lock_local() + var res = execute('disass s:LockLocal') + assert_match('<SNR>\d*_LockLocal\_s*' .. + 'var d = {a: 1}\_s*' .. + '\d PUSHS "a"\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d NEWDICT size 1\_s*' .. + '\d SETTYPE dict<number>\_s*' .. + '\d STORE $0\_s*' .. + 'lockvar d.a\_s*' .. + '\d LOAD $0\_s*' .. + '\d LOCKUNLOCK lockvar 2 d.a\_s*' .. + + 'const nr = 22\_s*' .. + '\d\+ PUSHNR 22\_s*' .. + '\d\+ LOCKCONST\_s*' .. + '\d\+ STORE $1', + res) +enddef + +def s:ScriptFuncTry() + try + echo "yes" + catch /fail/ + echo "no" + finally + throw "end" + endtry +enddef + +def Test_disassemble_try() + var res = execute('disass s:ScriptFuncTry') + assert_match('<SNR>\d*_ScriptFuncTry\_s*' .. + 'try\_s*' .. + '\d TRY catch -> \d\+, finally -> \d\+, endtry -> \d\+\_s*' .. + 'echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'catch /fail/\_s*' .. + '\d JUMP -> \d\+\_s*' .. + '\d PUSH v:exception\_s*' .. + '\d PUSHS "fail"\_s*' .. + '\d COMPARESTRING =\~\_s*' .. + '\d JUMP_IF_FALSE -> \d\+\_s*' .. + '\d CATCH\_s*' .. + 'echo "no"\_s*' .. + '\d\+ PUSHS "no"\_s*' .. + '\d\+ ECHO 1\_s*' .. + 'finally\_s*' .. + '\d\+ FINALLY\_s*' .. + 'throw "end"\_s*' .. + '\d\+ PUSHS "end"\_s*' .. + '\d\+ THROW\_s*' .. + 'endtry\_s*' .. + '\d\+ ENDTRY', + res) +enddef + +def s:ScriptFuncNew() + var ll = [1, "two", 333] + var dd = {one: 1, two: "val"} +enddef + +def Test_disassemble_new() + var res = execute('disass s:ScriptFuncNew') + assert_match('<SNR>\d*_ScriptFuncNew\_s*' .. + 'var ll = \[1, "two", 333\]\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHS "two"\_s*' .. + '\d PUSHNR 333\_s*' .. + '\d NEWLIST size 3\_s*' .. + '\d STORE $0\_s*' .. + 'var dd = {one: 1, two: "val"}\_s*' .. + '\d PUSHS "one"\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHS "two"\_s*' .. + '\d PUSHS "val"\_s*' .. + '\d NEWDICT size 2\_s*', + res) +enddef + +def s:FuncWithArg(arg: any) + echo arg +enddef + +func s:UserFunc() + echo 'nothing' +endfunc + +func s:UserFuncWithArg(arg) + echo a:arg +endfunc + +def s:ScriptFuncCall(): string + changenr() + char2nr("abc") + g:Test_disassemble_new() + FuncWithArg(343) + ScriptFuncNew() + s:ScriptFuncNew() + UserFunc() + UserFuncWithArg("foo") + var FuncRef = function("UserFunc") + FuncRef() + var FuncRefWithArg = function("UserFuncWithArg") + FuncRefWithArg("bar") + return "yes" +enddef + +def Test_disassemble_call() + var res = execute('disass s:ScriptFuncCall') + assert_match('<SNR>\d\+_ScriptFuncCall\_s*' .. + 'changenr()\_s*' .. + '\d BCALL changenr(argc 0)\_s*' .. + '\d DROP\_s*' .. + 'char2nr("abc")\_s*' .. + '\d PUSHS "abc"\_s*' .. + '\d BCALL char2nr(argc 1)\_s*' .. + '\d DROP\_s*' .. + 'g:Test_disassemble_new()\_s*' .. + '\d DCALL Test_disassemble_new(argc 0)\_s*' .. + '\d DROP\_s*' .. + 'FuncWithArg(343)\_s*' .. + '\d\+ PUSHNR 343\_s*' .. + '\d\+ DCALL <SNR>\d\+_FuncWithArg(argc 1)\_s*' .. + '\d\+ DROP\_s*' .. + 'ScriptFuncNew()\_s*' .. + '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' .. + '\d\+ DROP\_s*' .. + 's:ScriptFuncNew()\_s*' .. + '\d\+ DCALL <SNR>\d\+_ScriptFuncNew(argc 0)\_s*' .. + '\d\+ DROP\_s*' .. + 'UserFunc()\_s*' .. + '\d\+ UCALL <80><fd>R\d\+_UserFunc(argc 0)\_s*' .. + '\d\+ DROP\_s*' .. + 'UserFuncWithArg("foo")\_s*' .. + '\d\+ PUSHS "foo"\_s*' .. + '\d\+ UCALL <80><fd>R\d\+_UserFuncWithArg(argc 1)\_s*' .. + '\d\+ DROP\_s*' .. + 'var FuncRef = function("UserFunc")\_s*' .. + '\d\+ PUSHS "UserFunc"\_s*' .. + '\d\+ BCALL function(argc 1)\_s*' .. + '\d\+ STORE $0\_s*' .. + 'FuncRef()\_s*' .. + '\d\+ LOAD $\d\_s*' .. + '\d\+ PCALL (argc 0)\_s*' .. + '\d\+ DROP\_s*' .. + 'var FuncRefWithArg = function("UserFuncWithArg")\_s*' .. + '\d\+ PUSHS "UserFuncWithArg"\_s*' .. + '\d\+ BCALL function(argc 1)\_s*' .. + '\d\+ STORE $1\_s*' .. + 'FuncRefWithArg("bar")\_s*' .. + '\d\+ PUSHS "bar"\_s*' .. + '\d\+ LOAD $\d\_s*' .. + '\d\+ PCALL (argc 1)\_s*' .. + '\d\+ DROP\_s*' .. + 'return "yes"\_s*' .. + '\d\+ PUSHS "yes"\_s*' .. + '\d\+ RETURN', + res) +enddef + + +def s:CreateRefs() + var local = 'a' + def Append(arg: string) + local ..= arg + enddef + g:Append = Append + def Get(): string + return local + enddef + g:Get = Get +enddef + +def Test_disassemble_closure() + CreateRefs() + var res = execute('disass g:Append') + assert_match('<lambda>\d\_s*' .. + 'local ..= arg\_s*' .. + '\d LOADOUTER level 1 $0\_s*' .. + '\d LOAD arg\[-1\]\_s*' .. + '\d CONCAT size 2\_s*' .. + '\d STOREOUTER level 1 $0\_s*' .. + '\d RETURN void', + res) + + res = execute('disass g:Get') + assert_match('<lambda>\d\_s*' .. + 'return local\_s*' .. + '\d LOADOUTER level 1 $0\_s*' .. + '\d RETURN', + res) + + unlet g:Append + unlet g:Get +enddef + +def s:ClosureArg(arg: string) + var Ref = () => arg .. "x" +enddef + +def Test_disassemble_closure_arg() + var res = execute('disass s:ClosureArg') + assert_match('<SNR>\d\+_ClosureArg\_s*' .. + 'var Ref = () => arg .. "x"\_s*' .. + '\d FUNCREF <lambda>\d\+', + res) + var lres = execute('disass ' .. matchstr(res, '<lambda>\d\+')) + assert_match('<lambda>\d\+\_s*' .. + 'return arg .. "x"\_s*' .. + '\d LOADOUTER level 1 arg\[-1]\_s*' .. + '\d PUSHS "x"\_s*' .. + '\d CONCAT size 2\_s*' .. + '\d RETURN', + lres) +enddef + +def s:ClosureInLoop() + for i in range(5) + var ii = i + continue + break + if g:val + return + endif + g:Ref = () => ii + continue + break + if g:val + return + endif + endfor +enddef + +" Mainly check that ENDLOOP is only produced after a closure was created. +def Test_disassemble_closure_in_loop() + var res = execute('disass s:ClosureInLoop') + assert_match('<SNR>\d\+_ClosureInLoop\_s*' .. + 'for i in range(5)\_s*' .. + '\d\+ STORE -1 in $0\_s*' .. + '\d\+ PUSHNR 5\_s*' .. + '\d\+ BCALL range(argc 1)\_s*' .. + '\d\+ FOR $0 -> \d\+\_s*' .. + '\d\+ STORE $2\_s*' .. + + 'var ii = i\_s*' .. + '\d\+ LOAD $2\_s*' .. + '\d\+ STORE $3\_s*' .. + + 'continue\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + + 'break\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + + 'if g:val\_s*' .. + '\d\+ LOADG g:val\_s*' .. + '\d\+ COND2BOOL\_s*' .. + '\d\+ JUMP_IF_FALSE -> \d\+\_s*' .. + + ' return\_s*' .. + '\d\+ PUSHNR 0\_s*' .. + '\d\+ RETURN\_s*' .. + + 'endif\_s*' .. + 'g:Ref = () => ii\_s*' .. + '\d\+ FUNCREF <lambda>4 vars $3-$3\_s*' .. + '\d\+ STOREG g:Ref\_s*' .. + + 'continue\_s*' .. + '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + + 'break\_s*' .. + '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + + 'if g:val\_s*' .. + '\d\+ LOADG g:val\_s*' .. + '\d\+ COND2BOOL\_s*' .. + '\d\+ JUMP_IF_FALSE -> \d\+\_s*' .. + + ' return\_s*' .. + '\d\+ PUSHNR 0\_s*' .. + '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. + '\d\+ RETURN\_s*' .. + + 'endif\_s*' .. + 'endfor\_s*' .. + '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def EchoArg(arg: string): string + return arg +enddef +def s:RefThis(): func + return function('EchoArg') +enddef +def s:ScriptPCall() + RefThis()("text") +enddef + +def Test_disassemble_pcall() + var res = execute('disass s:ScriptPCall') + assert_match('<SNR>\d\+_ScriptPCall\_s*' .. + 'RefThis()("text")\_s*' .. + '\d DCALL <SNR>\d\+_RefThis(argc 0)\_s*' .. + '\d PUSHS "text"\_s*' .. + '\d PCALL top (argc 1)\_s*' .. + '\d PCALL end\_s*' .. + '\d DROP\_s*' .. + '\d RETURN void', + res) +enddef + + +def s:FuncWithForwardCall(): string + return g:DefinedLater("yes") +enddef + +def DefinedLater(arg: string): string + return arg +enddef + +def Test_disassemble_update_instr() + var res = execute('disass s:FuncWithForwardCall') + assert_match('FuncWithForwardCall\_s*' .. + 'return g:DefinedLater("yes")\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d DCALL DefinedLater(argc 1)\_s*' .. + '\d RETURN', + res) + + # Calling the function will change UCALL into the faster DCALL + assert_equal('yes', FuncWithForwardCall()) + + res = execute('disass s:FuncWithForwardCall') + assert_match('FuncWithForwardCall\_s*' .. + 'return g:DefinedLater("yes")\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d DCALL DefinedLater(argc 1)\_s*' .. + '\d RETURN', + res) +enddef + + +def FuncWithDefault(l: number, arg: string = "default", nr = 77): string + return arg .. nr +enddef + +def Test_disassemble_call_default() + var res = execute('disass FuncWithDefault') + assert_match('FuncWithDefault\_s*' .. + ' arg = "default"\_s*' .. + '\d JUMP_IF_ARG_SET arg\[-2\] -> 3\_s*' .. + '\d PUSHS "default"\_s*' .. + '\d STORE arg\[-2]\_s*' .. + ' nr = 77\_s*' .. + '3 JUMP_IF_ARG_SET arg\[-1\] -> 6\_s*' .. + '\d PUSHNR 77\_s*' .. + '\d STORE arg\[-1]\_s*' .. + ' return arg .. nr\_s*' .. + '6 LOAD arg\[-2]\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d 2STRING stack\[-1]\_s*' .. + '\d\+ CONCAT size 2\_s*' .. + '\d\+ RETURN', + res) +enddef + + +def s:HasEval() + if has("eval") + echo "yes" + else + echo "no" + endif +enddef + +def s:HasNothing() + if has("nothing") + echo "yes" + else + echo "no" + endif +enddef + +def s:HasSomething() + if has("nothing") + echo "nothing" + elseif has("something") + echo "something" + elseif has("eval") + echo "eval" + elseif has("less") + echo "less" + endif +enddef + +def s:HasGuiRunning() + if has("gui_running") + echo "yes" + else + echo "no" + endif +enddef + +def s:LenConstant(): number + return len("foo") + len("fighters") +enddef + +def Test_disassemble_const_expr() + var instr = execute('disassemble LenConstant') + assert_match('LenConstant\_s*' .. + 'return len("foo") + len("fighters")\_s*' .. + '\d PUSHNR 11\_s*', + instr) + assert_notmatch('BCALL len', instr) + + assert_equal("\nyes", execute('HasEval()')) + instr = execute('disassemble HasEval') + assert_match('HasEval\_s*' .. + 'if has("eval")\_s*' .. + 'echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'else\_s*' .. + 'echo "no"\_s*' .. + 'endif\_s*', + instr) + assert_notmatch('JUMP', instr) + + assert_equal("\nno", execute('HasNothing()')) + instr = execute('disassemble HasNothing') + assert_match('HasNothing\_s*' .. + 'if has("nothing")\_s*' .. + 'echo "yes"\_s*' .. + 'else\_s*' .. + 'echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif', + instr) + assert_notmatch('PUSHS "yes"', instr) + assert_notmatch('JUMP', instr) + + assert_equal("\neval", execute('HasSomething()')) + instr = execute('disassemble HasSomething') + assert_match('HasSomething.*' .. + 'if has("nothing")\_s*' .. + 'echo "nothing"\_s*' .. + 'elseif has("something")\_s*' .. + 'echo "something"\_s*' .. + 'elseif has("eval")\_s*' .. + 'echo "eval"\_s*' .. + '\d PUSHS "eval"\_s*' .. + '\d ECHO 1\_s*' .. + 'elseif has("less").*' .. + 'echo "less"\_s*' .. + 'endif', + instr) + assert_notmatch('PUSHS "nothing"', instr) + assert_notmatch('PUSHS "something"', instr) + assert_notmatch('PUSHS "less"', instr) + assert_notmatch('JUMP', instr) + + var result: string + var instr_expected: string + if has('gui') + if has('gui_running') + # GUI already running, always returns "yes" + result = "\nyes" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + 'endif' + else + result = "\nno" + if has('unix') + # GUI not running but can start later, call has() + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + '\d PUSHS "gui_running"\_s*' .. + '\d BCALL has(argc 1)\_s*' .. + '\d COND2BOOL\_s*' .. + '\d JUMP_IF_FALSE -> \d\_s*' .. + ' echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'else\_s*' .. + '\d JUMP -> \d\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + else + # GUI not running, always return "no" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + endif + endif + else + # GUI not supported, always return "no" + result = "\nno" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + endif + + assert_equal(result, execute('HasGuiRunning()')) + instr = execute('disassemble HasGuiRunning') + assert_match(instr_expected, instr) +enddef + +def ReturnInIf(): string + if 1 < 0 + return "maybe" + endif + if g:cond + return "yes" + else + return "no" + endif +enddef + +def Test_disassemble_return_in_if() + var instr = execute('disassemble ReturnInIf') + assert_match('ReturnInIf\_s*' .. + 'if 1 < 0\_s*' .. + ' return "maybe"\_s*' .. + 'endif\_s*' .. + 'if g:cond\_s*' .. + '0 LOADG g:cond\_s*' .. + '1 COND2BOOL\_s*' .. + '2 JUMP_IF_FALSE -> 5\_s*' .. + 'return "yes"\_s*' .. + '3 PUSHS "yes"\_s*' .. + '4 RETURN\_s*' .. + 'else\_s*' .. + ' return "no"\_s*' .. + '5 PUSHS "no"\_s*' .. + '6 RETURN$', + instr) +enddef + +def WithFunc() + var Funky1: func + var Funky2: func = function("len") + var Party2: func = funcref("UserFunc") +enddef + +def Test_disassemble_function() + var instr = execute('disassemble WithFunc') + assert_match('WithFunc\_s*' .. + 'var Funky1: func\_s*' .. + '0 PUSHFUNC "\[none]"\_s*' .. + '1 STORE $0\_s*' .. + 'var Funky2: func = function("len")\_s*' .. + '2 PUSHS "len"\_s*' .. + '3 BCALL function(argc 1)\_s*' .. + '4 STORE $1\_s*' .. + 'var Party2: func = funcref("UserFunc")\_s*' .. + '\d PUSHS "UserFunc"\_s*' .. + '\d BCALL funcref(argc 1)\_s*' .. + '\d STORE $2\_s*' .. + '\d RETURN void', + instr) +enddef + +if has('channel') + def WithChannel() + var job1: job + var job2: job = job_start("donothing") + var chan1: channel + enddef +endif + +def Test_disassemble_channel() + CheckFeature channel + + var instr = execute('disassemble WithChannel') + assert_match('WithChannel\_s*' .. + 'var job1: job\_s*' .. + '\d PUSHJOB "no process"\_s*' .. + '\d STORE $0\_s*' .. + 'var job2: job = job_start("donothing")\_s*' .. + '\d PUSHS "donothing"\_s*' .. + '\d BCALL job_start(argc 1)\_s*' .. + '\d STORE $1\_s*' .. + 'var chan1: channel\_s*' .. + '\d PUSHCHANNEL 0\_s*' .. + '\d STORE $2\_s*' .. + '\d RETURN void', + instr) +enddef + +def s:WithLambda(): string + var F = (a) => "X" .. a .. "X" + return F("x") +enddef + +def Test_disassemble_lambda() + assert_equal("XxX", WithLambda()) + var instr = execute('disassemble WithLambda') + assert_match('WithLambda\_s*' .. + 'var F = (a) => "X" .. a .. "X"\_s*' .. + '\d FUNCREF <lambda>\d\+\_s*' .. + '\d STORE $0\_s*' .. + 'return F("x")\_s*' .. + '\d PUSHS "x"\_s*' .. + '\d LOAD $0\_s*' .. + '\d PCALL (argc 1)\_s*' .. + '\d RETURN', + instr) + + var name = substitute(instr, '.*\(<lambda>\d\+\).*', '\1', '') + instr = execute('disassemble ' .. name) + assert_match('<lambda>\d\+\_s*' .. + 'return "X" .. a .. "X"\_s*' .. + '\d PUSHS "X"\_s*' .. + '\d LOAD arg\[-1\]\_s*' .. + '\d 2STRING_ANY stack\[-1\]\_s*' .. + '\d CONCAT size 2\_s*' .. + '\d PUSHS "X"\_s*' .. + '\d CONCAT size 2\_s*' .. + '\d RETURN', + instr) +enddef + +def s:LambdaWithType(): number + var Ref = (a: number) => a + 10 + return Ref(g:value) +enddef + +def Test_disassemble_lambda_with_type() + g:value = 5 + assert_equal(15, LambdaWithType()) + var instr = execute('disassemble LambdaWithType') + assert_match('LambdaWithType\_s*' .. + 'var Ref = (a: number) => a + 10\_s*' .. + '\d FUNCREF <lambda>\d\+\_s*' .. + '\d STORE $0\_s*' .. + 'return Ref(g:value)\_s*' .. + '\d LOADG g:value\_s*' .. + '\d LOAD $0\_s*' .. + '\d CHECKTYPE number stack\[-2\] arg 1\_s*' .. + '\d PCALL (argc 1)\_s*' .. + '\d RETURN', + instr) +enddef + +def NestedOuter() + def g:Inner() + echomsg "inner" + enddef +enddef + +def Test_disassemble_nested_func() + var instr = execute('disassemble NestedOuter') + assert_match('NestedOuter\_s*' .. + 'def g:Inner()\_s*' .. + 'echomsg "inner"\_s*' .. + 'enddef\_s*' .. + '\d NEWFUNC <lambda>\d\+ Inner\_s*' .. + '\d RETURN void', + instr) +enddef + +def NestedDefList() + def + def Info + def /Info + def /Info/ +enddef + +def Test_disassemble_nested_def_list() + var instr = execute('disassemble NestedDefList') + assert_match('NestedDefList\_s*' .. + 'def\_s*' .. + '\d DEF \_s*' .. + 'def Info\_s*' .. + '\d DEF Info\_s*' .. + 'def /Info\_s*' .. + '\d DEF /Info\_s*' .. + 'def /Info/\_s*' .. + '\d DEF /Info/\_s*' .. + '\d RETURN void', + instr) +enddef + +def s:AndOr(arg: any): string + if arg == 1 && arg != 2 || arg == 4 + return 'yes' + endif + return 'no' +enddef + +def Test_disassemble_and_or() + assert_equal("yes", AndOr(1)) + assert_equal("no", AndOr(2)) + assert_equal("yes", AndOr(4)) + var instr = execute('disassemble AndOr') + assert_match('AndOr\_s*' .. + 'if arg == 1 && arg != 2 || arg == 4\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d COMPAREANY ==\_s*' .. + '\d JUMP_IF_COND_FALSE -> \d\+\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d COMPAREANY !=\_s*' .. + '\d JUMP_IF_COND_TRUE -> \d\+\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ COMPAREANY ==\_s*' .. + '\d\+ JUMP_IF_FALSE -> \d\+', + instr) +enddef + +def s:AndConstant(arg: any): string + if true && arg + return "yes" + endif + if false && arg + return "never" + endif + return "no" +enddef + +def Test_disassemble_and_constant() + assert_equal("yes", AndConstant(1)) + assert_equal("no", AndConstant(false)) + var instr = execute('disassemble AndConstant') + assert_match('AndConstant\_s*' .. + 'if true && arg\_s*' .. + '0 LOAD arg\[-1\]\_s*' .. + '1 COND2BOOL\_s*' .. + '2 JUMP_IF_FALSE -> 5\_s*' .. + 'return "yes"\_s*' .. + '3 PUSHS "yes"\_s*' .. + '4 RETURN\_s*' .. + 'endif\_s*' .. + 'if false && arg\_s*' .. + 'return "never"\_s*' .. + 'endif\_s*' .. + 'return "no"\_s*' .. + '5 PUSHS "no"\_s*' .. + '6 RETURN', + instr) +enddef + +def s:ForLoop(): list<number> + var res: list<number> + for i in range(3) + res->add(i) + endfor + return res +enddef + +def Test_disassemble_for_loop() + assert_equal([0, 1, 2], ForLoop()) + var instr = execute('disassemble ForLoop') + assert_match('ForLoop\_s*' .. + 'var res: list<number>\_s*' .. + '\d NEWLIST size 0\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + + 'for i in range(3)\_s*' .. + '\d STORE -1 in $1\_s*' .. + '\d PUSHNR 3\_s*' .. + '\d BCALL range(argc 1)\_s*' .. + '\d FOR $1 -> \d\+\_s*' .. + '\d STORE $3\_s*' .. + + 'res->add(i)\_s*' .. + '\d LOAD $0\_s*' .. + '\d LOAD $3\_s*' .. + '\d\+ LISTAPPEND\_s*' .. + '\d\+ DROP\_s*' .. + + 'endfor\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + '\d\+ DROP', + instr) +enddef + +def s:ForLoopEval(): string + var res = "" + for str in eval('["one", "two"]') + res ..= str + endfor + return res +enddef + +def Test_disassemble_for_loop_eval() + assert_equal('onetwo', ForLoopEval()) + var instr = execute('disassemble ForLoopEval') + assert_match('ForLoopEval\_s*' .. + 'var res = ""\_s*' .. + '\d PUSHS ""\_s*' .. + '\d STORE $0\_s*' .. + + 'for str in eval(''\["one", "two"\]'')\_s*' .. + '\d STORE -1 in $1\_s*' .. + '\d PUSHS "\["one", "two"\]"\_s*' .. + '\d BCALL eval(argc 1)\_s*' .. + '\d FOR $1 -> \d\+\_s*' .. + '\d STORE $3\_s*' .. + + 'res ..= str\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ LOAD $3\_s*' .. + '\d 2STRING_ANY stack\[-1\]\_s*' .. + '\d\+ CONCAT size 2\_s*' .. + '\d\+ STORE $0\_s*' .. + + 'endfor\_s*' .. + '\d\+ JUMP -> 5\_s*' .. + '\d\+ DROP\_s*' .. + + 'return res\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ RETURN', + instr) +enddef + +def s:ForLoopUnpack() + for [x1, x2] in [[1, 2], [3, 4]] + echo x1 x2 + endfor +enddef + +def Test_disassemble_for_loop_unpack() + var instr = execute('disassemble ForLoopUnpack') + assert_match('ForLoopUnpack\_s*' .. + 'for \[x1, x2\] in \[\[1, 2\], \[3, 4\]\]\_s*' .. + '\d\+ STORE -1 in $0\_s*' .. + '\d\+ PUSHNR 1\_s*' .. + '\d\+ PUSHNR 2\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ PUSHNR 3\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ FOR $0 -> 16\_s*' .. + '\d\+ UNPACK 2\_s*' .. + '\d\+ STORE $2\_s*' .. + '\d\+ STORE $3\_s*' .. + + 'echo x1 x2\_s*' .. + '\d\+ LOAD $2\_s*' .. + '\d\+ LOAD $3\_s*' .. + '\d\+ ECHO 2\_s*' .. + + 'endfor\_s*' .. + '\d\+ JUMP -> 8\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + instr) +enddef + +def s:ForLoopContinue() + for nr in [1, 2] + try + echo "ok" + try + echo "deeper" + catch + continue + endtry + catch + echo "not ok" + endtry + endfor +enddef + +def Test_disassemble_for_loop_continue() + var instr = execute('disassemble ForLoopContinue') + assert_match('ForLoopContinue\_s*' .. + 'for nr in \[1, 2]\_s*' .. + '0 STORE -1 in $0\_s*' .. + '1 PUSHNR 1\_s*' .. + '2 PUSHNR 2\_s*' .. + '3 NEWLIST size 2\_s*' .. + '4 FOR $0 -> 22\_s*' .. + '5 STORE $2\_s*' .. + + 'try\_s*' .. + '6 TRY catch -> 17, endtry -> 20\_s*' .. + + 'echo "ok"\_s*' .. + '7 PUSHS "ok"\_s*' .. + '8 ECHO 1\_s*' .. + + 'try\_s*' .. + '9 TRY catch -> 13, endtry -> 15\_s*' .. + + 'echo "deeper"\_s*' .. + '10 PUSHS "deeper"\_s*' .. + '11 ECHO 1\_s*' .. + + 'catch\_s*' .. + '12 JUMP -> 15\_s*' .. + '13 CATCH\_s*' .. + + 'continue\_s*' .. + '14 TRY-CONTINUE 2 levels -> 4\_s*' .. + + 'endtry\_s*' .. + '15 ENDTRY\_s*' .. + + 'catch\_s*' .. + '16 JUMP -> 20\_s*' .. + '17 CATCH\_s*' .. + + 'echo "not ok"\_s*' .. + '18 PUSHS "not ok"\_s*' .. + '19 ECHO 1\_s*' .. + + 'endtry\_s*' .. + '20 ENDTRY\_s*' .. + + 'endfor\_s*' .. + '21 JUMP -> 4\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + instr) +enddef + +let g:number = 42 + +def s:TypeCast() + var l: list<number> = [23, <number>g:number] +enddef + +def Test_disassemble_typecast() + var instr = execute('disassemble TypeCast') + assert_match('TypeCast.*' .. + 'var l: list<number> = \[23, <number>g:number\].*' .. + '\d PUSHNR 23\_s*' .. + '\d LOADG g:number\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d NEWLIST size 2\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + '\d RETURN void\_s*', + instr) +enddef + +def s:Computing() + var nr = 3 + var nrres = nr + 7 + nrres = nr - 7 + nrres = nr * 7 + nrres = nr / 7 + nrres = nr % 7 + + var anyres = g:number + 7 + anyres = g:number - 7 + anyres = g:number * 7 + anyres = g:number / 7 + anyres = g:number % 7 + + var fl = 3.0 + var flres = fl + 7.0 + flres = fl - 7.0 + flres = fl * 7.0 + flres = fl / 7.0 +enddef + +def Test_disassemble_computing() + var instr = execute('disassemble Computing') + assert_match('Computing.*' .. + 'var nr = 3.*' .. + '\d STORE 3 in $0.*' .. + 'var nrres = nr + 7.*' .. + '\d LOAD $0.*' .. + '\d PUSHNR 7.*' .. + '\d OPNR +.*' .. + '\d STORE $1.*' .. + 'nrres = nr - 7.*' .. + '\d OPNR -.*' .. + 'nrres = nr \* 7.*' .. + '\d OPNR \*.*' .. + 'nrres = nr / 7.*' .. + '\d OPNR /.*' .. + 'nrres = nr % 7.*' .. + '\d OPNR %.*' .. + 'var anyres = g:number + 7.*' .. + '\d LOADG g:number.*' .. + '\d PUSHNR 7.*' .. + '\d OPANY +.*' .. + '\d STORE $2.*' .. + 'anyres = g:number - 7.*' .. + '\d OPANY -.*' .. + 'anyres = g:number \* 7.*' .. + '\d OPANY \*.*' .. + 'anyres = g:number / 7.*' .. + '\d OPANY /.*' .. + 'anyres = g:number % 7.*' .. + '\d OPANY %.*', + instr) + assert_match('Computing.*' .. + 'var fl = 3.0.*' .. + '\d PUSHF 3.0.*' .. + '\d STORE $3.*' .. + 'var flres = fl + 7.0.*' .. + '\d LOAD $3.*' .. + '\d PUSHF 7.0.*' .. + '\d OPFLOAT +.*' .. + '\d STORE $4.*' .. + 'flres = fl - 7.0.*' .. + '\d OPFLOAT -.*' .. + 'flres = fl \* 7.0.*' .. + '\d OPFLOAT \*.*' .. + 'flres = fl / 7.0.*' .. + '\d OPFLOAT /.*', + instr) +enddef + +def s:AddListBlob() + var reslist = [1, 2] + [3, 4] + var resblob = 0z1122 + 0z3344 +enddef + +def Test_disassemble_add_list_blob() + var instr = execute('disassemble AddListBlob') + assert_match('AddListBlob.*' .. + 'var reslist = \[1, 2] + \[3, 4].*' .. + '\d PUSHNR 1.*' .. + '\d PUSHNR 2.*' .. + '\d NEWLIST size 2.*' .. + '\d PUSHNR 3.*' .. + '\d PUSHNR 4.*' .. + '\d NEWLIST size 2.*' .. + '\d ADDLIST.*' .. + '\d STORE $.*.*' .. + 'var resblob = 0z1122 + 0z3344.*' .. + '\d PUSHBLOB 0z1122.*' .. + '\d PUSHBLOB 0z3344.*' .. + '\d ADDBLOB.*' .. + '\d STORE $.*', + instr) +enddef + +let g:aa = 'aa' +def s:ConcatString(): string + var res = g:aa .. "bb" + return res +enddef + +def Test_disassemble_concat() + var instr = execute('disassemble ConcatString') + assert_match('ConcatString.*' .. + 'var res = g:aa .. "bb".*' .. + '\d LOADG g:aa.*' .. + '\d PUSHS "bb".*' .. + '\d 2STRING_ANY stack\[-2].*' .. + '\d CONCAT.*' .. + '\d STORE $.*', + instr) + assert_equal('aabb', ConcatString()) +enddef + +def s:StringIndex(): string + var s = "abcd" + var res = s[1] + return res +enddef + +def Test_disassemble_string_index() + var instr = execute('disassemble StringIndex') + assert_match('StringIndex\_s*' .. + 'var s = "abcd"\_s*' .. + '\d PUSHS "abcd"\_s*' .. + '\d STORE $0\_s*' .. + 'var res = s\[1]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d STRINDEX\_s*' .. + '\d STORE $1\_s*', + instr) + assert_equal('b', StringIndex()) +enddef + +def s:StringSlice(): string + var s = "abcd" + var res = s[1 : 8] + return res +enddef + +def Test_disassemble_string_slice() + var instr = execute('disassemble StringSlice') + assert_match('StringSlice\_s*' .. + 'var s = "abcd"\_s*' .. + '\d PUSHS "abcd"\_s*' .. + '\d STORE $0\_s*' .. + 'var res = s\[1 : 8]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 8\_s*' .. + '\d STRSLICE\_s*' .. + '\d STORE $1\_s*', + instr) + assert_equal('bcd', StringSlice()) +enddef + +def s:ListIndex(): number + var l = [1, 2, 3] + var res = l[1] + return res +enddef + +def Test_disassemble_list_index() + var instr = execute('disassemble ListIndex') + assert_match('ListIndex\_s*' .. + 'var l = \[1, 2, 3]\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d PUSHNR 3\_s*' .. + '\d NEWLIST size 3\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + 'var res = l\[1]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d LISTINDEX\_s*' .. + '\d STORE $1\_s*', + instr) + assert_equal(2, ListIndex()) +enddef + +def s:ListSlice(): list<number> + var l = [1, 2, 3] + var res = l[1 : 8] + return res +enddef + +def Test_disassemble_list_slice() + var instr = execute('disassemble ListSlice') + assert_match('ListSlice\_s*' .. + 'var l = \[1, 2, 3]\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d PUSHNR 3\_s*' .. + '\d NEWLIST size 3\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + 'var res = l\[1 : 8]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 8\_s*' .. + '\d\+ LISTSLICE\_s*' .. + '\d\+ SETTYPE list<number>\_s*' .. + '\d\+ STORE $1\_s*', + instr) + assert_equal([2, 3], ListSlice()) +enddef + +def s:DictMember(): number + var d = {item: 1} + var res = d.item + res = d["item"] + return res +enddef + +def Test_disassemble_dict_member() + var instr = execute('disassemble DictMember') + assert_match('DictMember\_s*' .. + 'var d = {item: 1}\_s*' .. + '\d PUSHS "item"\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d NEWDICT size 1\_s*' .. + '\d SETTYPE dict<number>\_s*' .. + '\d STORE $0\_s*' .. + 'var res = d.item\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ MEMBER item\_s*' .. + '\d\+ USEDICT\_s*' .. + '\d\+ STORE $1\_s*' .. + 'res = d\["item"\]\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ PUSHS "item"\_s*' .. + '\d\+ MEMBER\_s*' .. + '\d\+ USEDICT\_s*' .. + '\d\+ STORE $1\_s*', + instr) + assert_equal(1, DictMember()) +enddef + +let somelist = [1, 2, 3, 4, 5] +def s:AnyIndex(): number + var res = g:somelist[2] + return res +enddef + +def Test_disassemble_any_index() + var instr = execute('disassemble AnyIndex') + assert_match('AnyIndex\_s*' .. + 'var res = g:somelist\[2\]\_s*' .. + '\d LOADG g:somelist\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d ANYINDEX\_s*' .. + '\d STORE $0\_s*' .. + 'return res\_s*' .. + '\d LOAD $0\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d RETURN', + instr) + assert_equal(3, AnyIndex()) +enddef + +def s:AnySlice(): list<number> + var res = g:somelist[1 : 3] + return res +enddef + +def Test_disassemble_any_slice() + var instr = execute('disassemble AnySlice') + assert_match('AnySlice\_s*' .. + 'var res = g:somelist\[1 : 3\]\_s*' .. + '\d LOADG g:somelist\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 3\_s*' .. + '\d ANYSLICE\_s*' .. + '\d STORE $0\_s*' .. + 'return res\_s*' .. + '\d LOAD $0\_s*' .. + '\d CHECKTYPE list<number> stack\[-1\]\_s*' .. + '\d RETURN', + instr) + assert_equal([2, 3, 4], AnySlice()) +enddef + +def s:NegateNumber(): number + g:nr = 9 + var plus = +g:nr + var minus = -g:nr + return minus +enddef + +def Test_disassemble_negate_number() + var instr = execute('disassemble NegateNumber') + assert_match('NegateNumber\_s*' .. + 'g:nr = 9\_s*' .. + '\d PUSHNR 9\_s*' .. + '\d STOREG g:nr\_s*' .. + 'var plus = +g:nr\_s*' .. + '\d LOADG g:nr\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d STORE $0\_s*' .. + 'var minus = -g:nr\_s*' .. + '\d LOADG g:nr\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d NEGATENR\_s*' .. + '\d STORE $1\_s*', + instr) + assert_equal(-9, NegateNumber()) +enddef + +def s:InvertBool(): bool + var flag = true + var invert = !flag + var res = !!flag + return res +enddef + +def Test_disassemble_invert_bool() + var instr = execute('disassemble InvertBool') + assert_match('InvertBool\_s*' .. + 'var flag = true\_s*' .. + '\d PUSH true\_s*' .. + '\d STORE $0\_s*' .. + 'var invert = !flag\_s*' .. + '\d LOAD $0\_s*' .. + '\d INVERT -1 (!val)\_s*' .. + '\d STORE $1\_s*' .. + 'var res = !!flag\_s*' .. + '\d LOAD $0\_s*' .. + '\d 2BOOL -1 (!!val)\_s*' .. + '\d STORE $2\_s*', + instr) + assert_equal(true, InvertBool()) +enddef + +def s:ReturnBool(): bool + var one = 1 + var zero = 0 + var none: number + var name: bool = one && zero || one + return name +enddef + +def Test_disassemble_return_bool() + var instr = execute('disassemble ReturnBool') + assert_match('ReturnBool\_s*' .. + 'var one = 1\_s*' .. + '0 STORE 1 in $0\_s*' .. + 'var zero = 0\_s*' .. + 'var none: number\_s*' .. + 'var name: bool = one && zero || one\_s*' .. + '1 LOAD $0\_s*' .. + '2 COND2BOOL\_s*' .. + '3 JUMP_IF_COND_FALSE -> 6\_s*' .. + '4 LOAD $1\_s*' .. + '5 COND2BOOL\_s*' .. + '6 JUMP_IF_COND_TRUE -> 9\_s*' .. + '7 LOAD $0\_s*' .. + '8 COND2BOOL\_s*' .. + '9 STORE $3\_s*' .. + 'return name\_s*' .. + '\d\+ LOAD $3\_s*' .. + '\d\+ RETURN', + instr) + assert_equal(true, InvertBool()) +enddef + +def s:AutoInit() + var t: number + t = 1 + t = 0 +enddef + +def Test_disassemble_auto_init() + var instr = execute('disassemble AutoInit') + assert_match('AutoInit\_s*' .. + 'var t: number\_s*' .. + 't = 1\_s*' .. + '\d STORE 1 in $0\_s*' .. + 't = 0\_s*' .. + '\d STORE 0 in $0\_s*' .. + '\d\+ RETURN void', + instr) +enddef + +def Test_disassemble_compare() + var cases = [ + ['true == isFalse', 'COMPAREBOOL =='], + ['true != isFalse', 'COMPAREBOOL !='], + ['v:none == isNull', 'COMPARESPECIAL =='], + ['v:none != isNull', 'COMPARESPECIAL !='], + ['"text" == isNull', 'COMPARENULL =='], + ['"text" != isNull', 'COMPARENULL !='], + + ['111 == aNumber', 'COMPARENR =='], + ['111 != aNumber', 'COMPARENR !='], + ['111 > aNumber', 'COMPARENR >'], + ['111 < aNumber', 'COMPARENR <'], + ['111 >= aNumber', 'COMPARENR >='], + ['111 <= aNumber', 'COMPARENR <='], + ['111 =~ aNumber', 'COMPARENR =\~'], + ['111 !~ aNumber', 'COMPARENR !\~'], + + ['"xx" != aString', 'COMPARESTRING !='], + ['"xx" > aString', 'COMPARESTRING >'], + ['"xx" < aString', 'COMPARESTRING <'], + ['"xx" >= aString', 'COMPARESTRING >='], + ['"xx" <= aString', 'COMPARESTRING <='], + ['"xx" =~ aString', 'COMPARESTRING =\~'], + ['"xx" !~ aString', 'COMPARESTRING !\~'], + ['"xx" is aString', 'COMPARESTRING is'], + ['"xx" isnot aString', 'COMPARESTRING isnot'], + + ['0z11 == aBlob', 'COMPAREBLOB =='], + ['0z11 != aBlob', 'COMPAREBLOB !='], + ['0z11 is aBlob', 'COMPAREBLOB is'], + ['0z11 isnot aBlob', 'COMPAREBLOB isnot'], + + ['[1, 2] == aList', 'COMPARELIST =='], + ['[1, 2] != aList', 'COMPARELIST !='], + ['[1, 2] is aList', 'COMPARELIST is'], + ['[1, 2] isnot aList', 'COMPARELIST isnot'], + + ['{a: 1} == aDict', 'COMPAREDICT =='], + ['{a: 1} != aDict', 'COMPAREDICT !='], + ['{a: 1} is aDict', 'COMPAREDICT is'], + ['{a: 1} isnot aDict', 'COMPAREDICT isnot'], + + ['(() => 33) == (() => 44)', 'COMPAREFUNC =='], + ['(() => 33) != (() => 44)', 'COMPAREFUNC !='], + ['(() => 33) is (() => 44)', 'COMPAREFUNC is'], + ['(() => 33) isnot (() => 44)', 'COMPAREFUNC isnot'], + + ['77 == g:xx', 'COMPAREANY =='], + ['77 != g:xx', 'COMPAREANY !='], + ['77 > g:xx', 'COMPAREANY >'], + ['77 < g:xx', 'COMPAREANY <'], + ['77 >= g:xx', 'COMPAREANY >='], + ['77 <= g:xx', 'COMPAREANY <='], + ['77 =~ g:xx', 'COMPAREANY =\~'], + ['77 !~ g:xx', 'COMPAREANY !\~'], + ['77 is g:xx', 'COMPAREANY is'], + ['77 isnot g:xx', 'COMPAREANY isnot'], + ] + var floatDecl = '' + cases->extend([ + ['1.1 == aFloat', 'COMPAREFLOAT =='], + ['1.1 != aFloat', 'COMPAREFLOAT !='], + ['1.1 > aFloat', 'COMPAREFLOAT >'], + ['1.1 < aFloat', 'COMPAREFLOAT <'], + ['1.1 >= aFloat', 'COMPAREFLOAT >='], + ['1.1 <= aFloat', 'COMPAREFLOAT <='], + ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'], + ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'], + ]) + floatDecl = 'var aFloat = 2.2' + + var nr = 1 + for case in cases + # declare local variables to get a non-constant with the right type + writefile(['def TestCase' .. nr .. '()', + ' var isFalse = false', + ' var isNull = v:null', + ' var aNumber = 222', + ' var aString = "yy"', + ' var aBlob = 0z22', + ' var aList = [3, 4]', + ' var aDict = {x: 2}', + floatDecl, + ' if ' .. case[0], + ' echo 42', + ' endif', + 'enddef'], 'Xdisassemble') + source Xdisassemble + var instr = execute('disassemble TestCase' .. nr) + assert_match('TestCase' .. nr .. '.*' .. + 'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' .. + '\d \(PUSH\|FUNCREF\).*' .. + '\d \(PUSH\|FUNCREF\|LOAD\).*' .. + '\d ' .. case[1] .. '.*' .. + '\d JUMP_IF_FALSE -> \d\+.*', + instr) + + nr += 1 + endfor + + delete('Xdisassemble') +enddef + +def s:FalsyOp() + echo g:flag ?? "yes" + echo [] ?? "empty list" + echo "" ?? "empty string" +enddef + +def Test_disassemble_falsy_op() + var res = execute('disass s:FalsyOp') + assert_match('\<SNR>\d*_FalsyOp\_s*' .. + 'echo g:flag ?? "yes"\_s*' .. + '0 LOADG g:flag\_s*' .. + '1 JUMP_AND_KEEP_IF_TRUE -> 3\_s*' .. + '2 PUSHS "yes"\_s*' .. + '3 ECHO 1\_s*' .. + 'echo \[\] ?? "empty list"\_s*' .. + '4 NEWLIST size 0\_s*' .. + '5 JUMP_AND_KEEP_IF_TRUE -> 7\_s*' .. + '6 PUSHS "empty list"\_s*' .. + '7 ECHO 1\_s*' .. + 'echo "" ?? "empty string"\_s*' .. + '\d\+ PUSHS "empty string"\_s*' .. + '\d\+ ECHO 1\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def Test_disassemble_compare_const() + var cases = [ + ['"xx" == "yy"', false], + ['"aa" == "aa"', true], + ['has("eval") ? true : false', true], + ['has("asdf") ? true : false', false], + ] + + var nr = 1 + for case in cases + writefile(['def TestCase' .. nr .. '()', + ' if ' .. case[0], + ' echo 42', + ' endif', + 'enddef'], 'Xdisassemble') + source Xdisassemble + var instr = execute('disassemble TestCase' .. nr) + if case[1] + # condition true, "echo 42" executed + assert_match('TestCase' .. nr .. '.*' .. + 'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' .. + '\d PUSHNR 42.*' .. + '\d ECHO 1.*' .. + '\d RETURN void', + instr) + else + # condition false, function just returns + assert_match('TestCase' .. nr .. '.*' .. + 'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' .. + 'echo 42[ \n]*' .. + 'endif[ \n]*' .. + '\d RETURN void', + instr) + endif + + nr += 1 + endfor + + delete('Xdisassemble') +enddef + +def s:Execute() + execute 'help vim9.txt' + var cmd = 'help vim9.txt' + execute cmd + var tag = 'vim9.txt' + execute 'help ' .. tag +enddef + +def Test_disassemble_execute() + var res = execute('disass s:Execute') + assert_match('\<SNR>\d*_Execute\_s*' .. + "execute 'help vim9.txt'\\_s*" .. + '\d PUSHS "help vim9.txt"\_s*' .. + '\d EXECUTE 1\_s*' .. + "var cmd = 'help vim9.txt'\\_s*" .. + '\d PUSHS "help vim9.txt"\_s*' .. + '\d STORE $0\_s*' .. + 'execute cmd\_s*' .. + '\d LOAD $0\_s*' .. + '\d EXECUTE 1\_s*' .. + "var tag = 'vim9.txt'\\_s*" .. + '\d PUSHS "vim9.txt"\_s*' .. + '\d STORE $1\_s*' .. + "execute 'help ' .. tag\\_s*" .. + '\d\+ PUSHS "help "\_s*' .. + '\d\+ LOAD $1\_s*' .. + '\d\+ CONCAT size 2\_s*' .. + '\d\+ EXECUTE 1\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:OnlyRange() + :$ + :123 + :'m +enddef + +def Test_disassemble_range_only() + var res = execute('disass s:OnlyRange') + assert_match('\<SNR>\d*_OnlyRange\_s*' .. + ':$\_s*' .. + '\d EXECRANGE $\_s*' .. + ':123\_s*' .. + '\d EXECRANGE 123\_s*' .. + ':''m\_s*' .. + '\d EXECRANGE ''m\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:StoreRange() + var l = [1, 2] + l[0 : 1] = [7, 8] +enddef + +def Test_disassemble_store_range() + var res = execute('disass s:StoreRange') + assert_match('\<SNR>\d*_StoreRange\_s*' .. + 'var l = \[1, 2]\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d NEWLIST size 2\_s*' .. + '\d SETTYPE list<number>\_s*' .. + '\d STORE $0\_s*' .. + + 'l\[0 : 1] = \[7, 8]\_s*' .. + '\d\+ PUSHNR 7\_s*' .. + '\d\+ PUSHNR 8\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ PUSHNR 0\_s*' .. + '\d\+ PUSHNR 1\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ STORERANGE\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:Echomsg() + echomsg 'some' 'message' + echoconsole 'nothing' + echoerr 'went' .. 'wrong' + var local = 'window' + echowin 'in' local + :5echowin 'five' +enddef + +def Test_disassemble_echomsg() + var res = execute('disass s:Echomsg') + assert_match('\<SNR>\d*_Echomsg\_s*' .. + "echomsg 'some' 'message'\\_s*" .. + '\d PUSHS "some"\_s*' .. + '\d PUSHS "message"\_s*' .. + '\d ECHOMSG 2\_s*' .. + "echoconsole 'nothing'\\_s*" .. + '\d PUSHS "nothing"\_s*' .. + '\d ECHOCONSOLE 1\_s*' .. + "echoerr 'went' .. 'wrong'\\_s*" .. + '\d PUSHS "wentwrong"\_s*' .. + '\d ECHOERR 1\_s*' .. + "var local = 'window'\\_s*" .. + '\d\+ PUSHS "window"\_s*' .. + '\d\+ STORE $0\_s*' .. + "echowin 'in' local\\_s*" .. + '\d\+ PUSHS "in"\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ ECHOWINDOW 2\_s*' .. + ":5echowin 'five'\\_s*" .. + '\d\+ PUSHS "five"\_s*' .. + '\d\+ ECHOWINDOW 1 (5 sec)\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def SomeStringArg(arg: string) + echo arg +enddef + +def SomeAnyArg(arg: any) + echo arg +enddef + +def SomeStringArgAndReturn(arg: string): string + return arg +enddef + +def Test_display_func() + var res1 = execute('function SomeStringArg') + assert_match('.* def SomeStringArg(arg: string)\_s*' .. + '\d *echo arg.*' .. + ' *enddef', + res1) + + var res2 = execute('function SomeAnyArg') + assert_match('.* def SomeAnyArg(arg: any)\_s*' .. + '\d *echo arg\_s*' .. + ' *enddef', + res2) + + var res3 = execute('function SomeStringArgAndReturn') + assert_match('.* def SomeStringArgAndReturn(arg: string): string\_s*' .. + '\d *return arg\_s*' .. + ' *enddef', + res3) +enddef + +def Test_vim9script_forward_func() + var lines =<< trim END + vim9script + def FuncOne(): string + return FuncTwo() + enddef + def FuncTwo(): string + return 'two' + enddef + g:res_FuncOne = execute('disass FuncOne') + END + writefile(lines, 'Xdisassemble', 'D') + source Xdisassemble + + # check that the first function calls the second with DCALL + assert_match('\<SNR>\d*_FuncOne\_s*' .. + 'return FuncTwo()\_s*' .. + '\d DCALL <SNR>\d\+_FuncTwo(argc 0)\_s*' .. + '\d RETURN', + g:res_FuncOne) + + unlet g:res_FuncOne +enddef + +def s:ConcatStrings(): string + return 'one' .. 'two' .. 'three' +enddef + +def s:ComputeConst(): number + return 2 + 3 * 4 / 6 + 7 +enddef + +def s:ComputeConstParen(): number + return ((2 + 4) * (8 / 2)) / (3 + 4) +enddef + +def Test_simplify_const_expr() + var res = execute('disass s:ConcatStrings') + assert_match('<SNR>\d*_ConcatStrings\_s*' .. + "return 'one' .. 'two' .. 'three'\\_s*" .. + '\d PUSHS "onetwothree"\_s*' .. + '\d RETURN', + res) + + res = execute('disass s:ComputeConst') + assert_match('<SNR>\d*_ComputeConst\_s*' .. + 'return 2 + 3 \* 4 / 6 + 7\_s*' .. + '\d PUSHNR 11\_s*' .. + '\d RETURN', + res) + + res = execute('disass s:ComputeConstParen') + assert_match('<SNR>\d*_ComputeConstParen\_s*' .. + 'return ((2 + 4) \* (8 / 2)) / (3 + 4)\_s*' .. + '\d PUSHNR 3\>\_s*' .. + '\d RETURN', + res) +enddef + +def s:CallAppend() + eval "some text"->append(2) +enddef + +def Test_shuffle() + var res = execute('disass s:CallAppend') + assert_match('<SNR>\d*_CallAppend\_s*' .. + 'eval "some text"->append(2)\_s*' .. + '\d PUSHS "some text"\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d SHUFFLE 2 up 1\_s*' .. + '\d BCALL append(argc 2)\_s*' .. + '\d DROP\_s*' .. + '\d RETURN void', + res) +enddef + + +def s:SilentMessage() + silent echomsg "text" + silent! echoerr "error" +enddef + +def Test_silent() + var res = execute('disass s:SilentMessage') + assert_match('<SNR>\d*_SilentMessage\_s*' .. + 'silent echomsg "text"\_s*' .. + '\d CMDMOD silent\_s*' .. + '\d PUSHS "text"\_s*' .. + '\d ECHOMSG 1\_s*' .. + '\d CMDMOD_REV\_s*' .. + 'silent! echoerr "error"\_s*' .. + '\d CMDMOD silent!\_s*' .. + '\d PUSHS "error"\_s*' .. + '\d ECHOERR 1\_s*' .. + '\d CMDMOD_REV\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:SilentIf() + silent if 4 == g:five + silent elseif 4 == g:five + endif +enddef + +def Test_silent_if() + var res = execute('disass s:SilentIf') + assert_match('<SNR>\d*_SilentIf\_s*' .. + 'silent if 4 == g:five\_s*' .. + '\d\+ CMDMOD silent\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ LOADG g:five\_s*' .. + '\d\+ COMPAREANY ==\_s*' .. + '\d\+ CMDMOD_REV\_s*' .. + '\d\+ JUMP_IF_FALSE -> \d\+\_s*' .. + 'silent elseif 4 == g:five\_s*' .. + '\d\+ JUMP -> \d\+\_s*' .. + '\d\+ CMDMOD silent\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ LOADG g:five\_s*' .. + '\d\+ COMPAREANY ==\_s*' .. + '\d\+ CMDMOD_REV\_s*' .. + '\d\+ JUMP_IF_FALSE -> \d\+\_s*' .. + 'endif\_s*' .. + '\d\+ RETURN void', + res) +enddef + +def s:SilentFor() + silent for i in [0] + endfor +enddef + +def Test_silent_for() + var res = execute('disass s:SilentFor') + assert_match('<SNR>\d*_SilentFor\_s*' .. + 'silent for i in \[0\]\_s*' .. + '\d CMDMOD silent\_s*' .. + '\d STORE -1 in $0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d NEWLIST size 1\_s*' .. + '\d CMDMOD_REV\_s*' .. + '5 FOR $0 -> 8\_s*' .. + '\d STORE $2\_s*' .. + + 'endfor\_s*' .. + '\d JUMP -> 5\_s*' .. + '8 DROP\_s*' .. + '\d RETURN void\_s*', + res) +enddef + +def s:SilentWhile() + silent while g:not + endwhile +enddef + +def Test_silent_while() + var res = execute('disass s:SilentWhile') + assert_match('<SNR>\d*_SilentWhile\_s*' .. + 'silent while g:not\_s*' .. + '0 CMDMOD silent\_s*' .. + '\d LOADG g:not\_s*' .. + '\d COND2BOOL\_s*' .. + '\d CMDMOD_REV\_s*' .. + '\d WHILE $0 -> 6\_s*' .. + + 'endwhile\_s*' .. + '\d JUMP -> 0\_s*' .. + '6 RETURN void\_s*', + res) +enddef + +def s:SilentReturn(): string + silent return "done" +enddef + +def Test_silent_return() + var res = execute('disass s:SilentReturn') + assert_match('<SNR>\d*_SilentReturn\_s*' .. + 'silent return "done"\_s*' .. + '\d CMDMOD silent\_s*' .. + '\d PUSHS "done"\_s*' .. + '\d CMDMOD_REV\_s*' .. + '\d RETURN', + res) +enddef + +def s:Profiled(): string + # comment + echo "profiled" + # comment + var some = "some text" + return "done" +enddef + +def Test_profiled() + if !has('profile') + MissingFeature 'profile' + endif + var res = execute('disass profile s:Profiled') + assert_match('<SNR>\d*_Profiled\_s*' .. + '# comment\_s*' .. + 'echo "profiled"\_s*' .. + '\d PROFILE START line 2\_s*' .. + '\d PUSHS "profiled"\_s*' .. + '\d ECHO 1\_s*' .. + '# comment\_s*' .. + 'var some = "some text"\_s*' .. + '\d PROFILE END\_s*' .. + '\d PROFILE START line 4\_s*' .. + '\d PUSHS "some text"\_s*' .. + '\d STORE $0\_s*' .. + 'return "done"\_s*' .. + '\d PROFILE END\_s*' .. + '\d PROFILE START line 5\_s*' .. + '\d PUSHS "done"\_s*' .. + '\d\+ RETURN\_s*' .. + '\d\+ PROFILE END', + res) +enddef + +def Test_debugged() + var res = execute('disass debug s:Profiled') + assert_match('<SNR>\d*_Profiled\_s*' .. + '# comment\_s*' .. + 'echo "profiled"\_s*' .. + '\d DEBUG line 1-2 varcount 0\_s*' .. + '\d PUSHS "profiled"\_s*' .. + '\d ECHO 1\_s*' .. + '# comment\_s*' .. + 'var some = "some text"\_s*' .. + '\d DEBUG line 3-4 varcount 0\_s*' .. + '\d PUSHS "some text"\_s*' .. + '\d STORE $0\_s*' .. + 'return "done"\_s*' .. + '\d DEBUG line 5-5 varcount 1\_s*' .. + '\d PUSHS "done"\_s*' .. + '\d RETURN\_s*', + res) +enddef + +def s:ElseifConstant() + if g:value + echo "one" + elseif true + echo "true" + elseif false + echo "false" + endif + if 0 + echo "yes" + elseif 0 + echo "no" + endif +enddef + +def Test_debug_elseif_constant() + var res = execute('disass debug s:ElseifConstant') + assert_match('<SNR>\d*_ElseifConstant\_s*' .. + 'if g:value\_s*' .. + '0 DEBUG line 1-1 varcount 0\_s*' .. + '1 LOADG g:value\_s*' .. + '2 COND2BOOL\_s*' .. + '3 JUMP_IF_FALSE -> 8\_s*' .. + 'echo "one"\_s*' .. + '4 DEBUG line 2-2 varcount 0\_s*' .. + '5 PUSHS "one"\_s*' .. + '6 ECHO 1\_s*' .. + 'elseif true\_s*' .. + '7 JUMP -> 12\_s*' .. + '8 DEBUG line 3-3 varcount 0\_s*' .. + 'echo "true"\_s*' .. + '9 DEBUG line 4-4 varcount 0\_s*' .. + '10 PUSHS "true"\_s*' .. + '11 ECHO 1\_s*' .. + 'elseif false\_s*' .. + 'echo "false"\_s*' .. + 'endif\_s*' .. + 'if 0\_s*' .. + '12 DEBUG line 8-8 varcount 0\_s*' .. + 'echo "yes"\_s*' .. + 'elseif 0\_s*' .. + '13 DEBUG line 11-10 varcount 0\_s*' .. + 'echo "no"\_s*' .. + 'endif\_s*' .. + '14 RETURN void*', + res) +enddef + +def s:DebugElseif() + var b = false + if b + eval 1 + 0 + silent elseif !b + eval 2 + 0 + endif +enddef + +def Test_debug_elseif() + var res = execute('disass debug s:DebugElseif') + assert_match('<SNR>\d*_DebugElseif\_s*' .. + 'var b = false\_s*' .. + '0 DEBUG line 1-1 varcount 0\_s*' .. + '1 PUSH false\_s*' .. + '2 STORE $0\_s*' .. + + 'if b\_s*' .. + '3 DEBUG line 2-2 varcount 1\_s*' .. + '4 LOAD $0\_s*' .. + '5 JUMP_IF_FALSE -> 10\_s*' .. + + 'eval 1 + 0\_s*' .. + '6 DEBUG line 3-3 varcount 1\_s*' .. + '7 PUSHNR 1\_s*' .. + '8 DROP\_s*' .. + + 'silent elseif !b\_s*' .. + '9 JUMP -> 20\_s*' .. + '10 CMDMOD silent\_s*' .. + '11 DEBUG line 4-4 varcount 1\_s*' .. + '12 LOAD $0\_s*' .. + '13 INVERT -1 (!val)\_s*' .. + '14 CMDMOD_REV\_s*' .. + '15 JUMP_IF_FALSE -> 20\_s*' .. + + 'eval 2 + 0\_s*' .. + '16 DEBUG line 5-5 varcount 1\_s*' .. + '17 PUSHNR 2\_s*' .. + '18 DROP\_s*' .. + + 'endif\_s*' .. + '19 DEBUG line 6-6 varcount 1\_s*' .. + '20 RETURN void*', + res) +enddef + +def s:DebugFor() + echo "hello" + for a in [0] + echo a + endfor +enddef + +def Test_debug_for() + var res = execute('disass debug s:DebugFor') + assert_match('<SNR>\d*_DebugFor\_s*' .. + 'echo "hello"\_s*' .. + '0 DEBUG line 1-1 varcount 0\_s*' .. + '1 PUSHS "hello"\_s*' .. + '2 ECHO 1\_s*' .. + + 'for a in \[0\]\_s*' .. + '3 DEBUG line 2-2 varcount 0\_s*' .. + '4 STORE -1 in $0\_s*' .. + '5 PUSHNR 0\_s*' .. + '6 NEWLIST size 1\_s*' .. + '7 DEBUG line 2-2 varcount 3\_s*' .. + '8 FOR $0 -> 15\_s*' .. + '9 STORE $2\_s*' .. + + 'echo a\_s*' .. + '10 DEBUG line 3-3 varcount 3\_s*' .. + '11 LOAD $2\_s*' .. + '12 ECHO 1\_s*' .. + + 'endfor\_s*' .. + '13 DEBUG line 4-4 varcount 3\_s*' .. + '14 JUMP -> 7\_s*' .. + '15 DROP\_s*' .. + '16 RETURN void*', + res) +enddef + +def s:TryCatch() + try + echo "try" + catch /error/ + echo "caught" + endtry +enddef + +def Test_debug_try_catch() + var res = execute('disass debug s:TryCatch') + assert_match('<SNR>\d*_TryCatch\_s*' .. + 'try\_s*' .. + '0 DEBUG line 1-1 varcount 0\_s*' .. + '1 TRY catch -> 7, endtry -> 17\_s*' .. + 'echo "try"\_s*' .. + '2 DEBUG line 2-2 varcount 0\_s*' .. + '3 PUSHS "try"\_s*' .. + '4 ECHO 1\_s*' .. + 'catch /error/\_s*' .. + '5 DEBUG line 3-3 varcount 0\_s*' .. + '6 JUMP -> 17\_s*' .. + '7 DEBUG line 4-3 varcount 0\_s*' .. + '8 PUSH v:exception\_s*' .. + '9 PUSHS "error"\_s*' .. + '10 COMPARESTRING =\~\_s*' .. + '11 JUMP_IF_FALSE -> 17\_s*' .. + '12 CATCH\_s*' .. + 'echo "caught"\_s*' .. + '13 DEBUG line 4-4 varcount 0\_s*' .. + '14 PUSHS "caught"\_s*' .. + '15 ECHO 1\_s*' .. + 'endtry\_s*' .. + '16 DEBUG line 5-5 varcount 0\_s*' .. + '17 ENDTRY\_s*' .. + '\d\+ RETURN void', + res) +enddef + +func s:Legacy() dict + echo 'legacy' +endfunc + +def s:UseMember() + var d = {func: Legacy} + var v = d.func() +enddef + +def Test_disassemble_dict_stack() + var res = execute('disass s:UseMember') + assert_match('<SNR>\d*_UseMember\_s*' .. + 'var d = {func: Legacy}\_s*' .. + '\d PUSHS "func"\_s*' .. + '\d PUSHFUNC "<80><fd>R\d\+_Legacy"\_s*' .. + '\d NEWDICT size 1\_s*' .. + '\d SETTYPE dict<func(...): any>\_s*' .. + '\d STORE $0\_s*' .. + + 'var v = d.func()\_s*' .. + '\d LOAD $0\_s*' .. + '\d MEMBER func\_s*' .. + '\d PCALL top (argc 0)\_s*' .. + '\d PCALL end\_s*' .. + '\d CLEARDICT\_s*' .. + '\d\+ STORE $1\_s*' .. + '\d\+ RETURN void*', + res) +enddef + +def s:RetLegacy(): string + legacy return "yes" +enddef + +def Test_disassemble_return_legacy() + var res = execute('disass s:RetLegacy') + assert_match('<SNR>\d*_RetLegacy\_s*' .. + 'legacy return "yes"\_s*' .. + '\d CMDMOD legacy\_s*' .. + '\d EVAL legacy "yes"\_s*' .. + '\d CHECKTYPE string stack\[-1]\_s*' .. + '\d CMDMOD_REV\_s*' .. + '\d RETURN', + res) +enddef + +def s:EchoMessages() + echohl ErrorMsg | echom v:exception | echohl NONE +enddef + +def Test_disassemble_nextcmd() + # splitting commands and removing trailing blanks should not change the line + var res = execute('disass s:EchoMessages') + assert_match('<SNR>\d*_EchoMessages\_s*' .. + 'echohl ErrorMsg | echom v:exception | echohl NONE', + res) +enddef + +def Test_disassemble_after_reload() + var lines =<< trim END + vim9script + if exists('g:ThisFunc') + finish + endif + var name: any + def g:ThisFunc(): number + g:name = name + return 0 + enddef + def g:ThatFunc(): number + name = g:name + return 0 + enddef + END + lines->writefile('Xreload.vim', 'D') + + source Xreload.vim + g:ThisFunc() + g:ThatFunc() + + source Xreload.vim + var res = execute('disass g:ThisFunc') + assert_match('ThisFunc\_s*' .. + 'g:name = name\_s*' .. + '\d LOADSCRIPT \[deleted\] from .*/Xreload.vim\_s*' .. + '\d STOREG g:name\_s*' .. + 'return 0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d RETURN\_s*', + res) + + res = execute('disass g:ThatFunc') + assert_match('ThatFunc\_s*' .. + 'name = g:name\_s*' .. + '\d LOADG g:name\_s*' .. + '\d STORESCRIPT \[deleted\] in .*/Xreload.vim\_s*' .. + 'return 0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d RETURN\_s*', + res) + + delfunc g:ThisFunc + delfunc g:ThatFunc +enddef + +def s:MakeString(x: number): string + return $"x={x} x^2={x * x}" +enddef + +def Test_disassemble_string_interp() + var instr = execute('disassemble s:MakeString') + assert_match('MakeString\_s*' .. + 'return $"x={x} x^2={x \* x}"\_s*' .. + '0 PUSHS "x="\_s*' .. + '1 LOAD arg\[-1\]\_s*' .. + '2 2STRING stack\[-1\]\_s*' .. + '3 PUSHS " x^2="\_s*' .. + '4 LOAD arg\[-1\]\_s*' .. + '5 LOAD arg\[-1\]\_s*' .. + '6 OPNR \*\_s*' .. + '7 2STRING stack\[-1\]\_s*' .. + '8 CONCAT size 4\_s*' .. + '9 RETURN\_s*', + instr) +enddef + +def BitShift() + var a = 1 << 2 + var b = 8 >> 1 + var c = a << b + var d = b << a +enddef + +def Test_disassemble_bitshift() + var instr = execute('disassemble BitShift') + assert_match('BitShift\_s*' .. + 'var a = 1 << 2\_s*' .. + '0 STORE 4 in $0\_s*' .. + 'var b = 8 >> 1\_s*' .. + '1 STORE 4 in $1\_s*' .. + 'var c = a << b\_s*' .. + '2 LOAD $0\_s*' .. + '3 LOAD $1\_s*' .. + '4 OPNR <<\_s*' .. + '5 STORE $2\_s*' .. + 'var d = b << a\_s*' .. + '6 LOAD $1\_s*' .. + '7 LOAD $0\_s*' .. + '8 OPNR <<\_s*' .. + '9 STORE $3\_s*' .. + '10 RETURN void', instr) +enddef + +def s:OneDefer() + defer delete("file") +enddef + +def Test_disassemble_defer() + var instr = execute('disassemble s:OneDefer') + assert_match('OneDefer\_s*' .. + 'defer delete("file")\_s*' .. + '\d PUSHFUNC "delete"\_s*' .. + '\d PUSHS "file"\_s*' .. + '\d DEFER 1 args\_s*' .. + '\d RETURN\_s*', + instr) +enddef + +def Test_disassemble_class_function() + var lines =<< trim END + vim9script + + class Cl + static def Fc(): string + return "x" + enddef + endclass + + g:instr = execute('disassemble Cl.Fc') + END + v9.CheckScriptSuccess(lines) + assert_match('Fc\_s*' .. + 'return "x"\_s*' .. + '\d PUSHS "x"\_s*' .. + '\d RETURN\_s*', + g:instr) + + lines =<< trim END + vim9script + + class Cl + def Fo(): string + return "y" + enddef + endclass + + g:instr = execute('disassemble Cl.Fo') + END + v9.CheckScriptSuccess(lines) + assert_match('Fo\_s*' .. + 'return "y"\_s*' .. + '\d PUSHS "y"\_s*' .. + '\d RETURN\_s*', + g:instr) + + unlet g:instr +enddef + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker |