Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 506f9ca72e | |||
| 7dd9c29cc0 | |||
| 242700e084 | |||
| 29b5ee3e68 | |||
| 896290ad3b | |||
| 876e8504c8 | |||
| 53a055b90a | |||
| 409d761b18 | |||
| f15315adb7 | |||
| b8b316aae0 | |||
| 985e5358c9 | |||
| ffebc13d08 | |||
| 3ff4df42ff | |||
| d6fbc73450 | |||
| 3d6ac9df92 | |||
| e8390e52d1 | |||
| 58daf1bb5c | |||
| cb8e2b07a4 | |||
| 46f9d34447 | |||
| 942c7fb069 | |||
| 53130fc638 | |||
| 8bf19dc060 | |||
| a5bfdf63d5 | |||
| 41860b041e | |||
| 25ef7e7fe3 | |||
| bcc409f302 | |||
| 42719e7000 | |||
| 0d6322c0aa | |||
| f7a4c40c50 | |||
| ec0af8046a | |||
| 73c80e1168 | |||
| 45ff418699 | |||
| 032277ae8b | |||
| 0bb4ac0fea | |||
| 4a90e3b2b4 | |||
| 8b7fefc8a9 | |||
| 6f59ef7a7c | |||
| 302c1c211d | |||
| 74791cfe52 | |||
| 453f68fa39 | |||
| aa9d2bf893 | |||
| 23b98f7d65 | |||
| 5d7bd73bcb | |||
| 34b9684b6b | |||
| ccbb0ed6c7 | |||
| f1298870e5 | |||
| 9cfd369c5d | |||
| f115feb16d | |||
| 241a96ebaa | |||
| 880cf573aa | |||
| 89f5ebc84b | |||
| e98298cce4 | |||
| 0d1899b32a | |||
| 3e0e289f2f | |||
| 11a8997ac7 | |||
| 2f4e1888c2 | |||
| a9389e4e69 | |||
| 2d3cede235 | |||
| 08a09b065b | |||
| 5fd076f043 | |||
| 2555868990 | |||
| 7eafbc22ae | |||
| 3ed0fcbe05 | |||
| b6019eb81b | |||
| 6eab9a32c9 | |||
| 91fc56c322 | |||
| 1887486d36 | |||
| bf6741eac4 | |||
| 491489ca7d | |||
| 91c4ab5bea |
+1
-1
@@ -14,8 +14,8 @@ aeb_fate_asm_scan.xrl
|
||||
_build/
|
||||
aefateasm
|
||||
include/aeb_fate_opcodes.hrl
|
||||
src/aeb_fate_code.erl
|
||||
src/aeb_fate_opcodes.erl
|
||||
src/aeb_fate_ops.erl
|
||||
src/aeb_fate_pp.erl
|
||||
*.erl~
|
||||
*.hrl~
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl
|
||||
GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_ops.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl
|
||||
GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template
|
||||
REBAR ?= rebar3
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
|
||||
-define(FATE_INTEGER_VALUE(X), (X)).
|
||||
-define(FATE_BOOLEAN_VALUE(X), (X)).
|
||||
-define(FATE_LIST_VALUE(X), (X)).
|
||||
-define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))).
|
||||
-define(FATE_STRING_VALUE(X), (X)).
|
||||
@@ -63,6 +64,7 @@
|
||||
-define(FATE_ORACLE_VALUE(X), (element(2, X))).
|
||||
-define(FATE_NAME_VALUE(X), (element(2, X))).
|
||||
-define(FATE_CHANNEL_VALUE(X), (element(2, X))).
|
||||
-define(FATE_BITS_VALUE(X), (element(2, X))).
|
||||
-define(FATE_MAP_VALUE(X), (X)).
|
||||
-define(FATE_MAP_SIZE(X), (map_size(X))).
|
||||
-define(FATE_STRING_SIZE(X), (byte_size(X))).
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
-define( 'SHA3', 16#20).
|
||||
|
||||
-define( 'CREATOR', 16#2f).
|
||||
-define( 'ADDRESS', 16#30).
|
||||
-define( 'BALANCE', 16#31).
|
||||
-define( 'ORIGIN', 16#32).
|
||||
@@ -165,6 +166,8 @@
|
||||
-define(PRIM_CALL_ORACLE_GET_ANSWER, 104).
|
||||
-define(PRIM_CALL_ORACLE_GET_QUESTION, 105).
|
||||
-define(PRIM_CALL_ORACLE_QUERY_FEE, 106).
|
||||
-define(PRIM_CALL_ORACLE_CHECK, 110).
|
||||
-define(PRIM_CALL_ORACLE_CHECK_QUERY, 111).
|
||||
|
||||
-define(PRIM_CALL_IN_AENS_RANGE(__TTYPE__), (((__TTYPE__) > 199) andalso ((__TTYPE__) < 300))).
|
||||
-define(PRIM_CALL_AENS_RESOLVE, 200).
|
||||
@@ -193,3 +196,7 @@
|
||||
|
||||
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
|
||||
-define(PRIM_CALL_AUTH_TX_HASH, 500).
|
||||
|
||||
-define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))).
|
||||
-define(PRIM_CALL_ADDR_IS_ORACLE, 600).
|
||||
-define(PRIM_CALL_ADDR_IS_CONTRACT, 601).
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||
%%% `rebar3 as eqc eunit --cover`
|
||||
%%% or `rebar3 as eqc eunit --module=aeb_fate_code`
|
||||
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
|
||||
%%%
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aeb_fate_code_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||
{ atom_to_list(PropName),
|
||||
{timeout, (Ms * 10) div 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_code_eqc, prop_opcodes, 200),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_serializes, 3000),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_fail_serializes, 3000),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_fuzz, 3000)
|
||||
]}.
|
||||
@@ -21,5 +21,7 @@
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
|
||||
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000)
|
||||
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_order, 2000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000)
|
||||
]}.
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000)
|
||||
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000)
|
||||
]}.
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||
%%%
|
||||
%%% We want to be sure that we can deserialize all FATE assembler that is accepted on chain.
|
||||
%%%
|
||||
%%% We test something slightly weaker here,
|
||||
%%% viz. All FATE assembler we serialize, we can deserialize
|
||||
%%%
|
||||
%%% Negative testing modelled:
|
||||
%%% Failure 1: function names differ from 4 bytes
|
||||
%%% Failure 2: pointer to empty code block
|
||||
%%% Failure 3: end_BB operation as not ending block or not at end of block
|
||||
%%% - empty code blocks
|
||||
%%% - blocks that are not of the form (not end_bb)* end_bb.
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aefate_code_eqc).
|
||||
|
||||
-include_lib("eqc/include/eqc.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
%%-define(Failure(Failures, Number), case lists:member(Number, Failures) of true -> 1; false -> 0 end)
|
||||
|
||||
|
||||
prop_serializes() ->
|
||||
in_parallel(
|
||||
?FORALL(FateCode, fate_code(0),
|
||||
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateCode]),
|
||||
begin
|
||||
Binary = aeb_fate_code:serialize(FateCode),
|
||||
?WHENFAIL(eqc:format("serialized: ~p~n", [Binary]),
|
||||
begin
|
||||
Decoded = aeb_fate_code:deserialize(Binary),
|
||||
measure(binary_size, size(Binary),
|
||||
equals(Decoded, FateCode))
|
||||
end)
|
||||
end))).
|
||||
|
||||
prop_fail_serializes() ->
|
||||
conjunction([{Failure, eqc:counterexample(
|
||||
?FORALL(FateCode, fate_code(Failure),
|
||||
?FORALL(Binary, catch aeb_fate_code:serialize(FateCode),
|
||||
is_binary(aeb_fate_code:serialize(FateCode)))))
|
||||
=/= true} || Failure <- [1,2,3,4, 5] ]).
|
||||
|
||||
prop_fuzz() ->
|
||||
in_parallel(
|
||||
?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)),
|
||||
?FORALL(InjectedBin, injection(Binary),
|
||||
try Org = aeb_fate_code:deserialize(InjectedBin),
|
||||
NewBin = aeb_fate_code:serialize(Org),
|
||||
NewOrg = aeb_fate_code:deserialize(NewBin),
|
||||
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
|
||||
equals(NewBin, InjectedBin))
|
||||
catch _:_ ->
|
||||
true
|
||||
end))).
|
||||
|
||||
prop_opcodes() ->
|
||||
?FORALL(Opcode, choose(0, 16#ff),
|
||||
try M = aeb_fate_opcodes:mnemonic(Opcode),
|
||||
?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]),
|
||||
conjunction([{valid, lists:member(Opcode, valid_opcodes())},
|
||||
{eq, equals(aeb_fate_opcodes:m_to_op(M), Opcode)}]))
|
||||
catch
|
||||
_:_ ->
|
||||
not lists:member(Opcode, valid_opcodes())
|
||||
end).
|
||||
|
||||
|
||||
valid_opcodes() ->
|
||||
lists:seq(0, 16#72) ++ lists:seq(16#fa, 16#fd).
|
||||
|
||||
|
||||
fate_code(Failure) ->
|
||||
?SIZED(Size,
|
||||
?LET({FMap, SMap, AMap},
|
||||
{non_empty(map(if Failure == 1 -> binary(1);
|
||||
true -> binary(4) end,
|
||||
{{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})),
|
||||
map(small_fate_data_key(5), small_fate_data(4)),
|
||||
map(small_fate_data_key(5), small_fate_data(4))},
|
||||
aeb_fate_code:update_annotations(
|
||||
aeb_fate_code:update_symbols(
|
||||
aeb_fate_code:update_functions(
|
||||
aeb_fate_code:new(), FMap), SMap), AMap))).
|
||||
|
||||
bbs_code(Failure) ->
|
||||
frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}},
|
||||
{10, ?LET(BBs, list(bb_code(Failure)),
|
||||
maps:from_list(
|
||||
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
|
||||
|
||||
bb_code(Failure) ->
|
||||
EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ],
|
||||
NonEndBB = valid_opcodes() -- EndBB,
|
||||
frequency(
|
||||
[{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(list(elements(NonEndBB))), bblock(Failure, Ops))},
|
||||
{if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {list(elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))},
|
||||
{10, ?LET({Ops, Op}, {list(elements(NonEndBB)), elements(EndBB)},
|
||||
bblock(Failure, Ops ++ [Op]))}]).
|
||||
|
||||
bblock(Failure, Ops) ->
|
||||
[ begin
|
||||
Mnemonic = aeb_fate_opcodes:mnemonic(Op),
|
||||
Arity = aeb_fate_opcodes:args(Op),
|
||||
case Arity of
|
||||
0 -> Mnemonic;
|
||||
_ -> list_to_tuple([Mnemonic |
|
||||
[ frequency([{if Failure == 5 -> 5; true -> 0 end, {stack, nat()}},
|
||||
{5, {stack, 0}},
|
||||
{5, {arg, nat()}},
|
||||
{5, {var, nat()}},
|
||||
{5, {immediate, small_fate_data(4)}}]) ||
|
||||
_ <- lists:seq(1, Arity) ]])
|
||||
end
|
||||
end || Op <- Ops ].
|
||||
|
||||
injection(Binary) ->
|
||||
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)},
|
||||
begin
|
||||
M = N * 8,
|
||||
<<X:M, _:8, Z/binary>> = Binary,
|
||||
<<X:M, Inj:8, Z/binary>>
|
||||
end).
|
||||
|
||||
small_fate_data(N) ->
|
||||
?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())).
|
||||
|
||||
small_fate_data_key(N) ->
|
||||
?SIZED(Size, ?LET(Data, aefate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))).
|
||||
@@ -49,8 +49,56 @@ prop_serializes() ->
|
||||
{size, size(Binary) < 500000}]))))
|
||||
end)).
|
||||
|
||||
prop_fuzz() ->
|
||||
in_parallel(
|
||||
?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)),
|
||||
?FORALL(InjectedBin, injection(Binary),
|
||||
try Org = aeb_fate_encoding:deserialize(InjectedBin),
|
||||
NewBin = aeb_fate_encoding:serialize(Org),
|
||||
NewOrg = aeb_fate_encoding:deserialize(NewBin),
|
||||
measure(success, 1,
|
||||
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
|
||||
equals(NewBin, InjectedBin)))
|
||||
catch _:_ ->
|
||||
true
|
||||
end))).
|
||||
|
||||
|
||||
prop_order() ->
|
||||
?FORALL(Items, vector(3, fate_data()),
|
||||
begin
|
||||
%% Use lt to take minimum
|
||||
Min = lt_min(Items),
|
||||
Max = lt_max(Items),
|
||||
conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])},
|
||||
{maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])}])
|
||||
end).
|
||||
|
||||
lt_min([X, Y | Rest]) ->
|
||||
case aeb_fate_data:lt(X, Y) of
|
||||
true -> lt_min([X | Rest]);
|
||||
false -> lt_min([Y| Rest])
|
||||
end;
|
||||
lt_min([X]) -> X.
|
||||
|
||||
lt_max([X, Y | Rest]) ->
|
||||
case aeb_fate_data:lt(X, Y) of
|
||||
true -> lt_max([Y | Rest]);
|
||||
false -> lt_max([X| Rest])
|
||||
end;
|
||||
lt_max([X]) -> X.
|
||||
|
||||
prop_idempotent() ->
|
||||
?FORALL(Items, list({fate_data_key(), fate_data()}),
|
||||
equals(aeb_fate_encoding:sort(Items),
|
||||
aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))).
|
||||
|
||||
|
||||
fate_data() ->
|
||||
?SIZED(Size, ?LET(Data, fate_data(Size, [map]), eqc_symbolic:eval(Data))).
|
||||
?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))).
|
||||
|
||||
fate_data_key() ->
|
||||
?SIZED(Size, ?LET(Data, fate_data(Size div 4, []), eqc_symbolic:eval(Data))).
|
||||
|
||||
fate_data(0, _Options) ->
|
||||
?LAZY(
|
||||
@@ -70,10 +118,12 @@ fate_data(0, _Options) ->
|
||||
fate_data(Size, Options) ->
|
||||
oneof([?LAZY(fate_data(Size - 1, Options)),
|
||||
?LAZY(fate_list( fate_data(Size div 5, Options) )),
|
||||
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) )),
|
||||
?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) ] ++
|
||||
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) ))] ++
|
||||
[?LAZY(fate_variant( list(fate_data(Size div 5, Options))))
|
||||
|| lists:member(variant, Options)
|
||||
] ++
|
||||
[
|
||||
?LAZY(fate_map( fate_data(Size div 8, Options -- [map]),
|
||||
?LAZY(fate_map( fate_data(Size div 8, Options -- [map, variant]),
|
||||
fate_data(Size div 5, Options)))
|
||||
|| lists:member(map, Options)
|
||||
]).
|
||||
@@ -120,3 +170,14 @@ non_quote_string() ->
|
||||
|
||||
char() ->
|
||||
choose(1, 255).
|
||||
|
||||
injection(Binary) ->
|
||||
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)},
|
||||
begin
|
||||
M = N * 8,
|
||||
<<X:M, _:8, Z/binary>> = Binary,
|
||||
<<X:M, Inj:8, Z/binary>>
|
||||
end).
|
||||
|
||||
is_empty(L) ->
|
||||
?WHENFAIL(eqc:format("~p\n", [L]), L == []).
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
|
||||
{deps, [ {eblake2, "1.0.0"}
|
||||
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
|
||||
{ref, "6dce265"}}}
|
||||
{ref, "816bf99"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
]}.
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
|
||||
]}
|
||||
]},
|
||||
{eqc, [{erl_opts, [{parse_transform, eqc_cover}]},
|
||||
{eqc, [{erl_opts, [{parse_transform, eqc_cover}, {d, 'EQC'}]},
|
||||
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
|
||||
]}
|
||||
]}.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
|
||||
{ref,"816bf994ffb5cee218c3f22dc5fea296c9e0882e"}},
|
||||
0},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Encode and decode data and function calls according to
|
||||
%%% Sophia-AEVM-ABI.
|
||||
%%% Sophia-AEVM-ABI
|
||||
%%% @end
|
||||
%%% Created : 25 Jan 2018
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_abi).
|
||||
-module(aeb_aevm_abi).
|
||||
-define(HASH_SIZE, 32).
|
||||
|
||||
-export([ create_calldata/4
|
||||
@@ -0,0 +1,23 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Encode and decode data and function calls according to
|
||||
%%% Sophia-FATE-ABI
|
||||
%%% @end
|
||||
%%% Created : 11 Jun 2019
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_fate_abi).
|
||||
|
||||
-export([ create_calldata/2 ]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
-spec create_calldata(list(), [term()]) -> {ok, binary()}.
|
||||
create_calldata(FunName, Args) ->
|
||||
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)),
|
||||
{ok, aeb_fate_encoding:serialize(
|
||||
aeb_fate_data:make_tuple({FunctionId,
|
||||
aeb_fate_data:make_tuple(list_to_tuple(Args))}))}.
|
||||
+45
-570
@@ -30,9 +30,7 @@
|
||||
%%% arg0
|
||||
%%% References to variables/registers start with var followed by an integer
|
||||
%%% var0
|
||||
%%% References to stack postions is either a (for stack 0)
|
||||
%%% or start with stack followed by an integer
|
||||
%%% stack1
|
||||
%%% References to the top of the stack is the letter a (for accumulator)
|
||||
%%% a
|
||||
%%%
|
||||
%%% Immediate values can be of 11 types:
|
||||
@@ -75,7 +73,8 @@
|
||||
%%% Hexdigits: [0123456789abcdef]
|
||||
%%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
|
||||
%%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
|
||||
%%% Characters any printable ascii character 0..255 (except " no quoting yet)
|
||||
%%% Characters: as a code literal - any printable ascii character 0..255 (except " no quoting yet)
|
||||
%%% the type supports an array of bytes (all values 0..255).
|
||||
%%% Key: any value except for a map
|
||||
%%% Bits: 01 or space
|
||||
%%% Elements: Nothing or Value , Elements
|
||||
@@ -89,13 +88,11 @@
|
||||
|
||||
-export([ assemble_file/3
|
||||
, asm_to_bytecode/2
|
||||
, bytecode_to_fate_code/2
|
||||
, function_call/1
|
||||
, pp/1
|
||||
, read_file/1
|
||||
, strip/1
|
||||
, to_asm/1
|
||||
, to_hexstring/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
|
||||
@@ -132,9 +129,10 @@ pp(FateCode) ->
|
||||
io_lib:format("~ts~n",[Listing]).
|
||||
|
||||
|
||||
to_asm(#{ functions := Functions
|
||||
, symbols := Symbols
|
||||
, annotations := Annotations} = _FateCode) ->
|
||||
to_asm(FateCode) ->
|
||||
Functions = aeb_fate_code:functions(FateCode),
|
||||
Symbols = aeb_fate_code:symbols(FateCode),
|
||||
Annotations = aeb_fate_code:annotations(FateCode),
|
||||
insert_comments(get_comments(Annotations), 1,
|
||||
lists:flatten(
|
||||
io_lib:format("~s",
|
||||
@@ -150,12 +148,6 @@ insert_comments([],_,[]) -> [];
|
||||
insert_comments([{L,C}|Rest], _, []) ->
|
||||
";; " ++ C ++ "\n" ++ insert_comments(Rest, L + 1, []).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
format_functions(Functions, Symbols) ->
|
||||
[format(lookup(Name, Symbols),
|
||||
Sig,
|
||||
@@ -218,521 +210,22 @@ asm_to_bytecode(AssemblerCode, Options) ->
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
Env = #{ fate_code => aeb_fate_code:new()
|
||||
, functions => #{}
|
||||
},
|
||||
|
||||
Env = to_bytecode(Tokens, none, #{ functions => #{}
|
||||
, symbols => #{}
|
||||
, annotations => #{}
|
||||
}, [], Options),
|
||||
|
||||
ByteList = serialize(Env),
|
||||
Signatures = serialize_sigs(Env),
|
||||
SymbolTable = serialize_symbol_table(Env),
|
||||
Annotatations = serialize_annotations(Env),
|
||||
ByteCode = << (aeser_rlp:encode(list_to_binary(ByteList)))/binary,
|
||||
(aeser_rlp:encode(list_to_binary(Signatures)))/binary,
|
||||
(aeser_rlp:encode(SymbolTable))/binary,
|
||||
(aeser_rlp:encode(Annotatations))/binary
|
||||
>>,
|
||||
|
||||
case proplists:lookup(pp_hex_string, Options) of
|
||||
{pp_hex_string, true} ->
|
||||
io:format("Code: ~s~n",[to_hexstring(ByteList)]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
|
||||
Env1 = to_bytecode(Tokens, none, Env, [], Options),
|
||||
FateCode = maps:get(fate_code, Env1),
|
||||
FunctionsMap = maps:get(functions, Env1),
|
||||
Functions = [X || {_, X} <- lists:sort(maps:to_list(FunctionsMap))],
|
||||
FunctionsBin = iolist_to_binary(Functions),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, FunctionsBin, Options),
|
||||
{Env, ByteCode}.
|
||||
|
||||
strip(ByteCode) ->
|
||||
{Code, _Rest} = aeser_rlp:decode_one(ByteCode),
|
||||
Code.
|
||||
|
||||
bytecode_to_fate_code(Bytes, _Options) ->
|
||||
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes),
|
||||
{Signatures, Rest2} = aeser_rlp:decode_one(Rest1),
|
||||
{SymbolTable, Rest3} = aeser_rlp:decode_one(Rest2),
|
||||
{Annotations, <<>>} = aeser_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.
|
||||
|
||||
|
||||
deserialize(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
|
||||
#{ function := none
|
||||
, bb := 0
|
||||
, current_bb_code := []
|
||||
} = Env) ->
|
||||
{Sig, Rest2} = deserialize_signature(Rest),
|
||||
Env2 = Env#{function => {<<A,B,C,D>>, Sig}},
|
||||
deserialize(Rest2, Env2);
|
||||
deserialize(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
|
||||
#{ function := {F, Sig}
|
||||
, bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program
|
||||
, functions := Funs} = Env) ->
|
||||
{NewSig, Rest2} = deserialize_signature(Rest),
|
||||
case Code of
|
||||
[] ->
|
||||
Env2 = Env#{ bb => 0
|
||||
, current_bb_code => []
|
||||
, function => {<<A,B,C,D>>, NewSig}
|
||||
, code => #{}
|
||||
, functions => Funs#{F => {Sig, Program}}},
|
||||
deserialize(Rest2, Env2);
|
||||
_ ->
|
||||
Env2 = Env#{ bb => 0
|
||||
, current_bb_code => []
|
||||
, function => {<<A,B,C,D>>, NewSig}
|
||||
, code => #{}
|
||||
, functions =>
|
||||
Funs#{F => {Sig,
|
||||
Program#{ BB => lists:reverse(Code)}}}},
|
||||
deserialize(Rest2, Env2)
|
||||
end;
|
||||
deserialize(<<Op:8, Rest/binary>>,
|
||||
#{ bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program} = Env) ->
|
||||
{Rest2, OpCode} = deserialize_op(Op, Rest, Code),
|
||||
case aeb_fate_opcodes:end_bb(Op) of
|
||||
true ->
|
||||
deserialize(Rest2, Env#{ bb => BB+1
|
||||
, current_bb_code => []
|
||||
, code => Program#{BB =>
|
||||
lists:reverse(OpCode)}});
|
||||
false ->
|
||||
deserialize(Rest2, Env#{ current_bb_code => OpCode})
|
||||
end;
|
||||
deserialize(<<>>, #{ function := {F, Sig}
|
||||
, bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program
|
||||
, functions := Funs} = Env) ->
|
||||
FunctionCode =
|
||||
case Code of
|
||||
[] -> Program;
|
||||
_ -> Program#{ BB => lists:reverse(Code)}
|
||||
end,
|
||||
Env#{ bb => 0
|
||||
, current_bb_code => []
|
||||
, function => none
|
||||
, code => #{}
|
||||
, functions => Funs#{F => {Sig, FunctionCode}}}.
|
||||
|
||||
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
|
||||
{L, Rest4} when is_list(L) ->
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
{Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
|
||||
, {Modifier0, Arg0}
|
||||
, {immediate, L}
|
||||
}
|
||||
| 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
|
||||
0 -> {Rest, [OpName | Code]};
|
||||
1 ->
|
||||
<<ArgType:8, Rest2/binary>> = Rest,
|
||||
{Arg, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
Modifier = bits_to_modifier(ArgType),
|
||||
{Rest3, [{OpName, {Modifier, Arg}} | Code]};
|
||||
2 ->
|
||||
<<ArgType:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
{Rest4, [{OpName, {Modifier0, Arg0},
|
||||
{Modifier1, Arg1}} | Code]};
|
||||
3 ->
|
||||
<<ArgType:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
{Rest5, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}}
|
||||
| Code]};
|
||||
4 ->
|
||||
<<ArgType:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
{Rest6, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}}
|
||||
| Code]};
|
||||
5 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
{Rest7, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
}
|
||||
| Code]};
|
||||
6 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
{Rest8, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
}
|
||||
| Code]};
|
||||
7 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
|
||||
{Rest9, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
, {Modifier6, Arg6}
|
||||
}
|
||||
| Code]};
|
||||
8 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
|
||||
{Arg7, Rest10} = aeb_fate_encoding:deserialize_one(Rest9),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
|
||||
Modifier7 = bits_to_modifier((ArgType2 bsr 6) band 2#11),
|
||||
{Rest10, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
, {Modifier6, Arg6}
|
||||
, {Modifier7, Arg7}
|
||||
}
|
||||
| Code]}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
deserialize_signatures(_Signatures, Env) -> Env.
|
||||
|
||||
deserialize_symbols(Table, Env) ->
|
||||
?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table),
|
||||
Env#{symbols => SymbolTable}.
|
||||
|
||||
deserialize_annotations(AnnotationsBin, Env) ->
|
||||
?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin),
|
||||
Env#{annotations => Annotations}.
|
||||
|
||||
|
||||
|
||||
serialize_sigs(_Env) -> [].
|
||||
|
||||
serialize_symbol_table(#{ symbols := Symbols }) ->
|
||||
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)).
|
||||
|
||||
serialize_annotations(#{ annotations := Annotations}) ->
|
||||
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
serialize(#{functions := Functions} =_Env) ->
|
||||
%% Sort the functions oon name to get a canonical serialisation.
|
||||
Code = [[?FUNCTION, Name, serialize_signature(Sig), C] ||
|
||||
{Name, {Sig, C}} <- lists:sort(maps:to_list(Functions))],
|
||||
serialize_code(lists:flatten(Code)).
|
||||
|
||||
|
||||
%% Argument encoding
|
||||
%% Agument Specification Byte
|
||||
%% bitpos: 6 4 2 0
|
||||
%% xx xx xx xx
|
||||
%% Arg3 Arg2 Arg1 Arg0
|
||||
%% For 5-8 args another Argument Spec Byte is used
|
||||
%% Bit pattern
|
||||
%% 00 : stack/unused (depending on instruction)
|
||||
%% 01 : argN
|
||||
%% 10 : varN
|
||||
%% 11 : immediate
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
, {Arg3Type, Arg3}
|
||||
, {Arg4Type, Arg4}
|
||||
, {Arg5Type, Arg5}
|
||||
, {Arg6Type, Arg6}
|
||||
, {Arg7Type, Arg7}
|
||||
| Rest]) ->
|
||||
ArgSpec1 =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4) bor
|
||||
(modifier_bits(Arg3Type) bsl 6),
|
||||
ArgSpec2 =
|
||||
modifier_bits(Arg4Type) bor
|
||||
(modifier_bits(Arg5Type) bsl 2) bor
|
||||
(modifier_bits(Arg6Type) bsl 4) bor
|
||||
(modifier_bits(Arg7Type) bsl 6),
|
||||
[ ArgSpec1
|
||||
, ArgSpec2
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
, serialize_data(Arg3Type, Arg3)
|
||||
, serialize_data(Arg4Type, Arg4)
|
||||
, serialize_data(Arg5Type, Arg5)
|
||||
, serialize_data(Arg6Type, Arg6)
|
||||
, serialize_data(Arg7Type, Arg7)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
, {Arg3Type, Arg3}
|
||||
, {Arg4Type, Arg4}
|
||||
, {Arg5Type, Arg5}
|
||||
, {Arg6Type, Arg6}
|
||||
| Rest]) ->
|
||||
ArgSpec1 =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4) bor
|
||||
(modifier_bits(Arg3Type) bsl 6),
|
||||
ArgSpec2 =
|
||||
modifier_bits(Arg4Type) bor
|
||||
(modifier_bits(Arg5Type) bsl 2) bor
|
||||
(modifier_bits(Arg6Type) bsl 4),
|
||||
[ ArgSpec1
|
||||
, ArgSpec2
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
, serialize_data(Arg3Type, Arg3)
|
||||
, serialize_data(Arg4Type, Arg4)
|
||||
, serialize_data(Arg5Type, Arg5)
|
||||
, serialize_data(Arg6Type, Arg6)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
, {Arg3Type, Arg3}
|
||||
, {Arg4Type, Arg4}
|
||||
, {Arg5Type, Arg5}
|
||||
| Rest]) ->
|
||||
ArgSpec1 =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4) bor
|
||||
(modifier_bits(Arg3Type) bsl 6),
|
||||
ArgSpec2 =
|
||||
modifier_bits(Arg4Type) bor
|
||||
(modifier_bits(Arg5Type) bsl 2),
|
||||
[ ArgSpec1
|
||||
, ArgSpec2
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
, serialize_data(Arg3Type, Arg3)
|
||||
, serialize_data(Arg4Type, Arg4)
|
||||
, serialize_data(Arg5Type, Arg5)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
, {Arg3Type, Arg3}
|
||||
, {Arg4Type, Arg4}
|
||||
| Rest]) ->
|
||||
ArgSpec1 =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4) bor
|
||||
(modifier_bits(Arg3Type) bsl 6),
|
||||
ArgSpec2 =
|
||||
modifier_bits(Arg4Type),
|
||||
[ ArgSpec1
|
||||
, ArgSpec2
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
, serialize_data(Arg3Type, Arg3)
|
||||
, serialize_data(Arg4Type, Arg4)
|
||||
| serialize_code(Rest)];
|
||||
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
, {Arg3Type, Arg3}| Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4) bor
|
||||
(modifier_bits(Arg3Type) bsl 6),
|
||||
[ ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
, serialize_data(Arg3Type, Arg3)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
, {Arg2Type, Arg2}
|
||||
| Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2) bor
|
||||
(modifier_bits(Arg2Type) bsl 4),
|
||||
[ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
, serialize_data(Arg2Type, Arg2)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0}
|
||||
, {Arg1Type, Arg1}
|
||||
| Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(Arg1Type) bsl 2),
|
||||
[ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(Arg1Type, Arg1)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ {Arg0Type, Arg0} | Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type),
|
||||
[ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
| serialize_code(Rest)];
|
||||
serialize_code([ ?SWITCH_VN
|
||||
, {Arg0Type, Arg0}
|
||||
, {immediate, L}
|
||||
| Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(immediate) bsl 2),
|
||||
[?SWITCH_VN
|
||||
, ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(immediate, L)] ++ serialize_code(Rest);
|
||||
serialize_code([B|Rest]) ->
|
||||
[B | serialize_code(Rest)];
|
||||
serialize_code([]) -> [].
|
||||
|
||||
|
||||
%% 00 : stack/unused (depending on instruction)
|
||||
%% 01 : argN
|
||||
%% 10 : varN
|
||||
%% 11 : immediate
|
||||
modifier_bits(immediate) -> 2#11;
|
||||
modifier_bits(var) -> 2#10;
|
||||
modifier_bits(arg) -> 2#01;
|
||||
modifier_bits(stack) -> 2#00.
|
||||
|
||||
bits_to_modifier(2#11) -> immediate;
|
||||
bits_to_modifier(2#10) -> var;
|
||||
bits_to_modifier(2#01) -> arg;
|
||||
bits_to_modifier(2#00) -> stack.
|
||||
|
||||
serialize_data(_, Data) ->
|
||||
aeb_fate_encoding:serialize(Data).
|
||||
|
||||
serialize_signature({Args, RetType}) ->
|
||||
[aeb_fate_encoding:serialize_type({tuple, Args}) |
|
||||
aeb_fate_encoding:serialize_type(RetType)].
|
||||
|
||||
|
||||
|
||||
deserialize_signature(Binary) ->
|
||||
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
|
||||
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
|
||||
{{Args, RetType}, Rest2}.
|
||||
|
||||
|
||||
|
||||
to_hexstring(ByteList) ->
|
||||
"0x" ++ lists:flatten(
|
||||
[io_lib:format("~2.16.0b", [X])
|
||||
|| X <- ByteList]).
|
||||
|
||||
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% Parser
|
||||
%% Asm tokens -> Fate code env
|
||||
@@ -749,8 +242,8 @@ to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts);
|
||||
to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts);
|
||||
to_bytecode([{stack,_line, N}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{stack, N}|Code], Opts);
|
||||
to_bytecode([{stack,_line}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{stack, 0}|Code], Opts);
|
||||
to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts);
|
||||
to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) ->
|
||||
@@ -795,8 +288,8 @@ to_bytecode([{signature,_line, {signature, Value}}|Rest],
|
||||
[{immediate, aeb_fate_data:make_signature(Value)}|Code],
|
||||
Opts);
|
||||
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
|
||||
{Hash, Env2} = insert_symbol(ID, Env),
|
||||
to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts);
|
||||
{Env2, Id} = insert_symbol(list_to_binary(ID), Env),
|
||||
to_bytecode(Rest, Address, Env2, [{immediate, Id}|Code], Opts);
|
||||
to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) ->
|
||||
{Map, Rest2} = parse_map(Rest),
|
||||
to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts);
|
||||
@@ -819,17 +312,8 @@ to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) ->
|
||||
Env2 = insert_annotation(comment, Line, Comment, Env),
|
||||
to_bytecode(Rest, Address, Env2, Code, Opts);
|
||||
|
||||
to_bytecode([], Address, Env, Code, Opts) ->
|
||||
Env2 = insert_fun(Address, Code, Env),
|
||||
#{functions := Funs} = Env2,
|
||||
case proplists:lookup(pp_opcodes, Opts) of
|
||||
{pp_opcodes, true} ->
|
||||
Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Funs)],
|
||||
io:format("opcodes ~p~n", [Ops]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
Env2.
|
||||
to_bytecode([], Address, Env, Code,_Opts) ->
|
||||
insert_fun(Address, Code, Env).
|
||||
|
||||
parse_map([{'}',_line}|Rest]) ->
|
||||
{#{}, Rest};
|
||||
@@ -906,15 +390,15 @@ parse_value([{start_variant,_line}|_] = Tokens) ->
|
||||
{Variant, Rest};
|
||||
parse_value([{string,_line, String} | Rest]) ->
|
||||
{aeb_fate_data:make_string(String), Rest};
|
||||
parse_value([{address,_line, {address, Address}} | Rest]) ->
|
||||
parse_value([{object,_line, {address, Address}} | Rest]) ->
|
||||
{aeb_fate_data:make_address(Address), Rest};
|
||||
parse_value([{address,_line, {contract, Address}} | Rest]) ->
|
||||
parse_value([{object,_line, {contract, Address}} | Rest]) ->
|
||||
{aeb_fate_data:make_contract(Address), Rest};
|
||||
parse_value([{address,_line, {oracle, Address}} | Rest]) ->
|
||||
parse_value([{object,_line, {oracle, Address}} | Rest]) ->
|
||||
{aeb_fate_data:make_oracle(Address), Rest};
|
||||
parse_value([{address,_line, {name, Address}} | Rest]) ->
|
||||
parse_value([{object,_line, {name, Address}} | Rest]) ->
|
||||
{aeb_fate_data:make_name(Address), Rest};
|
||||
parse_value([{address,_line, {channel, Address}} | Rest]) ->
|
||||
parse_value([{object,_line, {channel, Address}} | Rest]) ->
|
||||
{aeb_fate_data:make_channel(Address), Rest};
|
||||
parse_value([{hash,_line, Hash} | Rest]) ->
|
||||
{aeb_fate_data:make_hash(Hash), Rest};
|
||||
@@ -990,11 +474,24 @@ to_list_of_types(Tokens) ->
|
||||
%% State handling
|
||||
|
||||
insert_fun(none, [], Env) -> Env;
|
||||
insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) ->
|
||||
{Hash, Env2} = insert_symbol(Name, Env),
|
||||
Env2#{
|
||||
functions => Functions#{Hash => {{Type, RetType}, lists:reverse(Code)}}
|
||||
}.
|
||||
insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode
|
||||
, functions := Funs} = Env) ->
|
||||
Name = list_to_binary(NameString),
|
||||
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode),
|
||||
BodyByteCode = aeb_fate_code:serialize_code(lists:reverse(Code)),
|
||||
SigByteCode = aeb_fate_code:serialize_signature({ArgType, RetType}),
|
||||
FunByteCode = [?FUNCTION, Id, SigByteCode, BodyByteCode],
|
||||
Env#{ functions => Funs#{ Id => FunByteCode }
|
||||
, fate_code => FateCode1}.
|
||||
|
||||
insert_symbol(Name, #{ fate_code := FateCode } = Env) ->
|
||||
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode),
|
||||
{ Env#{ fate_code => FateCode1 }
|
||||
, Id}.
|
||||
|
||||
insert_annotation(comment, Line, Comment, #{ fate_code := FateCode } = Env) ->
|
||||
FateCode1 = aeb_fate_code:insert_annotation(comment, Line, Comment, FateCode),
|
||||
Env#{ fate_code => FateCode1}.
|
||||
|
||||
mk_hash(Id) ->
|
||||
%% Use first 4 bytes of blake hash
|
||||
@@ -1003,33 +500,11 @@ mk_hash(Id) ->
|
||||
|
||||
%% Handle annotations
|
||||
|
||||
insert_annotation(comment, Line, Comment, #{annotations := A} = Env) ->
|
||||
Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}),
|
||||
Value = aeb_fate_data:make_string(Comment),
|
||||
Env#{annotations => A#{ Key => Value}}.
|
||||
|
||||
get_comments(Annotations) ->
|
||||
[ {Line, Comment} ||
|
||||
{?FATE_TUPLE({?FATE_STRING_VALUE("comment"), Line}),
|
||||
?FATE_STRING_VALUE(Comment)} <- maps:to_list(Annotations)].
|
||||
|
||||
%% Symbols handling
|
||||
|
||||
insert_symbol(Id, Env) ->
|
||||
Hash = mk_hash(Id),
|
||||
insert_symbol(Id, Hash, Env).
|
||||
|
||||
insert_symbol(Id, Hash, #{symbols := Symbols} = Env) ->
|
||||
case maps:find(Hash, Symbols) of
|
||||
{ok, Id} -> {Hash, Env};
|
||||
{ok, Id2} ->
|
||||
%% Very unlikely...
|
||||
exit({two_symbols_with_same_hash, Id, Id2});
|
||||
error ->
|
||||
{Hash, Env#{symbols => Symbols#{ Id => Hash
|
||||
, Hash => Id}}}
|
||||
end.
|
||||
|
||||
%% Symbol table handling
|
||||
|
||||
lookup(Name, Symbols) ->
|
||||
|
||||
@@ -27,8 +27,7 @@ BITS = (\!)?\<[\s01]*\>
|
||||
Rules.
|
||||
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
|
||||
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
|
||||
a : {token, {stack, TokenLine, 0}}.
|
||||
a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
|
||||
a : {token, {stack, TokenLine}}.
|
||||
|
||||
true : {token, {boolean, TokenLine, true}}.
|
||||
false : {token, {boolean, TokenLine, false}}.
|
||||
@@ -108,7 +107,7 @@ parse_int(Chars) -> list_to_integer(Chars).
|
||||
|
||||
parse_arg("arg" ++ N) -> list_to_integer(N).
|
||||
parse_var("var" ++ N) -> list_to_integer(N).
|
||||
parse_acc("a" ++ N) -> list_to_integer(N).
|
||||
|
||||
|
||||
|
||||
parse_hash("#" ++ Chars) ->
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% ADT for fate byte code/fate code
|
||||
%%% @end
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_fate_code).
|
||||
|
||||
-export([ annotations/1
|
||||
, deserialize/1
|
||||
, functions/1
|
||||
, insert_annotation/4
|
||||
, insert_fun/4
|
||||
, insert_symbol/2
|
||||
, new/0
|
||||
, serialize/1
|
||||
, serialize/2
|
||||
, serialize/3
|
||||
, serialize_code/1
|
||||
, serialize_signature/1
|
||||
, symbol_identifier/1
|
||||
, symbols/1
|
||||
]).
|
||||
|
||||
-include("../include/aeb_fate_opcodes.hrl").
|
||||
-include("../include/aeb_fate_data.hrl").
|
||||
|
||||
-ifdef(EQC).
|
||||
-export([update_annotations/2
|
||||
, update_functions/2
|
||||
, update_symbols/2]).
|
||||
-endif.
|
||||
|
||||
-record(fcode, { functions = #{} :: map()
|
||||
, symbols = #{} :: map()
|
||||
, annotations = #{} :: map()
|
||||
}).
|
||||
|
||||
-define(HASH_BYTES, 32).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
new() ->
|
||||
#fcode{}.
|
||||
|
||||
annotations(#fcode{ annotations = As }) ->
|
||||
As.
|
||||
|
||||
functions(#fcode{ functions = Fs }) ->
|
||||
Fs.
|
||||
|
||||
symbols(#fcode{ symbols = Ss}) ->
|
||||
Ss.
|
||||
|
||||
update_annotations(#fcode{ annotations = As } = FCode, Anns) ->
|
||||
FCode#fcode{ annotations = maps:merge(As, Anns) }.
|
||||
|
||||
update_functions(#fcode{ functions = Fs } = FCode, Funs) ->
|
||||
FCode#fcode{ functions = maps:merge(Fs, Funs) }.
|
||||
|
||||
update_symbols(#fcode{ symbols = Ss } = FCode, Symbs) ->
|
||||
FCode#fcode{ symbols = maps:merge(Ss, Symbs) }.
|
||||
|
||||
symbol_identifier(Bin) ->
|
||||
%% First 4 bytes of blake hash
|
||||
{ok, <<X:4/binary,_/binary>> } = eblake2:blake2b(?HASH_BYTES, Bin),
|
||||
X.
|
||||
|
||||
insert_fun(Name, {ArgType, RetType}, #{} = BBs, FCode) ->
|
||||
{F1, ID} = insert_symbol(Name, FCode),
|
||||
update_functions(F1, #{ID => {{ArgType, RetType}, BBs}}).
|
||||
|
||||
insert_symbol(Name, #fcode{ symbols = Syms } = F) ->
|
||||
ID = symbol_identifier(Name),
|
||||
case maps:find(ID, Syms) of
|
||||
{ok, Name} ->
|
||||
{F, ID};
|
||||
{ok, X} ->
|
||||
error({two_symbols_with_same_hash, Name, X});
|
||||
error ->
|
||||
{update_symbols(F, #{ID => Name}), ID}
|
||||
end.
|
||||
|
||||
insert_annotation(comment =_Type, Line, Comment, FCode) ->
|
||||
Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}),
|
||||
Value = aeb_fate_data:make_string(Comment),
|
||||
update_annotations(FCode, #{ Key => Value }).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Serialization
|
||||
%%%===================================================================
|
||||
|
||||
serialize(#fcode{} = F) ->
|
||||
serialize(F, []).
|
||||
|
||||
serialize(#fcode{} = F, Options) ->
|
||||
serialize(F, serialize_functions(F), Options).
|
||||
|
||||
serialize(#fcode{} = F, Functions, Options) ->
|
||||
SymbolTable = serialize_symbol_table(F),
|
||||
Annotatations = serialize_annotations(F),
|
||||
ByteCode = << (aeser_rlp:encode(Functions))/binary,
|
||||
(aeser_rlp:encode(SymbolTable))/binary,
|
||||
(aeser_rlp:encode(Annotatations))/binary
|
||||
>>,
|
||||
|
||||
case proplists:lookup(pp_hex_string, Options) of
|
||||
{pp_hex_string, true} ->
|
||||
io:format("Code: ~s~n",[to_hexstring(Functions)]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
ByteCode.
|
||||
|
||||
to_hexstring(ByteList) ->
|
||||
"0x" ++ lists:flatten(
|
||||
[io_lib:format("~2.16.0b", [X])
|
||||
|| X <- ByteList]).
|
||||
|
||||
|
||||
serialize_functions(#fcode{ functions = Functions }) ->
|
||||
%% Sort the functions on name to get a canonical serialisation.
|
||||
iolist_to_binary(
|
||||
lists:foldr(fun({Id, {Sig, C}}, Acc) when byte_size(Id) == 4 ->
|
||||
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc];
|
||||
({Id, _}, _) ->
|
||||
error({illegal_function_id, Id})
|
||||
end, [], lists:sort(maps:to_list(Functions)))).
|
||||
|
||||
serialize_signature({Args, RetType}) ->
|
||||
[aeb_fate_encoding:serialize_type({tuple, Args}) |
|
||||
aeb_fate_encoding:serialize_type(RetType)].
|
||||
|
||||
serialize_symbol_table(#fcode{ symbols = Symbols }) ->
|
||||
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)).
|
||||
|
||||
serialize_annotations(#fcode{ annotations = Annotations}) ->
|
||||
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
|
||||
|
||||
serialize_bbs(#{} = BBs) ->
|
||||
serialize_bbs(BBs, 0, []).
|
||||
|
||||
serialize_bbs(BBs, N, Acc) ->
|
||||
case maps:get(N, BBs, none) of
|
||||
none ->
|
||||
%% Assert that the BBs were contiguous
|
||||
Size = maps:size(BBs),
|
||||
case Size =:= N of
|
||||
true ->
|
||||
lists:reverse(Acc);
|
||||
false ->
|
||||
error({not_contiguous_labels, lists:sort(maps:keys(BBs))})
|
||||
end;
|
||||
[] ->
|
||||
error({empty_code_block, N});
|
||||
BB ->
|
||||
serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc])
|
||||
end.
|
||||
|
||||
serialize_bb([Op], Acc) ->
|
||||
lists:reverse([serialize_op(true, Op)|Acc]);
|
||||
serialize_bb([Op|Rest], Acc) ->
|
||||
serialize_bb(Rest, [serialize_op(false, Op)|Acc]).
|
||||
%% serialize_bb([], Acc) ->
|
||||
%% lists:reverse(Acc).
|
||||
|
||||
serialize_op(Kind, Op) ->
|
||||
[Mnemonic|Args] =
|
||||
case is_tuple(Op) of
|
||||
true -> tuple_to_list(Op);
|
||||
false -> [Op]
|
||||
end,
|
||||
safe_serialize(Kind, aeb_fate_opcodes:m_to_op(Mnemonic), Args).
|
||||
|
||||
safe_serialize(Last, Op, Args) ->
|
||||
case length(Args) == aeb_fate_opcodes:args(Op) of
|
||||
true ->
|
||||
case Last == aeb_fate_opcodes:end_bb(Op) of
|
||||
true -> [Op|serialize_code(Args)];
|
||||
false ->
|
||||
error({wrong_opcode_in_bb, Op})
|
||||
end;
|
||||
false ->
|
||||
error({wrong_nr_args_opcode, Op})
|
||||
end.
|
||||
|
||||
|
||||
%% Argument encoding
|
||||
%% Argument Specification Byte
|
||||
%% bitpos: 6 4 2 0
|
||||
%% xx xx xx xx
|
||||
%% Arg3 Arg2 Arg1 Arg0
|
||||
%% For 5-8 args another Argument Spec Byte is used
|
||||
%% bitpos: 6 4 2 0 | 6 4 2 0
|
||||
%% xx xx xx xx | xx xx xx xx
|
||||
%% Arg7 Arg6 Arg5 Arg4 | Arg3 Arg2 Arg1 Arg0
|
||||
%% Bit pattern
|
||||
%% 00 : stack/unused (depending on instruction)
|
||||
%% 01 : argN
|
||||
%% 10 : varN
|
||||
%% 11 : immediate
|
||||
|
||||
serialize_code([{_,_}|_] = List ) ->
|
||||
%% Take out the full argument list.
|
||||
{Args, Rest} = lists:splitwith(fun({_, _}) -> true; (_) -> false end, List),
|
||||
%% Create the appropriate number of modifier bytes.
|
||||
Mods = << <<(modifier_bits(Type, X)):2>> || {Type, X} <- pad_args(lists:reverse(Args)) >>,
|
||||
case Mods of
|
||||
<<M1:8, M2:8>> ->
|
||||
[M1, M2 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++
|
||||
serialize_code(Rest);
|
||||
<<M1:8>> ->
|
||||
[M1 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++
|
||||
serialize_code(Rest)
|
||||
end;
|
||||
serialize_code([Op|Rest]) ->
|
||||
[Op|serialize_code(Rest)];
|
||||
serialize_code([]) ->
|
||||
[].
|
||||
|
||||
pad_args(List) ->
|
||||
case length(List) of
|
||||
0 -> List;
|
||||
N when N =< 4 ->
|
||||
lists:duplicate(4 - N, {stack, 0}) ++ List;
|
||||
N when N =< 8 ->
|
||||
lists:duplicate(8 - N, {stack, 0}) ++ List
|
||||
end.
|
||||
|
||||
serialize_data(_, Data) ->
|
||||
aeb_fate_encoding:serialize(Data).
|
||||
|
||||
%% 00 : stack/unused (depending on instruction)
|
||||
%% 01 : argN
|
||||
%% 10 : varN
|
||||
%% 11 : immediate
|
||||
modifier_bits(immediate, _) -> 2#11;
|
||||
modifier_bits(var, _) -> 2#10;
|
||||
modifier_bits(arg, _) -> 2#01;
|
||||
modifier_bits(stack, 0) -> 2#00;
|
||||
modifier_bits(Type, X) -> error({illegal_argument, Type, X}).
|
||||
|
||||
bits_to_modifier(2#11) -> immediate;
|
||||
bits_to_modifier(2#10) -> var;
|
||||
bits_to_modifier(2#01) -> arg;
|
||||
bits_to_modifier(2#00) -> stack.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Deserialization
|
||||
%%%===================================================================
|
||||
|
||||
deserialize(Bytes) ->
|
||||
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes),
|
||||
{SymbolTable, Rest2} = aeser_rlp:decode_one(Rest1),
|
||||
{Annotations, <<>>} = aeser_rlp:decode_one(Rest2),
|
||||
|
||||
Env = #{ function => none
|
||||
, bb => 0
|
||||
, current_bb_code => []
|
||||
, functions => #{}
|
||||
, code => #{}
|
||||
},
|
||||
#fcode{ functions = deserialize_functions(ByteCode, Env)
|
||||
, annotations = deserialize_annotations(Annotations)
|
||||
, symbols = deserialize_symbols(SymbolTable)
|
||||
}.
|
||||
|
||||
|
||||
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
|
||||
#{ function := none
|
||||
, bb := 0
|
||||
, current_bb_code := []
|
||||
} = Env) ->
|
||||
{Sig, Rest2} = deserialize_signature(Rest),
|
||||
Env2 = Env#{function => {<<A,B,C,D>>, Sig}},
|
||||
deserialize_functions(Rest2, Env2);
|
||||
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
|
||||
#{ function := {F, Sig}
|
||||
, bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program
|
||||
, functions := Funs} = Env) ->
|
||||
{NewSig, Rest2} = deserialize_signature(Rest),
|
||||
case Code of
|
||||
[] ->
|
||||
Env2 = Env#{ bb => 0
|
||||
, current_bb_code => []
|
||||
, function => {<<A,B,C,D>>, NewSig}
|
||||
, code => #{}
|
||||
, functions => Funs#{F => {Sig, Program}}},
|
||||
deserialize_functions(Rest2, Env2);
|
||||
_ ->
|
||||
Env2 = Env#{ bb => 0
|
||||
, current_bb_code => []
|
||||
, function => {<<A,B,C,D>>, NewSig}
|
||||
, code => #{}
|
||||
, functions =>
|
||||
Funs#{F => {Sig,
|
||||
Program#{ BB => lists:reverse(Code)}}}},
|
||||
deserialize_functions(Rest2, Env2)
|
||||
end;
|
||||
deserialize_functions(<<_Op:8, _Rest/binary>>,
|
||||
#{ function := none }) ->
|
||||
error({code_without_function});
|
||||
deserialize_functions(<<Op:8, Rest/binary>>,
|
||||
#{ bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program} = Env) ->
|
||||
{Rest2, OpCode} = deserialize_op(Op, Rest, Code),
|
||||
case aeb_fate_opcodes:end_bb(Op) of
|
||||
true ->
|
||||
deserialize_functions(Rest2, Env#{ bb => BB+1
|
||||
, current_bb_code => []
|
||||
, code => Program#{BB =>
|
||||
lists:reverse(OpCode)}});
|
||||
false ->
|
||||
deserialize_functions(Rest2, Env#{ current_bb_code => OpCode})
|
||||
end;
|
||||
deserialize_functions(<<>>, #{ function := none
|
||||
, functions := Funs}) ->
|
||||
Funs;
|
||||
deserialize_functions(<<>>, #{ function := {F, Sig}
|
||||
, bb := BB
|
||||
, current_bb_code := Code
|
||||
, code := Program
|
||||
, functions := Funs}) ->
|
||||
FunctionCode =
|
||||
case Code of
|
||||
[] -> Program;
|
||||
_ -> Program#{ BB => lists:reverse(Code)}
|
||||
end,
|
||||
Funs#{F => {Sig, FunctionCode}}.
|
||||
|
||||
deserialize_op(Op, Rest, Code) ->
|
||||
OpName = aeb_fate_opcodes:mnemonic(Op),
|
||||
case aeb_fate_opcodes:args(Op) of
|
||||
0 ->
|
||||
{Rest, [OpName | Code]};
|
||||
N ->
|
||||
{Args, Rest1} = deserialize_n_args(N, Rest),
|
||||
{Rest1, [list_to_tuple([OpName|Args])|Code]}
|
||||
end.
|
||||
|
||||
deserialize_n_args(N, <<M3:2, M2:2, M1:2, M0:2, Rest/binary>>) when N =< 4 ->
|
||||
{ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3]),
|
||||
assert_zero(Zeros),
|
||||
lists:mapfoldl(fun(M, Acc) ->
|
||||
case bits_to_modifier(M) of
|
||||
stack ->
|
||||
{{stack, 0}, Acc};
|
||||
Modifier ->
|
||||
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc),
|
||||
{{Modifier, Arg}, Acc2}
|
||||
end
|
||||
end, Rest, ArgMods);
|
||||
deserialize_n_args(N, <<M7:2, M6:2, M5:2, M4:2, M3:2, M2:2, M1:2, M0:2,
|
||||
Rest/binary>>) when N =< 8 ->
|
||||
{ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3, M4, M5, M6, M7]),
|
||||
assert_zero(Zeros),
|
||||
lists:mapfoldl(fun(M, Acc) ->
|
||||
case bits_to_modifier(M) of
|
||||
stack ->
|
||||
{{stack, 0}, Acc};
|
||||
Modifier ->
|
||||
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc),
|
||||
{{Modifier, Arg}, Acc2}
|
||||
end
|
||||
end, Rest, ArgMods).
|
||||
|
||||
deserialize_signature(Binary) ->
|
||||
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
|
||||
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
|
||||
{{Args, RetType}, Rest2}.
|
||||
|
||||
deserialize_symbols(Table) ->
|
||||
?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table),
|
||||
SymbolTable.
|
||||
|
||||
deserialize_annotations(AnnotationsBin) ->
|
||||
?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin),
|
||||
Annotations.
|
||||
|
||||
assert_zero([]) ->
|
||||
true;
|
||||
assert_zero([0|Rest]) ->
|
||||
assert_zero(Rest);
|
||||
assert_zero([_|_]) ->
|
||||
error(argument_defined_outside_range).
|
||||
+153
-1
@@ -96,7 +96,11 @@
|
||||
, make_bits/1
|
||||
, make_unit/0
|
||||
]).
|
||||
-export([format/1]).
|
||||
-export([
|
||||
elt/2
|
||||
, lt/2
|
||||
, format/1
|
||||
, ordinal/1]).
|
||||
|
||||
|
||||
make_boolean(true) -> ?FATE_TRUE;
|
||||
@@ -193,3 +197,151 @@ format_list(List) ->
|
||||
|
||||
format_kvs(List) ->
|
||||
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
|
||||
|
||||
|
||||
%% Total order of FATE terms.
|
||||
%% Integers < Booleans < Address < Channel < Contract < Name < Oracle
|
||||
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
|
||||
-spec ordinal(fate_type()) -> integer().
|
||||
ordinal(T) when ?IS_FATE_INTEGER(T) -> 0;
|
||||
ordinal(T) when ?IS_FATE_BOOLEAN(T) -> 1;
|
||||
ordinal(T) when ?IS_FATE_ADDRESS(T) -> 2;
|
||||
ordinal(T) when ?IS_FATE_CHANNEL(T) -> 3;
|
||||
ordinal(T) when ?IS_FATE_CONTRACT(T) -> 4;
|
||||
ordinal(T) when ?IS_FATE_NAME(T) -> 5;
|
||||
ordinal(T) when ?IS_FATE_ORACLE(T) -> 6;
|
||||
ordinal(T) when ?IS_FATE_HASH(T) -> 7;
|
||||
ordinal(T) when ?IS_FATE_SIGNATURE(T) -> 8;
|
||||
ordinal(T) when ?IS_FATE_BITS(T) -> 9;
|
||||
ordinal(T) when ?IS_FATE_STRING(T) -> 10;
|
||||
ordinal(T) when ?IS_FATE_TUPLE(T) -> 11;
|
||||
ordinal(T) when ?IS_FATE_MAP(T) -> 12;
|
||||
ordinal(T) when ?IS_FATE_LIST(T) -> 13;
|
||||
ordinal(T) when ?IS_FATE_VARIANT(T) -> 14.
|
||||
|
||||
|
||||
-spec lt(fate_type(), fate_type()) -> boolean().
|
||||
lt(A, B) ->
|
||||
O1 = ordinal(A),
|
||||
O2 = ordinal(B),
|
||||
if O1 == O2 -> lt(O1, A, B);
|
||||
true -> O1 < O2
|
||||
end.
|
||||
|
||||
%% Integers are ordered as usual.
|
||||
lt(0, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) ->
|
||||
?FATE_INTEGER_VALUE(A) < ?FATE_INTEGER_VALUE(B);
|
||||
%% false is smaller than true (true also for erlang booleans).
|
||||
lt(1, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) ->
|
||||
?FATE_BOOLEAN_VALUE(A) < ?FATE_BOOLEAN_VALUE(B);
|
||||
lt(9, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
|
||||
BitsA = ?FATE_BITS_VALUE(A),
|
||||
BitsB = ?FATE_BITS_VALUE(B),
|
||||
if BitsA < 0 ->
|
||||
if BitsB < 0 -> BitsA < BitsB;
|
||||
true -> false
|
||||
end;
|
||||
BitsB < 0 ->
|
||||
true;
|
||||
true -> BitsA < BitsB
|
||||
end;
|
||||
lt(10,?FATE_STRING(A), ?FATE_STRING(B)) ->
|
||||
SizeA = size(A),
|
||||
SizeB = size(B),
|
||||
case SizeA - SizeB of
|
||||
0 -> A < B;
|
||||
N -> N < 0
|
||||
end;
|
||||
|
||||
lt(11,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
|
||||
SizeA = size(A),
|
||||
SizeB = size(B),
|
||||
case SizeA - SizeB of
|
||||
0 -> tuple_elements_lt(0, A, B, SizeA);
|
||||
N -> N < 0
|
||||
end;
|
||||
lt(12, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
|
||||
SizeA = maps:size(A),
|
||||
SizeB = maps:size(B),
|
||||
case SizeA - SizeB of
|
||||
0 -> maps_lt(A, B);
|
||||
N -> N < 0
|
||||
end;
|
||||
lt(13, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false;
|
||||
lt(13, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true;
|
||||
lt(13, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
|
||||
O1 = ordinal(A),
|
||||
O2 = ordinal(B),
|
||||
if O1 == O2 ->
|
||||
if A == B -> lt(RA, RB);
|
||||
true -> A < B
|
||||
end;
|
||||
true -> O1 < O2
|
||||
end;
|
||||
lt(14, ?FATE_VARIANT(AritiesA, TagA, TA),
|
||||
?FATE_VARIANT(AritiesB, TagB, TB)) ->
|
||||
if length(AritiesA) < length(AritiesB) -> true;
|
||||
length(AritiesA) > length(AritiesB) -> false;
|
||||
true ->
|
||||
if AritiesA < AritiesB -> true;
|
||||
AritiesA > AritiesB -> false;
|
||||
true ->
|
||||
if TagA < TagB -> true;
|
||||
TagA > TagB -> false;
|
||||
true -> lt(make_tuple(TA), make_tuple(TB))
|
||||
end
|
||||
end
|
||||
end;
|
||||
lt(_, A, B) -> A < B.
|
||||
|
||||
tuple_elements_lt(N,_A,_B, N) ->
|
||||
false;
|
||||
tuple_elements_lt(N, A, B, Size) ->
|
||||
E = N + 1,
|
||||
EA = element(E, A),
|
||||
EB = element(E, B),
|
||||
if EA =:= EB -> tuple_elements_lt(E, A, B, Size);
|
||||
true -> lt(EA, EB)
|
||||
end.
|
||||
|
||||
maps_lt(A, B) ->
|
||||
IA = maps_iterator(A),
|
||||
IB = maps_iterator(B),
|
||||
maps_i_lt(IA, IB).
|
||||
|
||||
maps_i_lt(IA, IB) ->
|
||||
case {maps_next(IA), maps_next(IB)} of
|
||||
{none, none} -> false;
|
||||
{_, none} -> false;
|
||||
{none, _} -> true;
|
||||
{{KA1, VA1, IA2}, {KB1, VB1, IB2}} ->
|
||||
case lt(KA1, KB1) of
|
||||
true -> true;
|
||||
false ->
|
||||
case lt(KB1, KA1) of
|
||||
true -> false;
|
||||
false ->
|
||||
case lt(VA1, VB1) of
|
||||
true -> true;
|
||||
false ->
|
||||
case lt(VB1, VA1) of
|
||||
true -> false;
|
||||
false ->
|
||||
maps_i_lt(IA2, IB2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
maps_iterator(M) -> lists:sort(fun ({K1,_}, {K2,_}) -> lt(K1, K2) end, maps:to_list(M)).
|
||||
maps_next([]) -> none;
|
||||
maps_next([{K,V}|Rest]) -> {K, V, Rest}.
|
||||
|
||||
|
||||
-spec elt(fate_type(), fate_type()) -> boolean().
|
||||
elt(A, A) -> true;
|
||||
elt(A, B) ->
|
||||
R = lt(A, B),
|
||||
R.
|
||||
|
||||
|
||||
+126
-60
@@ -1,21 +1,29 @@
|
||||
%% Fate data (and instruction) serialization.
|
||||
%%
|
||||
%% The FATE serialization has to fullfill the following properties:
|
||||
%% * There has to be 1 and only 1 byte sequence
|
||||
%% representing each unique value in FATE.
|
||||
%% * A valid byte sequence has to be deserializable to a FATE value.
|
||||
%% * A valid byte sequence must not contain any trailing bytes.
|
||||
%% * A serialization is a sequence of 8-bit bytes.
|
||||
%%
|
||||
%% The serialization function should fullfill the following:
|
||||
%% * A valid FATE value should be serialized to a byte sequence.
|
||||
%% * Any other argument, not representing a valid FATE value should
|
||||
%% Assuming
|
||||
%% S is seralize/1 (fate_type() -> binary())
|
||||
%% D is deserialize/1 (binary) -> fate_type())
|
||||
%% V, V1, V2 are of the type fate_type()
|
||||
%% B is of the type binary()
|
||||
%% Then
|
||||
%% The FATE serialization has to fullfill the following properties:
|
||||
%% * For each value (V) in FATE there has to be a bytecode sequence (B)
|
||||
%% representing that value.
|
||||
%% * A valid byte sequence has to be deserializable to a FATE value.
|
||||
%% * A valid byte sequence must not contain any trailing bytes.
|
||||
%% * A serialization is a sequence of 8-bit bytes.
|
||||
%% The serialization function (S) should fullfill the following:
|
||||
%% * A valid FATE value should be serialized to a byte sequence.
|
||||
%% * Any other argument, not representing a valid FATE value should
|
||||
%% throw an exception
|
||||
%%
|
||||
%% The deserialization function should fullfill the following:
|
||||
%% * A valid byte sequence should be deserialized to a valid FATE value.
|
||||
%% * Any other argument, not representing a valid byte sequence should
|
||||
%% The deserialization function (D) should fullfill the following:
|
||||
%% * A valid byte sequence should be deserialized to a valid FATE value.
|
||||
%% * Any other argument, not representing a valid byte sequence should
|
||||
%% throw an exception
|
||||
%% The following equalities should hold:
|
||||
%% * D(S(V)) == V
|
||||
%% * if V1 == V2 then S(V1) == S(V2)
|
||||
%%
|
||||
%%
|
||||
%% History
|
||||
%% * First draft of FATE serialization encoding/decoding.
|
||||
@@ -40,6 +48,10 @@
|
||||
, serialize_type/1
|
||||
]).
|
||||
|
||||
-ifdef(EQC).
|
||||
-export([sort/1]).
|
||||
-endif.
|
||||
|
||||
-include("aeb_fate_data.hrl").
|
||||
|
||||
%% Definition of tag scheme.
|
||||
@@ -66,8 +78,8 @@
|
||||
%% 1011 0111
|
||||
%% 1100 0111
|
||||
%% 1101 0111
|
||||
%% 1110 0111
|
||||
%% 1111 0111
|
||||
-define(TYPE_VAR , 2#11100111). %% 1110 0111 | Id when 0 =< Id < 256 (type variable)
|
||||
-define(TYPE_ANY , 2#11110111). %% 1111 0111 - Any typedef
|
||||
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 | RLP encoded (size - 16) | [encoded elements],
|
||||
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 | [encoded elements] when 0 < size < 16
|
||||
%% 1111 Set below
|
||||
@@ -81,7 +93,6 @@
|
||||
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.)
|
||||
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
|
||||
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
|
||||
-define(NIL , 2#10111111). %% 1011 1111 - Empty list
|
||||
-define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
|
||||
-define(EMPTY_MAP , 2#11011111). %% 1101 1111
|
||||
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
|
||||
@@ -112,9 +123,7 @@
|
||||
-spec serialize(aeb_fate_data:fate_type()) -> binary().
|
||||
serialize(?FATE_TRUE) -> <<?TRUE>>;
|
||||
serialize(?FATE_FALSE) -> <<?FALSE>>;
|
||||
serialize(?FATE_NIL) -> <<?NIL>>; %% ! Untyped
|
||||
serialize(?FATE_UNIT) -> <<?EMPTY_TUPLE>>; %% ! Untyped
|
||||
serialize(M) when ?IS_FATE_MAP(M), ?FATE_MAP_SIZE(M) =:= 0 -> <<?EMPTY_MAP>>; %% ! Untyped
|
||||
serialize(?FATE_EMPTY_STRING) -> <<?EMPTY_STRING>>;
|
||||
serialize(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I);
|
||||
serialize(?FATE_BITS(Bits)) when is_integer(Bits) -> serialize_bits(Bits);
|
||||
@@ -128,7 +137,9 @@ serialize(String) when ?IS_FATE_STRING(String),
|
||||
?FATE_STRING_SIZE(String) > 0,
|
||||
?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE ->
|
||||
Bytes = ?FATE_STRING_VALUE(String),
|
||||
<<?LONG_STRING, (aeser_rlp:encode(Bytes))/binary>>;
|
||||
<<?LONG_STRING,
|
||||
(serialize_integer(?FATE_STRING_SIZE(String) - ?SHORT_STRING_SIZE))/binary
|
||||
, Bytes/binary>>;
|
||||
serialize(?FATE_ADDRESS(Address)) when is_binary(Address) ->
|
||||
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>;
|
||||
serialize(?FATE_HASH(Address)) when is_binary(Address) ->
|
||||
@@ -150,27 +161,28 @@ serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
|
||||
if S < ?SHORT_TUPLE_SIZE ->
|
||||
<<S:4, ?SHORT_TUPLE:4, Rest/binary>>;
|
||||
true ->
|
||||
Size = rlp_integer(S - ?SHORT_TUPLE_SIZE),
|
||||
Size = rlp_encode_int(S - ?SHORT_TUPLE_SIZE),
|
||||
<<?LONG_TUPLE:8, Size/binary, Rest/binary>>
|
||||
end;
|
||||
serialize(L) when ?IS_FATE_LIST(L) ->
|
||||
[_E|_] = List = ?FATE_LIST_VALUE(L),
|
||||
List = ?FATE_LIST_VALUE(L),
|
||||
S = length(List),
|
||||
Rest = << <<(serialize(El))/binary>> || El <- List >>,
|
||||
if S < ?SHORT_LIST_SIZE ->
|
||||
<<S:4, ?SHORT_LIST:4, Rest/binary>>;
|
||||
true ->
|
||||
Val = rlp_integer(S - ?SHORT_LIST_SIZE),
|
||||
Val = rlp_encode_int(S - ?SHORT_LIST_SIZE),
|
||||
<<?LONG_LIST, Val/binary, Rest/binary>>
|
||||
end;
|
||||
serialize(Map) when ?IS_FATE_MAP(Map) ->
|
||||
L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))),
|
||||
L = maps:to_list(?FATE_MAP_VALUE(Map)),
|
||||
Size = length(L),
|
||||
%% TODO: check all K same type, and all V same type
|
||||
%% check K =/= map
|
||||
Elements = << <<(serialize(K1))/binary, (serialize(V1))/binary>> || {K1,V1} <- L >>,
|
||||
Elements =
|
||||
list_to_binary([ <<(serialize(K))/binary, (serialize(V))/binary>> || {K, V} <- sort_and_check(L) ]),
|
||||
<<?MAP,
|
||||
(rlp_integer(Size))/binary,
|
||||
(rlp_encode_int(Size))/binary,
|
||||
(Elements)/binary>>;
|
||||
serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
|
||||
Arities = [A || A <- Arities, is_integer(A), A < 256],
|
||||
@@ -196,6 +208,8 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
|
||||
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
|
||||
serialize_type(integer) -> [?TYPE_INTEGER];
|
||||
serialize_type(boolean) -> [?TYPE_BOOLEAN];
|
||||
serialize_type(any) -> [?TYPE_ANY];
|
||||
serialize_type({tvar, N}) when 0 =< N, N =< 255 -> [?TYPE_VAR, N];
|
||||
serialize_type({list, T}) -> [?TYPE_LIST | serialize_type(T)];
|
||||
serialize_type({tuple, Ts}) ->
|
||||
case length(Ts) of
|
||||
@@ -223,6 +237,8 @@ serialize_type({variant, ListOfVariants}) ->
|
||||
-spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}.
|
||||
deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest};
|
||||
deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest};
|
||||
deserialize_type(<<?TYPE_ANY, Rest/binary>>) -> {any, Rest};
|
||||
deserialize_type(<<?TYPE_VAR, Id, Rest/binary>>) -> {{tvar, Id}, Rest};
|
||||
deserialize_type(<<?TYPE_LIST, Rest/binary>>) ->
|
||||
{T, Rest2} = deserialize_type(Rest),
|
||||
{{list, T}, Rest2};
|
||||
@@ -267,9 +283,23 @@ deserialize_types(N, Binary, Acc) ->
|
||||
|
||||
%% -----------------------------------------------------
|
||||
|
||||
rlp_integer(S) when S >= 0 ->
|
||||
rlp_encode_int(S) when S >= 0 ->
|
||||
aeser_rlp:encode(binary:encode_unsigned(S)).
|
||||
|
||||
|
||||
%% first byte of the binary gives the number of bytes we need <<129>> is 1, <<130>> = 2,
|
||||
%% so <<129, 0>> is <<0>> and <<130, 0, 0>> is <<0, 0>>
|
||||
rlp_decode_int(Binary) ->
|
||||
{Bin1, Rest} = aeser_rlp:decode_one(Binary),
|
||||
Int = binary:decode_unsigned(Bin1),
|
||||
ReEncode = rlp_encode_int(Int),
|
||||
case <<ReEncode/binary, Rest/binary>> == Binary of
|
||||
true ->
|
||||
{Int, Rest};
|
||||
false ->
|
||||
error({none_unique_encoding, Bin1, ReEncode})
|
||||
end.
|
||||
|
||||
serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
|
||||
V = ?FATE_INTEGER_VALUE(I),
|
||||
Abs = abs(V),
|
||||
@@ -279,20 +309,16 @@ serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
|
||||
end,
|
||||
if Abs < ?SMALL_INT_SIZE -> <<Sign:1, Abs:6, ?SMALL_INT:1>>;
|
||||
Sign =:= ?NEG_SIGN -> <<?NEG_BIG_INT,
|
||||
(rlp_integer(Abs - ?SMALL_INT_SIZE))/binary>>;
|
||||
(rlp_encode_int(Abs - ?SMALL_INT_SIZE))/binary>>;
|
||||
Sign =:= ?POS_SIGN -> <<?POS_BIG_INT,
|
||||
(rlp_integer(Abs - ?SMALL_INT_SIZE))/binary>>
|
||||
(rlp_encode_int(Abs - ?SMALL_INT_SIZE))/binary>>
|
||||
end.
|
||||
|
||||
serialize_bits(B) when is_integer(B) ->
|
||||
Abs = abs(B),
|
||||
Sign = case B < 0 of
|
||||
true -> ?NEG_SIGN;
|
||||
false -> ?POS_SIGN
|
||||
end,
|
||||
if
|
||||
Sign =:= ?NEG_SIGN -> <<?NEG_BITS, (rlp_integer(Abs))/binary>>;
|
||||
Sign =:= ?POS_SIGN -> <<?POS_BITS, (rlp_integer(Abs))/binary>>
|
||||
B < 0 -> <<?NEG_BITS, (rlp_encode_int(Abs))/binary>>;
|
||||
B >= 0 -> <<?POS_BITS, (rlp_encode_int(Abs))/binary>>
|
||||
end.
|
||||
|
||||
-spec deserialize(binary()) -> aeb_fate_data:fate_type().
|
||||
@@ -305,24 +331,33 @@ deserialize_one(B) -> deserialize2(B).
|
||||
deserialize2(<<?POS_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
|
||||
{?MAKE_FATE_INTEGER(I), Rest};
|
||||
deserialize2(<<?NEG_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
|
||||
{?MAKE_FATE_INTEGER(-I), Rest};
|
||||
if I =/= 0 -> {?MAKE_FATE_INTEGER(-I), Rest};
|
||||
I == 0 -> error({illegal_sign, I})
|
||||
end;
|
||||
deserialize2(<<?NEG_BIG_INT, Rest/binary>>) ->
|
||||
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE),
|
||||
{Bint, Rest2} = rlp_decode_int(Rest),
|
||||
{?MAKE_FATE_INTEGER(-Bint - ?SMALL_INT_SIZE),
|
||||
Rest2};
|
||||
deserialize2(<<?POS_BIG_INT, Rest/binary>>) ->
|
||||
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE),
|
||||
{Bint, Rest2} = rlp_decode_int(Rest),
|
||||
{?MAKE_FATE_INTEGER(Bint + ?SMALL_INT_SIZE),
|
||||
Rest2};
|
||||
deserialize2(<<?NEG_BITS, Rest/binary>>) ->
|
||||
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2};
|
||||
case rlp_decode_int(Rest) of
|
||||
{Pos, Rest2} when Pos > 0 ->
|
||||
{?FATE_BITS(-Pos), Rest2};
|
||||
{N, _} ->
|
||||
error({illegal_parameter, neg_bits, N})
|
||||
end;
|
||||
deserialize2(<<?POS_BITS, Rest/binary>>) ->
|
||||
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?FATE_BITS(binary:decode_unsigned(Bint)), Rest2};
|
||||
{Bint, Rest2} = rlp_decode_int(Rest),
|
||||
{?FATE_BITS(Bint), Rest2};
|
||||
deserialize2(<<?LONG_STRING, Rest/binary>>) ->
|
||||
{String, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?MAKE_FATE_STRING(String), Rest2};
|
||||
{S, Rest2} = deserialize_one(Rest),
|
||||
Size = S + ?SHORT_STRING_SIZE,
|
||||
String = binary:part(Rest2, 0, Size),
|
||||
Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)),
|
||||
{?MAKE_FATE_STRING(String), Rest3};
|
||||
deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
|
||||
String = binary:part(Rest, 0, S),
|
||||
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
|
||||
@@ -344,36 +379,37 @@ deserialize2(<<?TRUE, Rest/binary>>) ->
|
||||
{?FATE_TRUE, Rest};
|
||||
deserialize2(<<?FALSE, Rest/binary>>) ->
|
||||
{?FATE_FALSE, Rest};
|
||||
deserialize2(<<?NIL, Rest/binary>>) ->
|
||||
{?FATE_NIL, Rest};
|
||||
deserialize2(<<?EMPTY_TUPLE, Rest/binary>>) ->
|
||||
{?FATE_UNIT, Rest};
|
||||
deserialize2(<<?EMPTY_MAP, Rest/binary>>) ->
|
||||
{?MAKE_FATE_MAP(#{}), Rest};
|
||||
deserialize2(<<?EMPTY_STRING, Rest/binary>>) ->
|
||||
{?FATE_EMPTY_STRING, Rest};
|
||||
deserialize2(<<?LONG_TUPLE, Rest/binary>>) ->
|
||||
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
|
||||
N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE,
|
||||
{Size, Rest1} = rlp_decode_int(Rest),
|
||||
N = Size + ?SHORT_TUPLE_SIZE,
|
||||
{List, Rest2} = deserialize_elements(N, Rest1),
|
||||
{?FATE_TUPLE(list_to_tuple(List)), Rest2};
|
||||
deserialize2(<<S:4, ?SHORT_TUPLE:4, Rest/binary>>) ->
|
||||
{List, Rest1} = deserialize_elements(S, Rest),
|
||||
{?FATE_TUPLE(list_to_tuple(List)), Rest1};
|
||||
deserialize2(<<?LONG_LIST, Rest/binary>>) ->
|
||||
{BLength, Rest1} = aeser_rlp:decode_one(Rest),
|
||||
Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE,
|
||||
{Size, Rest1} = rlp_decode_int(Rest),
|
||||
Length = Size + ?SHORT_LIST_SIZE,
|
||||
{List, Rest2} = deserialize_elements(Length, Rest1),
|
||||
{?MAKE_FATE_LIST(List), Rest2};
|
||||
deserialize2(<<S:4, ?SHORT_LIST:4, Rest/binary>>) ->
|
||||
{List, Rest1} = deserialize_elements(S, Rest),
|
||||
{?MAKE_FATE_LIST(List), Rest1};
|
||||
deserialize2(<<?MAP, Rest/binary>>) ->
|
||||
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
|
||||
Size = binary:decode_unsigned(BSize),
|
||||
{Size, Rest1} = rlp_decode_int(Rest),
|
||||
{List, Rest2} = deserialize_elements(2*Size, Rest1),
|
||||
Map = insert_kv(List, #{}),
|
||||
{?MAKE_FATE_MAP(Map), Rest2};
|
||||
KVList = insert_kv(List),
|
||||
case sort_and_check(KVList) == KVList of
|
||||
true ->
|
||||
Map = maps:from_list(KVList),
|
||||
{?MAKE_FATE_MAP(Map), Rest2};
|
||||
false ->
|
||||
error({unknown_map_serialization_format, KVList})
|
||||
end;
|
||||
deserialize2(<<?VARIANT, Rest/binary>>) ->
|
||||
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
|
||||
Arities = binary_to_list(AritiesBin),
|
||||
@@ -390,8 +426,8 @@ deserialize2(<<?VARIANT, Rest/binary>>) ->
|
||||
end
|
||||
end.
|
||||
|
||||
insert_kv([], M) -> M;
|
||||
insert_kv([K,V|R], M) -> insert_kv(R, maps:put(K, V, M)).
|
||||
insert_kv([]) -> [];
|
||||
insert_kv([K, V | R]) -> [{K, V} | insert_kv(R)].
|
||||
|
||||
deserialize_elements(0, Rest) ->
|
||||
{[], Rest};
|
||||
@@ -399,3 +435,33 @@ deserialize_elements(N, Es) ->
|
||||
{E, Rest} = deserialize2(Es),
|
||||
{Tail, Rest2} = deserialize_elements(N-1, Rest),
|
||||
{[E|Tail], Rest2}.
|
||||
|
||||
|
||||
%% It is important to remove duplicated keys.
|
||||
%% For deserialize this check is needed to observe illegal duplicates.
|
||||
sort_and_check(List) ->
|
||||
UniqKeyList =
|
||||
lists:foldr(fun({K, V}, Acc) ->
|
||||
case valid_key_type(K) andalso not lists:keymember(K, 1, Acc) of
|
||||
true -> [{K,V}|Acc];
|
||||
false -> Acc
|
||||
end
|
||||
end, [], List),
|
||||
sort(UniqKeyList).
|
||||
|
||||
%% Sorting is used to get a unique result.
|
||||
%% Deserialization is checking whether the provided key-value pairs are sorted
|
||||
%% and raises an exception if not.
|
||||
|
||||
sort(KVList) ->
|
||||
SortFun = fun({K1, _}, {K2, _}) ->
|
||||
aeb_fate_data:elt(K1, K2)
|
||||
end,
|
||||
lists:sort(SortFun, KVList).
|
||||
|
||||
valid_key_type(K) when ?IS_FATE_MAP(K) ->
|
||||
error({map_as_key_in_map, K});
|
||||
valid_key_type(K) when ?IS_FATE_VARIANT(K) ->
|
||||
error({variant_as_key_in_map, K});
|
||||
valid_key_type(_K) ->
|
||||
true.
|
||||
|
||||
+186
-147
@@ -16,140 +16,164 @@ generate() -> generate("src/", "include/").
|
||||
get_ops() -> gen(ops_defs()).
|
||||
|
||||
generate(Src, Include) ->
|
||||
check_defs(ops_defs()),
|
||||
Ops = get_ops(),
|
||||
%% io:format("ops: ~p\n", [Ops]),
|
||||
HrlFile = Include ++ "aeb_fate_opcodes.hrl",
|
||||
generate_header_file(HrlFile, Ops),
|
||||
generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops),
|
||||
generate_code_ops(aeb_fate_code, Src, Ops),
|
||||
generate_code_ops(aeb_fate_ops, Src, Ops),
|
||||
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops),
|
||||
gen_asm_pp(aeb_fate_pp, Src, Ops).
|
||||
|
||||
check_defs(List) ->
|
||||
true = check_numbering(0, lists:keysort(2, List)).
|
||||
|
||||
check_numbering(N, [T|Rest]) ->
|
||||
OpCode = element(2, T),
|
||||
case OpCode of
|
||||
N -> check_numbering(N+1, Rest);
|
||||
16#fa -> check_numbering(16#fa+1, Rest);
|
||||
_ when OpCode < N -> {duplicate_opcode, OpCode};
|
||||
_ when OpCode > N -> {missing_opcode, N}
|
||||
end;
|
||||
check_numbering(_, []) -> true.
|
||||
|
||||
|
||||
%% TODO: Some real gas numbers...
|
||||
ops_defs() ->
|
||||
%% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation
|
||||
[ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0. The type of the retun value has to match the return type of the function."}
|
||||
, { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0. The type of the retun value has to match the return type of the function."}
|
||||
, { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack. The types of the arguments has to match the argument typs of the function."}
|
||||
, { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function. The types of the arguments has to match the argument typs of the function."}
|
||||
, { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
|
||||
, { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
|
||||
, { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."}
|
||||
, { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
|
||||
, { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'SWITCH_VN', 16#0a, 2, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."}
|
||||
, { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."}
|
||||
, { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."}
|
||||
, { 'POP', 16#0e, 1, false, 3, [a], pop, "Arg0 := top of stack."}
|
||||
, { 'STORE', 16#0f, 2, false, 3, [a,a], store, "Arg0 := Arg1."}
|
||||
, { 'INCA', 16#10, 0, false, 2, atomic, inc, "Increment accumulator."}
|
||||
, { 'INC', 16#11, 1, false, 2, [a], inc, "Increment argument."}
|
||||
, { 'DECA', 16#12, 0, false, 2, atomic, dec, "Decrement accumulator."}
|
||||
, { 'DEC', 16#13, 1, false, 2, [a], dec, "Decrement argument."}
|
||||
, { 'ADD', 16#14, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."}
|
||||
, { 'SUB', 16#15, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."}
|
||||
, { 'MUL', 16#16, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."}
|
||||
, { 'DIV', 16#17, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."}
|
||||
, { 'MOD', 16#18, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."}
|
||||
, { 'POW', 16#19, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."}
|
||||
, { 'LT', 16#20, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."}
|
||||
, { 'GT', 16#21, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."}
|
||||
, { 'EQ', 16#22, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."}
|
||||
, { 'ELT', 16#23, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."}
|
||||
, { 'EGT', 16#24, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."}
|
||||
, { 'NEQ', 16#25, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."}
|
||||
, { 'AND', 16#26, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."}
|
||||
, { 'OR', 16#27, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."}
|
||||
, { 'NOT', 16#28, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."}
|
||||
, { 'TUPLE', 16#29, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."}
|
||||
, { 'ELEMENT', 16#2a, 3, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."}
|
||||
, { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."}
|
||||
, { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."}
|
||||
, { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
|
||||
, { 'MAP_UPDATE', 16#2e, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
|
||||
, { 'MAP_DELETE', 16#2f, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."}
|
||||
, { 'MAP_MEMBER', 16#30, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."}
|
||||
, { 'MAP_FROM_LIST',16#31, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."}
|
||||
, { 'NIL', 16#32, 1, false, 3, [a], nil, "Arg0 := []."}
|
||||
, { 'IS_NIL', 16#33, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."}
|
||||
, { 'CONS', 16#34, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."}
|
||||
, { 'HD', 16#35, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."}
|
||||
, { 'TL', 16#36, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."}
|
||||
, { 'LENGTH', 16#37, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."}
|
||||
, { 'STR_EQ', 16#38, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."}
|
||||
, { 'STR_JOIN', 16#39, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."}
|
||||
, { 'INT_TO_STR', 16#40, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."}
|
||||
, { 'ADDR_TO_STR', 16#41, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."}
|
||||
, { 'STR_REVERSE', 16#42, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."}
|
||||
, { 'INT_TO_ADDR', 16#43, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."}
|
||||
, { 'VARIANT', 16#44, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
|
||||
, { 'VARIANT_TEST', 16#45, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."}
|
||||
, { 'VARIANT_ELEMENT',16#46, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."}
|
||||
, { 'BITS_NONEA', 16#47, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."}
|
||||
, { 'BITS_NONE', 16#48, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."}
|
||||
, { 'BITS_ALLA', 16#49, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."}
|
||||
, { 'BITS_ALL', 16#50, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."}
|
||||
, { 'BITS_ALL_N', 16#51, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."}
|
||||
, { 'BITS_SET', 16#52, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."}
|
||||
, { 'BITS_CLEAR', 16#53, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."}
|
||||
, { 'BITS_TEST', 16#54, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
|
||||
, { 'BITS_SUM', 16#55, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
|
||||
, { 'BITS_OR', 16#56, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."}
|
||||
, { 'BITS_AND', 16#57, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."}
|
||||
, { 'BITS_DIFF', 16#58, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."}
|
||||
, { 'ADDRESS', 16#59, 1, false, 3, [a], address, "Arg0 := The current contract address."}
|
||||
, { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract balance."}
|
||||
, { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."}
|
||||
, { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."}
|
||||
, { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."}
|
||||
, { 'BLOCKHASH', 16#5e, 2, false, 3, [a, a], blockhash, "Arg0 := The blockhash at height."}
|
||||
, { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."}
|
||||
, { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
|
||||
, { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."}
|
||||
, { 'MICROBLOCK', 16#62, 1, false, 3, [a], microblock, "Arg0 := The current micro block number."}
|
||||
, { 'DIFFICULTY', 16#63, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."}
|
||||
, { 'GASLIMIT', 16#64, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."}
|
||||
, { 'GAS', 16#65, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."}
|
||||
%% Opname, Opcode, end_bb, gas, format, Constructor, ArgType, ResType, Documentation
|
||||
[ { 'RETURN', 16#00, true, 2, [], return, {}, any, "Return from function call, top of stack is return value . The type of the retun value has to match the return type of the function."}
|
||||
, { 'RETURNR', 16#01, true, 2, [a], returnr, {any}, any, "Push Arg0 and return from function. The type of the retun value has to match the return type of the function."}
|
||||
, { 'CALL', 16#02, true, 4, [a], call, {string}, any, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."}
|
||||
, { 'CALL_R', 16#03, true, 8, [a,is,a], call_r, {contract, string, integer}, any, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."}
|
||||
, { 'CALL_T', 16#04, true, 4, [a], call_t, {string}, any, "Tail call to function Arg0. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
|
||||
, { 'CALL_TR', 16#05, true, 8, [a,is,a], call_tr, {contract, string, integer}, any, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
|
||||
, { 'CALL_GR', 16#06, true, 8, [a,is,a,a], call_gr, {contract, string, integer, integer}, any, "Remote call with gas cap in Arg3. Otherwise as CALL_R."}
|
||||
, { 'CALL_GTR', 16#07, true, 8, [a,is,a,a], call_gtr, {contract, string, integer, integer}, any, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."}
|
||||
, { 'JUMP', 16#08, true, 3, [ii], jump, {integer}, none, "Jump to a basic block. The basic block has to exist in the current function."}
|
||||
, { 'JUMPIF', 16#09, true, 4, [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
|
||||
, { 'SWITCH_V2', 16#0a, true, 4, [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'SWITCH_V3', 16#0b, true, 4, [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'SWITCH_VN', 16#0c, true, 4, [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
|
||||
, { 'CALL_VALUE', 16#0d, false, 3, [a], call_value, {}, integer, "The value sent in the current remote call."}
|
||||
, { 'PUSH', 16#0e, false, 2, [a], push, {any}, any, "Push argument to stack."}
|
||||
, { 'DUPA', 16#0f, false, 3, [], dup, {any}, any, "Duplicate top of stack."}
|
||||
, { 'DUP', 16#10, false, 3, [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
|
||||
, { 'POP', 16#11, false, 3, [a], pop, {integer}, integer, "Arg0 := top of stack."}
|
||||
, { 'INCA', 16#12, false, 2, [], inc, {integer}, integer, "Increment accumulator."}
|
||||
, { 'INC', 16#13, false, 2, [a], inc, {integer}, integer, "Increment argument."}
|
||||
, { 'DECA', 16#14, false, 2, [], dec, {integer}, integer, "Decrement accumulator."}
|
||||
, { 'DEC', 16#15, false, 2, [a], dec, {integer}, integer, "Decrement argument."}
|
||||
, { 'ADD', 16#16, false, 3, [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
|
||||
, { 'SUB', 16#17, false, 3, [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
|
||||
, { 'MUL', 16#18, false, 3, [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
|
||||
, { 'DIV', 16#19, false, 3, [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
|
||||
, { 'MOD', 16#1a, false, 3, [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
|
||||
, { 'POW', 16#1b, false, 3, [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
|
||||
, { 'STORE', 16#1c, false, 3, [a,a], store, {any}, any, "Arg0 := Arg1."}
|
||||
, { 'SHA3', 16#1d, false, 30, [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
|
||||
, { 'SHA256', 16#1e, false, 30, [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
|
||||
, { 'BLAKE2B', 16#1f, false, 30, [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
|
||||
, { 'LT', 16#20, false, 3, [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
|
||||
, { 'GT', 16#21, false, 3, [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
|
||||
, { 'EQ', 16#22, false, 3, [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
|
||||
, { 'ELT', 16#23, false, 3, [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
|
||||
, { 'EGT', 16#24, false, 3, [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
|
||||
, { 'NEQ', 16#25, false, 3, [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
|
||||
, { 'AND', 16#26, false, 3, [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
|
||||
, { 'OR', 16#27, false, 3, [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
|
||||
, { 'NOT', 16#28, false, 3, [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
|
||||
, { 'TUPLE', 16#29, false, 3, [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
|
||||
, { 'ELEMENT', 16#2a, false, 3, [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
|
||||
, { 'SETELEMENT', 16#2b, false, 3, [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
|
||||
, { 'MAP_EMPTY', 16#2c, false, 3, [a], map_empty, {}, map, "Arg0 := #{}."}
|
||||
, { 'MAP_LOOKUP', 16#2d, false, 3, [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
|
||||
, { 'MAP_LOOKUPD', 16#2e, false, 3, [a,a,a,a], map_lookup, {map, any, any}, any, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
|
||||
, { 'MAP_UPDATE', 16#2f, false, 3, [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
|
||||
, { 'MAP_DELETE', 16#30, false, 3, [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
|
||||
, { 'MAP_MEMBER', 16#31, false, 3, [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
|
||||
, { 'MAP_FROM_LIST', 16#32, false, 3, [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
|
||||
, { 'IS_NIL', 16#33, false, 3, [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
|
||||
, { 'CONS', 16#34, false, 3, [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
|
||||
, { 'HD', 16#35, false, 3, [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
|
||||
, { 'TL', 16#36, false, 3, [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
|
||||
, { 'LENGTH', 16#37, false, 3, [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
|
||||
, { 'NIL', 16#38, false, 3, [a], nil, {}, list, "Arg0 := []."}
|
||||
, { 'STR_JOIN', 16#39, false, 3, [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
|
||||
, { 'INT_TO_STR', 16#3a, false, 3, [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
|
||||
, { 'ADDR_TO_STR', 16#3b, false, 3, [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
|
||||
, { 'STR_REVERSE', 16#3c, false, 3, [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
|
||||
, { 'APPEND', 16#3d, false, 3, [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
|
||||
, { 'INT_TO_ADDR', 16#3e, false, 3, [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
|
||||
, { 'VARIANT', 16#3f, false, 3, [a,a,a,a], variant, {integer, integer, integer}, variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
|
||||
, { 'VARIANT_TEST', 16#40, false, 3, [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
|
||||
, { 'VARIANT_ELEMENT', 16#41, false, 3, [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
|
||||
, { 'BITS_NONEA', 16#42, false, 3, [], bits_none, {}, bits, "push an empty bitmap on the stack."}
|
||||
, { 'BITS_NONE', 16#43, false, 3, [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
|
||||
, { 'BITS_ALLA', 16#44, false, 3, [], bits_all, {}, bits, "push a full bitmap on the stack."}
|
||||
, { 'BITS_ALL', 16#45, false, 3, [a], bits_all, {}, bits, "Arg0 := full bitmap."}
|
||||
, { 'BITS_ALL_N', 16#46, false, 3, [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
|
||||
, { 'BITS_SET', 16#47, false, 3, [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
|
||||
, { 'BITS_CLEAR', 16#48, false, 3, [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
|
||||
, { 'BITS_TEST', 16#49, false, 3, [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
|
||||
, { 'BITS_SUM', 16#4a, false, 3, [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
|
||||
, { 'BITS_OR', 16#4b, false, 3, [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
|
||||
, { 'BITS_AND', 16#4c, false, 3, [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
|
||||
, { 'BITS_DIFF', 16#4d, false, 3, [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
|
||||
, { 'BALANCE', 16#4e, false, 3, [a], balance, {}, integer, "Arg0 := The current contract balance."}
|
||||
, { 'ORIGIN', 16#4f, false, 3, [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
|
||||
, { 'CALLER', 16#50, false, 3, [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
|
||||
, { 'GASPRICE', 16#51, false, 3, [a], gasprice, {}, integer, "Arg0 := The current gas price."}
|
||||
, { 'BLOCKHASH', 16#52, false, 3, [a,a], blockhash, {integer}, hash, "Arg0 := The blockhash at height."}
|
||||
, { 'BENEFICIARY', 16#53, false, 3, [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
|
||||
, { 'TIMESTAMP', 16#54, false, 3, [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
|
||||
, { 'GENERATION', 16#55, false, 3, [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
|
||||
, { 'MICROBLOCK', 16#56, false, 3, [a], microblock, {}, integer, "Arg0 := The current micro block number."}
|
||||
, { 'DIFFICULTY', 16#57, false, 3, [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
|
||||
, { 'GASLIMIT', 16#58, false, 3, [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
|
||||
, { 'GAS', 16#59, false, 3, [a], gas, {}, integer, "Arg0 := The amount of gas left."}
|
||||
, { 'ADDRESS', 16#5a, false, 3, [a], address, {}, address, "Arg0 := The current contract address."}
|
||||
|
||||
, { 'LOG0', 16#66, 2, false, 3, [a,a], log, "Create a log message in the call object."}
|
||||
, { 'LOG1', 16#67, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."}
|
||||
, { 'LOG2', 16#68, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."}
|
||||
, { 'LOG3', 16#69, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."}
|
||||
, { 'LOG4', 16#6a, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."}
|
||||
, { 'DEACTIVATE', 16#6b, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."}
|
||||
, { 'LOG0', 16#5b, false, 3, [a], log, {string}, none, "Create a log message in the call object."}
|
||||
, { 'LOG1', 16#5c, false, 3, [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
|
||||
, { 'LOG2', 16#5d, false, 3, [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
|
||||
, { 'LOG3', 16#5e, false, 3, [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
|
||||
, { 'LOG4', 16#5f, false, 3, [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
|
||||
%% Transaction ops
|
||||
, { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."}
|
||||
, { 'ORACLE_REGISTER', 16#6d, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."}
|
||||
, { 'SPEND', 16#60, false, 3, [a,a], spend, {address, integer}, none, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."}
|
||||
, { 'ORACLE_REGISTER', 16#61, false, 3, [a,a,a,a,a,a], oracle_register, {any,any,any,any,any}, any, "NYI"}
|
||||
%% TODO:
|
||||
, { 'ORACLE_QUERY', 16#6e, 0, false,3, atomic, oracle_query, ""}
|
||||
, { 'ORACLE_RESPOND', 16#6f, 0, false,3, atomic, oracle_respond, ""}
|
||||
, { 'ORACLE_EXTEND', 16#70, 0, false,3, atomic, oracle_extend, ""}
|
||||
, { 'ORACLE_GET_ANSWER', 16#71, 0, false,3, atomic, oracle_get_answer, ""}
|
||||
, { 'ORACLE_GET_QUESTION', 16#72, 0, false,3, atomic,oracle_get_question, ""}
|
||||
, { 'ORACLE_QUERY_FEE', 16#73, 0, false,3, atomic, oracle_query_fee, ""}
|
||||
, { 'AENS_RESOLVE', 16#74, 0, false,3, atomic, aens_resolve, ""}
|
||||
, { 'AENS_PRECLAIM', 16#75, 0, false,3, atomic, aens_preclaim, ""}
|
||||
, { 'AENS_CLAIM', 16#76, 0, false,3, atomic, aens_claim, ""}
|
||||
, { 'AENS_UPDATE', 16#77, 0, false,3, atomic, aend_update, ""}
|
||||
, { 'AENS_TRANSFER', 16#78, 0, false,3, atomic, aens_transfer, ""}
|
||||
, { 'AENS_REVOKE', 16#79, 0, false,3, atomic, aens_revoke, ""}
|
||||
, { 'ECVERIFY', 16#7a, 0, false,3, atomic, ecverify, ""}
|
||||
, { 'SHA3', 16#7b, 0, false,3, atomic, sha3, ""}
|
||||
, { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""}
|
||||
, { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""}
|
||||
, { 'BALANCE_OTHER', 16#7e, 2, false,3, [a,a], balance_other, "Arg0 := The balance of address Arg1."}
|
||||
, { 'SETELEMENT', 16#7f, 4, false,3, [a,a,a,a], setelement, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
|
||||
, { 'ORACLE_QUERY', 16#62, false, 3, [], oracle_query, {}, none, "NYI"}
|
||||
, { 'ORACLE_RESPOND', 16#63, false, 3, [], oracle_respond, {}, none, "NYI"}
|
||||
, { 'ORACLE_EXTEND', 16#64, false, 3, [], oracle_extend, {}, none, "NYI"}
|
||||
, { 'ORACLE_GET_ANSWER', 16#65, false, 3, [], oracle_get_answer, {}, none, "NYI"}
|
||||
, { 'ORACLE_GET_QUESTION', 16#66, false, 3, [], oracle_get_question, {}, none, "NYI"}
|
||||
, { 'ORACLE_QUERY_FEE', 16#67, false, 3, [], oracle_query_fee, {}, none, "NYI"}
|
||||
, { 'AENS_RESOLVE', 16#68, false, 3, [], aens_resolve, {}, none, "NYI"}
|
||||
, { 'AENS_PRECLAIM', 16#69, false, 3, [], aens_preclaim, {}, none, "NYI"}
|
||||
, { 'AENS_CLAIM', 16#6a, false, 3, [], aens_claim, {}, none, "NYI"}
|
||||
, { 'AENS_UPDATE', 16#6b, false, 3, [], aend_update, {}, none, "NYI"}
|
||||
, { 'AENS_TRANSFER', 16#6c, false, 3, [], aens_transfer, {}, none, "NYI"}
|
||||
, { 'AENS_REVOKE', 16#6d, false, 3, [], aens_revoke, {}, none, "NYI"}
|
||||
, { 'BALANCE_OTHER', 16#6e, false, 3, [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
|
||||
%% TODO: Reorder these before documenting the specification
|
||||
, { 'MAP_SIZE', 16#6f, false, 3, [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
|
||||
, { 'MAP_TO_LIST', 16#70, false, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
|
||||
, { 'STR_LENGTH', 16#71, false, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
|
||||
|
||||
, { 'ECVERIFY', 16#72, false, 1300, [a,a,a,a], ecverify, {hash, address, signature}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"}
|
||||
, { 'ECVERIFY_SECP256K1', 16#73, false, 1300, [a,a,a,a], ecverify_secp256k1, {hash, signature, signature}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"}
|
||||
|
||||
, { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."}
|
||||
, { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."}
|
||||
, { 'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."}
|
||||
, { 'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."}
|
||||
, { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."}
|
||||
%% FUNCTION 16#fe "Function declaration and entrypoint."
|
||||
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
|
||||
, { 'CONTRACT_TO_ADDRESS', 16#74, false, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
|
||||
, { 'AUTH_TX_HASH', 16#75, false, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
|
||||
|
||||
, { 'DEACTIVATE', 16#fa, false, 3, [], deactivate, {}, none, "Mark the current contract for deactivation."}
|
||||
, { 'ABORT', 16#fb, true, 3, [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
|
||||
, { 'EXIT', 16#fc, true, 3, [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
|
||||
, { 'NOP', 16#fd, false, 1, [], nop, {}, none, "The no op. does nothing."}
|
||||
%% FUNCTION 16#fe "Function declaration and entrypoint."
|
||||
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
|
||||
].
|
||||
|
||||
|
||||
@@ -215,7 +239,7 @@ generate_code_ops(Modulename, SrcDir, Ops) ->
|
||||
"-type fate_arg_immediate(T) :: {immediate, T}.\n"
|
||||
"-type fate_arg_var() :: {var, integer()}.\n"
|
||||
"-type fate_arg_arg() :: {arg, integer()}.\n"
|
||||
"-type fate_arg_stack() :: {stack, integer()}.\n"
|
||||
"-type fate_arg_stack() :: {stack, 0}.\n"
|
||||
"-type fate_arg() :: fate_arg_immediate()\n"
|
||||
" | fate_arg_var()\n"
|
||||
" | fate_arg_arg()\n"
|
||||
@@ -246,7 +270,7 @@ gen_type_exports(#{type_name := TypeName}) ->
|
||||
gen_constructor_exports(#{constructor_type := Function}) ->
|
||||
lists:flatten(io_lib:format(" , ~s\n", [Function])).
|
||||
|
||||
gen_constructors(#{constructor := Function, format := atomic,
|
||||
gen_constructors(#{constructor := Function, format := [],
|
||||
type_name := Type, opname := Name}) ->
|
||||
lists:flatten(io_lib:format("-spec ~s() -> ~s.\n"
|
||||
"~s() ->\n"
|
||||
@@ -287,7 +311,7 @@ gen_arg_uses(_, []) ->
|
||||
gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]);
|
||||
gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]);
|
||||
gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]);
|
||||
gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]);
|
||||
gen_arg_uses(N, [li]) -> io_lib:format("{immediate, Arg~w}", [N]);
|
||||
gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]);
|
||||
gen_arg_uses(N, [a | Args]) ->
|
||||
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args);
|
||||
@@ -320,9 +344,9 @@ gen_m_to_op(#{opname := Name, macro := Macro}) ->
|
||||
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
|
||||
[Name, Macro])).
|
||||
|
||||
gen_args(#{macro := Macro, args := Args}) ->
|
||||
gen_args(#{macro := Macro, arity := Arity}) ->
|
||||
lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n",
|
||||
[Macro, Args])).
|
||||
[Macro, Arity])).
|
||||
|
||||
gen_bb(#{macro := Macro, end_bb := EndBB}) ->
|
||||
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
|
||||
@@ -347,21 +371,22 @@ gen_defines(#{opname := Name, opcode := OpCode}) ->
|
||||
|
||||
gen([]) ->
|
||||
[];
|
||||
gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) ->
|
||||
gen([{OpName, OpCode, EndBB, Gas, FateFormat, Constructor, ArgTypes, ResType, Doc} | Rest]) ->
|
||||
Arity = length(FateFormat),
|
||||
Name = atom_to_list(OpName),
|
||||
LowerName = string:to_lower(Name),
|
||||
TypeName = "fate_" ++ LowerName ++ "()",
|
||||
Macro = "?" ++ Name,
|
||||
Type = case FateFormat of
|
||||
atomic -> io_lib:format("~w", [OpName]);
|
||||
ArgTypes ->
|
||||
io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)])
|
||||
[] -> io_lib:format("~w", [OpName]);
|
||||
Args ->
|
||||
io_lib:format("{~w, ~s}", [OpName, expand_types(Args)])
|
||||
end,
|
||||
ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]),
|
||||
ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Arity]),
|
||||
|
||||
[#{ opname => OpName
|
||||
, opcode => OpCode
|
||||
, args => Args
|
||||
, arity => Arity
|
||||
, end_bb => EndBB
|
||||
, format => FateFormat
|
||||
, macro => Macro
|
||||
@@ -371,6 +396,8 @@ gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -
|
||||
, type => Type
|
||||
, constructor => Constructor
|
||||
, constructor_type => ConstructorType
|
||||
, arg_types => ArgTypes
|
||||
, res_type => ResType
|
||||
}| gen(Rest)].
|
||||
|
||||
|
||||
@@ -381,7 +408,7 @@ expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts).
|
||||
expand_type(a) -> "fate_arg()";
|
||||
expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())";
|
||||
expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())";
|
||||
expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]";
|
||||
expand_type(li) -> "fate_arg_immediate([aeb_fate_data:fate_integer()])";
|
||||
expand_type(t) -> "aeb_fate_data:fate_type_type()".
|
||||
|
||||
generate_scanner(TemplateFile, Outfile, Path, Ops) ->
|
||||
@@ -423,28 +450,40 @@ gen_asm_pp(Module, Path, Ops) ->
|
||||
"format_arg(_, {immediate, I}) ->\n"
|
||||
" aeb_fate_data:format(I);\n"
|
||||
"format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n"
|
||||
"format_arg(a, {var, N}) when N < 0 -> io_lib:format(\"store~~p\", [-N]);\n"
|
||||
"format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n"
|
||||
"format_arg(a, {stack, 0}) -> \"a\";\n"
|
||||
"format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n"
|
||||
"format_arg(a, {stack, 0}) -> \"a\".\n\n"
|
||||
"lookup(Name, Symbols) ->\n"
|
||||
" maps:get(Name, Symbols, io_lib:format(\"~~w\",[Name])).\n\n"
|
||||
" maps:get(Name, Symbols, io_lib:format(\"~~p\",[Name])).\n\n"
|
||||
"~s"
|
||||
, [Formats]),
|
||||
|
||||
io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []),
|
||||
file:close(File).
|
||||
|
||||
gen_format(#{opname := Name}) when ('CALL' =:= Name) or (Name =:= 'CALL_T') ->
|
||||
io_lib:format("format_op({~w, {immediate, Function}}, Symbols) ->\n"
|
||||
"[\"~s \", lookup(Function, Symbols)];",
|
||||
[Name, atom_to_list(Name)]);
|
||||
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') ->
|
||||
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}}, Symbols) ->\n"
|
||||
"[\"~s \", lookup(Contract, Symbols), \".\", lookup(Function, Symbols)];\n"
|
||||
"format_op({~w, Contract, {immediate, Function}}, Symbols) ->\n"
|
||||
"[\"~s \", format_arg(a, Contract), \".\", lookup(Function, Symbols)];",
|
||||
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value}, Symbols) ->\n"
|
||||
" [\"~s \", lookup(Contract, Symbols), \".\", "
|
||||
"lookup(Function, Symbols), \" \", "
|
||||
"format_arg(a, Value)];\n"
|
||||
"format_op({~w, Contract, {immediate, Function}, Value}, Symbols) ->\n"
|
||||
"[\"~s \", format_arg(a, Contract), \".\", "
|
||||
"lookup(Function, Symbols), \" \", "
|
||||
"format_arg(a, Value)];\n",
|
||||
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
|
||||
gen_format(#{opname := Name, format := atomic}) ->
|
||||
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') or (Name =:= 'CALL_GTR') ->
|
||||
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value, Gas}, Symbols) ->\n"
|
||||
" [\"~s \", lookup(Contract, Symbols), \".\", "
|
||||
"lookup(Function, Symbols), \" \", "
|
||||
"format_arg(a, Value), \" \", "
|
||||
"format_arg(a, Gas)];\n"
|
||||
"format_op({~w, Contract, {immediate, Function}, Value, Gas}, Symbols) ->\n"
|
||||
"[\"~s \", format_arg(a, Contract), \".\", "
|
||||
"lookup(Function, Symbols), \" \", "
|
||||
"format_arg(a, Value), \" \", "
|
||||
"format_arg(a, Gas)];\n",
|
||||
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
|
||||
gen_format(#{opname := Name, format := []}) ->
|
||||
io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]);
|
||||
gen_format(#{opname := Name, format := Args}) ->
|
||||
NameAsString = atom_to_list(Name),
|
||||
@@ -533,7 +572,7 @@ test_asm_generator(Filename) ->
|
||||
file:close(File).
|
||||
|
||||
|
||||
gen_instruction(#{opname := Name, format := atomic}) ->
|
||||
gen_instruction(#{opname := Name, format := []}) ->
|
||||
io_lib:format(" ~s\n", [Name]);
|
||||
gen_instruction(#{opname := Name, format := ArgTypes}) ->
|
||||
Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])),
|
||||
@@ -549,7 +588,7 @@ gen_arg(t) -> "integer".
|
||||
|
||||
any_arg() ->
|
||||
element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}).
|
||||
stack_arg() -> "a" ++ integer_to_list(rand:uniform(255)-1).
|
||||
stack_arg() -> "a".
|
||||
arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1).
|
||||
var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1).
|
||||
imm_arg() ->
|
||||
@@ -655,7 +694,7 @@ generate_documentation(Filename) ->
|
||||
|
||||
gen_doc(#{ opname := Name
|
||||
, opcode := OpCode
|
||||
, args := _Args
|
||||
, arity := _Arity
|
||||
, end_bb := _EndBB
|
||||
, format := FateFormat
|
||||
, macro := _Macro
|
||||
@@ -668,7 +707,7 @@ gen_doc(#{ opname := Name
|
||||
}) ->
|
||||
Arguments =
|
||||
case FateFormat of
|
||||
atomic -> "";
|
||||
[] -> "";
|
||||
_ -> lists:join(" ",
|
||||
[format_arg_doc(A) ||
|
||||
A <-
|
||||
|
||||
@@ -51,6 +51,7 @@ opcode(?SHL) -> ?SHL;
|
||||
opcode(?SHR) -> ?SHR;
|
||||
opcode(?SAR) -> ?SAR;
|
||||
opcode(?SHA3) -> ?SHA3;
|
||||
opcode(?CREATOR) -> ?CREATOR;
|
||||
opcode(?ADDRESS) -> ?ADDRESS;
|
||||
opcode(?BALANCE) -> ?BALANCE;
|
||||
opcode(?ORIGIN) -> ?ORIGIN;
|
||||
@@ -191,6 +192,7 @@ mnemonic(?SHL) -> 'SHL' ;
|
||||
mnemonic(?SHR) -> 'SHR' ;
|
||||
mnemonic(?SAR) -> 'SAR' ;
|
||||
mnemonic(?SHA3) -> 'SHA3' ;
|
||||
mnemonic(?CREATOR) -> 'CREATOR' ;
|
||||
mnemonic(?ADDRESS) -> 'ADDRESS' ;
|
||||
mnemonic(?BALANCE) -> 'BALANCE' ;
|
||||
mnemonic(?ORIGIN) -> 'ORIGIN' ;
|
||||
@@ -332,6 +334,7 @@ m_to_op('SHL') -> ?SHL ;
|
||||
m_to_op('SHR') -> ?SHR ;
|
||||
m_to_op('SAR') -> ?SAR ;
|
||||
m_to_op('SHA3') -> ?SHA3 ;
|
||||
m_to_op('CREATOR') -> ?CREATOR ;
|
||||
m_to_op('ADDRESS') -> ?ADDRESS ;
|
||||
m_to_op('BALANCE') -> ?BALANCE ;
|
||||
m_to_op('ORIGIN') -> ?ORIGIN ;
|
||||
|
||||
@@ -25,12 +25,7 @@ read_file(File) ->
|
||||
Asm.
|
||||
|
||||
assemble(Asm) ->
|
||||
{Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, []),
|
||||
{Env, BC}.
|
||||
|
||||
disassemble(BC) ->
|
||||
aeb_fate_asm:bytecode_to_fate_code(BC, []).
|
||||
|
||||
aeb_fate_asm:asm_to_bytecode(Asm, []).
|
||||
|
||||
asm_disasm_idenity_test() ->
|
||||
check_roundtrip(identity).
|
||||
@@ -52,18 +47,18 @@ sources() ->
|
||||
, "tuple"
|
||||
, "mapofmap"
|
||||
, "immediates"
|
||||
, "all_instructions"
|
||||
%% , "all_instructions"
|
||||
].
|
||||
|
||||
check_roundtrip(File) ->
|
||||
AssemblerCode = read_file(File),
|
||||
{_Env, ByteCode} = assemble(AssemblerCode),
|
||||
FateCode = disassemble(ByteCode),
|
||||
FateCode = aeb_fate_code:deserialize(ByteCode),
|
||||
DissasmCode = aeb_fate_asm:to_asm(FateCode),
|
||||
io:format("~s~n", [AssemblerCode]),
|
||||
io:format("~s~n", [DissasmCode]),
|
||||
{_Env2, ByteCode2} = assemble(DissasmCode),
|
||||
ByteCode3 = aeb_fate_code:serialize(FateCode),
|
||||
Code1 = aeb_fate_asm:strip(ByteCode),
|
||||
Code2 = aeb_fate_asm:strip(ByteCode2),
|
||||
io:format("~s~n", [aeb_fate_asm:to_asm(disassemble(ByteCode2))]),
|
||||
?assertEqual(Code1, Code2).
|
||||
Code3 = aeb_fate_asm:strip(ByteCode3),
|
||||
?assertEqual(Code1, Code2),
|
||||
?assertEqual(Code1, Code3).
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
;; CONTRACT all_instructions
|
||||
|
||||
;; Dont expect this contract to typecheck or run.
|
||||
;; Just used to check assembler rountrip of all instruction.
|
||||
;; Just used to check assembler rountrip of all instructions.
|
||||
|
||||
FUNCTION foo () : {tuple, []}
|
||||
RETURN
|
||||
|
||||
RETURNR a13
|
||||
RETURNR a
|
||||
|
||||
CALL foo
|
||||
CALL "foo"
|
||||
|
||||
CALL_R arg125 foo
|
||||
CALL_R arg125 foo 0
|
||||
|
||||
CALL_T foo
|
||||
CALL_T "foo"
|
||||
|
||||
CALL_TR arg245 foo
|
||||
CALL_TR arg245 foo 4711
|
||||
|
||||
CALL_GTR arg245 foo 0 100
|
||||
|
||||
CALL_GR arg245 foo 0 4711
|
||||
|
||||
JUMP 5514251025295783441695716053282666408426
|
||||
|
||||
JUMPIF arg196 0x12c651665
|
||||
|
||||
SWITCH_V2 a27 63 33
|
||||
SWITCH_V2 a 63 33
|
||||
|
||||
SWITCH_V3 var4 0x1d61723dd 79 7
|
||||
|
||||
@@ -32,29 +36,29 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
DUP a
|
||||
|
||||
POP a107
|
||||
POP a
|
||||
|
||||
STORE arg183 var225
|
||||
|
||||
INCA
|
||||
|
||||
INC a25
|
||||
INC a
|
||||
|
||||
DECA
|
||||
|
||||
DEC a
|
||||
|
||||
ADD a217 a a
|
||||
ADD a a a
|
||||
|
||||
SUB arg35 arg165 var74
|
||||
|
||||
MUL 44 35 "foo"
|
||||
|
||||
DIV 263838340369912686645632650718169038811 a24 a
|
||||
DIV 263838340369912686645632650718169038811 a a
|
||||
|
||||
MOD var113 arg80 arg207
|
||||
|
||||
POW a176 a a123
|
||||
POW a a a
|
||||
|
||||
LT a 78 var81
|
||||
|
||||
@@ -62,11 +66,11 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
EQ 85 a arg164
|
||||
|
||||
ELT a161 arg226 a168
|
||||
ELT a arg226 a
|
||||
|
||||
EGT a131 1 var250
|
||||
EGT a 1 var250
|
||||
|
||||
NEQ a85 a a83
|
||||
NEQ a a a
|
||||
|
||||
AND var255 0x294a24f6 var189
|
||||
|
||||
@@ -74,37 +78,41 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
NOT arg124 a
|
||||
|
||||
TUPLE 5019186157739257888756115213149493826410
|
||||
TUPLE var999 5019186157739257888756115213149493826410
|
||||
|
||||
ELEMENT arg148 var25 a219
|
||||
ELEMENT arg148 var25 a
|
||||
|
||||
MAP_EMPTY a135
|
||||
MAP_EMPTY a
|
||||
|
||||
MAP_LOOKUP a82 a a143
|
||||
MAP_LOOKUP a a a
|
||||
|
||||
MAP_LOOKUPD var112 arg35 a163 var112
|
||||
MAP_LOOKUPD var112 arg35 a var112
|
||||
|
||||
MAP_UPDATE false a0 a56 a
|
||||
MAP_UPDATE false a a a
|
||||
|
||||
MAP_DELETE arg180 a var1
|
||||
|
||||
MAP_MEMBER a { true => 4} 94
|
||||
|
||||
MAP_FROM_LIST () a159
|
||||
MAP_FROM_LIST () a
|
||||
|
||||
MAP_TO_LIST a { true => 4 }
|
||||
|
||||
MAP_SIZE a { true => 42 }
|
||||
|
||||
NIL arg91
|
||||
|
||||
IS_NIL a121 var6
|
||||
IS_NIL a var6
|
||||
|
||||
CONS arg185 "foo" a114
|
||||
CONS arg185 "foo" a
|
||||
|
||||
HD a150 var124
|
||||
HD a var124
|
||||
|
||||
TL arg223 a
|
||||
|
||||
LENGTH var216 a143
|
||||
LENGTH var216 a
|
||||
|
||||
STR_EQ { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177
|
||||
APPEND { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a
|
||||
|
||||
STR_JOIN a a 7144184027126178769820155907121270843348
|
||||
|
||||
@@ -112,15 +120,17 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
ADDR_TO_STR a arg216
|
||||
|
||||
STR_REVERSE a174 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
STR_REVERSE a @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
STR_LENGTH a "foo"
|
||||
|
||||
INT_TO_ADDR arg127 var207
|
||||
|
||||
VARIANT a a 0x1f7b72200 a
|
||||
|
||||
VARIANT_TEST a26 arg217 a
|
||||
VARIANT_TEST a arg217 a
|
||||
|
||||
VARIANT_ELEMENT a86 arg103 arg108
|
||||
VARIANT_ELEMENT a arg103 arg108
|
||||
|
||||
BITS_NONEA
|
||||
|
||||
@@ -128,36 +138,38 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
BITS_ALLA
|
||||
|
||||
BITS_ALL a164
|
||||
BITS_ALL a
|
||||
|
||||
BITS_ALL_N a221 arg135
|
||||
BITS_ALL_N a arg135
|
||||
|
||||
BITS_SET arg150 a48 { 0x1a715e2a6 => 3}
|
||||
BITS_SET arg150 a { 0x1a715e2a6 => 3}
|
||||
|
||||
BITS_CLEAR arg98 a arg164
|
||||
|
||||
BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |)
|
||||
BITS_TEST a a (| [0,0,3] | 2 | (1, "foo", ()) |)
|
||||
|
||||
BITS_SUM a244 a71
|
||||
BITS_SUM a a
|
||||
|
||||
BITS_OR var20 var186 a
|
||||
|
||||
BITS_AND a187 4 arg203
|
||||
BITS_AND a 4 arg203
|
||||
|
||||
BITS_DIFF var200 arg247 var20
|
||||
|
||||
ADDRESS a237
|
||||
ADDRESS a
|
||||
|
||||
BALANCE a231
|
||||
BALANCE a
|
||||
|
||||
ORIGIN arg216
|
||||
|
||||
CALLER a27
|
||||
CALLER a
|
||||
|
||||
GASPRICE arg119
|
||||
|
||||
BLOCKHASH a arg110
|
||||
|
||||
CALL_VALUE a
|
||||
|
||||
BENEFICIARY var163
|
||||
|
||||
TIMESTAMP a
|
||||
@@ -172,15 +184,15 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
GAS var35
|
||||
|
||||
LOG0 a a85
|
||||
LOG0 a
|
||||
|
||||
LOG1 arg94 arg86 arg208
|
||||
LOG1 arg86 arg208
|
||||
|
||||
LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108
|
||||
LOG2 a a (| [0,1,3] | 2 | (1, "foo", ()) |)
|
||||
|
||||
LOG3 arg255 arg15 arg211 var139 arg44
|
||||
LOG3 arg15 arg211 var139 arg44
|
||||
|
||||
LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a
|
||||
LOG4 a a 9 a a
|
||||
|
||||
DEACTIVATE
|
||||
|
||||
@@ -214,15 +226,11 @@ FUNCTION foo () : {tuple, []}
|
||||
|
||||
ECVERIFY
|
||||
|
||||
SHA3
|
||||
SHA3 a
|
||||
|
||||
SHA256
|
||||
SHA256 a
|
||||
|
||||
BLAKE2B
|
||||
|
||||
DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo"
|
||||
|
||||
DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255
|
||||
BLAKE2B a
|
||||
|
||||
ABORT a
|
||||
|
||||
@@ -235,3 +243,7 @@ FUNCTION foo () : {tuple, []}
|
||||
BALANCE_OTHER a arg0
|
||||
|
||||
SETELEMENT a 2 (1, "two", 3) 2
|
||||
|
||||
AUTH_TX_HASH
|
||||
|
||||
CONTRACT_TO_ADDRESS @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
@@ -68,6 +68,18 @@ FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]
|
||||
FUNCTION address() : address
|
||||
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
FUNCTION oracle() : oracle
|
||||
RETURNR @ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
FUNCTION contract() : contract
|
||||
RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
FUNCTION name() : name
|
||||
RETURNR @nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
FUNCTION channel() : channel
|
||||
RETURNR @ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
|
||||
|
||||
;; Option(integer) = NONE | SOME(integer)
|
||||
FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]}
|
||||
RETURNR (| [0,1] | 0 | () |)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
FUNCTION call(integer):integer
|
||||
STORE var1 arg0
|
||||
PUSH 0
|
||||
CALL write
|
||||
CALL "write"
|
||||
PUSH var1
|
||||
RETURN
|
||||
|
||||
|
||||
@@ -19,27 +19,27 @@ FUNCTION inc(integer) -> integer
|
||||
|
||||
FUNCTION call(integer) -> integer
|
||||
INCA
|
||||
CALL inc
|
||||
CALL "inc"
|
||||
INCA
|
||||
RETURN
|
||||
|
||||
|
||||
FUNCTION tailcall(integer) -> integer
|
||||
INCA
|
||||
CALL_T inc
|
||||
CALL_T "inc"
|
||||
|
||||
FUNCTION remote_call(integer) : integer
|
||||
PUSH arg0
|
||||
CALL_R remote.add_five
|
||||
CALL_R remote.add_five 0
|
||||
INCA
|
||||
RETURN
|
||||
|
||||
FUNCTION remote_tailcall(integer) : integer
|
||||
PUSH arg0
|
||||
CALL_TR remote add_five
|
||||
CALL_TR remote add_five 0
|
||||
|
||||
;; Test the code from the shell
|
||||
;; _build/default/rel/aessembler/bin/aessembler console
|
||||
|
||||
;; aeb_aefa:file("../../../../test/asm_code/test.fate", []).
|
||||
;; f(Asm), f(Env), f(BC), Asm = aefa_asm:read_file("../../../../test/asm_code/test.fate"), {Env, BC} = aefa_asm:asm_to_bytecode(Asm, []), aefa_asm:bytecode_to_fate_code(BC, []).
|
||||
;; f(Asm), f(Env), f(BC), Asm = aefa_asm:read_file("../../../../test/asm_code/test.fate"), {Env, BC} = aefa_asm:asm_to_bytecode(Asm, []), aefa_asm:bytecode_to_fate_code(BC, []).
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
FUNCTION make_0tuple():{tuple, []}
|
||||
;; BB : 0
|
||||
TUPLE 0
|
||||
TUPLE a 0
|
||||
RETURN
|
||||
|
||||
FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]}
|
||||
;; BB : 0
|
||||
PUSH arg0
|
||||
PUSH arg1
|
||||
TUPLE 2
|
||||
TUPLE a 2
|
||||
RETURN
|
||||
|
||||
FUNCTION make_5tuple(integer, integer, integer, integer, integer):
|
||||
@@ -18,14 +18,14 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer):
|
||||
PUSH arg2
|
||||
PUSH arg3
|
||||
PUSH arg4
|
||||
TUPLE 5
|
||||
TUPLE a 5
|
||||
RETURN
|
||||
|
||||
FUNCTION element1(integer, integer): integer
|
||||
;; BB : 0
|
||||
PUSH arg0
|
||||
PUSH arg1
|
||||
TUPLE 2
|
||||
TUPLE a 2
|
||||
ELEMENT a 1 a
|
||||
RETURN
|
||||
|
||||
|
||||
Reference in New Issue
Block a user