Compare commits

..

12 Commits

Author SHA1 Message Date
Thomas Arts 9d056dc620 Add eqc profile 2019-02-28 13:34:03 +01:00
Thomas Arts a51a864059 Formatting differently 2019-02-28 13:33:47 +01:00
Thomas Arts 899bff9111 unit is printed () 2019-02-28 13:33:26 +01:00
Thomas Arts b5daedaf95 Since Tag < Size, Size cannot be zero 2019-02-28 10:38:33 +01:00
Erik Stenman 8dd8e89c1e Rename NUMBER op to GENERATION and add MICROBLOCK instruction. 2019-02-28 08:13:26 +01:00
Erik Stenman dd1a6a9c3d Generate tokens in scanner from definitions. 2019-02-28 07:55:51 +01:00
Erik Stenman 161b5a6106 Renumber opcodes. Add primops. 2019-02-27 17:47:08 +01:00
Erik Stenman 268208ec98 Use test target for ci. 2019-02-27 15:36:48 +01:00
Erik Stenman 9411a131fc Spell eunit the right way. 2019-02-27 15:34:06 +01:00
Erik Stenman c624f4956c Test targets and cleanup. 2019-02-27 15:34:06 +01:00
Erik Stenman ab150ce7f8 Generate the code from the makefile. Remove generated files. 2019-02-27 15:34:06 +01:00
Erik Stenman c85af9e7f3 Generate code for fate ops from spec. 2019-02-27 15:34:06 +01:00
31 changed files with 515 additions and 2381 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- dialyzer-cache-v1-
- run:
name: Build
command: rebar3 compile
command: make
- run:
name: Static Analysis
command: make dialyzer
-10
View File
@@ -16,13 +16,3 @@ aefateasm
include/aeb_fate_opcodes.hrl
src/aeb_fate_code.erl
src/aeb_fate_opcodes.erl
src/aeb_fate_pp.erl
*.erl~
*.hrl~
*.aes~
doc
cover
aefate
current_counterexample.eqc
.rebar3
ebin
+14 -9
View File
@@ -1,12 +1,10 @@
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
GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template
REBAR ?= rebar3
all: local
sources: $(GENERATED_SRC)
local: $(GENERATED_SRC)
local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl
@$(REBAR) as local release
console: local
@@ -14,12 +12,15 @@ console: local
clean:
@$(REBAR) clean
rm -f $(GENERATED_SRC)
rm -f ebin/*
rm -f src/aeb_fate_opcodes.erl
rm -f src/aeb_fate_code.erl
rm -f include/aeb_fate_opcodes.hrl
dialyzer: local
@$(REBAR) as local dialyzer
distclean: clean
@rm -rf _build/
@@ -29,8 +30,12 @@ eunit: local
test: local
@$(REBAR) as local eunit
ebin/%.beam: src/%.erl
ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin
erlc -o $(dir $@) $<
$(GENERATED_SRC): $(GENERATOR_DEPS)
src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl: ebin/aeb_fate_generate_ops.beam
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
ebin:
mkdir ebin
+6 -131
View File
@@ -1,134 +1,9 @@
# aebytecode
An library and stand alone assembler for aeternity bytecode.
aebytecode
=====
This version supports Aevm bytecode and Fate bytecode.
An OTP library
## Build
$ make
## Fate Code
Fate code exists in 3 formats:
1. Fate byte code. This format is under consensus.
2. Fate assembler. This is a text represenation of fate code.
This is not under consensus and other
implemenation and toolchains could have
their own format.
3. Internal. This is an Erlang representation of fate code
Used by this particular engin implementation.
This library handles all tree representations.
The byte code format is described in a separate document.
The internal format is described in a separate document.
The text representation is described below.
### Fate Assembler Code
Assembler code can be read from a file.
The assembler has the following format:
Comments start with 2 semicolons and runs till end of line
`;; This is a comment`
Opcode mnemonics start with an upper case letter.
`DUP`
Identifiers start with a lower case letter
`an_identifier`
References to function arguments start with arg followed by an integer
`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`
`a`
Immediate values can be of 11 types:
1. Integers as decimals: {Digits} or -{Digits}
`42`
`-2374683271468723648732648736498712634876147`
And integers as Hexadecimals:: 0x{Hexdigits}
`0x0deadbeef0`
2. Chain Objects. These are all addresses to different types of chain objects.
Each address is a 256 bits number encoded in base58 with checksum
with a prefix of "@" plus a type prefix followed by "_".
2a. Account Address: a base58c encoded number starting with @ak_ followed by a number of base58chars
'@ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2b. Contract address: @ct_{base58char}+
`@ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2c. Oracle address: @ok_{base58char}+
`@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Name address: @nm_{base58char}+
`@nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2e. Channel address: @ch_{base58char}+
`@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
3. Boolean true or false
`true`
`false`
4. Strings "{Characters}"
`"Hello"`
5. Map { Key => Value }
`{}`
`{ 1 => { "foo" => true, "bar" => false}`
6. Lists [ Elements ]
`[]`
`[1, 2]`
7. Bit field < Bits > or !< Bits >
`<000>`
`<1010 1010>`
`<>`
`!<>`
8. Tuples ( Elements )
`()`
`(1, "foo")`
9. Variants: (| [Arities] | Tag | ( Elements ) |)
`(| [1,3,5,2] | 3 | ( "foo", 12) |)`
10. Hashes: #{base64char}+
`#AQIDCioLFQ==`
11. Signatures: $sg_{base58char}+
Where
Digits: [0123456789]
Hexdigits: [0123456789abcdef]
base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
Characters: any printable ascii character 0..255 (except " no quoting yet)
Key: any value except for a map
Bits: 01 or space
Elements: Nothing or Value , Elements
Size: Digits (0 < Size < 256)
Tag: Digits (0 =< Tag < Size)
Build
-----
$ rebar3 compile
View File
+24 -49
View File
@@ -1,68 +1,43 @@
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_HASH_T, {hash, binary()}).
-define(FATE_SIGNATURE_T, {signature, binary()}).
-define(FATE_CONTRACT_T, {contract, <<_:256>>}).
-define(FATE_ORACLE_T, {oracle, <<_:256>>}).
-define(FATE_NAME_T, {name, <<_:256>>}).
-define(FATE_CHANNEL_T, {channel, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, ?FATE_BYTE_T, ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(IS_FATE_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_INTEGER(X), is_integer(X)).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_HASH(X), (is_tuple(X) andalso (hash == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_NAME(X), (is_tuple(X) andalso (name == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))).
-define(IS_FATE_VARIANT(X), (is_tuple(X)
andalso
(variant == element(1, X)
andalso is_list(element(2, X))
andalso is_integer(element(2, X))
andalso is_integer(element(3, X))
andalso is_tuple(element(4, X))
))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_HASH(X), {hash, X}).
-define(FATE_SIGNATURE(S), {signature, S}).
-define(FATE_CONTRACT(X), {contract, X}).
-define(FATE_ORACLE(X), {oracle, X}).
-define(FATE_NAME(X), {name, X}).
-define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_INTEGER_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)).
-define(FATE_ADDRESS_VALUE(X), (element(2, X))).
-define(FATE_HASH_VALUE(X), (element(2, X))).
-define(FATE_SIGNATURE_VALUE(X), (element(2, X))).
-define(FATE_CONTRACT_VALUE(X), (element(2, X))).
-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_MAP_VALUE(X), (X)).
-define(FATE_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_size(X))).
@@ -72,7 +47,7 @@
-define(FATE_VOID, void).
-define(FATE_EMPTY_STRING, <<>>).
-define(FATE_STRING(S), S).
-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}).
-define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}).
-define(MAKE_FATE_INTEGER(X), X).
-define(MAKE_FATE_LIST(X), X).
-15
View File
@@ -1,15 +0,0 @@
-record(pmap, {key_t :: aeb_aevm_data:type(),
val_t :: aeb_aevm_data:type(),
parent :: none | non_neg_integer(),
size = 0 :: non_neg_integer(),
data :: #{aeb_heap:binary_value() => aeb_heap:binary_value() | tombstone}
| stored}).
-record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} }
, next_id = 0 :: non_neg_integer() }).
-record(heap, { maps :: #maps{},
offset :: aeb_heap:offset(),
heap :: binary() | #{non_neg_integer() => non_neg_integer()} }).
+6 -10
View File
@@ -183,13 +183,9 @@
-define(PRIM_CALL_MAP_TOLIST, 305).
-define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 410).
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-define(PRIM_CALL_AUTH_TX_HASH, 500).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-11
View File
@@ -1,11 +0,0 @@
-define(Type(), aeb_aevm_data:type()).
-define(TYPEREP_WORD_TAG, 0).
-define(TYPEREP_STRING_TAG, 1).
-define(TYPEREP_LIST_TAG, 2).
-define(TYPEREP_TUPLE_TAG, 3).
-define(TYPEREP_VARIANT_TAG, 4).
-define(TYPEREP_TYPEREP_TAG, 5).
-define(TYPEREP_MAP_TAG, 6).
-define(TYPEREP_FUN_TAG, 7).
-25
View File
@@ -1,25 +0,0 @@
%%% @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_data`
%%% 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_data_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 * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000)
]}.
-25
View File
@@ -1,25 +0,0 @@
%%% @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_encoding`
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts
-module(aeb_fate_encoding_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 * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000)
]}.
-122
View File
@@ -1,122 +0,0 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%%
%%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc.
%%% In other words make some rather broad terms as well as some deep terms
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
prop_roundtrip() ->
?FORALL(FateData, fate_data(),
measure(bytes, size(term_to_binary(FateData)),
begin
Serialized = aeb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]),
equals(aeb_fate_encoding:deserialize(Serialized), FateData))
end)).
prop_format_scan() ->
?FORALL(FateData, fate_data(),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin
String = aeb_fate_data:format(FateData),
{ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)),
true
end)).
prop_serializes() ->
?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))),
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]),
begin
{T1, Binary} =
timer:tc( fun() ->
<< begin B = aeb_fate_encoding:serialize(Data),
<<B/binary>> end || Data <- FateDatas >>
end),
{T2, {FateData, _}} =
timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end),
measure(binary_size, size(Binary),
measure(encode, T1,
measure(decode, T2,
conjunction([{equal, equals(hd(FateDatas), FateData)},
{size, size(Binary) < 500000}]))))
end)).
fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map]), eqc_symbolic:eval(Data))).
fate_data(0, _Options) ->
?LAZY(
oneof([fate_integer(),
fate_boolean(),
fate_nil(),
fate_unit(),
fate_string(),
fate_address(),
fate_hash(),
fate_signature(),
fate_contract(),
fate_oracle(),
fate_name(),
fate_bits(),
fate_channel()]));
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_map( fate_data(Size div 8, Options -- [map]),
fate_data(Size div 5, Options)))
|| lists:member(map, Options)
]).
fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}.
fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}.
fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}.
fate_nil() -> {call, aeb_fate_data, make_list, [[]]}.
fate_unit() -> {call, aeb_fate_data, make_unit, []}.
fate_string() -> {call, aeb_fate_data, make_string,
[frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}.
fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}.
fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}.
fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}.
fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}.
fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}.
fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}.
fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}.
%% May shrink to fate_unit
fate_tuple(ListGen) ->
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}.
fate_variant(ListGen) ->
?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen},
{call, aeb_fate_data, make_variant,
[L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}).
fate_list(Gen) ->
{call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}.
fate_map(KeyGen, ValGen) ->
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}.
non_zero_binary(N) ->
Bits = N*8,
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
char() ->
choose(1, 255).
-49
View File
@@ -1,49 +0,0 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%% Properties for testing Fate type representations
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_type_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
prop_roundtrip() ->
?FORALL(FateType, fate_type(),
collect(FateType,
begin
Serialized = aeb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized),
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
begin
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized),
equals(Type, FateType)
end)
end)).
fate_type() ->
?SIZED(Size, fate_type(Size)).
fate_type(0) ->
oneof([integer,
boolean,
address,
hash,
signature,
contract,
oracle,
name,
channel,
bits,
string]);
fate_type(Size) ->
oneof([?LAZY(fate_type(Size div 2)),
{list, ?LAZY(fate_type(Size div 2))},
{tuple, list(?LAZY(fate_type(Size div 2)))},
{variant, list(?LAZY(fate_type(Size div 2)))},
?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))],
{map, T1, T2})]).
+1 -8
View File
@@ -1,12 +1,10 @@
%% -*- mode: erlang; indent-tabs-mode: nil -*-
{minimum_otp_vsn, "20.1"}.
{erl_opts, [debug_info]}.
{deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "6dce265"}}}
{ref, "b55c372"}}}
, {getopt, "1.0.1"}
]}.
@@ -14,11 +12,6 @@
{escript_main_app, aebytecode}.
{escript_name, aefateasm}.
{escript_emu_args, "%%!"}.
{pre_hooks,
[{"(linux|darwin|solaris|win32)", compile, "make sources"},
{"(freebsd)", compile, "gmake sources"}]}.
{provider_hooks, [{post, [{compile, escriptize}]}]}.
+1 -1
View File
@@ -1,7 +1,7 @@
{"1.1.0",
[{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
{ref,"b55c3726f4a21063721c68d6fa7fda39121edf11"}},
0},
{<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git",
-150
View File
@@ -1,150 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc
%%% Encode and decode data and function calls according to
%%% Sophia-AEVM-ABI.
%%% @end
%%% Created : 25 Jan 2018
%%%
%%%-------------------------------------------------------------------
-module(aeb_abi).
-define(HASH_SIZE, 32).
-export([ create_calldata/4
, check_calldata/2
, function_type_info/3
, function_type_hash/3
, arg_typerep_from_function/2
, type_hash_from_function_name/2
, typereps_from_type_hash/2
, function_name_from_type_hash/2
, get_function_hash_from_calldata/1
]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
-type function_name() :: binary(). %% String
-type typerep() :: aeb_aevm_data:type().
-type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name()
, ArgType :: binary() %% binary typerep
, OutType :: binary() %% binary typerep
}.
-type type_info() :: [function_type_info()].
%%%===================================================================
%%% API
%%%===================================================================
%%%===================================================================
%%% Handle calldata
create_calldata(FunName, Args, ArgTypes0, RetType) ->
ArgTypes = {tuple, ArgTypes0},
<<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data, {tuple, [word, ArgTypes]}, RetType}.
-spec check_calldata(binary(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo) ->
%% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of
{ok, Hash} ->
case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType};
{error, _} ->
{error, bad_call_data}
catch
_T:_E ->
{error, bad_call_data}
end;
{error, _} ->
{error, unknown_function}
end;
{error, _What} ->
{error, bad_call_data}
end.
-spec get_function_hash_from_calldata(CallData::binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
case aeb_heap:from_binary({tuple, [word]}, CallData) of
{ok, {HashInt}} -> {ok, <<HashInt:?HASH_SIZE/unit:8>>};
{error, _} = Error -> Error
end.
%%%===================================================================
%%% Handle type info from contract meta data
-spec function_type_info(function_name(), [typerep()], typerep()) ->
function_type_info().
function_type_info(Name, ArgTypes, OutType) ->
ArgType = {tuple, ArgTypes},
{ function_type_hash(Name, ArgType, OutType)
, Name
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
}.
-spec function_type_hash(function_name(), typerep(), typerep()) -> hash().
function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
Bin = iolist_to_binary([ Name
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
]),
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
{ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin),
Hash.
-spec arg_typerep_from_function(function_name(), type_info()) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
arg_typerep_from_function(Function, TypeInfo) ->
case lists:keyfind(Function, 2, TypeInfo) of
{_TypeHash, Function, ArgTypeBin,_OutTypeBin} ->
case aeb_heap:from_binary(typerep, ArgTypeBin) of
{ok, ArgType} -> {ok, ArgType};
{error,_} -> {error, bad_type_data}
end;
false ->
{error, unknown_function}
end.
-spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
case {aeb_heap:from_binary(typerep, ArgTypeBin),
aeb_heap:from_binary(typerep, OutTypeBin)} of
{{ok, ArgType}, {ok, OutType}} -> {ok, ArgType, OutType};
{_, _} -> {error, bad_type_data}
end;
false ->
{error, unknown_function}
end.
-spec function_name_from_type_hash(hash(), type_info()) ->
{'ok', function_name()}
| {'error', 'unknown_function'}.
function_name_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, Function,_ArgTypeBin,_OutTypeBin} ->
{ok, Function};
false ->
{error, unknown_function}
end.
-spec type_hash_from_function_name(function_name(), type_info()) ->
{'ok', hash()}
| {'error', 'unknown_function'}.
type_hash_from_function_name(Name, TypeInfo) ->
case lists:keyfind(Name, 2, TypeInfo) of
{TypeHash, Name,_ArgTypeBin,_OutTypeBin} ->
{ok, TypeHash};
false ->
{error, unknown_function}
end.
-30
View File
@@ -1,30 +0,0 @@
-module(aeb_aevm_data).
-export_type([data/0,
type/0,
heap/0]).
-type type() :: word | signed_word | string | typerep | function
| {list, type()}
| {option, type()}
| {tuple, [type()]}
| {variant, [[type()]]}.
-type data() :: none
| {some, data()}
| {option, data()}
| word
| string
| {list, data()}
| {tuple, [data()]}
| {variant, integer(), [data()]}
| integer()
| binary()
| [data()]
| {}
| {data()}
| {data(), data()}.
-type heap() :: binary().
+281 -430
View File
@@ -1,22 +1,6 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc Assembler for Fate machine code.
%%% @end
%%%
%%% Fate code exists in 3 formats:
%%%
%%% 1. Fate byte code. This format is under consensus.
%%% 2. Fate assembler. This is a text represenation of fate code.
%%% This is not under consensus and other
%%% implemenation and toolchains could have
%%% their own format.
%%% 3. Internal. This is an Erlang representation of fate code
%%% Used by this particular engin implementation.
%%%
%%% This library handles all tree representations.
%%% The byte code format is described in a separate document.
%%% The internal format is described in a separate document.
%%% The text representation is described here:
%%%
%%% Assembler code can be read from a file.
%%% The assembler has the following format
@@ -35,53 +19,34 @@
%%% stack1
%%% a
%%%
%%% Immediate values can be of 11 types:
%%% 1a. Integers as decimals: {Digits} or -{Digits}
%%% Immediates can be of 9 types:
%%% 1. Integers
%%% 42
%%% -2374683271468723648732648736498712634876147
%%% 1b. Integers as Hexadecimals:: 0x{Hexdigits}
%%% 2. Hexadecimal integers starting with 0x
%%% 0x0deadbeef0
%%% 2a. account addresses, a base58c encoded string prefixed with @ak_
%%% 2b. contract address: @ct_{base58char}+
%%% 2c. oracle address: @ok_{base58char}+
%%% 2d. name address: @nm_{base58char}+
%%% 2e. channel address: @ch_{base58char}+
%%% 3. Boolean true or false
%%% 3. addresses, a 256-bit hash strings starting with #
%%% followed by up to 64 hex chars
%%% #00000deadbeef
%%% 4. Boolean
%%% true
%%% false
%%% 4. Strings "{Characters}"
%%% 5. Strings
%%% "Hello"
%%% 5. Map { Key => Value }
%%% 6. Empty map
%%% {}
%%% { 1 => { "foo" => true, "bar" => false}
%%% 6. Lists [ Elements ]
%%% 7. Lists
%%% []
%%% [1, 2]
%%% 7. Bit field < Bits > or !< Bits >
%%% 8. Bit field
%%% <000>
%%% <1010 1010>
%%% <1010>
%%% <>
%%% !<>
%%% 8. Tuples ( Elements )
%%% 9. Tuples
%%% ()
%%% (1, "foo")
%%% 9. Variants: (| [Arities] | Tag | ( Elements ) |)
%%% (| [0,1,2] | 2 | ( "foo", 12) |)
%%% 10. Hashes: #{base64char}+
%%% #AQIDCioLFQ==
%%% 11. Signatures: $sg_{base58char}+
%%%
%%% Where Digits: [0123456789]
%%% Hexdigits: [0123456789abcdef]
%%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
%%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
%%% Characters any printable ascii character 0..255 (except " no quoting yet)
%%% Key: any value except for a map
%%% Bits: 01 or space
%%% Elements: Nothing or Value , Elements
%%% Size: Digits
%%% Tag: Digits
%%%
%%% @end
%%% Created : 21 Dec 2017
%%%-------------------------------------------------------------------
@@ -119,7 +84,7 @@ parse_function_call([{id,_,Name}, {'(',_}| Rest]) ->
to_args([{')', _}]) -> {[], []};
to_args(Tokens) ->
case parse_value(Tokens) of
case to_data(Tokens) of
{Arg, [{',', _} | Rest]} ->
{More, Rest2} = to_args(Rest),
{[Arg|More], Rest2};
@@ -127,6 +92,13 @@ to_args(Tokens) ->
{[Arg], Rest}
end.
to_data([{int,_line, Int}|Rest]) ->
{Int, Rest};
to_data([{boolean,_line, Bool}|Rest]) ->
{Bool, Rest};
to_data([{hash,_line, Hash}|Rest]) ->
{Hash, Rest}.
pp(FateCode) ->
Listing = to_asm(FateCode),
io_lib:format("~ts~n",[Listing]).
@@ -185,6 +157,14 @@ format_arg_types([T|Ts]) ->
, ", "
, format_arg_types(Ts)].
format_arg({immediate, I}) ->
aeb_fate_data:format(I);
format_arg({arg, N}) -> io_lib:format("arg~p", [N]);
format_arg({var, N}) -> io_lib:format("var~p", [N]);
format_arg({stack, 0}) -> "a";
format_arg({stack, N}) -> io_lib:format("a~p", [N]).
format_type(T) ->
%% TODO: Limit to ok types.
io_lib:format("~p", [T]).
@@ -200,10 +180,157 @@ format_code([], _) ->
"";
format_code([Op|Rest], Symbols) ->
[" ",
aeb_fate_pp:format_op(Op, Symbols),
format_op(Op, Symbols),
"\n",
format_code(Rest, Symbols)].
format_op('RETURN', _) -> "RETURN";
format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)];
format_op({'CALL', {immediate, Function}}, Symbols) ->
["CALL ", lookup(Function, Symbols)];
format_op({'CALL_T', {immediate, Function}}, Symbols) ->
["CALL_T ", lookup(Function, Symbols)];
format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) ->
["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)];
format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) ->
["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)];
format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) ->
["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)];
format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) ->
["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)];
format_op({'JUMP', {immediate, BB}}, _) ->
["JUMP ", io_lib:format("~p", [BB])];
format_op({'JUMPIF', Arg, {immediate, BB}}, _) ->
["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])];
format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) ->
["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2];
format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) ->
["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3];
format_op({'SWITCH_VN', Variant, BBs}, _) ->
["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]];
format_op({'PUSH', Arg0}, _) ->
["PUSH ", format_arg(Arg0)];
format_op('INCA', _) -> "INCA";
format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)];
format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)];
format_op('DECA', _) -> "DECA";
format_op({'ADD', Dest, Left, Right}, _) ->
["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'SUB', Dest, Left, Right}, _) ->
["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'MUL', Dest, Left, Right}, _) ->
["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'DIV', Dest, Left, Right}, _) ->
["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'MOD', Dest, Left, Right}, _) ->
["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'POW', Dest, Left, Right}, _) ->
["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'LT', Dest, Left, Right}, _) ->
["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'GT', Dest, Left, Right}, _) ->
["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'ELT', Dest, Left, Right}, _) ->
["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'EGT', Dest, Left, Right}, _) ->
["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'EQ', Dest, Left, Right}, _) ->
["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'NEQ', Dest, Left, Right}, _) ->
["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'AND', Dest, Left, Right}, _) ->
["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'OR', Dest, Left, Right}, _) ->
["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'NOT', Dest, Name}, _) ->
["NOT ", format_arg(Dest), " ", format_arg(Name)];
format_op({'TUPLE', {immediate, Size}}, _) ->
["TUPLE ", io_lib:format("~p", [Size])];
format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) ->
[ "ELEMENT "
, io_lib:format("~p ", [Type])
, format_arg(Dest), " "
, format_arg(Which), " "
, format_arg(Tuple)];
format_op({'MAP_EMPTY', Dest}, _) ->
["MAP_EMPTY ", format_arg(Dest)];
format_op({'MAP_LOOKUP', Dest, Map, Key}, _) ->
["MAP_LOOKUP ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_DELETE', Dest, Map, Key}, _) ->
["MAP_DELETE ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) ->
["MAP_LOOKUPD ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)];
format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) ->
["MAP_UPDATE ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)];
format_op({'MAP_MEMBER', Dest, Map, Key}, _) ->
["MAP_MEMBER ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_FROM_LIST', Dest, List}, _) ->
["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)];
format_op({'NIL', Dest}, _) ->
["NIL ", format_arg(Dest)];
format_op({'IS_NIL', Dest, List}, _) ->
["IS_NIL ", format_arg(Dest), " ", format_arg(List)];
format_op({'CONS', Dest, Hd, Tl}, _) ->
["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)];
format_op({'HD', Dest, List}, _) ->
["HD ", format_arg(Dest), " ", format_arg(List)];
format_op({'TL', Dest, List}, _) ->
["TL ", format_arg(Dest), " ", format_arg(List)];
format_op({'LENGTH', Dest, List}, _) ->
["LENGTH ", format_arg(Dest), " ", format_arg(List)];
format_op({'STR_EQ', Dest, Str1, Str2}, _) ->
["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)];
format_op({'STR_JOIN', Dest, Str1, Str2}, _) ->
["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)];
format_op({'INT_TO_STR', Dest, Str}, _) ->
["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'ADDR_TO_STR', Dest, Str}, _) ->
["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'STR_REVERSE', Dest, Str}, _) ->
["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)];
format_op({'INT_TO_ADDR', Dest, Str}, _) ->
["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) ->
["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)];
format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) ->
["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)];
format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) ->
["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " "
, format_arg(TagA), " ", format_arg(ElementsA)];
format_op('BITS_NONEA', _) -> "BITS_NONEA ";
format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)];
format_op('BITS_ALLA', _) -> "BITS_ALLA";
format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)];
format_op({'BITS_ALL_N', To, N}, _) ->
["BITS_ALL_N ", format_arg(To), " ", format_arg(N)];
format_op({'BITS_SET', To, Bits, Bit}, _) ->
["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_CLEAR', To, Bits, Bit}, _) ->
["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_TEST', To, Bits, Bit}, _) ->
["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_SUM', To, Bits}, _) ->
["BITS_SUM ", format_arg(To), " ", format_arg(Bits)];
format_op({'BITS_OR', To, Bits, Bit}, _) ->
["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_AND', To, Bits, Bit}, _) ->
["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_DIFF', To, Bits, Bit}, _) ->
["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op('DUPA', _) -> "DUPA";
format_op({'DUP', {immediate, N}}, _) ->
["DUP ", io_lib:format("~p", [N])];
format_op({'POP', Dest}, _) ->
["POP ", format_arg(Dest)];
format_op({'STORE', Var, What}, _) ->
["STORE ", format_arg(Var), " ", format_arg(What)];
format_op('NOP', _) -> "NOP".
read_file(Filename) ->
{ok, File} = file:read_file(Filename),
@@ -328,17 +455,33 @@ deserialize(<<>>, #{ function := {F, Sig}
, code => #{}
, functions => Funs#{F => {Sig, FunctionCode}}}.
deserialize_op(?ELEMENT, Rest, Code) ->
{Type, Rest2} = deserialize_type(Rest),
<<ArgType:8, Rest3/binary>> = Rest2,
{Arg0, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg1, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg2, 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),
{Rest6, [{ aeb_fate_opcodes:mnemonic(?ELEMENT)
, Type
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}}
| Code]};
deserialize_op(?SWITCH_VN, Rest, Code) ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
case aeb_fate_encoding:deserialize_one(Rest3) of
{L, Rest4} when is_list(L) ->
{N, Rest4} when is_integer(N), N >= 0 ->
Modifier0 = bits_to_modifier(ArgType band 2#11),
immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
{Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
{BBs, Rest5} = deserialize_n(N, Rest4),
{Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
, {Modifier0, Arg0}
, {immediate, L}
}
, {immediate, N}
, list_to_tuple(BBs)}
| Code]};
_ -> exit(bad_argument_to_switch_vn)
end;
@@ -387,107 +530,17 @@ deserialize_op(Op, Rest, Code) ->
, {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]}
| Code]}
end.
deserialize_n(N, Binary) ->
deserialize_n(N, Binary, []).
deserialize_n(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_n(N, Binary, Acc) ->
{Value, Rest} = aeb_fate_encoding:deserialize_one(Binary),
deserialize_n(N-1, Rest, [Value|Acc]).
@@ -527,114 +580,11 @@ serialize(#{functions := Functions} =_Env) ->
%% 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}
@@ -680,21 +630,43 @@ serialize_code([ {Arg0Type, Arg0} | Rest]) ->
[ArgSpec
, serialize_data(Arg0Type, Arg0)
| serialize_code(Rest)];
serialize_code([ ?ELEMENT
, ResType
| Rest]) ->
[?ELEMENT,
serialize_type(ResType)
| serialize_code(Rest)];
serialize_code([ ?SWITCH_VN
, {Arg0Type, Arg0}
, {immediate, L}
| Rest]) ->
, {immediate, N}
| Rest]) when is_integer(N), N >= 0 ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(immediate) bsl 2),
{Serialization, Rest2} = serialize_n_ints(N, Rest),
[?SWITCH_VN
, ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(immediate, L)] ++ serialize_code(Rest);
, serialize_data(immediate, N)
| Serialization] ++ serialize_code(Rest2);
serialize_code([B|Rest]) ->
[B | serialize_code(Rest)];
serialize_code([]) -> [].
serialize_n_ints(N, Rest) ->
serialize_n_ints(N, Rest, []).
serialize_n_ints(0, Rest, Acc) ->
%% Acc is a list of binaries.
{lists:reverse(Acc), Rest};
serialize_n_ints(N, [Int|Rest], Acc) when is_integer(Int), Int >= 0 ->
serialize_n_ints(N - 1, Rest, [aeb_fate_encoding:serialize(Int)|Acc]);
serialize_n_ints(_, [], _) ->
exit(not_enough_bbs_for_switch_vn);
serialize_n_ints(_, _, _) ->
exit(bad_bbs_value_for_switch_vn).
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
@@ -714,16 +686,36 @@ 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)].
[serialize_type({tuple, Args}) |
serialize_type(RetType)].
deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
{{tuple, Args}, Rest} = deserialize_type(Binary),
{RetType, Rest2} = deserialize_type(Rest),
{{Args, RetType}, Rest2}.
deserialize_type(<<0, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<1, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<2, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2};
deserialize_type(<<3, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<4, Rest/binary>>) -> {address, Rest};
deserialize_type(<<5, Rest/binary>>) -> {bits, Rest};
deserialize_type(<<6, Rest/binary>>) ->
{K, Rest2} = deserialize_type(Rest),
{V, Rest3} = deserialize_type(Rest2),
{{map, K, V}, Rest3}.
deserialize_types(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_types(N, Binary, Acc) ->
{T, Rest} = deserialize_type(Binary),
deserialize_types(N-1, Rest, [T | Acc]).
to_hexstring(ByteList) ->
@@ -742,6 +734,10 @@ to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_fun(Address, Code, Env),
{Fun, Rest2} = to_fun_def(Rest),
to_bytecode(Rest2, Fun, Env2, [], Opts);
to_bytecode([{mnemonic,_line, 'ELEMENT'}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_fate_opcodes:m_to_op('ELEMENT'),
{RetType, Rest2} = to_type(Rest),
to_bytecode(Rest2, Address, Env, [RetType, OpCode|Code], Opts);
to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_fate_opcodes:m_to_op(Op),
to_bytecode(Rest, Address, Env, [OpCode|Code], Opts);
@@ -755,66 +751,11 @@ 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) ->
to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts);
to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_string(String)}|Code],
Opts);
to_bytecode([{object,_line, {address, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_address(Value)}|Code],
Opts);
to_bytecode([{object,_line, {contract, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts);
to_bytecode([{object,_line, {oracle, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle(Value)}|Code],
Opts);
to_bytecode([{object,_line, {name, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_name(Value)}|Code],
Opts);
to_bytecode([{object,_line, {channel, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts);
to_bytecode([{hash,_line, Value}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_hash(Value)}|Code],
Opts);
to_bytecode([{signature,_line, {signature, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_signature(Value)}|Code],
Opts);
to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Hash}|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);
to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) ->
{Map, Rest2} = parse_map(Rest),
to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts);
to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) ->
{List, Rest2} = parse_list(Rest),
to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts);
to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) ->
{Elements, Rest2} = parse_tuple(Rest),
Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)),
to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts);
to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts);
to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts);
to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_annotation(comment, Line, Comment, Env),
to_bytecode(Rest, Address, Env2, Code, Opts);
@@ -831,95 +772,6 @@ to_bytecode([], Address, Env, Code, Opts) ->
end,
Env2.
parse_map([{'}',_line}|Rest]) ->
{#{}, Rest};
parse_map(Tokens) ->
{Key, [{arrow, _} | Rest]} = parse_value(Tokens),
{Value, Rest2} = parse_value(Rest),
case Rest2 of
[{',',_} | Rest3] ->
{Map, Rest4} = parse_map(Rest3),
{Map#{Key => Value}, Rest4};
[{'}',_} | Rest3] ->
{#{Key => Value}, Rest3}
end.
parse_list([{']',_line}|Rest]) ->
{[], Rest};
parse_list(Tokens) ->
{Head , Rest} = parse_value(Tokens),
case Rest of
[{',',_} | Rest2] ->
{Tail, Rest3} = parse_list(Rest2),
{[Head | Tail], Rest3};
[{']',_} | Rest3] ->
{[Head], Rest3}
end.
parse_tuple([{')',_line}|Rest]) ->
{[], Rest};
parse_tuple(Tokens) ->
{Head , Rest} = parse_value(Tokens),
case Rest of
[{',',_} | Rest2] ->
{Tail, Rest3} = parse_tuple(Rest2),
{[Head | Tail], Rest3};
[{')',_} | Rest3] ->
{[Head], Rest3}
end.
parse_variant([{start_variant,_line}
, {'[', _line}
| Rest]) ->
{Arities, Rest2} = parse_list(Rest),
%% Make sure Arities is a list of bytes.
Arities = [A || A <- Arities,
is_integer(A), A < 256],
[{'|',_}
, {int,_line, Tag}
, {'|',_}
, {'(',_} | Rest3] = Rest2,
{Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3),
Size = length(Arities),
if 0 =< Tag, Tag < Size ->
Arity = lists:nth(Tag+1, Arities),
if length(Elements) =:= Arity ->
{Arities, Tag, list_to_tuple(Elements), Rest4}
end
end.
parse_value([{int,_line, Int} | Rest]) -> {Int, Rest};
parse_value([{boolean,_line, Bool} | Rest]) -> {Bool, Rest};
parse_value([{'{',_line} | Rest]) -> parse_map(Rest);
parse_value([{'[',_line} | Rest]) -> parse_list(Rest);
parse_value([{'(',_line} | Rest]) ->
{T, Rest2} = parse_tuple(Rest),
{aeb_fate_data:make_tuple(list_to_tuple(T)), Rest2};
parse_value([{bits,_line, Bits} | Rest]) ->
{aeb_fate_data:make_bits(Bits), Rest};
parse_value([{start_variant,_line}|_] = Tokens) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
{Variant, Rest};
parse_value([{string,_line, String} | Rest]) ->
{aeb_fate_data:make_string(String), Rest};
parse_value([{address,_line, {address, Address}} | Rest]) ->
{aeb_fate_data:make_address(Address), Rest};
parse_value([{address,_line, {contract, Address}} | Rest]) ->
{aeb_fate_data:make_contract(Address), Rest};
parse_value([{address,_line, {oracle, Address}} | Rest]) ->
{aeb_fate_data:make_oracle(Address), Rest};
parse_value([{address,_line, {name, Address}} | Rest]) ->
{aeb_fate_data:make_name(Address), Rest};
parse_value([{address,_line, {channel, Address}} | Rest]) ->
{aeb_fate_data:make_channel(Address), Rest};
parse_value([{hash,_line, Hash} | Rest]) ->
{aeb_fate_data:make_hash(Hash), Rest};
parse_value([{signature,_line, Hash} | Rest]) ->
{aeb_fate_data:make_signature(Hash), Rest}.
to_fun_def([{id, _, Name}, {'(', _} | Rest]) ->
{ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest),
@@ -939,17 +791,11 @@ to_arg_types(Tokens) ->
%% Type handling
to_type([{id, _, "integer"} | Rest]) -> {integer, Rest};
to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest};
to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "contract"} | Rest]) -> {contract, Rest};
to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest};
to_type([{id, _, "name"} | Rest]) -> {name, Rest};
to_type([{id, _, "channel"} | Rest]) -> {channel, Rest};
to_type([{id, _, "hash"} | Rest]) -> {hash, Rest};
to_type([{id, _, "signature"} | Rest]) -> {signature, Rest};
to_type([{id, _, "bits"} | Rest]) -> {bits, Rest};
to_type([{id, _, "integer"} | Rest]) -> {integer, Rest};
to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest};
to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "bits"} | Rest]) -> {bits, Rest};
to_type([{'{', _}, {id, _, "list"}, {',', _} | Rest]) ->
%% TODO: Error handling
{ListType, [{'}', _}| Rest2]} = to_type(Rest),
@@ -962,15 +808,7 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
%% TODO: Error handling
{KeyType, [{',', _}| Rest2]} = to_type(Rest),
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
{{map, KeyType, ValueType}, Rest3};
to_type([{'{', _}
, {id, _, "variant"}
, {',', _}
, {'[', _}
| Rest]) ->
{ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest),
{{variant, ElementTypes}, Rest2}.
{{map, KeyType, ValueType}, Rest3}.
to_list_of_types([{']', _} | Rest]) -> {[], Rest};
to_list_of_types(Tokens) ->
@@ -982,6 +820,19 @@ to_list_of_types(Tokens) ->
{[Type], Rest}
end.
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [0];
serialize_type(boolean) -> [1];
serialize_type({list, T}) -> [2 | serialize_type(T)];
serialize_type({tuple, Ts}) ->
case length(Ts) of
N when N =< 255 ->
[3, N | [serialize_type(T) || T <- Ts]]
end;
serialize_type(address) -> 4;
serialize_type(bits) -> 5;
serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)].
%% -------------------------------------------------------------------
%% Helper functions
+10 -53
View File
@@ -4,7 +4,7 @@
%%% @doc
%%% Handling FATE code.
%%% @end
%%% ###REPLACEWITHNOTE###
###REPLACEWITHNOTE###
%%%-------------------------------------------------------------------
Definitions.
@@ -12,17 +12,12 @@ DIGIT = [0-9]
HEXDIGIT = [0-9a-fA-F]
LOWER = [a-z_]
UPPER = [A-Z]
BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
OBJECT = @[a-z][a-z]_{BASE58}+
HASH = #{BASE64}+
SIG = \$sg_{BASE58}+
HASH = #{HEXDIGIT}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
STRING = "[^"]*"
BITS = (\!)?\<[\s01]*\>
Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
@@ -33,41 +28,23 @@ a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}.
%% ###REPLACEWITHOPTOKENS###
###REPLACEWITHOPTOKENS###
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
{SIG} :
{token, {signature, TokenLine, parse_object(TokenChars)}}.
{OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} :
{token, {id, TokenLine, TokenChars}}.
{HEX} :
{token, {int, TokenLine, parse_hex(TokenChars)}}.
{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
-{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
%% Due to the definition of STRING the tokens start and end with a quote ".
{STRING} :
{token, {string, TokenLine, unicode:characters_to_binary(
lists:sublist(TokenChars, 2, length(TokenChars) - 2))}}.
{BITS} :
{token, {bits, TokenLine, bits(TokenChars)}}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
%% Symbols
\-\> : {token, {to, TokenLine}}.
\: : {token, {to, TokenLine}}.
\=\> : {token, {arrow, TokenLine}}.
\(\| : {token, {start_variant, TokenLine}}.
\|\) : {token, {end_variant, TokenLine}}.
\-\> : {token, {'to', TokenLine}}.
\: : {token, {'to', TokenLine}}.
, : {token, {',', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
@@ -75,7 +52,6 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
\] : {token, {']', TokenLine}}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
\| : {token, {'|', TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
@@ -112,17 +88,8 @@ parse_acc("a" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) ->
base64:decode(Chars).
parse_object([_|Chars]) ->
case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin};
{name, Bin} -> {name, Bin};
{channel, Bin} -> {channel, Bin};
{signature, Bin} -> {signature, Bin}
end.
N = list_to_integer(Chars, 16),
<<N:256>>.
scan(S) ->
string(S).
@@ -130,13 +97,3 @@ scan(S) ->
drop_prefix(C, [C|Rest]) ->
drop_prefix(C, Rest);
drop_prefix(_, Tail) -> Tail.
bits([$!, $< | Rest]) ->
bits(Rest, -1);
bits([$< | Rest]) ->
bits(Rest, 0).
bits([$> |_Rest], Acc) -> Acc;
bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1);
bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1);
bits([$ | Rest], Acc) -> bits(Rest, Acc).
+117 -118
View File
@@ -4,39 +4,27 @@
-module(aeb_fate_data).
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_HASH_T.
-type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_name() :: ?FATE_NAME_T.
-type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_signature() :: ?FATE_SIGNATURE_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_type_type() :: integer
| boolean
| {list, fate_type_type()}
| {map, fate_type_type(), fate_type_type()}
| {tuple, [fate_type_type()]}
| {list, fate_type()}
| {map, fate_type(), fate_type()}
| {tuple, [fate_type()]}
| address
| hash
| signature
| contract
| oracle
| name
| channel
| bits
| string
| {variant, [fate_type_type()]}.
| bits
| {variant, integer()}.
-type fate_type() ::
@@ -48,15 +36,9 @@
| fate_tuple()
| fate_string()
| fate_address()
| fate_hash()
| fate_signature()
| fate_contract()
| fate_oracle()
| fate_name()
| fate_channel()
| fate_variant()
| fate_map()
| fate_bits().
| fate_type_type().
-export_type([fate_type/0
, fate_boolean/0
@@ -67,15 +49,8 @@
, fate_tuple/0
, fate_string/0
, fate_address/0
, fate_hash/0
, fate_signature/0
, fate_contract/0
, fate_oracle/0
, fate_name/0
, fate_channel/0
, fate_variant/0
, fate_map/0
, fate_bits/0
, fate_type_type/0
]).
@@ -87,56 +62,71 @@
, make_string/1
, make_map/1
, make_address/1
, make_hash/1
, make_signature/1
, make_contract/1
, make_oracle/1
, make_name/1
, make_channel/1
, make_bits/1
, make_unit/0
, tuple_to_list/1
, decode/1
, encode/1
]).
-export([format/1]).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_boolean(true) -> ?FATE_TRUE;
make_boolean(false) -> ?FATE_FALSE.
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(X) -> ?FATE_ADDRESS(X).
make_hash(X) -> ?FATE_HASH(X).
make_signature(X) -> ?FATE_SIGNATURE(X).
make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X).
make_name(X) -> ?FATE_NAME(X).
make_channel(X) -> ?FATE_CHANNEL(X).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_string(S) when is_list(S) ->
?FATE_STRING(list_to_binary(lists:flatten(S)));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(A) -> ?FATE_ADDRESS(A).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
%% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities
%% and should match the size of the given tuple.
make_variant(Arities, Tag, Values) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
?FATE_VARIANT(Arities, Tag, Values)
end
end.
make_variant(Size, Tag, Values) when is_integer(Size), is_integer(Tag)
, 0 =< Size
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
?FATE_VARIANT(Size, Tag, Values).
tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T).
%% Encode is a convinience function for testing, encoding an Erlang term
%% to a Fate term, but it can not distinguish between e.g. 32-byte strings
%% and addresses. Therfore an extra tuple layer on the erlang side for
%% addresses and bits.
encode({bits, Term}) when is_integer(Term) -> make_bits(Term);
%% TODO: check that each byte is in base58
encode({address, B}) when is_binary(B) -> make_address(B);
encode({address, I}) when is_integer(I) -> B = <<I:256>>, make_address(B);
encode({address, S}) when is_list(S) -> make_address(base58_to_address(S));
encode({variant, Size, Tag, Values}) -> make_variant(Size, Tag, Values);
encode(Term) when is_integer(Term) -> make_integer(Term);
encode(Term) when is_boolean(Term) -> make_boolean(Term);
encode(Term) when is_list(Term) -> make_list([encode(E) || E <- Term]);
encode(Term) when is_tuple(Term) ->
make_tuple(list_to_tuple([encode(E) || E <- erlang:tuple_to_list(Term)]));
encode(Term) when is_map(Term) ->
make_map(maps:from_list([{encode(K), encode(V)} || {K,V} <- maps:to_list(Term)]));
encode(Term) when is_binary(Term) -> make_string(Term).
decode(I) when ?IS_FATE_INTEGER(I) -> I;
decode(?FATE_TRUE) -> true;
decode(?FATE_FALSE) -> false;
decode(L) when ?IS_FATE_LIST(L) -> [decode(E) || E <- L];
decode(?FATE_ADDRESS(<<Address:256>>)) -> {address, Address};
decode(?FATE_BITS(Bits)) -> {bits, Bits};
decode(?FATE_TUPLE(T)) -> erlang:list_to_tuple([decode(E) || E <- T]);
decode(?FATE_VARIANT(Size, Tag, Values)) -> {variant, Size, Tag, Values};
decode(S) when ?IS_FATE_STRING(S) -> binary_to_list(S);
decode(M) when ?IS_FATE_MAP(M) ->
maps:from_list([{decode(K), decode(V)} || {K, V} <- maps:to_list(M)]).
-spec format(fate_type()) -> iolist().
format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I));
format(?FATE_TRUE) -> "true";
@@ -146,50 +136,59 @@ format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L));
format(?FATE_UNIT) -> "()";
format(?FATE_TUPLE(T)) ->
["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"];
format(S) when ?IS_FATE_STRING(S) -> ["\"", S, "\""];
format(?FATE_BITS(B)) when B >= 0 ->
["<", format_bits(B, "") , ">"];
format(?FATE_BITS(B)) when B < 0 ->
["!< ", format_nbits(-B-1, "") , " >"];
format(?FATE_VARIANT(Arities, Tag, T)) ->
format(S) when ?IS_FATE_STRING(S) -> [S];
format(?FATE_VARIANT(Size, Tag, T)) ->
["(| ",
lists:join("| ",
[format_arities(Arities),
integer_to_list(Tag) |
[format(make_tuple(T))]]),
lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) |
[format(E) || E <- erlang:tuple_to_list(T)]]),
" |)"];
format(M) when ?IS_FATE_MAP(M) ->
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_HASH(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_SIGNATURE(X)) ->
["$", aeser_api_encoder:encode(signature, X)];
format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_NAME(X)) ->
["@", aeser_api_encoder:encode(name, X)];
format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)];
format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)];
format(V) -> exit({not_a_fate_type, V}).
format_bits(0, Acc) -> Acc;
format_bits(N, Acc) ->
Bit = $0 + (N band 1),
format_bits(N bsr 1, [Bit|Acc]).
format_nbits(0, Acc) -> Acc;
format_nbits(N, Acc) ->
Bit = $1 - (N band 1),
format_nbits(N bsr 1, [Bit|Acc]).
format_arities(Arities) ->
["[ ", lists:join(", ", [integer_to_list(E) || E <- Arities]), " ]"].
format_list(List) ->
format_list(List) ->
["[ ", lists:join(", ", [format(E) || E <- List]), " ]"].
format_kvs(List) ->
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
%% -- Local base 58 library
base58char(Char) ->
binary:at(<<"123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
"abcdefghijkmnopqrstuvwxyz">>, Char).
char_to_base58(C) ->
binary:at(<<0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9,10,11,12,13,14,15,16,0,17,
18,19,20,21,0,22,23,24,25,26,27,28,29,30,31,32,0,0,0,0,0,0,
33,34,35,36,37,38,39,40,41,42,43,0,44,45,46,47,48,49,50,51,
52,53,54,55,56,57>>, C-$1).
base58_to_integer(C, []) -> C;
base58_to_integer(C, [X | Xs]) ->
base58_to_integer(C * 58 + char_to_base58(X), Xs).
base58_to_integer([]) -> error;
base58_to_integer([Char]) -> char_to_base58(Char);
base58_to_integer([Char | Str]) ->
base58_to_integer(char_to_base58(Char), Str).
base58_to_address(Base58) ->
I = base58_to_integer(Base58),
Bin = <<I:256>>,
Bin.
address_to_base58(<<A:256>>) ->
integer_to_base58(A).
integer_to_base58(0) -> <<"1">>;
integer_to_base58(Integer) ->
Base58String = integer_to_base58(Integer, []),
list_to_binary(Base58String).
integer_to_base58(0, Acc) -> Acc;
integer_to_base58(Integer, Acc) ->
Quot = Integer div 58,
Rem = Integer rem 58,
integer_to_base58(Quot, [base58char(Rem)|Acc]).
+31 -172
View File
@@ -28,6 +28,8 @@
%% TODO:
%% * Make the code production ready.
%% (add tests, document exported functions).
%% * Handle Variant types better.
%% * Handle type representations.
%% * Handle instructions.
%%
%% ------------------------------------------------------------------------
@@ -35,9 +37,7 @@
-export([ deserialize/1
, deserialize_one/1
, deserialize_type/1
, serialize/1
, serialize_type/1
]).
-include("aeb_fate_data.hrl").
@@ -47,62 +47,38 @@
-define(SMALL_INT , 2#0). %% sxxxxxx 0 - 6 bit integer with sign bit
%% 1 Set below
-define(LONG_STRING , 2#00000001). %% 000000 01 | RLP encoded array - when size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 | [bytes] - when 0 < xxxxxx:size < 64
-define(LONG_STRING , 2#00000001). %% 000000 01 - RLP encoded array, size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 - [bytes], 0 < xxxxxx:size < 64
%% 11 Set below
-define(SHORT_LIST , 2#0011). %% xxxx 0011 | [encoded elements] when 0 < length < 16
%% xxxx 0111
-define(TYPE_INTEGER , 2#00000111). %% 0000 0111 - Integer typedef
-define(TYPE_BOOLEAN , 2#00010111). %% 0001 0111 - Boolean typedef
-define(TYPE_LIST , 2#00100111). %% 0010 0111 | Type
-define(TYPE_TUPLE , 2#00110111). %% 0011 0111 | Size | [Element Types]
-define(TYPE_OBJECT , 2#01000111). %% 0100 0111 | ObjectType
-define(TYPE_BITS , 2#01010111). %% 0101 0111 - Bits typedef
-define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
%% 1001 0111
%% 1010 0111
%% 1011 0111
%% 1100 0111
%% 1101 0111
%% 1110 0111
%% 1111 0111
-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
-define(SHORT_LIST , 2#0011). %% xxxx 0011 - [encoded elements], 0 < length < 16
%% xxxx 0111 - FREE (For typedefs in future)
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 - RLP encoded (size - 16) + [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 - [encoded elements], 0 < size < 16
%% 1111 Set below
-define(LONG_LIST , 2#00011111). %% 0001 1111 | RLP encoded (length - 16) | [encoded lements]
-define(MAP , 2#00101111). %% 0010 1111 | RLP encoded size | [encoded key, encoded value]
-define(LONG_LIST , 2#00011111). %% 0001 1111 - RLP encoded (length - 16) + [Elements]
-define(MAP , 2#00101111). %% 0010 1111 - RLP encoded size + [encoded key, encoded value]
-define(EMPTY_TUPLE , 2#00111111). %% 0011 1111
-define(POS_BITS , 2#01001111). %% 0100 1111 | RLP encoded integer (to be interpreted as bitfield)
-define(POS_BITS , 2#01001111). %% 0100 1111 - RLP encoded integer (to be interpreted as bitfield)
-define(EMPTY_STRING , 2#01011111). %% 0101 1111
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 | RLP encoded (integer - 64)
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 - RLP encoded (integer - 64)
-define(FALSE , 2#01111111). %% 0111 1111
%% %% 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(ADDRESS , 2#10011111). %% 1001 1111 - [32 bytes]
-define(VARIANT , 2#10101111). %% 1010 1111 - encoded size + 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(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)
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 - RLP encoded (integer - 64)
-define(TRUE , 2#11111111). %% 1111 1111
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE, 16).
-define(SMALL_INT_SIZE, 64).
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE , 16).
-define(SMALL_INT_SIZE , 64).
-define(SHORT_STRING_SIZE, 64).
-define(POS_SIGN, 0).
-define(NEG_SIGN, 1).
%% Object types
-define(OTYPE_ADDRESS, 0).
-define(OTYPE_HASH, 1).
-define(OTYPE_SIGNATURE, 2).
-define(OTYPE_CONTRACT, 3).
-define(OTYPE_ORACLE, 4).
-define(OTYPE_NAME, 5).
-define(OTYPE_CHANNEL, 6).
%% --------------------------------------------------
%% Serialize
@@ -130,19 +106,7 @@ serialize(String) when ?IS_FATE_STRING(String),
Bytes = ?FATE_STRING_VALUE(String),
<<?LONG_STRING, (aeser_rlp:encode(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) ->
<<?OBJECT, ?OTYPE_HASH, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_SIGNATURE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_SIGNATURE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_NAME(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_NAME, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>;
<<?ADDRESS, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
S = size(T),
L = tuple_to_list(T),
@@ -164,7 +128,7 @@ serialize(L) when ?IS_FATE_LIST(L) ->
<<?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 = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)),
Size = length(L),
%% TODO: check all K same type, and all V same type
%% check K =/= map
@@ -172,97 +136,11 @@ serialize(Map) when ?IS_FATE_MAP(Map) ->
<<?MAP,
(rlp_integer(Size))/binary,
(Elements)/binary>>;
serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
EncodedArities = aeser_rlp:encode(list_to_binary(Arities)),
<<?VARIANT,
EncodedArities/binary,
Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>
end
end.
%% -----------------------------------------------------
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [?TYPE_INTEGER];
serialize_type(boolean) -> [?TYPE_BOOLEAN];
serialize_type({list, T}) -> [?TYPE_LIST | serialize_type(T)];
serialize_type({tuple, Ts}) ->
case length(Ts) of
N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
end;
serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS];
serialize_type(hash) -> [?TYPE_OBJECT, ?OTYPE_HASH];
serialize_type(signature) -> [?TYPE_OBJECT, ?OTYPE_SIGNATURE];
serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT];
serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE];
serialize_type(name) -> [?TYPE_OBJECT, ?OTYPE_NAME];
serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL];
serialize_type(bits) -> [?TYPE_BITS];
serialize_type({map, K, V}) -> [?TYPE_MAP
| serialize_type(K) ++ serialize_type(V)];
serialize_type(string) -> [?TYPE_STRING];
serialize_type({variant, ListOfVariants}) ->
Size = length(ListOfVariants),
if Size < 256 ->
[?TYPE_VARIANT, Size | [serialize_type(T) || T <- ListOfVariants]]
end.
-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_LIST, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2};
deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) ->
case ObjectType of
?OTYPE_ADDRESS -> {address, Rest};
?OTYPE_HASH -> {hash, Rest};
?OTYPE_SIGNATURE -> {signature, Rest};
?OTYPE_CONTRACT -> {contract, Rest};
?OTYPE_ORACLE -> {oracle, Rest};
?OTYPE_NAME -> {name, Rest};
?OTYPE_CHANNEL -> {channel, Rest}
end;
deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest};
deserialize_type(<<?TYPE_MAP, Rest/binary>>) ->
{K, Rest2} = deserialize_type(Rest),
{V, Rest3} = deserialize_type(Rest2),
{{map, K, V}, Rest3};
deserialize_type(<<?TYPE_STRING, Rest/binary>>) ->
{string, Rest};
deserialize_type(<<?TYPE_VARIANT, Size, Rest/binary>>) ->
{Variants, Rest2} = deserialize_variants(Size, Rest, []),
{{variant, Variants}, Rest2}.
deserialize_variants(0, Rest, Variants) ->
{lists:reverse(Variants), Rest};
deserialize_variants(N, Rest, Variants) ->
{T, Rest2} = deserialize_type(Rest),
deserialize_variants(N-1, Rest2, [T|Variants]).
deserialize_types(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_types(N, Binary, Acc) ->
{T, Rest} = deserialize_type(Binary),
deserialize_types(N-1, Rest, [T | Acc]).
serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 < Size, Size < 256,
0 =< Tag, Tag < Size ->
<<?VARIANT, Size:8, Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>.
%% -----------------------------------------------------
@@ -327,19 +205,9 @@ 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)),
{?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
deserialize2(<<?ADDRESS, Rest/binary>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest),
Val =
case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
?OTYPE_HASH -> ?FATE_HASH(A);
?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A);
?OTYPE_CONTRACT -> ?FATE_CONTRACT(A);
?OTYPE_ORACLE -> ?FATE_ORACLE(A);
?OTYPE_NAME -> ?FATE_NAME(A);
?OTYPE_CHANNEL -> ?FATE_CHANNEL(A)
end,
{Val, Rest2};
{?FATE_ADDRESS(A), Rest2};
deserialize2(<<?TRUE, Rest/binary>>) ->
{?FATE_TRUE, Rest};
deserialize2(<<?FALSE, Rest/binary>>) ->
@@ -374,20 +242,11 @@ deserialize2(<<?MAP, Rest/binary>>) ->
{List, Rest2} = deserialize_elements(2*Size, Rest1),
Map = insert_kv(List, #{}),
{?MAKE_FATE_MAP(Map), Rest2};
deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin),
Size = length(Arities),
deserialize2(<<?VARIANT, Size:8, Tag:8, Rest/binary>>) ->
if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size});
true ->
{?FATE_TUPLE(T), Rest3} = deserialize2(Rest2),
Arity = lists:nth(Tag+1, Arities),
NumElements = size(T),
if NumElements =/= Arity ->
exit({tag_does_not_match_type_in_variant, Tag, Arity});
true ->
{?FATE_VARIANT(Arities, Tag, T), Rest3}
end
{?FATE_TUPLE(T), Rest2} = deserialize2(Rest),
{?FATE_VARIANT(Size, Tag, T), Rest2}
end.
insert_kv([], M) -> M;
+13 -300
View File
@@ -1,29 +1,24 @@
-module(aeb_fate_generate_ops).
-export([ gen_and_halt/1
, generate/0
, generate_documentation/1
, get_ops/0
, test_asm_generator/1]).
, generate/0]).
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
generate(atom_to_list(SrcDirArg),
atom_to_list(IncludeDirArg)),
halt().
generate() -> generate("src/", "include/").
get_ops() -> gen(ops_defs()).
generate() ->
generate("src/", "include/").
generate(Src, Include) ->
Ops = get_ops(),
Ops = gen(ops_defs()),
%% 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_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops),
gen_asm_pp(aeb_fate_pp, Src, Ops).
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops).
%% TODO: Some real gas numbers...
ops_defs() ->
@@ -38,7 +33,7 @@ ops_defs() ->
, { '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."}
, { '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."}
@@ -64,7 +59,7 @@ ops_defs() ->
, { '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)."}
, { 'ELEMENT', 16#2a, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"}
, { '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."}
@@ -100,11 +95,11 @@ ops_defs() ->
, { '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."}
, { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract address."}
, { '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."}
, { 'BLOCKHASH', 16#5e, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has 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."}
@@ -139,14 +134,9 @@ ops_defs() ->
, { '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."}
, { '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."}
, {'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."
@@ -395,9 +385,9 @@ gen_token(#{opname := OpName}) ->
io_lib:format("~-28s: {token, {mnemonic, TokenLine, ~w}}.\n",
[Name, OpName]).
insert_tokens_in_template(<<"%% ###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) ->
insert_tokens_in_template(<<"###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) ->
[Tokens, Rest];
insert_tokens_in_template(<<"%%% ###REPLACEWITHNOTE###", Rest/binary >>, Tokens) ->
insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) ->
[
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
@@ -408,282 +398,5 @@ insert_tokens_in_template(<<"%%% ###REPLACEWITHNOTE###", Rest/binary >>, Tokens)
insert_tokens_in_template(<<B,Rest/binary>>, Tokens) ->
[B|insert_tokens_in_template(Rest, Tokens)].
gen_asm_pp(Module, Path, Ops) ->
Filename = filename:join(Path, atom_to_list(Module)) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Formats = lists:flatten([gen_format(Op)++"\n" || Op <- Ops]),
io:format(File, "~s", [prelude(" Provide pretty printing functuions for "
"Fate instructions.\n")]),
io:format(File, "-module(~w).\n\n", [Module]),
io:format(File,
"-export([format_op/2]).\n\n"
"format_arg(li, {immediate, LI}) ->\n"
" aeb_fate_data:format(LI);\n"
"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}) -> 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"
"lookup(Name, Symbols) ->\n"
" maps:get(Name, Symbols, io_lib:format(\"~~w\",[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)];",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := atomic}) ->
io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := Args}) ->
NameAsString = atom_to_list(Name),
case Args of
[T0] ->
io_lib:format(
"format_op({~w, Arg0}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0)];",
[Name, NameAsString, T0]);
[T0, T1] ->
io_lib:format(
"format_op({~w, Arg0, Arg1}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1)];",
[Name, NameAsString, T0, T1]);
[T0, T1, T2] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2)];",
[Name, NameAsString, T0, T1, T2]);
[T0, T1, T2, T3] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3)];",
[Name, NameAsString, T0, T1, T2, T3]);
[T0, T1, T2, T3, T4] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4)];",
[Name, NameAsString, T0, T1, T2, T3, T4]);
[T0, T1, T2, T3, T4, T5] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5]);
[T0, T1, T2, T3, T4, T5, T6] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]);
[T0, T1, T2, T3, T4, T5, T6, T7] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6),"
"\" \", format_arg(~w, Arg7)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6, T7])
end.
test_asm_generator(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_instruction(Op)++"\n" || Op <- get_ops()]),
io:format(File,
";; CONTRACT all_instructions\n\n"
";; Dont expect this contract to typecheck or run.\n"
";; Just used to check assembler rountrip of all instruction.\n\n"
"FUNCTION foo () : {tuple, []}\n"
"~s"
, [Instructions]),
io:format(File, " RETURNR ()\n", []),
file:close(File).
gen_instruction(#{opname := Name, format := atomic}) ->
io_lib:format(" ~s\n", [Name]);
gen_instruction(#{opname := Name, format := ArgTypes}) ->
Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])),
I = io_lib:format(" ~s ~s\n", [Name, Args]),
I.
%% This should be done with a Quick Check generator...
gen_arg(a) -> any_arg();
gen_arg(is) -> "foo";
gen_arg(ii) -> gen_int();
gen_arg(li) -> "[1, 2, 3]";
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).
arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1).
var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1).
imm_arg() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_map();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_key() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_string();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_boolean() ->
element(rand:uniform(2), {"true", "false"}).
gen_int() ->
element(rand:uniform(4),
{ integer_to_list(rand:uniform(round(math:pow(10,40))))
, integer_to_list(rand:uniform(10))
, integer_to_list(rand:uniform(100))
, io_lib:format("0x~.16b",[rand:uniform(round(math:pow(10,10)))])}).
gen_address() -> "#nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv".
gen_string() -> "\"foo\"".
gen_map() -> "{ " ++ gen_key() ++ " => " ++ imm_arg() ++ "}".
gen_list() ->
case rand:uniform(4) of
1 -> "[]";
2 -> "[" ++ lists:join(", ", gen_list_elements()) ++ " ]";
3 -> "[ " ++ imm_arg() ++ " ]";
4 -> "[ " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ]"
end.
%% Not type correct.
gen_list_elements() ->
case rand:uniform(3) of
1 -> [imm_arg() | gen_list_elements()];
2 -> [];
3 -> [imm_arg()]
end.
gen_bits() ->
element(rand:uniform(3),
{"<>"
,"!<>"
, "101010"}).
gen_tuple() ->
case rand:uniform(3) of
1 -> "()";
2 -> "(42)";
3 -> "(" ++ imm_arg() ++ ")"
end.
gen_variant() ->
case rand:uniform(3) of
1 -> "(| 5 | 2 | (1, \"foo\", ()) |)";
2 -> "(| 2 | 1 | ( " ++ imm_arg() ++ " ) |)";
3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)"
end.
%% TODO: add gas cost.
generate_documentation(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]),
io:format(File,
"### Operations\n\n"
"| OpCode | Name | Args | Description |\n"
"| --- | --- | --- | --- |\n"
"~s"
, [Instructions]),
io:format(File, "\n", []),
file:close(File).
gen_doc(#{ opname := Name
, opcode := OpCode
, args := _Args
, end_bb := _EndBB
, format := FateFormat
, macro := _Macro
, type_name := _TypeName
, doc := Doc
, gas := _Gas
, type := _Type
, constructor := _Constructor
, constructor_type := _ConstructorType
}) ->
Arguments =
case FateFormat of
atomic -> "";
_ -> lists:join(" ",
[format_arg_doc(A) ||
A <-
lists:zip(FateFormat,
lists:seq(0,length(FateFormat)-1))])
end,
io_lib:format("| 0x~.16b | ~w | ~s | ~s |\n",
[ OpCode
, Name
, Arguments
, Doc]).
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type".
-301
View File
@@ -1,301 +0,0 @@
-module(aeb_heap).
-export([ to_binary/1
, to_binary/2
, from_heap/3
, from_binary/2
, from_binary/3
, maps_with_next_id/1
, set_next_id/2
, heap_fragment/3
, heap_value/3
, heap_value/4
, heap_value_pointer/1
, heap_value_maps/1
, heap_value_offset/1
, heap_value_heap/1
, heap_fragment_maps/1
, heap_fragment_offset/1
, heap_fragment_heap/1
]).
-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]).
-include_lib("aebytecode/include/aeb_typerep_def.hrl").
-include_lib("aebytecode/include/aeb_heap.hrl").
-type word() :: non_neg_integer().
-type pointer() :: word().
-opaque heap_fragment() :: #heap{}.
-type offset() :: non_neg_integer().
-type binary_value() :: binary().
-type heap_value() :: {pointer(), heap_fragment()}.
-spec maps_with_next_id(heap_fragment()) -> #maps{}.
%% Create just a maps value, don't keep rest of Heap
maps_with_next_id(#heap{maps = #maps{next_id = N}}) ->
#maps{ next_id = N }.
-spec set_next_id(heap_fragment(), non_neg_integer()) -> heap_fragment().
set_next_id(Heap, N) ->
Heap#heap{ maps = Heap#heap.maps#maps{ next_id = N } }.
%% -- data type heap_fragment
-spec heap_fragment(binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Heap) ->
heap_fragment(#maps{ next_id = 0 }, 0, Heap).
-spec heap_fragment(#maps{}, offset(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Maps, Offset, Heap) ->
#heap{maps = Maps, offset = Offset, heap = Heap}.
-spec heap_fragment_maps(heap_fragment()) -> #maps{}.
heap_fragment_maps(#heap{maps = Maps}) ->
Maps.
-spec heap_fragment_offset(heap_fragment()) -> offset().
heap_fragment_offset(#heap{offset = Offs}) ->
Offs.
-spec heap_fragment_heap(heap_fragment()) -> binary() | #{non_neg_integer() => non_neg_integer()}.
heap_fragment_heap(#heap{heap = Heap}) ->
Heap.
%% -- data type heap_value
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_value().
heap_value(Maps, Ptr, Heap) ->
heap_value(Maps, Ptr, Heap, 0).
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}, offset()) -> heap_value().
heap_value(Maps, Ptr, Heap, Offs) ->
{Ptr, heap_fragment(Maps, Offs, Heap)}.
-spec heap_value_pointer(heap_value()) -> pointer().
heap_value_pointer({Ptr, _}) -> Ptr.
-spec heap_value_maps(heap_value()) -> #maps{}.
heap_value_maps({_, Heap}) -> Heap#heap.maps.
-spec heap_value_offset(heap_value()) -> offset().
heap_value_offset({_, Heap}) -> Heap#heap.offset.
-spec heap_value_heap(heap_value()) ->
binary() | #{non_neg_integer() => non_neg_integer()}.
heap_value_heap({_, Heap}) -> Heap#heap.heap.
%% -- Value to binary --------------------------------------------------------
-spec to_binary(aeb_aevm_data:data()) -> aeb_aevm_data:heap().
%% Encode the data as a heap where the first word is the value (for unboxed
%% types) or a pointer to the value (for boxed types).
to_binary(Data) ->
to_binary(Data, 0).
to_binary(Data, BaseAddress) ->
{Address, Memory} = to_binary1(Data, BaseAddress + 32),
R = <<Address:256, Memory/binary>>,
R.
%% Allocate the data in memory, from the given address. Return a pair
%% of memory contents from that address and the value representing the
%% data.
to_binary1(Data,_Address) when is_integer(Data) ->
{Data,<<>>};
to_binary1(Data, Address) when is_binary(Data) ->
%% a string
Words = aeb_memory:binary_to_words(Data),
{Address,<<(size(Data)):256, << <<W:256>> || W <- Words>>/binary>>};
to_binary1(none, Address) -> to_binary1({variant, 0, []}, Address);
to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address);
to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address);
to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address);
to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_TAG}, Address);
to_binary1(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address);
to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, Address);
to_binary1({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address);
to_binary1({tuple, Ts}, Address) -> to_binary1({?TYPEREP_TUPLE_TAG, Ts}, Address);
to_binary1({variant, Cons}, Address) -> to_binary1({?TYPEREP_VARIANT_TAG, Cons}, Address);
to_binary1({map, K, V}, Address) -> to_binary1({?TYPEREP_MAP_TAG, K, V}, Address);
to_binary1({variant, Tag, Args}, Address) ->
to_binary1(list_to_tuple([Tag | Args]), Address);
to_binary1(Map, Address) when is_map(Map) ->
Size = maps:size(Map),
%% Sort according to binary ordering
KVs = lists:sort([ {to_binary(K), to_binary(V)} || {K, V} <- maps:to_list(Map) ]),
{Address, <<Size:256, << <<(byte_size(K)):256, K/binary,
(byte_size(V)):256, V/binary>> || {K, V} <- KVs >>/binary >>};
to_binary1({}, _Address) ->
{0, <<>>};
to_binary1(Data, Address) when is_tuple(Data) ->
{Elems,Memory} = to_binaries(tuple_to_list(Data),Address+32*size(Data)),
ElemsBin = << <<W:256>> || W <- Elems>>,
{Address,<< ElemsBin/binary, Memory/binary >>};
to_binary1([],_Address) ->
<<Nil:256>> = <<(-1):256>>,
{Nil,<<>>};
to_binary1([H|T],Address) ->
to_binary1({H,T},Address).
to_binaries([],_Address) ->
{[],<<>>};
to_binaries([H|T],Address) ->
{HRep,HMem} = to_binary1(H,Address),
{TRep,TMem} = to_binaries(T,Address+size(HMem)),
{[HRep|TRep],<<HMem/binary, TMem/binary>>}.
%% Interpret a return value (a binary) using a type rep.
-spec from_heap(Type :: ?Type(), Heap :: binary(), Ptr :: integer()) ->
{ok, term()} | {error, term()}.
from_heap(Type, Heap, Ptr) ->
try {ok, from_binary(#{}, Type, Heap, Ptr)}
catch _:Err ->
%% io:format("** Error: from_heap failed with ~p\n ~p\n", [Err, erlang:get_stacktrace()]),
{error, Err}
end.
%% Base address is the address of the first word of the given heap.
-spec from_binary(T :: ?Type(),
Heap :: binary(),
BaseAddr :: non_neg_integer()) ->
{ok, term()} | {error, term()}.
from_binary(T, Heap = <<V:256, _/binary>>, BaseAddr) ->
from_heap(T, <<0:BaseAddr/unit:8, Heap/binary>>, V);
from_binary(_, Bin, _BaseAddr) ->
{error, {binary_too_short, Bin}}.
-spec from_binary(?Type(), binary()) -> {ok, term()} | {error, term()}.
from_binary(T, Heap) ->
from_binary(T, Heap, 0).
from_binary(_, word, _, V) ->
V;
from_binary(_, signed_word, _, V) ->
<<N:256/signed>> = <<V:256>>,
N;
from_binary(_, bool, _, V) ->
case V of
0 -> false;
1 -> true
end;
from_binary(_, string, Heap, V) ->
StringSize = heap_word(Heap,V),
BitAddr = 8*(V+32),
<<_:BitAddr,Bytes:StringSize/binary,_/binary>> = Heap,
Bytes;
from_binary(_, {tuple, []}, _, _) ->
{};
from_binary(Visited, {tuple,Cpts}, Heap, V) ->
check_circular_refs(Visited, V),
NewVisited = Visited#{V => true},
ElementNums = lists:seq(0, length(Cpts)-1),
TypesAndPointers = lists:zip(Cpts, ElementNums),
ElementAddress = fun(Index) -> V + 32 * Index end,
Element = fun(Index) ->
heap_word(Heap, ElementAddress(Index))
end,
Convert = fun(Type, Index) ->
from_binary(NewVisited, Type, Heap, Element(Index))
end,
Elements = [Convert(T, I) || {T,I} <- TypesAndPointers],
list_to_tuple(Elements);
from_binary(Visited, {list, Elem}, Heap, V) ->
<<Nil:256>> = <<(-1):256>>,
if V==Nil ->
[];
true ->
{H,T} = from_binary(Visited, {tuple,[Elem,{list,Elem}]},Heap,V),
[H|T]
end;
from_binary(Visited, {option, A}, Heap, V) ->
from_binary(Visited, {variant_t, [{none, []}, {some, [A]}]}, Heap, V);
from_binary(Visited, {variant, Cons}, Heap, V) ->
Tag = heap_word(Heap, V),
Args = lists:nth(Tag + 1, Cons),
Visited1 = Visited#{V => true},
{variant, Tag, tuple_to_list(from_binary(Visited1, {tuple, Args}, Heap, V + 32))};
from_binary(Visited, {variant_t, TCons}, Heap, V) -> %% Tagged variants
{Tags, Cons} = lists:unzip(TCons),
{variant, I, Args} = from_binary(Visited, {variant, Cons}, Heap, V),
Tag = lists:nth(I + 1, Tags),
case Args of
[] -> Tag;
_ -> list_to_tuple([Tag | Args])
end;
from_binary(_Visited, {map, A, B}, Heap, Ptr) ->
%% FORMAT: [Size] [KeySize] Key [ValSize] Val .. [KeySize] Key [ValSize] Val
Size = heap_word(Heap, Ptr),
map_binary_to_value(A, B, Size, Heap, Ptr + 32);
from_binary(Visited, typerep, Heap, V) ->
check_circular_refs(Visited, V),
Tag = heap_word(Heap, V),
Arg1 = fun(T, I) -> from_binary(Visited#{V => true}, T, Heap, heap_word(Heap, V + 32 * I)) end,
Arg = fun(T) -> Arg1(T, 1) end,
case Tag of
?TYPEREP_WORD_TAG -> word;
?TYPEREP_STRING_TAG -> string;
?TYPEREP_TYPEREP_TAG -> typerep;
?TYPEREP_LIST_TAG -> {list, Arg(typerep)};
?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})};
?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})};
?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)};
?TYPEREP_FUN_TAG -> function
end.
map_binary_to_value(KeyType, ValType, N, Bin, Ptr) ->
%% Avoid looping on bogus sizes
MaxN = byte_size(Bin) div 64,
Heap = heap_fragment(Bin),
map_from_binary({value, KeyType, ValType}, min(N, MaxN), Heap, Ptr, #{}).
map_from_binary(_, 0, _, _, Map) -> Map;
map_from_binary({value, KeyType, ValType} = Output, I, Heap, Ptr, Map) ->
KeySize = get_word(Heap, Ptr),
KeyPtr = Ptr + 32,
KeyBin = get_chunk(Heap, KeyPtr, KeySize),
ValSize = get_word(Heap, KeyPtr + KeySize),
ValPtr = KeyPtr + KeySize + 32,
ValBin = get_chunk(Heap, ValPtr, ValSize),
%% Keys and values are self contained binaries
{ok, Key} = from_binary(KeyType, KeyBin),
{ok, Val} = from_binary(ValType, ValBin),
map_from_binary(Output, I - 1, Heap, ValPtr + ValSize, Map#{Key => Val}).
check_circular_refs(Visited, V) ->
case maps:is_key(V, Visited) of
true -> exit(circular_references);
false -> ok
end.
heap_word(Heap, Addr) when is_binary(Heap) ->
BitSize = 8*Addr,
<<_:BitSize,W:256,_/binary>> = Heap,
W;
heap_word(Heap, Addr) when is_map(Heap) ->
0 = Addr rem 32, %% Check that it's word aligned.
maps:get(Addr, Heap, 0).
get_word(#heap{offset = Offs, heap = Mem}, Addr) when Addr >= Offs ->
get_word(Mem, Addr - Offs);
get_word(Mem, Addr) when is_binary(Mem) ->
<<_:Addr/unit:8, Word:256, _/binary>> = Mem,
Word.
get_chunk(#heap{offset = Offs, heap = Mem}, Addr, Bytes) when Addr >= Offs ->
get_chunk(Mem, Addr - Offs, Bytes);
get_chunk(Mem, Addr, Bytes) when is_binary(Mem) ->
<<_:Addr/unit:8, Chunk:Bytes/binary, _/binary>> = Mem,
Chunk.
-19
View File
@@ -1,19 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc
%%% Memory speifics that compiler and VM need to agree upon
%%% @end
%%% Created : 19 Dec 2018
%%%-------------------------------------------------------------------
-module(aeb_memory).
-export([binary_to_words/1]).
binary_to_words(<<>>) ->
[];
binary_to_words(<<N:256,Bin/binary>>) ->
[N|binary_to_words(Bin)];
binary_to_words(Bin) ->
binary_to_words(<<Bin/binary,0>>).
+1 -1
View File
@@ -1,6 +1,6 @@
{application, aebytecode,
[{description, "Bytecode definitions, serialization and deserialization for aeternity."},
{vsn, "2.1.0"},
{vsn, "2.0.1"},
{registered, []},
{applications,
[kernel,
-4
View File
@@ -50,9 +50,6 @@ sources() ->
, "remote"
, "test"
, "tuple"
, "mapofmap"
, "immediates"
, "all_instructions"
].
check_roundtrip(File) ->
@@ -65,5 +62,4 @@ check_roundtrip(File) ->
{_Env2, ByteCode2} = assemble(DissasmCode),
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).
+7 -14
View File
@@ -47,11 +47,6 @@ sources() ->
[aeb_fate_data:make_integer(0),
aeb_fate_data:make_integer(1),
True, False, Unit, Nil, EmptyString, EmptyMap,
aeb_fate_data:make_hash(<<1,2,3,4,5>>),
aeb_fate_data:make_signature(<<1,2,3,4,5>>),
aeb_fate_data:make_contract(<<1,2,3,4,5>>),
aeb_fate_data:make_name(<<1,2,3,4,5>>),
aeb_fate_data:make_channel(<<1,2,3,4,5>>),
aeb_fate_data:make_list([True]),
aeb_fate_data:make_address(
<<0,1,2,3,4,5,6,7,8,9,
@@ -66,7 +61,6 @@ sources() ->
"0123456789012345678901234567890123456789">>), %% Magic concat 80 char string.
aeb_fate_data:make_tuple({True, FortyTwo}),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(16))),
aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}),
aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}),
aeb_fate_data:make_list(make_int_list(3)),
@@ -77,13 +71,12 @@ sources() ->
aeb_fate_data:make_bits(1),
aeb_fate_data:make_bits(-1),
aeb_fate_data:make_list(make_int_list(65)),
aeb_fate_data:make_variant([1,2,3], 0, {FortyTwo}),
aeb_fate_data:make_variant([2,0], 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant([0,0,0], 0, {})]),
aeb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}),
aeb_fate_data:make_variant([0,1,2,3,4,5],
3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)})
aeb_fate_data:make_variant(2, 0, {FortyTwo}),
aeb_fate_data:make_variant(2, 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant(3, 0, {})]),
aeb_fate_data:make_variant(255, 254, {}),
aeb_fate_data:make_variant(5, 3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)})
].
-237
View File
@@ -1,237 +0,0 @@
;; CONTRACT all_instructions
;; Dont expect this contract to typecheck or run.
;; Just used to check assembler rountrip of all instruction.
FUNCTION foo () : {tuple, []}
RETURN
RETURNR a13
CALL foo
CALL_R arg125 foo
CALL_T foo
CALL_TR arg245 foo
JUMP 5514251025295783441695716053282666408426
JUMPIF arg196 0x12c651665
SWITCH_V2 a27 63 33
SWITCH_V3 var4 0x1d61723dd 79 7
SWITCH_VN arg0 [1, 2, 3]
PUSH var80
DUPA
DUP a
POP a107
STORE arg183 var225
INCA
INC a25
DECA
DEC a
ADD a217 a a
SUB arg35 arg165 var74
MUL 44 35 "foo"
DIV 263838340369912686645632650718169038811 a24 a
MOD var113 arg80 arg207
POW a176 a a123
LT a 78 var81
GT arg19 4729414120208894485838100532547810615352 var175
EQ 85 a arg164
ELT a161 arg226 a168
EGT a131 1 var250
NEQ a85 a a83
AND var255 0x294a24f6 var189
OR (| [2,0] | 0 | ( (), (42) ) |) arg168 var107
NOT arg124 a
TUPLE 5019186157739257888756115213149493826410
ELEMENT arg148 var25 a219
MAP_EMPTY a135
MAP_LOOKUP a82 a a143
MAP_LOOKUPD var112 arg35 a163 var112
MAP_UPDATE false a0 a56 a
MAP_DELETE arg180 a var1
MAP_MEMBER a { true => 4} 94
MAP_FROM_LIST () a159
NIL arg91
IS_NIL a121 var6
CONS arg185 "foo" a114
HD a150 var124
TL arg223 a
LENGTH var216 a143
STR_EQ { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177
STR_JOIN a a 7144184027126178769820155907121270843348
INT_TO_STR var238 a
ADDR_TO_STR a arg216
STR_REVERSE a174 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
INT_TO_ADDR arg127 var207
VARIANT a a 0x1f7b72200 a
VARIANT_TEST a26 arg217 a
VARIANT_ELEMENT a86 arg103 arg108
BITS_NONEA
BITS_NONE a
BITS_ALLA
BITS_ALL a164
BITS_ALL_N a221 arg135
BITS_SET arg150 a48 { 0x1a715e2a6 => 3}
BITS_CLEAR arg98 a arg164
BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |)
BITS_SUM a244 a71
BITS_OR var20 var186 a
BITS_AND a187 4 arg203
BITS_DIFF var200 arg247 var20
ADDRESS a237
BALANCE a231
ORIGIN arg216
CALLER a27
GASPRICE arg119
BLOCKHASH a arg110
BENEFICIARY var163
TIMESTAMP a
GENERATION 242795038229506961431398379342231049652
MICROBLOCK arg43
DIFFICULTY var24
GASLIMIT arg220
GAS var35
LOG0 a a85
LOG1 arg94 arg86 arg208
LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108
LOG3 arg255 arg15 arg211 var139 arg44
LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a
DEACTIVATE
SPEND @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv var136
ORACLE_REGISTER arg29 48 ((| [0,1,3] | 2 | (1, "foo", ()) |)) arg65 { <> => false} <>
ORACLE_QUERY
ORACLE_RESPOND
ORACLE_EXTEND
ORACLE_GET_ANSWER
ORACLE_GET_QUESTION
ORACLE_QUERY_FEE
AENS_RESOLVE
AENS_PRECLAIM
AENS_CLAIM
AENS_UPDATE
AENS_TRANSFER
AENS_REVOKE
ECVERIFY
SHA3
SHA256
BLAKE2B
DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo"
DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255
ABORT a
EXIT var120
NOP
RETURNR ()
BALANCE_OTHER a arg0
SETELEMENT a 2 (1, "two", 3) 2
-77
View File
@@ -1,77 +0,0 @@
;; CONTRACT immediates
FUNCTION integer() : integer
RETURNR 42
FUNCTION neg_integer() : integer
RETURNR -2374683271468723648732648736498712634876147
FUNCTION hex_integer() : integer
RETURNR 0x0deadbeef0
FUNCTION bool() : boolean
RETURNR true
FUNCTION bool_f() : boolean
RETURNR false
FUNCTION string() : string
RETURNR "Hello"
FUNCTION map() : {map, integer, boolean}
RETURNR {}
FUNCTION map2() : {map, integer, boolean}
RETURNR {1 => true}
FUNCTION map3() : {map, integer, boolean}
RETURNR {1 => true,
2 => false}
FUNCTION map4() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
FUNCTION nil() : {list, integer}
RETURNR []
FUNCTION list1() : {list, integer}
RETURNR [1]
FUNCTION list2() : {list, integer}
RETURNR [1, 2]
FUNCTION no_bits() : bits
RETURNR <>
FUNCTION all_bits() : bits
RETURNR !<>
FUNCTION some_bits() : bits
RETURNR <101010>
FUNCTION many_bits() : bits
RETURNR !<010101>
FUNCTION group_bits() : bits
RETURNR <1010 1010 0011 1001>
FUNCTION unit() : {tuple, []}
RETURNR ()
FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]}
RETURNR (42, true, "FooBar", (1, 2))
FUNCTION address() : address
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 0 | () |)
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 1 | (42) |)
-7
View File
@@ -1,7 +0,0 @@
;; CONTRACT mapofmap
FUNCTION map() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
+2 -2
View File
@@ -26,10 +26,10 @@ FUNCTION element1(integer, integer): integer
PUSH arg0
PUSH arg1
TUPLE 2
ELEMENT a 1 a
ELEMENT integer a 1 a
RETURN
FUNCTION element({tuple, [integer, integer]}, integer): integer
;; BB : 0
ELEMENT a arg1 arg0
ELEMENT integer a arg1 arg0
RETURN