Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 163e805f55 | |||
| d69375e72b | |||
| 2b3603e86f | |||
| 31d188ce81 | |||
| dfca41c98d | |||
| 1526ad3bf0 | |||
| 4e325ff203 | |||
| e8253b0970 | |||
| 230e2187ce | |||
| a6a2686d53 | |||
| 56cf62b487 | |||
| 15628c9a05 | |||
| 431b311ae5 | |||
| 28f6c42647 | |||
| 734c8e8e40 | |||
| 436ba457e9 | |||
| 0261b76314 | |||
| e1e2236a26 | |||
| 04d64dfe9b | |||
| afcc6fd31a | |||
| 6466ddb866 | |||
| 75f3eeffa7 | |||
| a670d1ca6c | |||
| 96c0fab3ab | |||
| 90659342c0 | |||
| f88fe008bf | |||
| f5f007e74d | |||
| 0c01215b6a | |||
| fa64bbb56c | |||
| fe7e3a638a | |||
| d2c70509dd | |||
| 15ee836ddb | |||
| 68e6a2163e | |||
| 9041423906 | |||
| 913abb6c7b | |||
| 2d599df0ea | |||
| 54aace97af | |||
| 7e7f061b34 | |||
| 333bf53537 | |||
| 0528ee1229 | |||
| 8425eb80c5 | |||
| c00c4a5ac3 | |||
| 662b611e6d | |||
| 9abeb21eee | |||
| 34ae94e3e7 | |||
| d4da5e69ad | |||
| 417a34ecd1 | |||
| b35ccb8eb6 | |||
| 04571f757a | |||
| 9763a1a6f5 | |||
| 6c60f1e37f | |||
| 23695330ef | |||
| 43652e0843 | |||
| 6f67da1292 | |||
| 20c8fbabc9 | |||
| fccc570bee | |||
| 8fc929b1ee |
@@ -19,7 +19,7 @@ jobs:
|
||||
- dialyzer-cache-v1-
|
||||
- run:
|
||||
name: Build
|
||||
command: make
|
||||
command: rebar3 compile
|
||||
- run:
|
||||
name: Static Analysis
|
||||
command: make dialyzer
|
||||
|
||||
+10
@@ -16,3 +16,13 @@ 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
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
|
||||
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
|
||||
|
||||
local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl
|
||||
sources: $(GENERATED_SRC)
|
||||
|
||||
local: $(GENERATED_SRC)
|
||||
@$(REBAR) as local release
|
||||
|
||||
console: local
|
||||
@@ -12,15 +14,12 @@ console: local
|
||||
|
||||
clean:
|
||||
@$(REBAR) clean
|
||||
rm -f src/aeb_fate_opcodes.erl
|
||||
rm -f src/aeb_fate_code.erl
|
||||
rm -f include/aeb_fate_opcodes.hrl
|
||||
rm -f $(GENERATED_SRC)
|
||||
rm -f ebin/*
|
||||
|
||||
dialyzer: local
|
||||
@$(REBAR) as local dialyzer
|
||||
|
||||
|
||||
|
||||
distclean: clean
|
||||
@rm -rf _build/
|
||||
|
||||
@@ -30,12 +29,8 @@ eunit: local
|
||||
test: local
|
||||
@$(REBAR) as local eunit
|
||||
|
||||
|
||||
ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin
|
||||
ebin/%.beam: src/%.erl
|
||||
erlc -o $(dir $@) $<
|
||||
|
||||
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
|
||||
$(GENERATED_SRC): $(GENERATOR_DEPS)
|
||||
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
|
||||
|
||||
ebin:
|
||||
mkdir ebin
|
||||
|
||||
@@ -1,9 +1,134 @@
|
||||
aebytecode
|
||||
=====
|
||||
# aebytecode
|
||||
An library and stand alone assembler for aeternity bytecode.
|
||||
|
||||
An OTP library
|
||||
This version supports Aevm bytecode and Fate bytecode.
|
||||
|
||||
Build
|
||||
-----
|
||||
## 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)
|
||||
|
||||
$ rebar3 compile
|
||||
|
||||
+49
-24
@@ -1,43 +1,68 @@
|
||||
-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(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(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_integer(element(2, X))
|
||||
andalso is_list(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_BITS(B), {bits, B}).
|
||||
-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_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))).
|
||||
@@ -47,7 +72,7 @@
|
||||
-define(FATE_VOID, void).
|
||||
-define(FATE_EMPTY_STRING, <<>>).
|
||||
-define(FATE_STRING(S), S).
|
||||
-define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}).
|
||||
-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}).
|
||||
|
||||
-define(MAKE_FATE_INTEGER(X), X).
|
||||
-define(MAKE_FATE_LIST(X), X).
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
-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()} }).
|
||||
|
||||
+10
-6
@@ -183,9 +183,13 @@
|
||||
-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, 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).
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
-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).
|
||||
@@ -0,0 +1,25 @@
|
||||
%%% @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)
|
||||
]}.
|
||||
@@ -0,0 +1,25 @@
|
||||
%%% @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)
|
||||
]}.
|
||||
@@ -0,0 +1,122 @@
|
||||
%%% @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).
|
||||
@@ -0,0 +1,49 @@
|
||||
%%% @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})]).
|
||||
+8
-1
@@ -1,10 +1,12 @@
|
||||
%% -*- 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, "b55c372"}}}
|
||||
{ref, "6dce265"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
]}.
|
||||
|
||||
@@ -12,6 +14,11 @@
|
||||
{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
@@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"b55c3726f4a21063721c68d6fa7fda39121edf11"}},
|
||||
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
|
||||
0},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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.
|
||||
@@ -0,0 +1,30 @@
|
||||
-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().
|
||||
|
||||
+430
-281
@@ -1,6 +1,22 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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
|
||||
@@ -19,34 +35,53 @@
|
||||
%%% stack1
|
||||
%%% a
|
||||
%%%
|
||||
%%% Immediates can be of 9 types:
|
||||
%%% 1. Integers
|
||||
%%% Immediate values can be of 11 types:
|
||||
%%% 1a. Integers as decimals: {Digits} or -{Digits}
|
||||
%%% 42
|
||||
%%% -2374683271468723648732648736498712634876147
|
||||
%%% 2. Hexadecimal integers starting with 0x
|
||||
%%% 1b. Integers as Hexadecimals:: 0x{Hexdigits}
|
||||
%%% 0x0deadbeef0
|
||||
%%% 3. addresses, a 256-bit hash strings starting with #
|
||||
%%% followed by up to 64 hex chars
|
||||
%%% #00000deadbeef
|
||||
%%% 4. Boolean
|
||||
%%% 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
|
||||
%%% true
|
||||
%%% false
|
||||
%%% 5. Strings
|
||||
%%% 4. Strings "{Characters}"
|
||||
%%% "Hello"
|
||||
%%% 6. Empty map
|
||||
%%% 5. Map { Key => Value }
|
||||
%%% {}
|
||||
%%% 7. Lists
|
||||
%%% { 1 => { "foo" => true, "bar" => false}
|
||||
%%% 6. Lists [ Elements ]
|
||||
%%% []
|
||||
%%% [1, 2]
|
||||
%%% 8. Bit field
|
||||
%%% 7. Bit field < Bits > or !< Bits >
|
||||
%%% <000>
|
||||
%%% <1010>
|
||||
%%% <1010 1010>
|
||||
%%% <>
|
||||
%%% !<>
|
||||
%%% 9. Tuples
|
||||
%%% 8. Tuples ( Elements )
|
||||
%%% ()
|
||||
%%% (1, "foo")
|
||||
%%% @end
|
||||
%%% 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
|
||||
%%%
|
||||
%%% Created : 21 Dec 2017
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
@@ -84,7 +119,7 @@ parse_function_call([{id,_,Name}, {'(',_}| Rest]) ->
|
||||
|
||||
to_args([{')', _}]) -> {[], []};
|
||||
to_args(Tokens) ->
|
||||
case to_data(Tokens) of
|
||||
case parse_value(Tokens) of
|
||||
{Arg, [{',', _} | Rest]} ->
|
||||
{More, Rest2} = to_args(Rest),
|
||||
{[Arg|More], Rest2};
|
||||
@@ -92,13 +127,6 @@ 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]).
|
||||
@@ -157,14 +185,6 @@ 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]).
|
||||
@@ -180,157 +200,10 @@ format_code([], _) ->
|
||||
"";
|
||||
format_code([Op|Rest], Symbols) ->
|
||||
[" ",
|
||||
format_op(Op, Symbols),
|
||||
aeb_fate_pp: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),
|
||||
@@ -455,33 +328,17 @@ 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
|
||||
{N, Rest4} when is_integer(N), N >= 0 ->
|
||||
{L, Rest4} when is_list(L) ->
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
{BBs, Rest5} = deserialize_n(N, Rest4),
|
||||
{Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
|
||||
{Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
|
||||
, {Modifier0, Arg0}
|
||||
, {immediate, N}
|
||||
, list_to_tuple(BBs)}
|
||||
, {immediate, L}
|
||||
}
|
||||
| Code]};
|
||||
_ -> exit(bad_argument_to_switch_vn)
|
||||
end;
|
||||
@@ -530,17 +387,107 @@ deserialize_op(Op, Rest, Code) ->
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}}
|
||||
| Code]}
|
||||
| Code]};
|
||||
5 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
{Rest7, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
}
|
||||
| Code]};
|
||||
6 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
{Rest8, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
}
|
||||
| Code]};
|
||||
7 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
|
||||
{Rest9, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
, {Modifier6, Arg6}
|
||||
}
|
||||
| Code]};
|
||||
8 ->
|
||||
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
|
||||
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
|
||||
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
|
||||
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
|
||||
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
|
||||
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
|
||||
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
|
||||
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
|
||||
{Arg7, Rest10} = aeb_fate_encoding:deserialize_one(Rest9),
|
||||
Modifier0 = bits_to_modifier(ArgType band 2#11),
|
||||
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
|
||||
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
|
||||
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
|
||||
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
|
||||
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
|
||||
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
|
||||
Modifier7 = bits_to_modifier((ArgType2 bsr 6) band 2#11),
|
||||
{Rest10, [{ OpName
|
||||
, {Modifier0, Arg0}
|
||||
, {Modifier1, Arg1}
|
||||
, {Modifier2, Arg2}
|
||||
, {Modifier3, Arg3}
|
||||
, {Modifier4, Arg4}
|
||||
, {Modifier5, Arg5}
|
||||
, {Modifier6, Arg6}
|
||||
, {Modifier7, Arg7}
|
||||
}
|
||||
| Code]}
|
||||
end.
|
||||
|
||||
deserialize_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]).
|
||||
|
||||
|
||||
|
||||
@@ -580,11 +527,114 @@ 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}
|
||||
@@ -630,43 +680,21 @@ 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, N}
|
||||
| Rest]) when is_integer(N), N >= 0 ->
|
||||
, {immediate, L}
|
||||
| Rest]) ->
|
||||
ArgSpec =
|
||||
modifier_bits(Arg0Type) bor
|
||||
(modifier_bits(immediate) bsl 2),
|
||||
{Serialization, Rest2} = serialize_n_ints(N, Rest),
|
||||
[?SWITCH_VN
|
||||
, ArgSpec
|
||||
, serialize_data(Arg0Type, Arg0)
|
||||
, serialize_data(immediate, N)
|
||||
| Serialization] ++ serialize_code(Rest2);
|
||||
, serialize_data(immediate, L)] ++ serialize_code(Rest);
|
||||
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
|
||||
@@ -686,36 +714,16 @@ serialize_data(_, Data) ->
|
||||
aeb_fate_encoding:serialize(Data).
|
||||
|
||||
serialize_signature({Args, RetType}) ->
|
||||
[serialize_type({tuple, Args}) |
|
||||
serialize_type(RetType)].
|
||||
[aeb_fate_encoding:serialize_type({tuple, Args}) |
|
||||
aeb_fate_encoding:serialize_type(RetType)].
|
||||
|
||||
|
||||
|
||||
deserialize_signature(Binary) ->
|
||||
{{tuple, Args}, Rest} = deserialize_type(Binary),
|
||||
{RetType, Rest2} = deserialize_type(Rest),
|
||||
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
|
||||
{RetType, Rest2} = aeb_fate_encoding: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) ->
|
||||
@@ -734,10 +742,6 @@ 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);
|
||||
@@ -751,11 +755,66 @@ 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([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{immediate, Hash}|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([{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);
|
||||
@@ -772,6 +831,95 @@ 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),
|
||||
@@ -791,11 +939,17 @@ 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, _, "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, _, "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, _, "list"}, {',', _} | Rest]) ->
|
||||
%% TODO: Error handling
|
||||
{ListType, [{'}', _}| Rest2]} = to_type(Rest),
|
||||
@@ -808,7 +962,15 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
|
||||
%% TODO: Error handling
|
||||
{KeyType, [{',', _}| Rest2]} = to_type(Rest),
|
||||
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
|
||||
{{map, KeyType, ValueType}, Rest3}.
|
||||
{{map, KeyType, ValueType}, Rest3};
|
||||
to_type([{'{', _}
|
||||
, {id, _, "variant"}
|
||||
, {',', _}
|
||||
, {'[', _}
|
||||
| Rest]) ->
|
||||
{ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest),
|
||||
{{variant, ElementTypes}, Rest2}.
|
||||
|
||||
|
||||
to_list_of_types([{']', _} | Rest]) -> {[], Rest};
|
||||
to_list_of_types(Tokens) ->
|
||||
@@ -820,19 +982,6 @@ 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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
%%% @doc
|
||||
%%% Handling FATE code.
|
||||
%%% @end
|
||||
###REPLACEWITHNOTE###
|
||||
%%% ###REPLACEWITHNOTE###
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
Definitions.
|
||||
@@ -12,12 +12,17 @@ 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}+
|
||||
HASH = #{HEXDIGIT}+
|
||||
OBJECT = @[a-z][a-z]_{BASE58}+
|
||||
HASH = #{BASE64}+
|
||||
SIG = \$sg_{BASE58}+
|
||||
WS = [\000-\s]
|
||||
ID = {LOWER}[a-zA-Z0-9_]*
|
||||
|
||||
STRING = "[^"]*"
|
||||
BITS = (\!)?\<[\s01]*\>
|
||||
|
||||
Rules.
|
||||
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
|
||||
@@ -28,23 +33,41 @@ 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)}}.
|
||||
{HASH} :
|
||||
{token, {hash, TokenLine, parse_hash(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)}}.
|
||||
|
||||
|
||||
%% Symbols
|
||||
\-\> : {token, {'to', TokenLine}}.
|
||||
\: : {token, {'to', TokenLine}}.
|
||||
\-\> : {token, {to, TokenLine}}.
|
||||
\: : {token, {to, TokenLine}}.
|
||||
|
||||
\=\> : {token, {arrow, TokenLine}}.
|
||||
\(\| : {token, {start_variant, TokenLine}}.
|
||||
\|\) : {token, {end_variant, TokenLine}}.
|
||||
|
||||
, : {token, {',', TokenLine}}.
|
||||
\( : {token, {'(', TokenLine}}.
|
||||
\) : {token, {')', TokenLine}}.
|
||||
@@ -52,6 +75,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
|
||||
\] : {token, {']', TokenLine}}.
|
||||
\{ : {token, {'{', TokenLine}}.
|
||||
\} : {token, {'}', TokenLine}}.
|
||||
\| : {token, {'|', TokenLine}}.
|
||||
|
||||
;;.* :
|
||||
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
|
||||
@@ -88,8 +112,17 @@ parse_acc("a" ++ N) -> list_to_integer(N).
|
||||
|
||||
|
||||
parse_hash("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
<<N:256>>.
|
||||
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.
|
||||
|
||||
scan(S) ->
|
||||
string(S).
|
||||
@@ -97,3 +130,13 @@ 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).
|
||||
|
||||
+116
-115
@@ -4,27 +4,39 @@
|
||||
|
||||
-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_variant() :: ?FATE_VARIANT_T.
|
||||
|
||||
-type fate_tuple() :: ?FATE_TUPLE_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_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_type_type() :: integer
|
||||
| boolean
|
||||
| {list, fate_type()}
|
||||
| {map, fate_type(), fate_type()}
|
||||
| {tuple, [fate_type()]}
|
||||
| {list, fate_type_type()}
|
||||
| {map, fate_type_type(), fate_type_type()}
|
||||
| {tuple, [fate_type_type()]}
|
||||
| address
|
||||
| hash
|
||||
| signature
|
||||
| contract
|
||||
| oracle
|
||||
| name
|
||||
| channel
|
||||
| bits
|
||||
| {variant, integer()}.
|
||||
| string
|
||||
| {variant, [fate_type_type()]}.
|
||||
|
||||
|
||||
-type fate_type() ::
|
||||
@@ -36,9 +48,15 @@
|
||||
| fate_tuple()
|
||||
| fate_string()
|
||||
| fate_address()
|
||||
| fate_hash()
|
||||
| fate_signature()
|
||||
| fate_contract()
|
||||
| fate_oracle()
|
||||
| fate_name()
|
||||
| fate_channel()
|
||||
| fate_variant()
|
||||
| fate_map()
|
||||
| fate_type_type().
|
||||
| fate_bits().
|
||||
|
||||
-export_type([fate_type/0
|
||||
, fate_boolean/0
|
||||
@@ -49,8 +67,15 @@
|
||||
, 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
|
||||
]).
|
||||
|
||||
@@ -62,71 +87,56 @@
|
||||
, 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_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).
|
||||
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_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).
|
||||
%% 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.
|
||||
|
||||
|
||||
|
||||
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";
|
||||
@@ -136,59 +146,50 @@ 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_VARIANT(Size, Tag, 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)) ->
|
||||
["(| ",
|
||||
lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) |
|
||||
[format(E) || E <- erlang:tuple_to_list(T)]]),
|
||||
lists:join("| ",
|
||||
[format_arities(Arities),
|
||||
integer_to_list(Tag) |
|
||||
[format(make_tuple(T))]]),
|
||||
" |)"];
|
||||
format(M) when ?IS_FATE_MAP(M) ->
|
||||
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
|
||||
format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)];
|
||||
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(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) ->
|
||||
["[ ", 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]).
|
||||
|
||||
+172
-31
@@ -28,8 +28,6 @@
|
||||
%% TODO:
|
||||
%% * Make the code production ready.
|
||||
%% (add tests, document exported functions).
|
||||
%% * Handle Variant types better.
|
||||
%% * Handle type representations.
|
||||
%% * Handle instructions.
|
||||
%%
|
||||
%% ------------------------------------------------------------------------
|
||||
@@ -37,7 +35,9 @@
|
||||
|
||||
-export([ deserialize/1
|
||||
, deserialize_one/1
|
||||
, deserialize_type/1
|
||||
, serialize/1
|
||||
, serialize_type/1
|
||||
]).
|
||||
|
||||
-include("aeb_fate_data.hrl").
|
||||
@@ -47,38 +47,62 @@
|
||||
|
||||
-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, size >= 64
|
||||
-define(SHORT_STRING , 2#01). %% xxxxxx 01 - [bytes], 0 < xxxxxx:size < 64
|
||||
-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
|
||||
%% 11 Set below
|
||||
-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
|
||||
-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
|
||||
%% 1111 Set below
|
||||
-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(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(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(ADDRESS , 2#10011111). %% 1001 1111 - [32 bytes]
|
||||
-define(VARIANT , 2#10101111). %% 1010 1111 - encoded size + encoded tag + encoded values
|
||||
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
|
||||
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
|
||||
-define(NIL , 2#10111111). %% 1011 1111 - Empty list
|
||||
-define(NEG_BITS , 2#11001111). %% 1100 1111 - RLP encoded integer (infinite 1:s bitfield)
|
||||
-define(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
|
||||
@@ -106,7 +130,19 @@ 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) ->
|
||||
<<?ADDRESS, (aeser_rlp:encode(Address))/binary>>;
|
||||
<<?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>>;
|
||||
serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
|
||||
S = size(T),
|
||||
L = tuple_to_list(T),
|
||||
@@ -128,7 +164,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}|_] = maps:to_list(?FATE_MAP_VALUE(Map)),
|
||||
L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))),
|
||||
Size = length(L),
|
||||
%% TODO: check all K same type, and all V same type
|
||||
%% check K =/= map
|
||||
@@ -136,11 +172,97 @@ serialize(Map) when ?IS_FATE_MAP(Map) ->
|
||||
<<?MAP,
|
||||
(rlp_integer(Size))/binary,
|
||||
(Elements)/binary>>;
|
||||
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
|
||||
>>.
|
||||
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]).
|
||||
|
||||
|
||||
%% -----------------------------------------------------
|
||||
@@ -205,9 +327,19 @@ 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(<<?ADDRESS, Rest/binary>>) ->
|
||||
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
|
||||
{A, Rest2} = aeser_rlp:decode_one(Rest),
|
||||
{?FATE_ADDRESS(A), Rest2};
|
||||
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};
|
||||
deserialize2(<<?TRUE, Rest/binary>>) ->
|
||||
{?FATE_TRUE, Rest};
|
||||
deserialize2(<<?FALSE, Rest/binary>>) ->
|
||||
@@ -242,11 +374,20 @@ deserialize2(<<?MAP, Rest/binary>>) ->
|
||||
{List, Rest2} = deserialize_elements(2*Size, Rest1),
|
||||
Map = insert_kv(List, #{}),
|
||||
{?MAKE_FATE_MAP(Map), Rest2};
|
||||
deserialize2(<<?VARIANT, Size:8, Tag:8, Rest/binary>>) ->
|
||||
deserialize2(<<?VARIANT, Rest/binary>>) ->
|
||||
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
|
||||
Arities = binary_to_list(AritiesBin),
|
||||
Size = length(Arities),
|
||||
if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size});
|
||||
true ->
|
||||
{?FATE_TUPLE(T), Rest2} = deserialize2(Rest),
|
||||
{?FATE_VARIANT(Size, Tag, T), Rest2}
|
||||
{?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
|
||||
end.
|
||||
|
||||
insert_kv([], M) -> M;
|
||||
|
||||
+300
-13
@@ -1,24 +1,29 @@
|
||||
-module(aeb_fate_generate_ops).
|
||||
|
||||
-export([ gen_and_halt/1
|
||||
, generate/0]).
|
||||
, generate/0
|
||||
, generate_documentation/1
|
||||
, get_ops/0
|
||||
, test_asm_generator/1]).
|
||||
|
||||
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
|
||||
generate(atom_to_list(SrcDirArg),
|
||||
atom_to_list(IncludeDirArg)),
|
||||
halt().
|
||||
|
||||
generate() ->
|
||||
generate("src/", "include/").
|
||||
generate() -> generate("src/", "include/").
|
||||
|
||||
get_ops() -> gen(ops_defs()).
|
||||
|
||||
generate(Src, Include) ->
|
||||
Ops = gen(ops_defs()),
|
||||
Ops = get_ops(),
|
||||
%% io:format("ops: ~p\n", [Ops]),
|
||||
HrlFile = Include ++ "aeb_fate_opcodes.hrl",
|
||||
generate_header_file(HrlFile, Ops),
|
||||
generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops),
|
||||
generate_code_ops(aeb_fate_code, Src, Ops),
|
||||
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops).
|
||||
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops),
|
||||
gen_asm_pp(aeb_fate_pp, Src, Ops).
|
||||
|
||||
%% TODO: Some real gas numbers...
|
||||
ops_defs() ->
|
||||
@@ -33,7 +38,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."}
|
||||
@@ -59,7 +64,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, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"}
|
||||
, { 'ELEMENT', 16#2a, 3, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."}
|
||||
, { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."}
|
||||
, { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."}
|
||||
, { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
|
||||
@@ -95,11 +100,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 address."}
|
||||
, { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract balance."}
|
||||
, { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."}
|
||||
, { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."}
|
||||
, { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."}
|
||||
, { 'BLOCKHASH', 16#5e, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height?
|
||||
, { 'BLOCKHASH', 16#5e, 2, false, 3, [a, a], blockhash, "Arg0 := The blockhash at height."}
|
||||
, { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."}
|
||||
, { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
|
||||
, { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."}
|
||||
@@ -134,9 +139,14 @@ 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."}
|
||||
|
||||
, {'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."}
|
||||
|
||||
, { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."}
|
||||
, { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."}
|
||||
, { 'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."}
|
||||
, { 'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."}
|
||||
, { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."}
|
||||
%% FUNCTION 16#fe "Function declaration and entrypoint."
|
||||
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
|
||||
@@ -385,9 +395,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"
|
||||
@@ -398,5 +408,282 @@ 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".
|
||||
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
-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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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,6 +1,6 @@
|
||||
{application, aebytecode,
|
||||
[{description, "Bytecode definitions, serialization and deserialization for aeternity."},
|
||||
{vsn, "2.0.1"},
|
||||
{vsn, "2.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -50,6 +50,9 @@ sources() ->
|
||||
, "remote"
|
||||
, "test"
|
||||
, "tuple"
|
||||
, "mapofmap"
|
||||
, "immediates"
|
||||
, "all_instructions"
|
||||
].
|
||||
|
||||
check_roundtrip(File) ->
|
||||
@@ -62,4 +65,5 @@ 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).
|
||||
|
||||
@@ -47,6 +47,11 @@ 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,
|
||||
@@ -61,6 +66,7 @@ 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)),
|
||||
@@ -71,12 +77,13 @@ 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(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">>)})
|
||||
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">>)})
|
||||
|
||||
].
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
;; 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
|
||||
@@ -0,0 +1,77 @@
|
||||
;; 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) |)
|
||||
@@ -0,0 +1,7 @@
|
||||
;; CONTRACT mapofmap
|
||||
FUNCTION map() : {map, integer, {map, string, boolean}}
|
||||
RETURNR {1 => { "foo" => true, "bar" => false},
|
||||
2 => {},
|
||||
3 => { "foo" => false}}
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ FUNCTION element1(integer, integer): integer
|
||||
PUSH arg0
|
||||
PUSH arg1
|
||||
TUPLE 2
|
||||
ELEMENT integer a 1 a
|
||||
ELEMENT a 1 a
|
||||
RETURN
|
||||
|
||||
FUNCTION element({tuple, [integer, integer]}, integer): integer
|
||||
;; BB : 0
|
||||
ELEMENT integer a arg1 arg0
|
||||
ELEMENT a arg1 arg0
|
||||
RETURN
|
||||
|
||||
Reference in New Issue
Block a user