From d04a827f05a175a0e1d0d32340e0b2ff9d1cd6af Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Sat, 23 Feb 2019 22:13:19 +0100 Subject: [PATCH] Add fate code pretty printer. Add symbol table to binary. Add tests of rundtrip serialization and deserialization. --- Makefile | 20 +++ include/aeb_fate_opcodes.hrl | 12 +- src/aeb_fate_asm.erl | 334 ++++++++++++++++++++++++++++++++--- src/aeb_fate_asm_scan.xrl | 3 +- src/aeb_fate_opcodes.erl | 14 +- test/aeb_data_test.erl | 12 ++ test/aeb_fate_asm_test.erl | 66 +++++++ test/aeb_serialize_test.erl | 82 +++++++++ test/asm_code/map.fate | 3 +- test/asm_code/tuple.fate | 3 +- 10 files changed, 506 insertions(+), 43 deletions(-) create mode 100644 Makefile create mode 100644 test/aeb_data_test.erl create mode 100644 test/aeb_fate_asm_test.erl create mode 100644 test/aeb_serialize_test.erl diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d04708 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ + + +REBAR ?= rebar3 + +all: local + +local: + @$(REBAR) as local release + +console: + @$(REBAR) as local shell + +clean: + @$(REBAR) clean + +distclean: clean + @rm -rf _build/ + +test: local + @$(REBAR) as local eunit diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index 56de42c..90f5b6a 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -104,15 +104,15 @@ -define('MAP_LOOKUPD' , 16#69). -define('SWITCH_V2' , 16#6a). -define('SWITCH_V3' , 16#6b). --define('SWITCH_V4' , 16#6c). --define('SWITCH_V5' , 16#6d). --define('BITS_ALL_N' , 16#6e). --define('BITS_NONEA' , 16#6f). --define('BITS_ALLA' , 16#70). +-define('SWITCH_VN' , 16#6c). +-define('BITS_ALL_N' , 16#6d). +-define('BITS_NONEA' , 16#6e). +-define('BITS_ALLA' , 16#6f). +-define('DUPA' , 16#70). -define('INCA' , 16#71). -define('DECA' , 16#72). -define('POPA' , 16#73). --define('DUPA' , 16#74). + -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 40d235a..9bbb38e 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -53,10 +53,12 @@ , function_call/1 , pp/1 , read_file/1 + , to_asm/1 , to_hexstring/1 ]). -include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). +-include_lib("aebytecode/include/aeb_fate_data.hrl"). -define(HASH_BYTES, 32). assemble_file(InFile, OutFile, Options) -> @@ -91,20 +93,215 @@ to_data([{boolean,_line, Bool}|Rest]) -> to_data([{hash,_line, Hash}|Rest]) -> {Hash, Rest}. +pp(FateCode) -> + Listing = to_asm(FateCode), + io_lib:format("~ts~n",[Listing]). -pp(Asm) -> - Listing = format(Asm), - io:format("~s~n", [Listing]). -format(Asm) -> format(Asm, 0). +to_asm(#{ functions := Functions + , symbols := Symbols} = _FateCode) -> + lists:flatten( + io_lib:format("~s",[ + [format(lookup(Name, Symbols), + Sig, + lists:sort(maps:to_list(CodeMap)), + Symbols) || + {Name, {Sig, CodeMap}} <- maps:to_list(Functions)]])). -format([{comment, Comment} | Rest], Address) -> - ";; " ++ Comment ++ "\n" ++ format(Rest, Address); -format([Mnemonic | Rest], Address) -> - _Op = aeb_fate_opcodes:m_to_op(Mnemonic), - " " ++ atom_to_list(Mnemonic) ++ "\n" - ++ format(Rest, Address + 1); -format([],_) -> []. + +format(Name, Sig, BBs, Symbols) -> + [ "FUNCTION " + , Name + , format_sig(Sig) + , "\n" + , format_bbs(BBs, Symbols)]. + +format_sig({Args, RetType}) -> + [ "( " + , format_arg_types(Args) + , ") : " + , format_type(RetType)]. + +format_arg_types([]) -> ""; +format_arg_types([T]) -> format_type(T); +format_arg_types([T|Ts]) -> + [format_type(T) + , ", " + , format_arg_types(Ts)]. + +format_arg({immediate, I}) -> + aeb_fate_data:format(I); +format_arg({arg, N}) -> io_lib:format("arg~p", [N]); +format_arg({var, N}) -> io_lib:format("var~p", [N]); +format_arg({stack, 0}) -> "a"; +format_arg({stack, N}) -> io_lib:format("a~p", [N]). + + +format_type(T) -> + %% TODO: Limit to ok types. + io_lib:format("~p", [T]). + +format_bbs([], _) -> + []; +format_bbs([{BB, Code}|Rest], Symbols) -> + [ io_lib:format(" ;; BB : ~p~n", [BB]) + , format_code(Code, Symbols) + | format_bbs(Rest, Symbols)]. + +format_code([], _) -> + ""; +format_code([Op|Rest], Symbols) -> + [" ", + format_op(Op, Symbols), + "\n", + format_code(Rest, Symbols)]. + +format_op('RETURN', _) -> "RETURN"; +format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)]; +format_op({'CALL', {immediate, Function}}, Symbols) -> + ["CALL ", lookup(Function, Symbols)]; +format_op({'CALL_T', {immediate, Function}}, Symbols) -> + ["CALL_T ", lookup(Function, Symbols)]; +format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) -> + ["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; +format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) -> + ["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)]; +format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) -> + ["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; +format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) -> + ["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)]; +format_op({'JUMP', {immediate, BB}}, _) -> + ["JUMP ", io_lib:format("~p", [BB])]; +format_op({'JUMPIF', Arg, {immediate, BB}}, _) -> + ["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])]; +format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) -> + ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2]; +format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) -> + ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3]; +format_op({'SWITCH_VN', Variant, BBs}, _) -> + ["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]]; +format_op({'PUSH', Arg0}, _) -> + ["PUSH ", format_arg(Arg0)]; +format_op('INCA', _) -> "INCA"; +format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)]; +format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)]; +format_op('DECA', _) -> "DECA"; +format_op({'ADD', Dest, Left, Right}, _) -> + ["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'SUB', Dest, Left, Right}, _) -> + ["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'MUL', Dest, Left, Right}, _) -> + ["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'DIV', Dest, Left, Right}, _) -> + ["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'MOD', Dest, Left, Right}, _) -> + ["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'POW', Dest, Left, Right}, _) -> + ["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'LT', Dest, Left, Right}, _) -> + ["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'GT', Dest, Left, Right}, _) -> + ["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'ELT', Dest, Left, Right}, _) -> + ["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'EGT', Dest, Left, Right}, _) -> + ["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'EQ', Dest, Left, Right}, _) -> + ["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'NEQ', Dest, Left, Right}, _) -> + ["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'AND', Dest, Left, Right}, _) -> + ["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'OR', Dest, Left, Right}, _) -> + ["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'NOT', Dest, Name}, _) -> + ["NOT ", format_arg(Dest), " ", format_arg(Name)]; +format_op({'TUPLE', {immediate, Size}}, _) -> + ["TUPLE ", io_lib:format("~p", [Size])]; +format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) -> + [ "ELEMENT " + , io_lib:format("~p ", [Type]) + , format_arg(Dest), " " + , format_arg(Which), " " + , format_arg(Tuple)]; +format_op({'MAP_EMPTY', Dest}, _) -> + ["MAP_EMPTY ", format_arg(Dest)]; +format_op({'MAP_LOOKUP', Dest, Map, Key}, _) -> + ["MAP_LOOKUP ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_DELETE', Dest, Map, Key}, _) -> + ["MAP_DELETE ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) -> + ["MAP_LOOKUPD ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)]; +format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) -> + ["MAP_UPDATE ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)]; +format_op({'MAP_MEMBER', Dest, Map, Key}, _) -> + ["MAP_MEMBER ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_FROM_LIST', Dest, List}, _) -> + ["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)]; +format_op({'NIL', Dest}, _) -> + ["NIL ", format_arg(Dest)]; +format_op({'IS_NIL', Dest, List}, _) -> + ["IS_NIL ", format_arg(Dest), " ", format_arg(List)]; +format_op({'CONS', Dest, Hd, Tl}, _) -> + ["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)]; +format_op({'HD', Dest, List}, _) -> + ["HD ", format_arg(Dest), " ", format_arg(List)]; +format_op({'TL', Dest, List}, _) -> + ["TL ", format_arg(Dest), " ", format_arg(List)]; +format_op({'LENGTH', Dest, List}, _) -> + ["LENGTH ", format_arg(Dest), " ", format_arg(List)]; +format_op({'STR_EQ', Dest, Str1, Str2}, _) -> + ["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; +format_op({'STR_JOIN', Dest, Str1, Str2}, _) -> + ["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; +format_op({'INT_TO_STR', Dest, Str}, _) -> + ["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'ADDR_TO_STR', Dest, Str}, _) -> + ["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'STR_REVERSE', Dest, Str}, _) -> + ["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'INT_TO_ADDR', Dest, Str}, _) -> + ["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) -> + ["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)]; +format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) -> + ["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)]; +format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) -> + ["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " " + , format_arg(TagA), " ", format_arg(ElementsA)]; +format_op('BITS_NONEA', _) -> "BITS_NONEA "; +format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)]; +format_op('BITS_ALLA', _) -> "BITS_ALLA"; +format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)]; +format_op({'BITS_ALL_N', To, N}, _) -> + ["BITS_ALL_N ", format_arg(To), " ", format_arg(N)]; +format_op({'BITS_SET', To, Bits, Bit}, _) -> + ["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_CLEAR', To, Bits, Bit}, _) -> + ["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_TEST', To, Bits, Bit}, _) -> + ["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_SUM', To, Bits}, _) -> + ["BITS_SUM ", format_arg(To), " ", format_arg(Bits)]; +format_op({'BITS_OR', To, Bits, Bit}, _) -> + ["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_AND', To, Bits, Bit}, _) -> + ["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_DIFF', To, Bits, Bit}, _) -> + ["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op('DUPA', _) -> "DUPA"; +format_op({'DUP', {immediate, N}}, _) -> + ["DUP ", io_lib:format("~p", [N])]; +format_op({'POP', Dest}, _) -> + ["POP ", format_arg(Dest)]; +format_op({'STORE', Var, What}, _) -> + ["STORE ", format_arg(Var), " ", format_arg(What)]; +format_op('NOP', _) -> "NOP". read_file(Filename) -> @@ -126,6 +323,14 @@ asm_to_bytecode(AssemblerCode, Options) -> }, [], Options), ByteList = serialize(Env), + Signatures = serialize_sigs(Env), + SymbolTable = serialize_symbol_table(Env), + Annotatations = serialize_annotations(Env), + ByteCode = << (aeb_rlp:encode(list_to_binary(ByteList)))/binary, + (aeb_rlp:encode(list_to_binary(Signatures)))/binary, + (aeb_rlp:encode(SymbolTable))/binary, + (aeb_rlp:encode(list_to_binary(Annotatations)))/binary + >>, case proplists:lookup(pp_hex_string, Options) of {pp_hex_string, true} -> @@ -134,15 +339,25 @@ asm_to_bytecode(AssemblerCode, Options) -> ok end, - {Env, list_to_binary(ByteList)}. + {Env, ByteCode}. + +bytecode_to_fate_code(Bytes, _Options) -> + {ByteCode, Rest1} = aeb_rlp:decode_one(Bytes), + {Signatures, Rest2} = aeb_rlp:decode_one(Rest1), + {SymbolTable, Rest3} = aeb_rlp:decode_one(Rest2), + {Annotations, <<>>} = aeb_rlp:decode_one(Rest3), + + Env1 = deserialize(ByteCode, #{ function => none + , bb => 0 + , current_bb_code => [] + , functions => #{} + , code => #{} + }), + Env2 = deserialize_signatures(Signatures, Env1), + Env3 = deserialize_symbols(SymbolTable, Env2), + Env4 = deserialize_annotations(Annotations, Env3), + Env4. -bytecode_to_fate_code(ByteCode,_Options) -> - deserialize(ByteCode, #{ function => none - , bb => 0 - , current_bb_code => [] - , functions => #{} - , code => #{} - }). deserialize(<>, #{ function := none @@ -222,6 +437,21 @@ deserialize_op(?ELEMENT, Rest, Code) -> , {Modifier1, Arg1} , {Modifier2, Arg2}} | Code]}; +deserialize_op(?SWITCH_VN, Rest, Code) -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + case aeb_fate_encoding:deserialize_one(Rest3) of + {N, Rest4} when is_integer(N), N >= 0 -> + Modifier0 = bits_to_modifier(ArgType band 2#11), + immediate = bits_to_modifier((ArgType bsr 2) band 2#11), + {BBs, Rest5} = deserialize_n(N, Rest4), + {Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN) + , {Modifier0, Arg0} + , {immediate, N} + , list_to_tuple(BBs)} + | Code]}; + _ -> exit(bad_argument_to_switch_vn) + end; deserialize_op(Op, Rest, Code) -> OpName = aeb_fate_opcodes:mnemonic(Op), case aeb_fate_opcodes:args(Op) of @@ -270,10 +500,41 @@ deserialize_op(Op, Rest, Code) -> | Code]} end. +deserialize_n(N, Binary) -> + deserialize_n(N, Binary, []). + +deserialize_n(0, Binary, Acc) -> + {lists:reverse(Acc), Binary}; +deserialize_n(N, Binary, Acc) -> + {Value, Rest} = aeb_fate_encoding:deserialize_one(Binary), + deserialize_n(N-1, Rest, [Value|Acc]). + + + +deserialize_signatures(_Signatures, Env) -> Env. + +deserialize_symbols(Table, Env) -> + ?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table), + Env#{symbols => SymbolTable}. + +deserialize_annotations(_Annotations, Env) -> Env. + +serialize_sigs(_Env) -> []. + +serialize_symbol_table(#{ symbols := Symbols }) -> + aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)). + +serialize_annotations(_Env) -> + []. + + + + serialize(#{functions := Functions} =_Env) -> + %% Sort the functions oon name to get a canonical serialisation. Code = [[?FUNCTION, Name, serialize_signature(Sig), C] || - {Name, {Sig, C}} <- maps:to_list(Functions)], + {Name, {Sig, C}} <- lists:sort(maps:to_list(Functions))], serialize_code(lists:flatten(Code)). @@ -335,13 +596,41 @@ serialize_code([ {Arg0Type, Arg0} | Rest]) -> serialize_code([ ?ELEMENT , ResType | Rest]) -> - [?ELEMENT, + [?ELEMENT, serialize_type(ResType) | serialize_code(Rest)]; +serialize_code([ ?SWITCH_VN + , {Arg0Type, Arg0} + , {immediate, N} + | Rest]) when is_integer(N), N >= 0 -> + ArgSpec = + modifier_bits(Arg0Type) bor + (modifier_bits(immediate) bsl 2), + {Serialization, Rest2} = serialize_n_ints(N, Rest), + [?SWITCH_VN + , ArgSpec + , serialize_data(Arg0Type, Arg0) + , serialize_data(immediate, N) + | Serialization] ++ serialize_code(Rest2); serialize_code([B|Rest]) -> [B | serialize_code(Rest)]; serialize_code([]) -> []. +serialize_n_ints(N, Rest) -> + serialize_n_ints(N, Rest, []). + +serialize_n_ints(0, Rest, Acc) -> + %% Acc is a list of binaries. + {lists:reverse(Acc), Rest}; +serialize_n_ints(N, [Int|Rest], Acc) when is_integer(Int), Int >= 0 -> + serialize_n_ints(N - 1, Rest, [aeb_fate_encoding:serialize(Int)|Acc]); +serialize_n_ints(_, [], _) -> + exit(not_enough_bbs_for_switch_vn); +serialize_n_ints(_, _, _) -> + exit(bad_bbs_value_for_switch_vn). + + + %% 00 : stack/unused (depending on instruction) %% 01 : argN %% 10 : varN @@ -520,3 +809,6 @@ insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> , Hash => Id}}} end. + +lookup(Name, Symbols) -> + maps:get(Name, Symbols, Name). diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl index fce9f27..5833315 100644 --- a/src/aeb_fate_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -40,8 +40,7 @@ JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}. SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}. -SWITCH_V4 : {token, {mnemonic, TokenLine, 'SWITCH_V4'}}. -SWITCH_V5 : {token, {mnemonic, TokenLine, 'SWITCH_V5'}}. +SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}. PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. DUP : {token, {mnemonic, TokenLine, 'DUP'}}. diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl index 9240793..84d93f7 100644 --- a/src/aeb_fate_opcodes.erl +++ b/src/aeb_fate_opcodes.erl @@ -132,8 +132,7 @@ mnemonic(?RETURNR) -> 'RETURNR' ; mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ; mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ; mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ; -mnemonic(?SWITCH_V4) -> 'SWITCH_V4' ; -mnemonic(?SWITCH_V5) -> 'SWITCH_V5' ; +mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ; mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ; mnemonic(?FUNCTION) -> 'FUNCTION' ; mnemonic(?EXTEND) -> 'EXTEND'. @@ -246,9 +245,7 @@ m_to_op('RETURNR') -> ?RETURNR ; m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; m_to_op('SWITCH_V2') -> ?SWITCH_V2 ; m_to_op('SWITCH_V3') -> ?SWITCH_V3 ; -m_to_op('SWITCH_V4') -> ?SWITCH_V4 ; -m_to_op('SWITCH_V5') -> ?SWITCH_V5 ; -m_to_op('BITS_ALL_N') -> ?BITS_ALL_N ; +m_to_op('SWITCH_VN') -> ?SWITCH_VN ; m_to_op('FUNCTION') -> ?FUNCTION ; m_to_op('EXTEND') -> ?EXTEND. @@ -345,9 +342,7 @@ args(?VARIANT) -> 4; args(?MAP_UPDATE) -> 4; args(?MAP_LOOKUPD) -> 4; -args(?SWITCH_V4) -> 5; - -args(?SWITCH_V5) -> 6; +args(?SWITCH_VN) -> 2; args(_) -> 0. %% TODO do not allow this @@ -361,8 +356,7 @@ end_bb(?CALL_R) -> true; end_bb(?CALL_TR) -> true; end_bb(?SWITCH_V2) -> true; end_bb(?SWITCH_V3) -> true; -end_bb(?SWITCH_V4) -> true; -end_bb(?SWITCH_V5) -> true; +end_bb(?SWITCH_VN) -> true; end_bb(?ABORT) -> true; end_bb(?EXIT) -> true; diff --git a/test/aeb_data_test.erl b/test/aeb_data_test.erl new file mode 100644 index 0000000..7c25932 --- /dev/null +++ b/test/aeb_data_test.erl @@ -0,0 +1,12 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate data +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_data_test). + +-include_lib("eunit/include/eunit.hrl"). + +format_integer_test() -> + "0" = aeb_fate_data:format(0). diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl new file mode 100644 index 0000000..f6cf17e --- /dev/null +++ b/test/aeb_fate_asm_test.erl @@ -0,0 +1,66 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate serialization +%%% +%%% To run: +%%% TEST=aeb_fate_asm_test rebar3 eunit +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_fate_asm_test). + +-include_lib("eunit/include/eunit.hrl"). + +asm_path() -> + filename:join(code:lib_dir(aebytecode, test), "asm_code"). + + +file_path(File) -> + filename:join(asm_path(), File) ++ ".fate". + +read_file(File) -> + FilePath = file_path(File), + Asm = aeb_fate_asm:read_file(FilePath), + Asm. + +assemble(Asm) -> + {Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, []), + {Env, BC}. + +disassemble(BC) -> + aeb_fate_asm:bytecode_to_fate_code(BC, []). + + +asm_disasm_idenity_test() -> + check_roundtrip(identity). + +asm_disasm_files_test_() -> + [{lists:flatten(io_lib:format("~p", [X])), + fun() -> check_roundtrip(X) end} + || X <- sources()]. + +sources() -> + [ "arith" + , "bool" + , "comp" + , "jumpif" + , "map" + , "memory" + , "remote" + , "test" + , "tuple" + ]. + +check_roundtrip(File) -> + AssemblerCode = read_file(File), + {Env, ByteCode} = assemble(AssemblerCode), + FateCode = disassemble(ByteCode), + DissasmCode = aeb_fate_asm:to_asm(FateCode), + io:format("~s~n", [AssemblerCode]), + io:format("~s~n", [DissasmCode]), + {Env2, ByteCode2} = assemble(DissasmCode), + ?assertEqual(ByteCode, ByteCode2). + + + diff --git a/test/aeb_serialize_test.erl b/test/aeb_serialize_test.erl new file mode 100644 index 0000000..dcbe4e2 --- /dev/null +++ b/test/aeb_serialize_test.erl @@ -0,0 +1,82 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate serialization +%%% +%%% To run: +%%% TEST=aeb_serialize_test rebar3 eunit +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_serialize_test). + +-include_lib("eunit/include/eunit.hrl"). + +serialize_integer_test() -> + <<0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(0)), + <<2>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1)), + <<126>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(63)), + <<111, 0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(64)), + <<111,130,255,255>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(65535 + 64)), + <<111,184,129,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> = + aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1 bsl 1024 + 64)). + +serialize_deserialize_test_() -> + [{lists:flatten(io_lib:format("~p", [X])), + fun() -> + ?assertEqual(X, + aeb_fate_encoding:deserialize(aeb_fate_encoding:serialize(X))) + end} + || X <- sources()]. + +make_int_list(N) -> [aeb_fate_data:make_integer(I) || I <- lists:seq(1, N)]. + +sources() -> + FortyTwo = aeb_fate_data:make_integer(42), + Unit = aeb_fate_data:make_unit(), + True = aeb_fate_data:make_boolean(true), + False = aeb_fate_data:make_boolean(false), + Nil = aeb_fate_data:make_list([]), + EmptyString = aeb_fate_data:make_string(""), + EmptyMap = aeb_fate_data:make_map(#{}), + [aeb_fate_data:make_integer(0), + aeb_fate_data:make_integer(1), + True, False, Unit, Nil, EmptyString, EmptyMap, + aeb_fate_data:make_list([True]), + aeb_fate_data:make_address( + <<0,1,2,3,4,5,6,7,8,9, + 0,1,2,3,4,5,6,7,8,9, + 0,1,2,3,4,5,6,7,8,9, + 1,2>>), + aeb_fate_data:make_string(<<"Hello">>), + aeb_fate_data:make_string( + <<"0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789">>), %% Magic concat 80 char string. + aeb_fate_data:make_tuple({True, FortyTwo}), + aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))), + aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}), + aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}), + aeb_fate_data:make_list(make_int_list(3)), + aeb_fate_data:make_integer(-65), + aeb_fate_data:make_integer(65), + aeb_fate_data:make_integer(-32432847932847928374983), + aeb_fate_data:make_bits(0), + aeb_fate_data:make_bits(1), + aeb_fate_data:make_bits(-1), + aeb_fate_data:make_list(make_int_list(65)), + aeb_fate_data:make_variant(2, 0, {FortyTwo}), + aeb_fate_data:make_variant(2, 1, {}), + aeb_fate_data:make_list([aeb_fate_data:make_variant(3, 0, {})]), + aeb_fate_data:make_variant(255, 254, {}), + aeb_fate_data:make_variant(5, 3, {aeb_fate_data:make_boolean(true), + aeb_fate_data:make_list(make_int_list(3)), + aeb_fate_data:make_string(<<"foo">>)}) + + ]. diff --git a/test/asm_code/map.fate b/test/asm_code/map.fate index beb1589..d841ea3 100644 --- a/test/asm_code/map.fate +++ b/test/asm_code/map.fate @@ -22,8 +22,7 @@ FUNCTION map_member({map, integer, boolean}, integer):boolean FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean} MAP_DELETE a arg0 arg1 RETURN - - + FUNCTION map_member({map, integer, boolean}, integer) : boolean MAP_MEMBER a arg0 arg1 RETURN diff --git a/test/asm_code/tuple.fate b/test/asm_code/tuple.fate index 573166b..74a265d 100644 --- a/test/asm_code/tuple.fate +++ b/test/asm_code/tuple.fate @@ -1,4 +1,3 @@ -;;CONTRACT tuple FUNCTION make_0tuple():{tuple, []} TUPLE 0 RETURN @@ -18,7 +17,7 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer): PUSH arg4 TUPLE 5 RETURN - + FUNCTION element1(integer, integer): integer PUSH arg0 PUSH arg1