Add fate code pretty printer. Add symbol table to binary. Add tests of rundtrip serialization and deserialization.
This commit is contained in:
parent
7e26912bf9
commit
d04a827f05
20
Makefile
Normal file
20
Makefile
Normal file
@ -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
|
@ -104,15 +104,15 @@
|
|||||||
-define('MAP_LOOKUPD' , 16#69).
|
-define('MAP_LOOKUPD' , 16#69).
|
||||||
-define('SWITCH_V2' , 16#6a).
|
-define('SWITCH_V2' , 16#6a).
|
||||||
-define('SWITCH_V3' , 16#6b).
|
-define('SWITCH_V3' , 16#6b).
|
||||||
-define('SWITCH_V4' , 16#6c).
|
-define('SWITCH_VN' , 16#6c).
|
||||||
-define('SWITCH_V5' , 16#6d).
|
-define('BITS_ALL_N' , 16#6d).
|
||||||
-define('BITS_ALL_N' , 16#6e).
|
-define('BITS_NONEA' , 16#6e).
|
||||||
-define('BITS_NONEA' , 16#6f).
|
-define('BITS_ALLA' , 16#6f).
|
||||||
-define('BITS_ALLA' , 16#70).
|
-define('DUPA' , 16#70).
|
||||||
-define('INCA' , 16#71).
|
-define('INCA' , 16#71).
|
||||||
-define('DECA' , 16#72).
|
-define('DECA' , 16#72).
|
||||||
-define('POPA' , 16#73).
|
-define('POPA' , 16#73).
|
||||||
-define('DUPA' , 16#74).
|
|
||||||
|
|
||||||
-define('FUNCTION' , 16#fe).
|
-define('FUNCTION' , 16#fe).
|
||||||
-define('EXTEND' , 16#ff).
|
-define('EXTEND' , 16#ff).
|
||||||
|
@ -53,10 +53,12 @@
|
|||||||
, function_call/1
|
, function_call/1
|
||||||
, pp/1
|
, pp/1
|
||||||
, read_file/1
|
, read_file/1
|
||||||
|
, to_asm/1
|
||||||
, to_hexstring/1
|
, to_hexstring/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
|
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
|
||||||
|
-include_lib("aebytecode/include/aeb_fate_data.hrl").
|
||||||
-define(HASH_BYTES, 32).
|
-define(HASH_BYTES, 32).
|
||||||
|
|
||||||
assemble_file(InFile, OutFile, Options) ->
|
assemble_file(InFile, OutFile, Options) ->
|
||||||
@ -91,20 +93,215 @@ to_data([{boolean,_line, Bool}|Rest]) ->
|
|||||||
to_data([{hash,_line, Hash}|Rest]) ->
|
to_data([{hash,_line, Hash}|Rest]) ->
|
||||||
{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(Name, Sig, BBs, Symbols) ->
|
||||||
format([Mnemonic | Rest], Address) ->
|
[ "FUNCTION "
|
||||||
_Op = aeb_fate_opcodes:m_to_op(Mnemonic),
|
, Name
|
||||||
" " ++ atom_to_list(Mnemonic) ++ "\n"
|
, format_sig(Sig)
|
||||||
++ format(Rest, Address + 1);
|
, "\n"
|
||||||
format([],_) -> [].
|
, 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) ->
|
read_file(Filename) ->
|
||||||
@ -126,6 +323,14 @@ asm_to_bytecode(AssemblerCode, Options) ->
|
|||||||
}, [], Options),
|
}, [], Options),
|
||||||
|
|
||||||
ByteList = serialize(Env),
|
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
|
case proplists:lookup(pp_hex_string, Options) of
|
||||||
{pp_hex_string, true} ->
|
{pp_hex_string, true} ->
|
||||||
@ -134,15 +339,25 @@ asm_to_bytecode(AssemblerCode, Options) ->
|
|||||||
ok
|
ok
|
||||||
end,
|
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:8, A, B, C, D, Rest/binary>>,
|
deserialize(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
|
||||||
#{ function := none
|
#{ function := none
|
||||||
@ -222,6 +437,21 @@ deserialize_op(?ELEMENT, Rest, Code) ->
|
|||||||
, {Modifier1, Arg1}
|
, {Modifier1, Arg1}
|
||||||
, {Modifier2, Arg2}}
|
, {Modifier2, Arg2}}
|
||||||
| Code]};
|
| Code]};
|
||||||
|
deserialize_op(?SWITCH_VN, Rest, Code) ->
|
||||||
|
<<ArgType:8, Rest2/binary>> = 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) ->
|
deserialize_op(Op, Rest, Code) ->
|
||||||
OpName = aeb_fate_opcodes:mnemonic(Op),
|
OpName = aeb_fate_opcodes:mnemonic(Op),
|
||||||
case aeb_fate_opcodes:args(Op) of
|
case aeb_fate_opcodes:args(Op) of
|
||||||
@ -270,10 +500,41 @@ deserialize_op(Op, Rest, Code) ->
|
|||||||
| Code]}
|
| Code]}
|
||||||
end.
|
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) ->
|
serialize(#{functions := Functions} =_Env) ->
|
||||||
|
%% Sort the functions oon name to get a canonical serialisation.
|
||||||
Code = [[?FUNCTION, Name, serialize_signature(Sig), C] ||
|
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)).
|
serialize_code(lists:flatten(Code)).
|
||||||
|
|
||||||
|
|
||||||
@ -335,13 +596,41 @@ serialize_code([ {Arg0Type, Arg0} | Rest]) ->
|
|||||||
serialize_code([ ?ELEMENT
|
serialize_code([ ?ELEMENT
|
||||||
, ResType
|
, ResType
|
||||||
| Rest]) ->
|
| Rest]) ->
|
||||||
[?ELEMENT,
|
[?ELEMENT,
|
||||||
serialize_type(ResType)
|
serialize_type(ResType)
|
||||||
| serialize_code(Rest)];
|
| 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]) ->
|
serialize_code([B|Rest]) ->
|
||||||
[B | serialize_code(Rest)];
|
[B | serialize_code(Rest)];
|
||||||
serialize_code([]) -> [].
|
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)
|
%% 00 : stack/unused (depending on instruction)
|
||||||
%% 01 : argN
|
%% 01 : argN
|
||||||
%% 10 : varN
|
%% 10 : varN
|
||||||
@ -520,3 +809,6 @@ insert_symbol(Id, Hash, #{symbols := Symbols} = Env) ->
|
|||||||
, Hash => Id}}}
|
, Hash => Id}}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
lookup(Name, Symbols) ->
|
||||||
|
maps:get(Name, Symbols, Name).
|
||||||
|
@ -40,8 +40,7 @@ JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}.
|
|||||||
JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}.
|
JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}.
|
||||||
SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}.
|
SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}.
|
||||||
SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}.
|
SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}.
|
||||||
SWITCH_V4 : {token, {mnemonic, TokenLine, 'SWITCH_V4'}}.
|
SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}.
|
||||||
SWITCH_V5 : {token, {mnemonic, TokenLine, 'SWITCH_V5'}}.
|
|
||||||
|
|
||||||
PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}.
|
PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}.
|
||||||
DUP : {token, {mnemonic, TokenLine, 'DUP'}}.
|
DUP : {token, {mnemonic, TokenLine, 'DUP'}}.
|
||||||
|
@ -132,8 +132,7 @@ mnemonic(?RETURNR) -> 'RETURNR' ;
|
|||||||
mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ;
|
mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ;
|
||||||
mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ;
|
mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ;
|
||||||
mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ;
|
mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ;
|
||||||
mnemonic(?SWITCH_V4) -> 'SWITCH_V4' ;
|
mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ;
|
||||||
mnemonic(?SWITCH_V5) -> 'SWITCH_V5' ;
|
|
||||||
mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ;
|
mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ;
|
||||||
mnemonic(?FUNCTION) -> 'FUNCTION' ;
|
mnemonic(?FUNCTION) -> 'FUNCTION' ;
|
||||||
mnemonic(?EXTEND) -> 'EXTEND'.
|
mnemonic(?EXTEND) -> 'EXTEND'.
|
||||||
@ -246,9 +245,7 @@ m_to_op('RETURNR') -> ?RETURNR ;
|
|||||||
m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ;
|
m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ;
|
||||||
m_to_op('SWITCH_V2') -> ?SWITCH_V2 ;
|
m_to_op('SWITCH_V2') -> ?SWITCH_V2 ;
|
||||||
m_to_op('SWITCH_V3') -> ?SWITCH_V3 ;
|
m_to_op('SWITCH_V3') -> ?SWITCH_V3 ;
|
||||||
m_to_op('SWITCH_V4') -> ?SWITCH_V4 ;
|
m_to_op('SWITCH_VN') -> ?SWITCH_VN ;
|
||||||
m_to_op('SWITCH_V5') -> ?SWITCH_V5 ;
|
|
||||||
m_to_op('BITS_ALL_N') -> ?BITS_ALL_N ;
|
|
||||||
m_to_op('FUNCTION') -> ?FUNCTION ;
|
m_to_op('FUNCTION') -> ?FUNCTION ;
|
||||||
m_to_op('EXTEND') -> ?EXTEND.
|
m_to_op('EXTEND') -> ?EXTEND.
|
||||||
|
|
||||||
@ -345,9 +342,7 @@ args(?VARIANT) -> 4;
|
|||||||
args(?MAP_UPDATE) -> 4;
|
args(?MAP_UPDATE) -> 4;
|
||||||
args(?MAP_LOOKUPD) -> 4;
|
args(?MAP_LOOKUPD) -> 4;
|
||||||
|
|
||||||
args(?SWITCH_V4) -> 5;
|
args(?SWITCH_VN) -> 2;
|
||||||
|
|
||||||
args(?SWITCH_V5) -> 6;
|
|
||||||
|
|
||||||
args(_) -> 0. %% TODO do not allow this
|
args(_) -> 0. %% TODO do not allow this
|
||||||
|
|
||||||
@ -361,8 +356,7 @@ end_bb(?CALL_R) -> true;
|
|||||||
end_bb(?CALL_TR) -> true;
|
end_bb(?CALL_TR) -> true;
|
||||||
end_bb(?SWITCH_V2) -> true;
|
end_bb(?SWITCH_V2) -> true;
|
||||||
end_bb(?SWITCH_V3) -> true;
|
end_bb(?SWITCH_V3) -> true;
|
||||||
end_bb(?SWITCH_V4) -> true;
|
end_bb(?SWITCH_VN) -> true;
|
||||||
end_bb(?SWITCH_V5) -> true;
|
|
||||||
end_bb(?ABORT) -> true;
|
end_bb(?ABORT) -> true;
|
||||||
end_bb(?EXIT) -> true;
|
end_bb(?EXIT) -> true;
|
||||||
|
|
||||||
|
12
test/aeb_data_test.erl
Normal file
12
test/aeb_data_test.erl
Normal file
@ -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).
|
66
test/aeb_fate_asm_test.erl
Normal file
66
test/aeb_fate_asm_test.erl
Normal file
@ -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).
|
||||||
|
|
||||||
|
|
||||||
|
|
82
test/aeb_serialize_test.erl
Normal file
82
test/aeb_serialize_test.erl
Normal file
@ -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">>)})
|
||||||
|
|
||||||
|
].
|
@ -22,8 +22,7 @@ FUNCTION map_member({map, integer, boolean}, integer):boolean
|
|||||||
FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean}
|
FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean}
|
||||||
MAP_DELETE a arg0 arg1
|
MAP_DELETE a arg0 arg1
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
|
|
||||||
FUNCTION map_member({map, integer, boolean}, integer) : boolean
|
FUNCTION map_member({map, integer, boolean}, integer) : boolean
|
||||||
MAP_MEMBER a arg0 arg1
|
MAP_MEMBER a arg0 arg1
|
||||||
RETURN
|
RETURN
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
;;CONTRACT tuple
|
|
||||||
FUNCTION make_0tuple():{tuple, []}
|
FUNCTION make_0tuple():{tuple, []}
|
||||||
TUPLE 0
|
TUPLE 0
|
||||||
RETURN
|
RETURN
|
||||||
@ -18,7 +17,7 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer):
|
|||||||
PUSH arg4
|
PUSH arg4
|
||||||
TUPLE 5
|
TUPLE 5
|
||||||
RETURN
|
RETURN
|
||||||
|
|
||||||
FUNCTION element1(integer, integer): integer
|
FUNCTION element1(integer, integer): integer
|
||||||
PUSH arg0
|
PUSH arg0
|
||||||
PUSH arg1
|
PUSH arg1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user