Compare commits

..

52 Commits

Author SHA1 Message Date
Thomas Arts be9935cd7e Merge branch 'master' into quickcheck-ci 2019-02-11 13:11:46 +01:00
Hans Svensson 026ff52528 Merge pull request #24 from aeternity/merge_roma
Merge ROMA branch to master
2019-02-08 14:13:10 +01:00
Hans Svensson 3ba89d9f55 Merge ROMA into MINERVA 2019-02-08 13:38:37 +01:00
Robert Virding 0b4c2f14fe Move module documentation to separate files (#23) 2019-02-08 10:31:35 +01:00
Robert Virding b65c4edd19 Merge pull request #22 from aeternity/fix-compiler-interface
Remove specific filename extension handling
2019-02-06 15:17:53 +01:00
Robert Virding 515f444e7c Remove specific filename extension handling
And take the chance to make file handling errors ahve the same format
as other errors.
2019-02-06 14:02:46 +01:00
Hans Svensson 267fef3a5b Merge pull request #21 from aeternity/roma_standalone
Roma standalone
2019-01-31 09:51:56 +01:00
Hans Svensson 362373c0d7 Add escript post_hooks 2019-01-30 10:58:19 +01:00
Dincho Todorov 65b6791176 Initial CircleCI integration (#20) 2019-01-30 10:54:52 +01:00
Hans Svensson 87e5562f74 Super simple standalone version of the compiler 2019-01-29 15:25:39 +01:00
Dincho Todorov d3fa04483e Initial CircleCI integration (#20) 2019-01-29 15:35:41 +02:00
Ulf Norell b8cb7ab1b5 Fix incorrect type specs
h/t OTP-21 dialyzer
2019-01-29 13:58:05 +01:00
Hans Svensson ceb7de2119 Remove a leftover reference to enacl 2019-01-29 13:58:00 +01:00
Ulf Norell 2edafe0adc Merge pull request #19 from aeternity/dialyzer-warnings
Fix incorrect type specs
2019-01-29 09:19:13 +01:00
Ulf Norell 3a7c8f905a Fix incorrect type specs
h/t OTP-21 dialyzer
2019-01-28 14:54:34 +01:00
Hans Svensson 9026e1fe6b Merge pull request #18 from aeternity/minor_fixing_up
Cleanup whitespace, bad typespec, and remaining enacl reference
2019-01-28 11:24:29 +01:00
Hans Svensson f1f2b09294 Cleanup whitespace, bad typespec, and remaining enacl reference 2019-01-28 10:50:32 +01:00
Ulf Norell 53299b9b17 Merge pull request #17 from aeternity/PT-163478903-builtin-bits-type
PT-163478903 builtin bits type
2019-01-28 10:05:09 +01:00
Robert Virding 64fd91197b Merge pull request #9 from aeternity/interface-to-sophia
PT-163063316 Interface to sophia
2019-01-25 16:20:38 +01:00
Robert Virding a73abf8e8e Fix testing to use new error message format 2019-01-25 16:16:20 +01:00
Robert Virding db1c0fa05a Improve pretty printing the code AST 2019-01-25 16:16:20 +01:00
Robert Virding 70ad303e16 Document the aeso_compiler module
The module doc format is loosely based on the standard erlang doc
formats.
2019-01-25 16:16:20 +01:00
Robert Virding fe1a2758c3 Improve the interface to the compiler
It is now more consistent though we can still discuss how we want the
interface to look.
2019-01-25 16:16:20 +01:00
Ulf Norell 79de25b3a5 Fix minor bugs in compilation of bit fields 2019-01-25 16:09:31 +01:00
Ulf Norell 3e1290efaf Add Bits.all and rename Bits.zero to Bits.none 2019-01-25 16:09:31 +01:00
Ulf Norell 9c77622c7c Add set operations on bit fields (union, isect, diff) 2019-01-25 16:09:31 +01:00
Ulf Norell a367d5040a Add builtin bit field type 2019-01-25 16:09:31 +01:00
Ulf Norell d8bf0bda45 Remove integer bit operations 2019-01-25 16:09:31 +01:00
Hans Svensson 922107e438 Merge pull request #16 from aeternity/git_mess_up_fix
Fix git mess up
2019-01-25 10:53:06 +01:00
Hans Svensson f133483a90 Fix git mess up 2019-01-25 10:49:36 +01:00
Hans Svensson be42ee08ab Merge pull request #15 from aeternity/fix_string_concat
Refactor String.concat to not shift 256 bits
2019-01-25 10:36:09 +01:00
Hans Svensson 86285a8ae0 Refactor String.concat to not shift 256 bits
Adding SafeMath to VM_AEVM_SOPHIA_2 will break String.concat otherwise.
2019-01-25 09:57:23 +01:00
Thomas Arts aa2e6aa218 machinery for running QuickCheck
No script needed if we make sure extra_src_dirs has different name than "eqc"

Obsolete QuickCheck property
2019-01-24 09:11:26 +01:00
Hans Svensson c5c73097fc Merge pull request #13 from aeternity/PT-163388694-add_native_bit_shift
Use ?SHL and ?SHR for 'bsl' and 'bsr'
2019-01-23 13:15:14 +01:00
Hans Svensson 23ccce4c22 Use ?SHL and ?SHR for 'bsl' and 'bsr' 2019-01-22 21:53:30 +01:00
Ulf Norell 387fdf5c34 Merge pull request #12 from aeternity/PT-163362624-generic-hash
PT-163362624 generic hash functions
2019-01-22 15:04:47 +01:00
Ulf Norell f0dcda27bc Update aebytecode commit 2019-01-22 14:54:38 +01:00
Ulf Norell 5fd24fec86 Add more hash primops 2019-01-22 09:09:37 +01:00
Hans Svensson 4bedbfee61 Merge pull request #11 from aeternity/PT-163083710-implement_ecverify
Add Crypto.ecverify
2019-01-22 08:51:10 +01:00
Hans Svensson e4c46ee16f Bump aebytecode dependency 2019-01-21 20:14:20 +01:00
Hans Svensson d8fff8f20f Add Crypto.ecverify 2019-01-21 14:20:15 +01:00
Luca Favatella b074aa3323 Rename description of node in readme (#10) 2019-01-18 10:58:26 +00:00
Hans Svensson 90e45e63d4 Merge pull request #8 from aeternity/add_erlang_blake2b
Add Erlang implementation of Blake2B
2019-01-14 12:19:13 +01:00
Hans Svensson 0d78a5e4a0 Add Erlang implementation of Blake2B
This removes the dependency on enacl, and NIFs.
2019-01-14 10:58:25 +01:00
Hans Svensson b61e3270f9 Merge pull request #6 from aeternity/improve_builtins
PT-163146624 Improve builtins
2019-01-14 10:58:12 +01:00
Ulf Norell 7503ef2f3c Merge pull request #7 from aeternity/PT-163146461-missing-record-fields
PT-163146461 Check for missing fields in record expressions
2019-01-14 08:50:13 +01:00
Hans Svensson 028334ecb6 Avoid exporting all internal functions 2019-01-11 16:13:17 +01:00
Ulf Norell 783d74dff1 Check for missing fields in record expressions 2019-01-11 14:23:53 +01:00
Hans Svensson 335cd4743a Use generalized baseX_int for int_to_str 2019-01-11 13:48:26 +01:00
Hans Svensson 77212b5eb3 rename int_digits baseX_digits 2019-01-11 13:48:26 +01:00
Hans Svensson 79307c34df generalize base58_int to baseX_int 2019-01-11 13:48:26 +01:00
Hans Svensson 9187659a1e Refactoring builtins and improve base58 encoding 2019-01-11 13:48:26 +01:00
30 changed files with 1245 additions and 479 deletions
+37
View File
@@ -0,0 +1,37 @@
version: 2.1
executors:
aebuilder:
docker:
- image: aeternity/builder
user: builder
working_directory: ~/aesophia
jobs:
build:
executor: aebuilder
steps:
- checkout
- restore_cache:
keys:
- dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
- dialyzer-cache-v2-{{ .Branch }}-
- dialyzer-cache-v2-
- run:
name: Build
command: rebar3 compile
- run:
name: Static Analysis
command: rebar3 dialyzer
- run:
name: Eunit
command: rebar3 eunit
- run:
name: Common Tests
command: rebar3 ct
- save_cache:
key: dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
paths:
- _build/default/rebar3_20.3.8_plt
- store_artifacts:
path: _build/test/logs
+3
View File
@@ -0,0 +1,3 @@
{build, "rebar3 as eqc compile"}.
{test_root, "."}.
{test_path, "_build/eqc/lib/aesophia/quickcheck"}. %% here are the properties
+5
View File
@@ -16,3 +16,8 @@ _build
.idea .idea
*.iml *.iml
rebar3.crashdump rebar3.crashdump
current_counterexample.eqc
.qcci
*.erl~
*.aes~
+8 -2
View File
@@ -5,6 +5,12 @@ This is the __sophia__ compiler for the æternity system which compiles contract
For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md). For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md).
It is an OTP application written in Erlang and is by default included in It is an OTP application written in Erlang and is by default included in
[æternity Epoch](https://github.com/aeternity/epoch). However, it can [the æternity node](https://github.com/aeternity/epoch). However, it can
also be included in other system to compile contracts coded in sophia which also be included in other systems to compile contracts coded in sophia which
can then be loaded into the æternity system. can then be loaded into the æternity system.
## Interface Modules
The basic modules for interfacing the compiler:
* [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
+86
View File
@@ -0,0 +1,86 @@
# aeso_compiler
### Module
### aeso_compiler
The Sophia compiler
### Description
This module provides the interface to the standard Sophia compiler. It
returns the compiled module in a map which can then be loaded.
### Types
``` erlang
contract_string() = string() | binary()
contract_map() = #{bytecode => binary(),
compiler_version => string(),
contract_souce => string(),
type_info => type_info()}
type_info()
errorstring() = binary()
```
### Exports
#### file(File)
#### file(File, Options) -> CompRet
#### from_string(ContractString, Options) -> CompRet
Types
``` erlang
ContractString = contract_string()
Options = [Option]
CompRet = {ok,ContractMap} | {error,ErrorString}
ContractMap = contract_map()
ErrorString = errorstring()
```
Compile a contract defined in a file or in a string.
The **pp_** options all print to standard output the following:
`pp_sophia_code` - print the input Sophia code.
`pp_ast` - print the AST of the code
`pp_types` - print information about the types
`pp_typed_ast` - print the AST with type information at each node
`pp_icode` - print the internal code structure
`pp_assembler` - print the generated assembler code
`pp_bytecode` - print the bytecode instructions
#### check_call(ContractString, Options) -> CheckRet
Types
```
ContractString = string() | binary()
CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}
Types = [Type]
Type = term()
```
Check a call in contract through the `__call` function.
#### sophia_type_to_typerep(String) -> TypeRep
Types
``` erlang
{ok,TypeRep} | {error, badtype}
```
Get the type representation of a type declaration.
#### version() -> Version
Types
``` erlang
Version = integer()
```
Get the current version of the Sophia compiler.
+109
View File
@@ -0,0 +1,109 @@
%%% File : aeso_heap_eqc.erl
%%% Author : Ulf Norell
%%% Description :
%%% Created : 28 May 2018 by Ulf Norell
-module(aeso_heap_eqc).
-compile([export_all, nowarn_export_all]).
-include_lib("eqc/include/eqc.hrl").
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
sandbox(Code) ->
Parent = self(),
Tag = make_ref(),
{Pid, Ref} = spawn_monitor(fun() -> Parent ! {Tag, Code()} end),
receive
{Tag, Res} -> erlang:demonitor(Ref, [flush]), {ok, Res};
{'DOWN', Ref, process, Pid, Reason} -> {error, Reason}
after 100 ->
exit(Pid, kill),
{error, loop}
end.
prop_from_binary() ->
?FORALL({T, Bin}, {type(), blob()},
begin
Tag = fun(X) when is_atom(X) -> X; (X) when is_tuple(X) -> element(1, X) end,
case ?SANDBOX(aeso_heap:from_binary(T, Bin)) of
{ok, Res} -> collect({Tag(T), element(1, Res)}, true);
Err -> equals(Err, {ok, '_'})
end end).
type() -> ?LET(Depth, choose(0, 2), type(Depth, true)).
type(Depth, TypeRep) ->
oneof(
[ elements([word, string] ++ [typerep || TypeRep]) ] ++
[ ?LETSHRINK([T], [type(Depth - 1, TypeRep)], {list, T}) || Depth > 0 ] ++
[ ?LETSHRINK([T], [type(Depth - 1, TypeRep)], {option, T}) || Depth > 0 ] ++
[ ?LETSHRINK(Ts, list(type(Depth - 1, TypeRep)), {tuple, Ts}) || Depth > 0 ] ++
[ ?LETSHRINK([K, V], vector(2, type(Depth - 1, TypeRep)), {map, K, V}) || Depth > 0 ] ++
[]
).
blob() ->
?LET(Blobs, list(oneof([ ?LET(Ws, words(), return(from_words(Ws)))
, binary() ])),
return(list_to_binary(Blobs))).
words() -> list(word()).
word() ->
frequency(
[ {4, ?LET(N, nat(), 32 * N)}
, {1, choose(0, 320)}
, {2, -1}
, {2, elements(["foo", "zzzzz"])} ]).
from_words(Ws) ->
<< <<(from_word(W))/binary>> || W <- Ws >>.
from_word(W) when is_integer(W) ->
<<W:256>>;
from_word(S) when is_list(S) ->
Len = length(S),
Bin = <<(list_to_binary(S))/binary, 0:(32 - Len)/unit:8>>,
<<Len:256, Bin/binary>>.
typerep() -> ?LET(Depth, choose(0, 2),
?LET(T, type(Depth, true), return(typerep(T)))).
typerep(word) -> word;
typerep(string) -> string;
typerep(typerep) -> typerep;
typerep({tuple, Ts}) -> {tuple, typerep(Ts)};
typerep({list, T}) -> {list, typerep(T)};
typerep({variant, Cs}) -> {variant, typerep(Cs)};
typerep({option, T}) -> {variant, [[], [typerep(T)]]};
typerep({map, K, V}) -> {list, typerep({tuple, [K, V]})};
typerep([]) -> [];
typerep([T | Ts]) -> [typerep(T) | typerep(Ts)].
value(word) ->
<<N:256>> = <<(-1):256>>,
choose(0, N);
value(string) ->
?LET(N, choose(0, 128), binary(N));
value(typerep) ->
typerep();
value({list, T}) ->
list(value(T));
value({option, T}) ->
weighted_default({1, none}, {3, {some, value(T)}});
value({tuple, Ts}) ->
?LET(Vs, [ value(T) || T <- Ts ], list_to_tuple(Vs));
value({map, K, V}) ->
map(value(K), value(V));
value({variant, Cs}) ->
?LET(I, choose(0, length(Cs) - 1),
{variant, I, [ value(T) || T <- lists:nth(I + 1, Cs) ]}).
typed_val() ->
?LET(T, type(), ?LET(V, value(T), return({T, V}))).
prop_roundtrip() ->
?FORALL(T, type(),
?FORALL(V, value(T),
?FORALL(B, choose(0, 4),
equals(aeso_heap:from_binary(T, aeso_heap:to_binary(V, B * 32), B * 32),
{ok, V})))).
+59
View File
@@ -0,0 +1,59 @@
%%% File : aeso_utils_eqc.erl
%%% Author : Ulf Norell
%%% Description :
%%% Created : 2 Jul 2018 by Ulf Norell
-module(aeso_utils_eqc).
-compile([export_all, nowarn_export_all]).
-include_lib("eqc/include/eqc.hrl").
%% QuickCheck property
graph() ->
?LET(M, map(choose(0, 10), list(choose(0, 10))),
return(complete(M))).
complete(G) ->
Is = lists:usort(lists:concat(maps:values(G))),
maps:merge(maps:from_list([ {I, []} || I <- Is ]), G).
prop_scc() ->
?FORALL(G, graph(),
begin
SCCs = aeso_utils:scc(G),
BadSCC = fun({acyclic, I}) -> reachable_from(G, I, I);
({cyclic, Is}) -> [] /= [ {I, J} || I <- Is, J <- Is, not reachable_from(G, I, J) ]
end,
ToList = fun({acyclic, I}) -> [I];
({cyclic, Is}) -> Is end,
?WHENFAIL(eqc:format("SCCs = ~p\n", [SCCs]),
conjunction(
[ {elems, equals(lists:sort(lists:flatmap(ToList, SCCs)), lists:sort(maps:keys(G)))}
, {sorted, equals([], [ {I, J} || {I, Js} <- maps:to_list(G),
J <- Js,
find_component(I, SCCs) < find_component(J, SCCs) ])}
, {precise, equals([], [ SCC || SCC <- SCCs, BadSCC(SCC) ])}
]))
end).
reachable_from(Graph, I, J) ->
reachable_from1(Graph, maps:get(I, Graph, []), J).
reachable_from1(_, [], _) -> false;
reachable_from1(_, [I | _], I) -> true;
reachable_from1(Graph, [I | Is], J) ->
case maps:get(I, Graph, undefined) of
undefined -> reachable_from1(Graph, Is, J);
Js -> reachable_from1(maps:remove(I, Graph), Js ++ Is, J)
end.
find_component(X, SCCs) ->
ISCCs = lists:zip(SCCs, lists:seq(1, length(SCCs))),
HasX = fun({acyclic, Y}) -> X == Y;
({cyclic, Ys}) -> lists:member(X, Ys) end,
case [ I || {SCC, I} <- ISCCs, HasX(SCC) ] of
[I | _] -> I;
[] -> false
end.
+23 -6
View File
@@ -1,15 +1,32 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
%% NOTE: When possible deps are referenced by Git ref to ensure consistency between builds.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
{ref,"99bf097"}}}, {ref,"720510a"}}}
, {getopt, "1.0.1"}
% waiting for https://github.com/jlouis/enacl/pull/40 to be merged
{enacl, {git, "https://github.com/aeternity/enacl.git",
{ref, "26180f4"}}}
]}. ]}.
{profiles, [ {eqc, [{erl_opts, [{parse_transform, eqc_cover}]},
{deps, [{eqc_ci, "1.0.0"}]},
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
]}
]}.
{escript_incl_apps, [aesophia, aebytecode, getopt]}.
{escript_main_app, aesophia}.
{escript_name, aesophia}.
{escript_emu_args, "%%! +sbtu +A0\n"}.
{provider_hooks, [{post, [{compile, escriptize}]}]}.
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
escriptize,
"cp \"$REBAR_BUILD_DIR/bin/aesophia\" ./aesophia"},
{"win32",
escriptize,
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aesophia* "
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
]}.
{dialyzer, [ {dialyzer, [
{warnings, [unknown]}, {warnings, [unknown]},
{plt_apps, all_deps}, {plt_apps, all_deps},
+7 -5
View File
@@ -1,8 +1,10 @@
{"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"99bf097759dedbe7553f87a796bc7e1c7322e64b"}}, {ref,"720510a24de32c9bad6486f34ca7babde124bf1e"}},
0}, 0},
{<<"enacl">>, {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
{git,"https://github.com/aeternity/enacl.git", [
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}}, {pkg_hash,[
0}]. {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
].
+6 -6
View File
@@ -27,8 +27,8 @@
-type typerep() :: aeso_sophia:type(). -type typerep() :: aeso_sophia:type().
-type function_type_info() :: { FunctionHash :: hash() -type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name() , FunctionName :: function_name()
, ArgType :: aeso_sophia:heap() %% binary typerep , ArgType :: binary() %% binary typerep
, OutType :: aeso_sophia:heap() %% binary typerep , OutType :: binary() %% binary typerep
}. }.
-type type_info() :: [function_type_info()]. -type type_info() :: [function_type_info()].
@@ -84,8 +84,8 @@ check_given_type(FunName, GivenArgs, GivenRet, CalldataType, ExpectRet) ->
{expected, ExpectArgs, '=>', ExpectRet}}} {expected, ExpectArgs, '=>', ExpectRet}}}
end. end.
-spec check_calldata(aeso_sophia:heap(), type_info()) -> -spec check_calldata(binary(), type_info()) ->
{'ok', typerep()} | {'error', atom()}. {'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo) -> check_calldata(CallData, TypeInfo) ->
%% The first element of the CallData should be the function name %% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of case get_function_hash_from_calldata(CallData) of
@@ -136,7 +136,7 @@ function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
, aeso_heap:to_binary(OutType) , aeso_heap:to_binary(OutType)
]), ]),
%% Calculate a 256 bit digest BLAKE2b hash value of a binary %% Calculate a 256 bit digest BLAKE2b hash value of a binary
{ok, Hash} = enacl:generichash(?HASH_SIZE, Bin), {ok, Hash} = aeso_blake2:blake2b(?HASH_SIZE, Bin),
Hash. Hash.
-spec arg_typerep_from_function(function_name(), type_info()) -> -spec arg_typerep_from_function(function_name(), type_info()) ->
@@ -153,7 +153,7 @@ arg_typerep_from_function(Function, TypeInfo) ->
end. end.
-spec typereps_from_type_hash(hash(), type_info()) -> -spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. {'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) -> typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash,_Function, ArgTypeBin, OutTypeBin} -> {TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
+5 -4
View File
@@ -17,11 +17,12 @@ line({symbol, Line, _}) -> Line.
symbol_name({symbol, _, Name}) -> Name. symbol_name({symbol, _, Name}) -> Name.
pp(Ast) -> pp(Ast) ->
%% TODO: Actually do *Pretty* printing. %% io:format("Tree:\n~p\n",[Ast]),
io:format("~p~n", [Ast]). String = prettypr:format(aeso_pretty:decls(Ast, [])),
io:format("Ast:\n~s\n", [String]).
pp_typed(TypedAst) -> pp_typed(TypedAst) ->
%% io:format("Typed tree:\n~p\n",[TypedAst]),
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])), String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
%%io:format("Typed tree:\n~p\n",[TypedAst]), io:format("Type ast:\n~s\n",[String]).
io:format("Type info:\n~s\n",[String]).
+75 -14
View File
@@ -50,7 +50,13 @@
, kind :: project | create | update %% Projection constraints can match contract , kind :: project | create | update %% Projection constraints can match contract
, context :: why_record() }). %% types, but field constraints only record types. , context :: why_record() }). %% types, but field constraints only record types.
-type field_constraint() :: #field_constraint{}. %% Constraint checking that 'record_t' has precisely 'fields'.
-record(record_create_constraint,
{ record_t :: utype()
, fields :: [aeso_syntax:id()]
, context :: why_record() }).
-type field_constraint() :: #field_constraint{} | #record_create_constraint{}.
-record(field_info, -record(field_info,
{ field_t :: utype() { field_t :: utype()
@@ -73,6 +79,7 @@ global_env() ->
Event = {id, Ann, "event"}, Event = {id, Ann, "event"},
State = {id, Ann, "state"}, State = {id, Ann, "state"},
Hash = {id, Ann, "hash"}, Hash = {id, Ann, "hash"},
Bits = {id, Ann, "bits"},
Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end,
Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end,
Unit = {tuple_t, Ann, []}, Unit = {tuple_t, Ann, []},
@@ -142,12 +149,29 @@ global_env() ->
{["Map", "delete"], Fun([K, Map(K, V)], Map(K, V))}, {["Map", "delete"], Fun([K, Map(K, V)], Map(K, V))},
{["Map", "member"], Fun([K, Map(K, V)], Bool)}, {["Map", "member"], Fun([K, Map(K, V)], Bool)},
{["Map", "size"], Fun1(Map(K, V), Int)}, {["Map", "size"], Fun1(Map(K, V), Int)},
%% Crypto/Curve operations
{["Crypto", "ecverify"], Fun([Hash, Address, SignId], Bool)},
{["Crypto", "sha3"], Fun1(A, Hash)},
{["Crypto", "sha256"], Fun1(A, Hash)},
{["Crypto", "blake2b"], Fun1(A, Hash)},
%% Strings %% Strings
{["String", "length"], Fun1(String, Int)}, {["String", "length"], Fun1(String, Int)},
{["String", "concat"], Fun([String, String], String)}, {["String", "concat"], Fun([String, String], String)},
{["String", "sha3"], Fun1(String, Int)}, {["String", "sha3"], Fun1(String, Hash)},
{["String", "sha256"], Fun1(String, Hash)},
{["String", "blake2b"], Fun1(String, Hash)},
%% Bits
{["Bits", "set"], Fun([Bits, Int], Bits)},
{["Bits", "clear"], Fun([Bits, Int], Bits)},
{["Bits", "test"], Fun([Bits, Int], Bool)},
{["Bits", "sum"], Fun1(Bits, Int)},
{["Bits", "intersection"], Fun([Bits, Bits], Bits)},
{["Bits", "union"], Fun([Bits, Bits], Bits)},
{["Bits", "difference"], Fun([Bits, Bits], Bits)},
{["Bits", "none"], Bits},
{["Bits", "all"], Bits},
%% Conversion %% Conversion
{["Int", "to_str"], Fun1(Int, String)}, {["Int", "to_str"], Fun1(Int, String)},
{["Address", "to_str"], Fun1(Address, String)} {["Address", "to_str"], Fun1(Address, String)}
]. ].
@@ -514,10 +538,15 @@ infer_expr(Env, {record, Attrs, Fields}) ->
RecordType = fresh_uvar(Attrs), RecordType = fresh_uvar(Attrs),
NewFields = [{field, A, FieldName, infer_expr(Env, Expr)} NewFields = [{field, A, FieldName, infer_expr(Env, Expr)}
|| {field, A, FieldName, Expr} <- Fields], || {field, A, FieldName, Expr} <- Fields],
constrain([begin RecordType1 = unfold_types_in_type(RecordType),
constrain([ #record_create_constraint{
record_t = RecordType1,
fields = [ FieldName || {field, _, [{proj, _, FieldName}], _} <- Fields ],
context = Attrs } ] ++
[begin
[{proj, _, FieldName}] = LV, [{proj, _, FieldName}] = LV,
#field_constraint{ #field_constraint{
record_t = unfold_types_in_type(RecordType), record_t = RecordType1,
field = FieldName, field = FieldName,
field_t = T, field_t = T,
kind = create, kind = create,
@@ -673,8 +702,7 @@ infer_infix({BoolOp, As})
{fun_t, As, [], [Bool,Bool], Bool}; {fun_t, As, [], [Bool,Bool], Bool};
infer_infix({IntOp, As}) infer_infix({IntOp, As})
when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/'; when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/';
IntOp == '^'; IntOp == 'mod'; IntOp == 'bsl'; IntOp == 'bsr'; IntOp == '^'; IntOp == 'mod' ->
IntOp == 'band'; IntOp == 'bor'; IntOp == 'bxor' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int, Int], Int}; {fun_t, As, [], [Int, Int], Int};
infer_infix({RelOp, As}) infer_infix({RelOp, As})
@@ -696,8 +724,7 @@ infer_infix({'++', As}) ->
infer_prefix({'!',As}) -> infer_prefix({'!',As}) ->
Bool = {id, As, "bool"}, Bool = {id, As, "bool"},
{fun_t, As, [], [Bool], Bool}; {fun_t, As, [], [Bool], Bool};
infer_prefix({IntOp,As}) infer_prefix({IntOp,As}) when IntOp =:= '-' ->
when IntOp =:= '-'; IntOp =:= 'bnot' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int], Int}. {fun_t, As, [], [Int], Int}.
@@ -971,7 +998,32 @@ get_field_constraints() ->
ets_tab2list(field_constraints). ets_tab2list(field_constraints).
solve_field_constraints() -> solve_field_constraints() ->
solve_field_constraints(get_field_constraints()). FieldCs =
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end,
get_field_constraints()),
solve_field_constraints(FieldCs).
check_record_create_constraints([]) -> ok;
check_record_create_constraints([C | Cs]) ->
#record_create_constraint{
record_t = Type,
fields = Fields,
context = When } = C,
Type1 = unfold_types_in_type(instantiate(Type)),
try lookup_type(record_type_name(Type1)) of
{_, {record_t, RecFields}} ->
ActualNames = [ Fld || {field_t, _, {id, _, Fld}, _} <- RecFields ],
GivenNames = [ Fld || {id, _, Fld} <- Fields ],
case ActualNames -- GivenNames of %% We know already that we don't have too many fields
[] -> ok;
Missing -> type_error({missing_fields, When, Type1, Missing})
end;
_ -> %% We can get here if there are other type errors.
ok
catch _:_ -> %% Might be unsolved, we get a different error in that case
ok
end,
check_record_create_constraints(Cs).
-spec solve_field_constraints([field_constraint()]) -> ok. -spec solve_field_constraints([field_constraint()]) -> ok.
solve_field_constraints(Constraints) -> solve_field_constraints(Constraints) ->
@@ -1071,8 +1123,10 @@ solve_known_record_types(Constraints) ->
DerefConstraints--SolvedConstraints. DerefConstraints--SolvedConstraints.
destroy_and_report_unsolved_field_constraints() -> destroy_and_report_unsolved_field_constraints() ->
Unsolved = get_field_constraints(), {FieldCs, CreateCs} =
Unknown = solve_known_record_types(Unsolved), lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
get_field_constraints()),
Unknown = solve_known_record_types(FieldCs),
if Unknown == [] -> ok; if Unknown == [] -> ok;
true -> true ->
case solve_unknown_record_types(Unknown) of case solve_unknown_record_types(Unknown) of
@@ -1080,6 +1134,7 @@ destroy_and_report_unsolved_field_constraints() ->
Errors -> [ type_error(Err) || Err <- Errors ] Errors -> [ type_error(Err) || Err <- Errors ]
end end
end, end,
check_record_create_constraints(CreateCs),
destroy_field_constraints(), destroy_field_constraints(),
ok. ok.
@@ -1400,6 +1455,12 @@ pp_error({ambiguous_record, Fields = [{_, First} | _], Candidates}) ->
[ [" - ", pp(C), " (at ", pp_loc(C), ")\n"] || C <- Candidates ]]); [ [" - ", pp(C), " (at ", pp_loc(C), ")\n"] || C <- Candidates ]]);
pp_error({missing_field, Field, Rec}) -> pp_error({missing_field, Field, Rec}) ->
io_lib:format("Record type ~s does not have field ~s (at ~s)\n", [pp(Rec), pp(Field), pp_loc(Field)]); io_lib:format("Record type ~s does not have field ~s (at ~s)\n", [pp(Rec), pp(Field), pp_loc(Field)]);
pp_error({missing_fields, Ann, RecType, Fields}) ->
Many = length(Fields) > 1,
S = [ "s" || Many ],
Are = if Many -> "are"; true -> "is" end,
io_lib:format("The field~s ~s ~s missing when constructing an element of type ~s (at ~s)\n",
[S, string:join(Fields, ", "), Are, pp(RecType), pp_loc(Ann)]);
pp_error({no_records_with_all_fields, Fields = [{_, First} | _]}) -> pp_error({no_records_with_all_fields, Fields = [{_, First} | _]}) ->
S = [ "s" || length(Fields) > 1 ], S = [ "s" || length(Fields) > 1 ],
io_lib:format("No record type with field~s ~s (at ~s)\n", io_lib:format("No record type with field~s ~s (at ~s)\n",
+54 -6
View File
@@ -316,6 +316,23 @@ ast_body({map, _, Map, [Upd]}, Icode) ->
ast_body({map, Ann, Map, [Upd | Upds]}, Icode) -> ast_body({map, Ann, Map, [Upd | Upds]}, Icode) ->
ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode); ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode);
%% Crypto
ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[word, word, sign_t()], word);
ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA256, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "blake2b"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B, Term, Type, Icode);
ast_body(?qid_app(["String", "sha256"], [String], _, _), Icode) ->
string_hash_primop(?PRIM_CALL_CRYPTO_SHA256_STRING, String, Icode);
ast_body(?qid_app(["String", "blake2b"], [String], _, _), Icode) ->
string_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B_STRING, String, Icode);
%% Strings %% Strings
%% -- String length %% -- String length
ast_body(?qid_app(["String", "length"], [String], _, _), Icode) -> ast_body(?qid_app(["String", "length"], [String], _, _), Icode) ->
@@ -331,6 +348,33 @@ ast_body(?qid_app(["String", "concat"], [String1, String2], _, _), Icode) ->
ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) -> ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) ->
#unop{ op = 'sha3', rand = ast_body(String, Icode) }; #unop{ op = 'sha3', rand = ast_body(String, Icode) };
%% -- Bits
ast_body(?qid_app(["Bits", Fun], Args, _, _), Icode)
when Fun == "test"; Fun == "set"; Fun == "clear";
Fun == "union"; Fun == "intersection"; Fun == "difference" ->
C = fun(N) when is_integer(N) -> #integer{ value = N };
(X) -> X end,
Bin = fun(O) -> fun(A, B) -> #binop{ op = O, left = C(A), right = C(B) } end end,
And = Bin('band'),
Or = Bin('bor'),
Bsl = fun(A, B) -> (Bin('bsl'))(B, A) end, %% flipped arguments
Bsr = fun(A, B) -> (Bin('bsr'))(B, A) end,
Neg = fun(A) -> #unop{ op = 'bnot', rand = C(A) } end,
case [Fun | [ ast_body(Arg, Icode) || Arg <- Args ]] of
["test", Bits, Ix] -> And(Bsr(Bits, Ix), 1);
["set", Bits, Ix] -> Or(Bits, Bsl(1, Ix));
["clear", Bits, Ix] -> And(Bits, Neg(Bsl(1, Ix)));
["union", A, B] -> Or(A, B);
["intersection", A, B] -> And(A, B);
["difference", A, B] -> And(A, Neg(And(A, B)))
end;
ast_body({qid, _, ["Bits", "none"]}, _Icode) ->
#integer{ value = 0 };
ast_body({qid, _, ["Bits", "all"]}, _Icode) ->
#integer{ value = 1 bsl 256 - 1 };
ast_body(?qid_app(["Bits", "sum"], [Bits], _, _), Icode) ->
builtin_call(popcount, [ast_body(Bits, Icode), #integer{ value = 0 }]);
%% -- Conversion %% -- Conversion
ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) -> ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) ->
builtin_call(int_to_str, [ast_body(Int, Icode)]); builtin_call(int_to_str, [ast_body(Int, Icode)]);
@@ -509,12 +553,6 @@ ast_binop(Op, Ann, {typed, _, A, Type}, B, Icode)
ast_binop('++', _, A, B, Icode) -> ast_binop('++', _, A, B, Icode) ->
#funcall{ function = #var_ref{ name = {builtin, list_concat} }, #funcall{ function = #var_ref{ name = {builtin, list_concat} },
args = [ast_body(A, Icode), ast_body(B, Icode)] }; args = [ast_body(A, Icode), ast_body(B, Icode)] };
ast_binop('bsl', _, A, B, Icode) ->
#binop{op = '*', left = ast_body(A, Icode),
right = #binop{op = '^', left = {integer, 2}, right = ast_body(B, Icode)}};
ast_binop('bsr', _, A, B, Icode) ->
#binop{op = 'div', left = ast_body(A, Icode),
right = #binop{op = '^', left = {integer, 2}, right = ast_body(B, Icode)}};
ast_binop(Op, _, A, B, Icode) -> ast_binop(Op, _, A, B, Icode) ->
#binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}. #binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}.
@@ -593,6 +631,16 @@ prim_call(Prim, Amount, Args, ArgTypes, OutType) ->
type_hash= #integer{value = TypeHash} type_hash= #integer{value = TypeHash}
}. }.
generic_hash_primop(PrimOp, Term, Type, Icode) ->
ArgType = ast_type(Type, Icode),
TypeValue = type_value(ArgType),
prim_call(PrimOp, #integer{value = 0},
[TypeValue, ast_body(Term, Icode)],
[typerep, ArgType], word).
string_hash_primop(PrimOp, String, Icode) ->
prim_call(PrimOp, #integer{value = 0}, [ast_body(String, Icode)], [string], word).
make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) -> make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) ->
TVars = [ X || {tvar, _, X} <- Args ], TVars = [ X || {tvar, _, X} <- Args ],
fun(Types) -> fun(Types) ->
+149
View File
@@ -0,0 +1,149 @@
%%%=============================================================================
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net
%%% @end
%%%=============================================================================
-module(aeso_blake2).
-export([ blake2b/2
, blake2b/3
]).
-define(MAX_64BIT, 16#ffffffffffffffff).
-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}.
blake2b(HashLen, Msg) ->
blake2b(HashLen, Msg, <<>>).
-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}.
blake2b(HashLen, Msg0, Key) ->
%% If message should be keyed, prepend message with padded key.
Msg = <<(pad(128, Key))/binary, Msg0/binary>>,
%% Set up the initial state
Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen),
<<H0:64, H1_7/binary>> = blake_iv(),
H = <<(H0 bxor Init):64, H1_7/binary>>,
%% Perform the compression - message will be chopped into 128-byte chunks.
State = blake2b_compress(H, Msg, 0),
%% Just return the requested part of the hash
{ok, binary_part(to_little_endian(State), {0, HashLen})}.
blake2b_compress(H, <<Chunk:(128*8), Rest/binary>>, BCompr) when Rest /= <<>> ->
H1 = blake2b_compress(H, <<Chunk:(128*8)>>, BCompr + 128, false),
blake2b_compress(H1, Rest, BCompr + 128);
blake2b_compress(H, SmallChunk, BCompr) ->
Size = byte_size(SmallChunk),
FillSize = (128 - Size) * 8,
blake2b_compress(H, <<SmallChunk/binary, 0:FillSize>>, BCompr + Size, true).
blake2b_compress(H, Chunk0, BCompr, Last) ->
Chunk = to_big_endian(Chunk0),
<<V0_11:(12*64), V12:64, V13:64, V14:64, V15:64>> = <<H/binary, (blake_iv())/binary>>,
V12_ = V12 bxor (BCompr band ?MAX_64BIT),
V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT),
V14_ = case Last of
false -> V14;
true -> V14 bxor ?MAX_64BIT
end,
V = <<V0_11:(12*64), V12_:64, V13_:64, V14_:64, V15:64>>,
<<VLow:(8*64), VHigh:(8*64)>> =
lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)),
<<HInt:(8*64)>> = H,
<<((HInt bxor VLow) bxor VHigh):(8*64)>>.
blake2b_mix(Rnd, Chunk, V) ->
<<V0:64, V1:64, V2:64, V3:64, V4:64, V5:64, V6:64, V7:64, V8:64,
V9:64, V10:64, V11:64, V12:64, V13:64, V14:64, V15:64>> = V,
<<M0:64, M1:64, M2:64, M3:64, M4:64, M5:64, M6:64, M7:64, M8:64,
M9:64, M10:64, M11:64, M12:64, M13:64, M14:64, M15:64>> = Chunk,
Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15},
M = fun(Ix) -> element(Ix+1, Ms) end,
[S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10),
{Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)),
{Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)),
{Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)),
{Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)),
{Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)),
{Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)),
{Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)),
{Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)),
<<Vy0:64, Vy1:64, Vy2:64, Vy3:64, Vy4:64, Vy5:64, Vy6:64, Vy7:64, Vy8:64,
Vy9:64, Vy10:64, Vy11:64, Vy12:64, Vy13:64, Vy14:64, Vy15:64>>.
blake2b_mix(Va, Vb, Vc, Vd, X, Y) ->
Va1 = (Va + Vb + X) band ?MAX_64BIT,
Vd1 = rotr64(32, Vd bxor Va1),
Vc1 = (Vc + Vd1) band ?MAX_64BIT,
Vb1 = rotr64(24, Vb bxor Vc1),
Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT,
Vd2 = rotr64(16, Va2 bxor Vd1),
Vc2 = (Vc1 + Vd2) band ?MAX_64BIT,
Vb2 = rotr64(63, Vb1 bxor Vc2),
{Va2, Vb2, Vc2, Vd2}.
blake_iv() ->
IV0 = 16#6A09E667F3BCC908,
IV1 = 16#BB67AE8584CAA73B,
IV2 = 16#3C6EF372FE94F82B,
IV3 = 16#A54FF53A5F1D36F1,
IV4 = 16#510E527FADE682D1,
IV5 = 16#9B05688C2B3E6C1F,
IV6 = 16#1F83D9ABFB41BD6B,
IV7 = 16#5BE0CD19137E2179,
<<IV0:64, IV1:64, IV2:64, IV3:64, IV4:64, IV5:64, IV6:64, IV7:64>>.
sigma(N) ->
{_, Row} = lists:keyfind(N, 1, sigma()), Row.
sigma() ->
[{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]},
{1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]},
{2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]},
{3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]},
{4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]},
{5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]},
{6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]},
{7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]},
{8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]},
{9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}].
rotr64(N, I64) ->
<<I64rot:64>> = rotr641(N, <<I64:64>>),
I64rot.
rotr641(16, <<X:(64-16), Y:16>>) -> <<Y:16, X:(64-16)>>;
rotr641(24, <<X:(64-24), Y:24>>) -> <<Y:24, X:(64-24)>>;
rotr641(32, <<X:(64-32), Y:32>>) -> <<Y:32, X:(64-32)>>;
rotr641(63, <<X:(64-63), Y:63>>) -> <<Y:63, X:(64-63)>>.
pad(N, Bin) ->
case (N - (byte_size(Bin) rem N)) rem N of
0 -> Bin;
Pad -> <<Bin/binary, 0:(Pad *8)>>
end.
to_big_endian(Bin) -> to_big_endian(Bin, <<>>).
to_big_endian(<<>>, Acc) -> Acc;
to_big_endian(<<UInt64:1/little-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
to_big_endian(Rest, <<Acc/binary, UInt64:1/big-unsigned-integer-unit:64>>).
to_little_endian(Bin) -> to_little_endian(Bin, <<>>).
to_little_endian(<<>>, Acc) -> Acc;
to_little_endian(<<UInt64:1/big-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
to_little_endian(Rest, <<Acc/binary, UInt64:1/little-unsigned-integer-unit:64>>).
+255 -265
View File
@@ -38,11 +38,12 @@ builtin_deps1({map_upd, Type}) -> [{map_get, Type}, map_put];
builtin_deps1({map_upd_default, Type}) -> [{map_lookup_default, Type}, map_put]; builtin_deps1({map_upd_default, Type}) -> [{map_lookup_default, Type}, map_put];
builtin_deps1(map_from_list) -> [map_put]; builtin_deps1(map_from_list) -> [map_put];
builtin_deps1(str_equal) -> [str_equal_p]; builtin_deps1(str_equal) -> [str_equal_p];
builtin_deps1(string_concat) -> [string_concat_inner1, string_concat_inner2]; builtin_deps1(string_concat) -> [string_concat_inner1, string_copy, string_shift_copy];
builtin_deps1(int_to_str) -> [int_to_str_, int_digits]; builtin_deps1(int_to_str) -> [{baseX_int, 10}];
builtin_deps1(addr_to_str) -> [base58_int, string_concat]; builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
builtin_deps1(base58_int) -> [base58_int_encode, base58_int_pad, string_reverse, string_concat]; builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
builtin_deps1(base58_int_encode) -> [base58_int_encode_, base58_tab]; builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}];
builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}];
builtin_deps1(string_reverse) -> [string_reverse_]; builtin_deps1(string_reverse) -> [string_reverse_];
builtin_deps1(_) -> []. builtin_deps1(_) -> [].
@@ -80,8 +81,9 @@ option_some(X) -> {tuple, [{integer, 1}, X]}.
-define(EXP(A, B), op('^', A, B)). -define(EXP(A, B), op('^', A, B)).
-define(AND(A, B), op('&&', A, B)). -define(AND(A, B), op('&&', A, B)).
-define(BSL(X, B), ?MUL(X, ?EXP(2, ?MUL(B, 8)))). %% Bit shift operations takes their arguments backwards!?
-define(BSR(X, B), ?DIV(X, ?EXP(2, ?MUL(B, 8)))). -define(BSL(X, B), op('bsl', ?MUL(B, 8), X)).
-define(BSR(X, B), op('bsr', ?MUL(B, 8), X)).
op(Op, A, B) -> {binop, Op, operand(A), operand(B)}. op(Op, A, B) -> {binop, Op, operand(A), operand(B)}.
@@ -108,8 +110,6 @@ check_event_type(Evts, Icode) ->
|| {constr_t, _, {con, _, Name}, Types} <- Evts, T <- Types ]. || {constr_t, _, {con, _, Name}, Types} <- Evts, T <- Types ].
check_event_type(EvtName, Type, Icode) -> check_event_type(EvtName, Type, Icode) ->
io:format("~p: ~p??\n", [EvtName, Type]),
io:format("=> ~p\n", [aeso_ast_to_icode:ast_typerep(Type, Icode)]),
VMType = VMType =
try try
aeso_ast_to_icode:ast_typerep(Type, Icode) aeso_ast_to_icode:ast_typerep(Type, Icode)
@@ -123,11 +123,49 @@ check_event_type(EvtName, Type, Icode) ->
false -> error({EvtName, payload_should_be_string, is, VMType}) false -> error({EvtName, payload_should_be_string, is, VMType})
end. end.
bfun(B, {IArgs, IExpr, IRet}) ->
{{builtin, B}, [private], IArgs, IExpr, IRet}.
builtin_function(BF) ->
case BF of
{event, EventT} -> bfun(BF, builtin_event(EventT));
abort -> bfun(BF, builtin_abort());
{map_lookup, Type} -> bfun(BF, builtin_map_lookup(Type));
map_put -> bfun(BF, builtin_map_put());
map_delete -> bfun(BF, builtin_map_delete());
map_size -> bfun(BF, builtin_map_size());
{map_get, Type} -> bfun(BF, builtin_map_get(Type));
{map_lookup_default, Type} -> bfun(BF, builtin_map_lookup_default(Type));
map_member -> bfun(BF, builtin_map_member());
{map_upd, Type} -> bfun(BF, builtin_map_upd(Type));
{map_upd_default, Type} -> bfun(BF, builtin_map_upd_default(Type));
map_from_list -> bfun(BF, builtin_map_from_list());
list_concat -> bfun(BF, builtin_list_concat());
string_length -> bfun(BF, builtin_string_length());
string_concat -> bfun(BF, builtin_string_concat());
string_concat_inner1 -> bfun(BF, builtin_string_concat_inner1());
string_copy -> bfun(BF, builtin_string_copy());
string_shift_copy -> bfun(BF, builtin_string_shift_copy());
str_equal_p -> bfun(BF, builtin_str_equal_p());
str_equal -> bfun(BF, builtin_str_equal());
popcount -> bfun(BF, builtin_popcount());
int_to_str -> bfun(BF, builtin_int_to_str());
addr_to_str -> bfun(BF, builtin_addr_to_str());
{baseX_int, X} -> bfun(BF, builtin_baseX_int(X));
{baseX_digits, X} -> bfun(BF, builtin_baseX_digits(X));
{baseX_tab, X} -> bfun(BF, builtin_baseX_tab(X));
{baseX_int_pad, X} -> bfun(BF, builtin_baseX_int_pad(X));
{baseX_int_encode, X} -> bfun(BF, builtin_baseX_int_encode(X));
{baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X));
string_reverse -> bfun(BF, builtin_string_reverse());
string_reverse_ -> bfun(BF, builtin_string_reverse_())
end.
%% Event primitive (dependent on Event type) %% Event primitive (dependent on Event type)
%% %%
%% We need to switch on the event and prepare the correct #event for icode_to_asm %% We need to switch on the event and prepare the correct #event for icode_to_asm
%% NOTE: we assume all errors are already checked! %% NOTE: we assume all errors are already checked!
builtin_function(Builtin = {event, EventT}) -> builtin_event(EventT) ->
A = fun(X) -> aeb_opcodes:mnemonic(X) end, A = fun(X) -> aeb_opcodes:mnemonic(X) end,
VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end, VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end,
ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end, ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end,
@@ -149,149 +187,132 @@ builtin_function(Builtin = {event, EventT}) ->
{variant_t, Cons} = EventT, {variant_t, Cons} = EventT,
Tags = lists:seq(0, length(Cons) - 1), Tags = lists:seq(0, length(Cons) - 1),
{{builtin, Builtin}, [private], {[{"e", event}],
[{"e", event}], {switch, v(e),
{switch, v(e), [{Pat(Tag, Types), Clause(Tag, Con, Types)}
[{Pat(Tag, Types), Clause(Tag, Con, Types)} || {Tag, {constr_t, _, Con, Types}} <- lists:zip(Tags, Cons) ]},
|| {Tag, {constr_t, _, Con, Types}} <- lists:zip(Tags, Cons) ]}, {tuple, []}}.
{tuple, []}};
%% Abort primitive. %% Abort primitive.
builtin_function(abort) -> builtin_abort() ->
A = fun(X) -> aeb_opcodes:mnemonic(X) end, A = fun(X) -> aeb_opcodes:mnemonic(X) end,
{{builtin, abort}, [private], {[{"s", string}],
[{"s", string}],
{inline_asm, [A(?PUSH1),0, %% Push a dummy 0 for the first arg {inline_asm, [A(?PUSH1),0, %% Push a dummy 0 for the first arg
A(?REVERT)]}, %% Stack: 0,Ptr A(?REVERT)]}, %% Stack: 0,Ptr
{tuple,[]}}; {tuple,[]}}.
%% Map primitives %% Map primitives
builtin_function(Builtin = {map_lookup, Type}) -> builtin_map_lookup(Type) ->
Ret = aeso_icode:option_typerep(Type), Ret = aeso_icode:option_typerep(Type),
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}],
[{"m", word}, {"k", word}], prim_call(?PRIM_CALL_MAP_GET, #integer{value = 0},
prim_call(?PRIM_CALL_MAP_GET, #integer{value = 0}, [#var_ref{name = "m"}, #var_ref{name = "k"}],
[#var_ref{name = "m"}, #var_ref{name = "k"}], [word, word], Ret),
[word, word], Ret), Ret}.
Ret};
builtin_function(Builtin = map_put) -> builtin_map_put() ->
%% We don't need the types for put. %% We don't need the types for put.
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}, {"v", word}],
[{"m", word}, {"k", word}, {"v", word}], prim_call(?PRIM_CALL_MAP_PUT, #integer{value = 0},
prim_call(?PRIM_CALL_MAP_PUT, #integer{value = 0}, [v(m), v(k), v(v)], [word, word, word], word),
[v(m), v(k), v(v)], word}.
[word, word, word], word),
word};
builtin_function(Builtin = map_delete) -> builtin_map_delete() ->
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}],
[{"m", word}, {"k", word}], prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0},
prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0}, [v(m), v(k)], [word, word], word),
[v(m), v(k)], word}.
[word, word], word),
word};
builtin_function(Builtin = map_size) -> builtin_map_size() ->
Name = {builtin, Builtin}, {[{"m", word}],
{Name, [private], [{"m", word}], prim_call(?PRIM_CALL_MAP_SIZE, #integer{value = 0},
prim_call(?PRIM_CALL_MAP_SIZE, #integer{value = 0}, [v(m)], [word], word),
[v(m)], [word], word), word}.
word};
%% Map builtins %% Map builtins
builtin_function(Builtin = {map_get, Type}) -> builtin_map_get(Type) ->
%% function map_get(m, k) = %% function map_get(m, k) =
%% switch(map_lookup(m, k)) %% switch(map_lookup(m, k))
%% Some(v) => v %% Some(v) => v
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}],
[{"m", word}, {"k", word}], {switch, ?call({map_lookup, Type}, [v(m), v(k)]), [{option_some(v(v)), v(v)}]},
{switch, ?call({map_lookup, Type}, [v(m), v(k)]), Type}.
[{option_some(v(v)), v(v)}]},
Type};
builtin_function(Builtin = {map_lookup_default, Type}) -> builtin_map_lookup_default(Type) ->
%% function map_lookup_default(m, k, default) = %% function map_lookup_default(m, k, default) =
%% switch(map_lookup(m, k)) %% switch(map_lookup(m, k))
%% None => default %% None => default
%% Some(v) => v %% Some(v) => v
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}, {"default", Type}],
[{"m", word}, {"k", word}, {"default", Type}], {switch, ?call({map_lookup, Type}, [v(m), v(k)]),
{switch, ?call({map_lookup, Type}, [v(m), v(k)]), [{option_none(), v(default)},
[{option_none(), v(default)}, {option_some(v(v)), v(v)}]},
{option_some(v(v)), v(v)}]}, Type}.
Type};
builtin_function(Builtin = map_member) -> builtin_map_member() ->
%% function map_member(m, k) : bool = %% function map_member(m, k) : bool =
%% switch(Map.lookup(m, k)) %% switch(Map.lookup(m, k))
%% None => false %% None => false
%% _ => true %% _ => true
{{builtin, Builtin}, [private], {[{"m", word}, {"k", word}],
[{"m", word}, {"k", word}], {switch, ?call({map_lookup, word}, [v(m), v(k)]),
{switch, ?call({map_lookup, word}, [v(m), v(k)]), [{option_none(), {integer, 0}},
[{option_none(), {integer, 0}}, {{var_ref, "_"}, {integer, 1}}]},
{{var_ref, "_"}, {integer, 1}}]}, word}.
word};
builtin_function(Builtin = {map_upd, Type}) -> builtin_map_upd(Type) ->
%% function map_upd(map, key, fun) = %% function map_upd(map, key, fun) =
%% map_put(map, key, fun(map_get(map, key))) %% map_put(map, key, fun(map_get(map, key)))
{{builtin, Builtin}, [private], {[{"map", word}, {"key", word}, {"valfun", word}],
[{"map", word}, {"key", word}, {"valfun", word}],
?call(map_put, ?call(map_put,
[v(map), v(key), [v(map), v(key),
#funcall{ function = v(valfun), #funcall{ function = v(valfun),
args = [?call({map_get, Type}, [v(map), v(key)])] }]), args = [?call({map_get, Type}, [v(map), v(key)])] }]),
word}; word}.
builtin_function(Builtin = {map_upd_default, Type}) -> builtin_map_upd_default(Type) ->
%% function map_upd(map, key, val, fun) = %% function map_upd(map, key, val, fun) =
%% map_put(map, key, fun(map_lookup_default(map, key, val))) %% map_put(map, key, fun(map_lookup_default(map, key, val)))
{{builtin, Builtin}, [private], {[{"map", word}, {"key", word}, {"val", word}, {"valfun", word}],
[{"map", word}, {"key", word}, {"val", word}, {"valfun", word}],
?call(map_put, ?call(map_put,
[v(map), v(key), [v(map), v(key),
#funcall{ function = v(valfun), #funcall{ function = v(valfun),
args = [?call({map_lookup_default, Type}, [v(map), v(key), v(val)])] }]), args = [?call({map_lookup_default, Type}, [v(map), v(key), v(val)])] }]),
word}; word}.
builtin_function(Builtin = map_from_list) -> builtin_map_from_list() ->
%% function map_from_list(xs, acc) = %% function map_from_list(xs, acc) =
%% switch(xs) %% switch(xs)
%% [] => acc %% [] => acc
%% (k, v) :: xs => map_from_list(xs, acc { [k] = v }) %% (k, v) :: xs => map_from_list(xs, acc { [k] = v })
{{builtin, Builtin}, [private], {[{"xs", {list, {tuple, [word, word]}}}, {"acc", word}],
[{"xs", {list, {tuple, [word, word]}}}, {"acc", word}],
{switch, v(xs), {switch, v(xs),
[{{list, []}, v(acc)}, [{{list, []}, v(acc)},
{{binop, '::', {tuple, [v(k), v(v)]}, v(ys)}, {{binop, '::', {tuple, [v(k), v(v)]}, v(ys)},
?call(map_from_list, ?call(map_from_list,
[v(ys), ?call(map_put, [v(acc), v(k), v(v)])])}]}, [v(ys), ?call(map_put, [v(acc), v(k), v(v)])])}]},
word}; word}.
%% list_concat %% list_concat
%% %%
%% Concatenates two lists. %% Concatenates two lists.
builtin_function(list_concat) -> builtin_list_concat() ->
{{builtin, list_concat}, [private], {[{"l1", {list, word}}, {"l2", {list, word}}],
[{"l1", {list, word}}, {"l2", {list, word}}],
{switch, v(l1), {switch, v(l1),
[{{list, []}, v(l2)}, [{{list, []}, v(l2)},
{{binop, '::', v(hd), v(tl)}, {{binop, '::', v(hd), v(tl)},
{binop, '::', v(hd), ?call(list_concat, [v(tl), v(l2)])}} {binop, '::', v(hd), ?call(list_concat, [v(tl), v(l2)])}}
] ]
}, },
word}; word}.
builtin_function(string_length) -> builtin_string_length() ->
%% function length(str) = %% function length(str) =
%% switch(str) %% switch(str)
%% {n} -> n // (ab)use the representation %% {n} -> n // (ab)use the representation
{{builtin, string_length}, [private], {[{"s", string}],
[{"s", string}], ?DEREF(n, s, ?V(n)),
?DEREF(n, s, ?V(n)), word}.
word};
%% str_concat - concatenate two strings %% str_concat - concatenate two strings
%% %%
@@ -299,212 +320,181 @@ builtin_function(string_length) ->
%% top of the Heap and the address to it is returned. The tricky bit is when %% top of the Heap and the address to it is returned. The tricky bit is when
%% the words from the second string has to be shifted to fit next to the first %% the words from the second string has to be shifted to fit next to the first
%% string. %% string.
builtin_function(string_concat) -> builtin_string_concat() ->
{{builtin, string_concat}, [private], {[{"s1", string}, {"s2", string}],
[{"s1", string}, {"s2", string}],
?DEREF(n1, s1, ?DEREF(n1, s1,
?DEREF(n2, s2, ?DEREF(n2, s2,
{ifte, ?EQ(n2, 0), {ifte, ?EQ(n1, 0),
?V(s1), %% Second string is empty return first string ?V(s2), %% First string is empty return second string
?LET(ret, {inline_asm, [?A(?MSIZE)]}, {ifte, ?EQ(n2, 0),
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len ?V(s1), %% Second string is empty return first string
?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]), ?LET(ret, {inline_asm, [?A(?MSIZE)]},
{inline_asm, [?A(?POP)]}, %% Discard fun ret val {seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
?V(ret) %% Put the actual return value ?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]),
]})} {inline_asm, [?A(?POP)]}, %% Discard fun ret val
?V(ret) %% Put the actual return value
]})}
}
)), )),
word}; word}.
builtin_function(string_concat_inner1) -> builtin_string_concat_inner1() ->
%% Copy all whole words from the first string, and set up for word fusion %% Copy all whole words from the first string, and set up for word fusion
%% Special case when the length of the first string is divisible by 32. %% Special case when the length of the first string is divisible by 32.
{{builtin, string_concat_inner1}, [private], {[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}],
[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}], ?LET(w1, ?call(string_copy, [?V(n1), ?V(p1)]),
?DEREF(w1, p1, ?LET(nx, ?MOD(n1, 32),
{ifte, ?GT(n1, 32), {ifte, ?EQ(nx, 0),
{seq, [?V(w1), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?LET(w2, ?call(string_copy, [?V(n2), ?V(p2)]),
?call(string_concat_inner1, [?SUB(n1, 32), ?NXT(p1), ?V(n2), ?V(p2)])]}, {seq, [?V(w2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}),
{ifte, ?EQ(n1, 0), ?call(string_shift_copy, [?V(nx), ?V(w1), ?V(n2), ?V(p2)])
?call(string_concat_inner2, [?I(32), ?I(0), ?V(n2), ?V(p2)]), })),
?call(string_concat_inner2, [?SUB(32, n1), ?V(w1), ?V(n2), ?V(p2)])} word}.
builtin_string_copy() ->
{[{"n", word}, {"p", pointer}],
?DEREF(w, p,
{ifte, ?GT(n, 31),
{seq, [?V(w), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(string_copy, [?SUB(n, 32), ?NXT(p)])]},
?V(w)
}), }),
word}; word}.
builtin_function(string_concat_inner2) -> builtin_string_shift_copy() ->
%% Current "work in progess" word 'x', has 'o' bytes that are "free" - fill them from {[{"off", word}, {"dst", word}, {"n", word}, {"p", pointer}],
%% words of the second string. ?DEREF(w, p,
{{builtin, string_concat_inner2}, [private], {seq, [?ADD(dst, ?BSR(w, off)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
[{"o", word}, {"x", word}, {"n2", word}, {"p2", pointer}], {ifte, ?GT(n, ?SUB(32, off)),
{ifte, ?LT(n2, 1), ?call(string_shift_copy, [?V(off), ?BSL(w, ?SUB(32, off)), ?SUB(n, 32), ?NXT(p)]),
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, %% Use MSIZE as dummy return value {inline_asm, [?A(?MSIZE)]}}]
?DEREF(w2, p2, }),
{ifte, ?GT(n2, o), word}.
{seq, [?ADD(x, ?BSR(w2, ?SUB(32, o))),
{inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(string_concat_inner2,
[?V(o), ?BSL(w2, o), ?SUB(n2, 32), ?NXT(p2)])
]},
{seq, [?ADD(x, ?BSR(w2, ?SUB(32, o))),
{inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]} %% Use MSIZE as dummy return value
})
},
word};
builtin_function(str_equal_p) -> builtin_str_equal_p() ->
%% function str_equal_p(n, p1, p2) = %% function str_equal_p(n, p1, p2) =
%% if(n =< 0) true %% if(n =< 0) true
%% else %% else
%% let w1 = *p1 %% let w1 = *p1
%% let w2 = *p2 %% let w2 = *p2
%% w1 == w2 && str_equal_p(n - 32, p1 + 32, p2 + 32) %% w1 == w2 && str_equal_p(n - 32, p1 + 32, p2 + 32)
{{builtin, str_equal_p}, [private], {[{"n", word}, {"p1", pointer}, {"p2", pointer}],
[{"n", word}, {"p1", pointer}, {"p2", pointer}], {ifte, ?LT(n, 1),
{ifte, ?LT(n, 1), ?I(1),
?I(1), ?DEREF(w1, p1,
?DEREF(w1, p1, ?DEREF(w2, p2,
?DEREF(w2, p2, ?AND(?EQ(w1, w2),
?AND(?EQ(w1, w2), ?call(str_equal_p, [?SUB(n, 32), ?NXT(p1), ?NXT(p2)]))))},
?call(str_equal_p, [?SUB(n, 32), ?NXT(p1), ?NXT(p2)]))))}, word}.
word};
builtin_function(str_equal) -> builtin_str_equal() ->
%% function str_equal(s1, s2) = %% function str_equal(s1, s2) =
%% let n1 = length(s1) %% let n1 = length(s1)
%% let n2 = length(s2) %% let n2 = length(s2)
%% n1 == n2 && str_equal_p(n1, s1 + 32, s2 + 32) %% n1 == n2 && str_equal_p(n1, s1 + 32, s2 + 32)
{{builtin, str_equal}, [private], {[{"s1", string}, {"s2", string}],
[{"s1", string}, {"s2", string}], ?DEREF(n1, s1,
?DEREF(n1, s1, ?DEREF(n2, s2,
?DEREF(n2, s2, ?AND(?EQ(n1, n2), ?call(str_equal_p, [?V(n1), ?NXT(s1), ?NXT(s2)]))
?AND(?EQ(n1, n2), ?call(str_equal_p, [?V(n1), ?NXT(s1), ?NXT(s2)])) )),
)), word}.
word};
builtin_function(int_to_str) -> %% Count the number of 1s in a bit field.
{{builtin, int_to_str}, [private], builtin_popcount() ->
[{"i0", word}], %% function popcount(bits, acc) =
{switch, {ifte, ?LT(i0, 0), %% if (bits == 0) acc
{tuple, [?I(2), ?NEG(i0), ?BSL(45, 31)]}, %% else popcount(bits bsr 1, acc + bits band 1)
{tuple, [?I(1), ?V(i0), ?I(0)]}}, {[{"bits", word}, {"acc", word}],
[{{tuple, [v(off), v(i), v(x)]}, {ifte, ?EQ(bits, 0),
?LET(ret, {inline_asm, [?A(?MSIZE)]}, ?V(acc),
?LET(n, ?call(int_digits, [?DIV(i, 10), ?I(0)]), ?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))])
?LET(fac, ?EXP(10, n), }, word}.
{seq, [?ADD(n, off), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store str len
?call(int_to_str_,
[?MOD(i, fac), ?ADD(x, ?BSL(?ADD(48, ?DIV(i, fac)), ?SUB(32, off))), ?DIV(fac, 10), ?V(off)]),
{inline_asm, [?A(?POP)]}, ?V(ret)]}
)))}]},
word};
builtin_function(int_to_str_) -> builtin_int_to_str() ->
{{builtin, int_to_str_}, [private], {[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}.
[{"x", word}, {"y", word}, {"fac", word}, {"n", word}],
{ifte, ?EQ(fac, 0),
{seq, [?V(y), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?V(n)]},
{ifte, ?EQ(?MOD(n, 32), 0),
%% We've filled a word, write it and start on new word
{seq, [?V(y), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(int_to_str_,
[?MOD(x, fac), ?BSL(?ADD(48, ?DIV(x, fac)), 31),
?DIV(fac, 10), ?I(1)])]},
?call(int_to_str_,
[?MOD(x, fac), ?ADD(y, ?BSL(?ADD(48, ?DIV(x, fac)), ?SUB(31, n))),
?DIV(fac, 10), ?ADD(n, 1)])}
},
word};
builtin_function(int_digits) -> builtin_baseX_tab(_X = 10) ->
{{builtin, int_digits}, [private], {[{"ix", word}], ?ADD($0, ix), word};
[{"x", word}, {"n", word}], builtin_baseX_tab(_X = 58) ->
{ifte, ?EQ(x, 0), ?V(n), ?call(int_digits, [?DIV(x, 10), ?ADD(n, 1)])}, <<Fst32:256>> = <<"123456789ABCDEFGHJKLMNPQRSTUVWXY">>,
word}; <<Lst26:256>> = <<"Zabcdefghijkmnopqrstuvwxyz", 0:48>>,
{[{"ix", word}],
{ifte, ?LT(ix, 32),
?BYTE(ix, Fst32),
?BYTE(?SUB(ix, 32), Lst26)
},
word}.
builtin_function(base58_tab) -> builtin_baseX_int(X) ->
Fst32 = 22252025330403739761829862310514590177935513752035045390683118730099851483225, {[{"w", word}],
Lst26 = 40880219588527126470443504235291962205031881694701834176631306799289575931904, ?LET(ret, {inline_asm, [?A(?MSIZE)]},
{{builtin, base58_tab}, [private], {seq, [?call({baseX_int_pad, X}, [?V(w), ?I(0), ?I(0)]), {inline_asm, [?A(?POP)]}, ?V(ret)]}),
[{"ix", word}], word}.
{ifte, ?LT(ix, 32),
?BYTE(ix, Fst32),
?BYTE(?SUB(ix, 32), Lst26)
}, word};
builtin_function(base58_int) -> builtin_baseX_int_pad(X = 10) ->
{{builtin, base58_int}, [private], {[{"src", word}, {"ix", word}, {"dst", word}],
[{"w", word}], {ifte, ?LT(src, 0),
?LET(str0, ?call(base58_int_encode, [?V(w)]), ?call({baseX_int_encode, X}, [?NEG(src), ?I(1), ?BSL($-, 31)]),
?LET(str1, ?call(base58_int_pad, [?V(w), ?I(0), ?I(0)]), ?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)])},
?LET(str2, ?call(string_concat, [?V(str0), ?V(str1)]), word};
?call(string_reverse, [?V(str2)]) builtin_baseX_int_pad(X = 58) ->
))), {[{"src", word}, {"ix", word}, {"dst", word}],
word}; {ifte, ?GT(?ADD(?DIV(ix, 31), ?BYTE(ix, src)), 0),
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)]),
?call({baseX_int_pad, X}, [?V(src), ?ADD(ix, 1), ?ADD(dst, ?BSL($1, ?SUB(31, ix)))])},
word}.
builtin_function(string_reverse) -> builtin_baseX_int_encode(X) ->
{{builtin, string_reverse}, [private], {[{"src", word}, {"ix", word}, {"dst", word}],
[{"s", string}], ?LET(n, ?call({baseX_digits, X}, [?V(src), ?I(0)]),
?DEREF(n, s, {seq, [?ADD(n, ?ADD(ix, 1)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?LET(ret, {inline_asm, [?A(?MSIZE)]}, ?call({baseX_int_encode_, X}, [?V(src), ?V(dst), ?EXP(X, n), ?V(ix)])]}),
{seq, [?V(n), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, word}.
?call(string_reverse_, [?NXT(s), ?I(0), ?I(31), ?SUB(?V(n), 1)]),
{inline_asm, [?A(?POP)]}, ?V(ret)]})),
word};
builtin_function(string_reverse_) -> builtin_baseX_int_encode_(X) ->
{{builtin, string_reverse_}, [private], {[{"src", word}, {"dst", word}, {"fac", word}, {"ix", word}],
[{"p", pointer}, {"x", word}, {"i1", word}, {"i2", word}], {ifte, ?EQ(fac, 0),
{ifte, ?LT(i2, 0), {seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, {ifte, ?EQ(ix, 32),
?LET(p1, ?ADD(p, ?MUL(?DIV(i2, 32), 32)), %% We've filled a word, write it and start on new word
?DEREF(w, p1, {seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?LET(b, ?BYTE(?MOD(i2, 32), w), ?call({baseX_int_encode_, X}, [?V(src), ?I(0), ?V(fac), ?I(0)])]},
{ifte, ?LT(i1, 0), ?call({baseX_int_encode_, X},
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, [?MOD(src, fac), ?ADD(dst, ?BSL(?call({baseX_tab, X}, [?DIV(src, fac)]), ?SUB(31, ix))),
?call(string_reverse_, ?DIV(fac, X), ?ADD(ix, 1)])}
[?V(p), ?BSL(b, 31), ?I(30), ?SUB(i2, 1)])]}, },
?call(string_reverse_, word}.
[?V(p), ?ADD(x, ?BSL(b, i1)), ?SUB(i1, 1), ?SUB(i2, 1)])})))},
word};
builtin_function(base58_int_pad) -> builtin_baseX_digits(X) ->
{{builtin, base58_int_pad}, [private], {[{"x0", word}, {"dgts", word}],
[{"w", word}, {"i", word}, {"x", word}], ?LET(x1, ?DIV(x0, X),
{ifte, ?GT(?BYTE(i, w), 0), {ifte, ?EQ(x1, 0), ?V(dgts), ?call({baseX_digits, X}, [?V(x1), ?ADD(dgts, 1)])}),
{seq, [?V(i), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, word}.
?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
{inline_asm, [?A(?PUSH1), 64, ?A(?MSIZE), ?A(?SUB)]}]},
?call(base58_int_pad, [?V(w), ?ADD(i, 1),
?ADD(x, ?BSL(49, ?SUB(31, i)))])},
word};
builtin_function(base58_int_encode) -> builtin_string_reverse() ->
{{builtin, base58_int_encode}, [private], {[{"s", string}],
[{"w", word}], ?DEREF(n, s,
?LET(ret, {inline_asm, [?A(?MSIZE), ?A(?PUSH1), 0, ?A(?MSIZE), ?A(?MSTORE)]}, %% write placeholder ?LET(ret, {inline_asm, [?A(?MSIZE)]},
?LET(n, ?call(base58_int_encode_, [?V(w), ?I(0), ?I(0), ?I(31)]), {seq, [?V(n), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
{seq, [?V(ret), {inline_asm, [?A(?DUP2), ?A(?SWAP1), ?A(?MSTORE)]}, ?call(string_reverse_, [?NXT(s), ?I(0), ?I(31), ?SUB(?V(n), 1)]),
?V(ret)]})), {inline_asm, [?A(?POP)]}, ?V(ret)]})),
word}; word}.
builtin_function(base58_int_encode_) -> builtin_string_reverse_() ->
{{builtin, base58_int_encode_}, [private], {[{"p", pointer}, {"x", word}, {"i1", word}, {"i2", word}],
[{"w", word}, {"x", word}, {"n", word}, {"i", word}], {ifte, ?LT(i2, 0),
{ifte, ?EQ(w, 0), {seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?V(n)]}, ?LET(p1, ?ADD(p, ?MUL(?DIV(i2, 32), 32)),
{ifte, ?LT(i, 0), ?DEREF(w, p1,
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?LET(b, ?BYTE(?MOD(i2, 32), w),
?call(base58_int_encode_, {ifte, ?LT(i1, 0),
[?DIV(w, 58), ?BSL(?call(base58_tab, [?MOD(w, 58)]), 31), {seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?ADD(n, 1), ?I(30)])]}, ?call(string_reverse_,
?call(base58_int_encode_, [?V(p), ?BSL(b, 31), ?I(30), ?SUB(i2, 1)])]},
[?DIV(w, 58), ?ADD(x, ?BSL(?call(base58_tab, [?MOD(w, 58)]), i)), ?call(string_reverse_,
?ADD(n, 1), ?SUB(i, 1)])}}, [?V(p), ?ADD(x, ?BSL(b, i1)), ?SUB(i1, 1), ?SUB(i2, 1)])})))},
word}; word}.
builtin_addr_to_str() ->
builtin_function(addr_to_str) -> {[{"a", word}], ?call({baseX_int, 58}, [?V(a)]), word}.
{{builtin, addr_to_str}, [private],
[{"a", word}],
?call(base58_int, [?V(a)]),
word}.
+85 -55
View File
@@ -21,8 +21,8 @@
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-type option() :: pp_sophia_code | pp_ast | pp_icode | pp_assembler | -type option() :: pp_sophia_code | pp_ast | pp_types | pp_typed_ast |
pp_bytecode. pp_icode| pp_assembler | pp_bytecode.
-type options() :: [option()]. -type options() :: [option()].
-export_type([ option/0 -export_type([ option/0
@@ -38,34 +38,58 @@
version() -> version() ->
?COMPILER_VERSION. ?COMPILER_VERSION.
-spec file(string()) -> map(). -spec file(string()) -> {ok, map()} | {error, binary()}.
file(Filename) -> file(Filename) ->
file(Filename, []). file(Filename, []).
-spec file(string(), options()) -> map(). -spec file(string(), options()) -> {ok, map()} | {error, binary()}.
file(Filename, Options) -> file(File, Options) ->
C = read_contract(Filename), case read_contract(File) of
from_string(C, Options). {ok, Bin} -> from_string(Bin, Options);
{error, Error} ->
ErrorString = [File,": ",file:format_error(Error)],
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
end.
-spec from_string(string(), options()) -> map(). -spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
from_string(ContractBin, Options) when is_binary(ContractBin) ->
from_string(binary_to_list(ContractBin), Options);
from_string(ContractString, Options) -> from_string(ContractString, Options) ->
Ast = parse(ContractString, Options), try
ok = pp_sophia_code(Ast, Options), Ast = parse(ContractString, Options),
ok = pp_ast(Ast, Options), ok = pp_sophia_code(Ast, Options),
TypedAst = aeso_ast_infer_types:infer(Ast, Options), ok = pp_ast(Ast, Options),
%% pp_types is handled inside aeso_ast_infer_types. TypedAst = aeso_ast_infer_types:infer(Ast, Options),
ok = pp_typed_ast(TypedAst, Options), %% pp_types is handled inside aeso_ast_infer_types.
ICode = to_icode(TypedAst, Options), ok = pp_typed_ast(TypedAst, Options),
TypeInfo = extract_type_info(ICode), ICode = to_icode(TypedAst, Options),
ok = pp_icode(ICode, Options), TypeInfo = extract_type_info(ICode),
Assembler = assemble(ICode, Options), ok = pp_icode(ICode, Options),
ok = pp_assembler(Assembler, Options), Assembler = assemble(ICode, Options),
ByteCodeList = to_bytecode(Assembler, Options), ok = pp_assembler(Assembler, Options),
ByteCode = << << B:8 >> || B <- ByteCodeList >>, ByteCodeList = to_bytecode(Assembler, Options),
ok = pp_bytecode(ByteCode, Options), ByteCode = << << B:8 >> || B <- ByteCodeList >>,
#{byte_code => ByteCode, type_info => TypeInfo, ok = pp_bytecode(ByteCode, Options),
contract_source => ContractString, {ok, #{byte_code => ByteCode,
compiler_version => version()}. compiler_version => version(),
contract_source => ContractString,
type_info => TypeInfo
}}
catch
%% The compiler errors.
error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
error:{code_errors, Errors} ->
{error, join_errors("Code errors", Errors,
fun (E) -> io_lib:format("~p", [E]) end)}
%% General programming errors in the compiler just signal error.
end.
join_errors(Prefix, Errors, Pfun) ->
Ess = [ Pfun(E) || E <- Errors ],
list_to_binary(string:join([Prefix|Ess], "\n")).
-define(CALL_NAME, "__call"). -define(CALL_NAME, "__call").
@@ -76,30 +100,40 @@ from_string(ContractString, Options) ->
-spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()} -spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()}
when Type :: term(). when Type :: term().
check_call(ContractString, Options) -> check_call(ContractString, Options) ->
Ast = parse(ContractString, Options), try
ok = pp_sophia_code(Ast, Options), Ast = parse(ContractString, Options),
ok = pp_ast(Ast, Options), ok = pp_sophia_code(Ast, Options),
TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]), ok = pp_ast(Ast, Options),
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]),
ok = pp_typed_ast(TypedAst, Options), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
Icode = to_icode(TypedAst, Options), ok = pp_typed_ast(TypedAst, Options),
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], Icode = to_icode(TypedAst, Options),
RetVMType = case RetType of ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
{id, _, "_"} -> any; RetVMType = case RetType of
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode) {id, _, "_"} -> any;
end, _ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
ok = pp_icode(Icode, Options), end,
#{ functions := Funs } = Icode, ok = pp_icode(Icode, Options),
ArgIcode = get_arg_icode(Funs), #{ functions := Funs } = Icode,
try [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ] of ArgIcode = get_arg_icode(Funs),
ArgTerms -> ArgTerms = [ icode_to_term(T, Arg) ||
{ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms} {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
catch throw:Err -> {ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms}
{error, Err} catch
error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch, {error, missing_call_function}} ->
{error, join_errors("Type errors", ["missing __call function"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error, join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
-spec create_calldata(map(), string(), string()) -> -spec create_calldata(map(), string(), string()) ->
{ok, aeso_sophia:heap(), aeso_sophia:type(), aeso_sophia:type()} {ok, binary(), aeso_sophia:type(), aeso_sophia:type()}
| {error, argument_syntax_error}. | {error, argument_syntax_error}.
create_calldata(Contract, "", CallCode) when is_map(Contract) -> create_calldata(Contract, "", CallCode) when is_map(Contract) ->
case check_call(CallCode, []) of case check_call(CallCode, []) of
@@ -113,7 +147,7 @@ create_calldata(Contract, Function, Argument) when is_map(Contract) ->
%% Function should be "foo : type", and %% Function should be "foo : type", and
%% Argument should be "Arg1, Arg2, .., ArgN" (no parens) %% Argument should be "Arg1, Arg2, .., ArgN" (no parens)
case string:lexemes(Function, ": ") of case string:lexemes(Function, ": ") of
%% If function is a single word fallback to old calldata generation %% If function is a single word fallback to old calldata generation
[FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument); [FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument);
[FunName | _] -> [FunName | _] ->
Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space
@@ -247,13 +281,9 @@ parse_string(Text) ->
parse_error(Pos, ErrorString) parse_error(Pos, ErrorString)
end. end.
parse_error({Line,Pos}, ErrorString) -> parse_error({Line, Pos}, ErrorString) ->
Error = io_lib:format("line ~p, column ~p: ~s", [Line,Pos,ErrorString]), Error = io_lib:format("line ~p, column ~p: ~s", [Line, Pos, ErrorString]),
error({parse_errors,[Error]}). error({parse_errors, [Error]}).
read_contract(Name) -> read_contract(Name) ->
{ok, Bin} = file:read_file(filename:join(contract_path(), lists:concat([Name, ".aes"]))), file:read_file(Name).
binary_to_list(Bin).
contract_path() ->
"apps/aesophia/test/contracts".
+1
View File
@@ -59,6 +59,7 @@ builtin_types() ->
Word = fun([]) -> word end, Word = fun([]) -> word end,
#{ "bool" => Word #{ "bool" => Word
, "int" => Word , "int" => Word
, "bits" => Word
, "string" => fun([]) -> string end , "string" => fun([]) -> string end
, "address" => Word , "address" => Word
, "hash" => Word , "hash" => Word
+2
View File
@@ -562,6 +562,8 @@ assemble_infix('^') -> i(?EXP);
assemble_infix('bor') -> i(?OR); assemble_infix('bor') -> i(?OR);
assemble_infix('band') -> i(?AND); assemble_infix('band') -> i(?AND);
assemble_infix('bxor') -> i(?XOR); assemble_infix('bxor') -> i(?XOR);
assemble_infix('bsl') -> i(?SHL);
assemble_infix('bsr') -> i(?SHR);
assemble_infix('<') -> i(?SLT); %% comparisons are SIGNED assemble_infix('<') -> i(?SLT); %% comparisons are SIGNED
assemble_infix('>') -> i(?SGT); assemble_infix('>') -> i(?SGT);
assemble_infix('==') -> i(?EQ); assemble_infix('==') -> i(?EQ);
+3 -3
View File
@@ -176,11 +176,11 @@ expr200() -> infixr(expr300(), binop('||')).
expr300() -> infixr(expr400(), binop('&&')). expr300() -> infixr(expr400(), binop('&&')).
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])). expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
expr500() -> infixr(expr600(), binop(['::', '++'])). expr500() -> infixr(expr600(), binop(['::', '++'])).
expr600() -> infixl(expr650(), binop(['+', '-', 'bor', 'bxor', 'bsr', 'bsl'])). expr600() -> infixl(expr650(), binop(['+', '-'])).
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)). expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
expr700() -> infixl(expr750(), binop(['*', '/', mod, 'band'])). expr700() -> infixl(expr750(), binop(['*', '/', mod])).
expr750() -> infixl(expr800(), binop(['^'])). expr750() -> infixl(expr800(), binop(['^'])).
expr800() -> ?RULE(many(choice(token('!'), token('bnot'))), expr900(), prefixes(_1, _2)). expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)). expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
exprAtom() -> exprAtom() ->
+2 -8
View File
@@ -170,7 +170,7 @@ expr(E) -> expr_p(0, E).
%% -- Not exported ----------------------------------------------------------- %% -- Not exported -----------------------------------------------------------
-spec name(aeso_syntax:id() | aeso_syntax:con() | aeso_syntax:tvar()) -> doc(). -spec name(aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon() | aeso_syntax:tvar()) -> doc().
name({id, _, Name}) -> text(Name); name({id, _, Name}) -> text(Name);
name({con, _, Name}) -> text(Name); name({con, _, Name}) -> text(Name);
name({qid, _, Names}) -> text(string:join(Names, ".")); name({qid, _, Names}) -> text(string:join(Names, "."));
@@ -359,20 +359,14 @@ bin_prec('++') -> {500, 600, 500};
bin_prec('::') -> {500, 600, 500}; bin_prec('::') -> {500, 600, 500};
bin_prec('+') -> {600, 600, 650}; bin_prec('+') -> {600, 600, 650};
bin_prec('-') -> {600, 600, 650}; bin_prec('-') -> {600, 600, 650};
bin_prec('bor') -> {600, 600, 650};
bin_prec('bxor') -> {600, 600, 650};
bin_prec('bsl') -> {600, 600, 650};
bin_prec('bsr') -> {600, 600, 650};
bin_prec('*') -> {700, 700, 750}; bin_prec('*') -> {700, 700, 750};
bin_prec('/') -> {700, 700, 750}; bin_prec('/') -> {700, 700, 750};
bin_prec(mod) -> {700, 700, 750}; bin_prec(mod) -> {700, 700, 750};
bin_prec('band') -> {700, 700, 750};
bin_prec('^') -> {750, 750, 800}. bin_prec('^') -> {750, 750, 800}.
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}. -spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
un_prec('-') -> {650, 650}; un_prec('-') -> {650, 650};
un_prec('!') -> {800, 800}; un_prec('!') -> {800, 800}.
un_prec('bnot') -> {800, 800}.
equals(Ann, A, B) -> equals(Ann, A, B) ->
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}. {app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
+1 -2
View File
@@ -37,8 +37,7 @@ lexer() ->
, {"[^/*]+|[/*]", skip()} ], , {"[^/*]+|[/*]", skip()} ],
Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal", "stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal"],
"band", "bor", "bxor", "bsl", "bsr", "bnot"],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =
+2 -2
View File
@@ -75,10 +75,10 @@
-type op() :: bin_op() | un_op(). -type op() :: bin_op() | un_op().
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' | 'band' | 'bor' | 'bsl' | 'bsr' | 'bxor' -type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!=' | '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '||' | '&&' | '..'. | '||' | '&&' | '..'.
-type un_op() :: '-' | '!' | 'bnot'. -type un_op() :: '-' | '!'.
-type expr() -type expr()
:: {lam, ann(), [arg()], expr()} :: {lam, ann(), [arg()], expr()}
+1 -1
View File
@@ -5,8 +5,8 @@
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
enacl,
syntax_tools, syntax_tools,
getopt,
aebytecode aebytecode
]}, ]},
{env,[]}, {env,[]},
+71
View File
@@ -0,0 +1,71 @@
-module(aesophia).
-export([main/1]).
-define(OPT_SPEC,
[ {src_file, undefined, undefined, string, "Sophia source code file"}
, {verbose, $v, "verbose", undefined, "Verbose output"}
, {help, $h, "help", undefined, "Show this message"}
, {outfile, $o, "out", string, "Output file (experimental)"} ]).
usage() ->
getopt:usage(?OPT_SPEC, "aesophia").
main(Args) ->
case getopt:parse(?OPT_SPEC, Args) of
{ok, {Opts, []}} ->
case proplists:get_value(help, Opts, false) of
false ->
compile(Opts);
true ->
usage()
end;
{ok, {_, NonOpts}} ->
io:format("Can't understand ~p\n\n", [NonOpts]),
usage();
{error, {Reason, Data}} ->
io:format("Error: ~s ~p\n\n", [Reason, Data]),
usage()
end.
compile(Opts) ->
case proplists:get_value(src_file, Opts, undefined) of
undefined ->
io:format("Error: no input source file\n\n"),
usage();
File ->
compile(File, Opts)
end.
compile(File, Opts) ->
Verbose = proplists:get_value(verbose, Opts, false),
OutFile = proplists:get_value(outfile, Opts, undefined),
try
Res = aeso_compiler:file(File, [pp_ast || Verbose]),
write_outfile(OutFile, Res),
io:format("\nCompiled successfully!\n")
catch
%% The compiler errors.
error:{type_errors, Errors} ->
io:format("\n~s\n", [string:join(["** Type errors\n" | Errors], "\n")]);
error:{parse_errors, Errors} ->
io:format("\n~s\n", [string:join(["** Parse errors\n" | Errors], "\n")]);
error:{code_errors, Errors} ->
ErrorStrings = [ io_lib:format("~p", [E]) || E <- Errors ],
io:format("\n~s\n", [string:join(["** Code errors\n" | ErrorStrings], "\n")]);
%% General programming errors in the compiler.
error:Error ->
Where = hd(erlang:get_stacktrace()),
ErrorString = io_lib:format("Error: ~p in\n ~p", [Error,Where]),
io:format("\n~s\n", [ErrorString])
end.
write_outfile(undefined, _) -> ok;
write_outfile(Out, ResMap) ->
%% Lazy approach
file:write_file(Out, term_to_binary(ResMap)),
io:format("Output written to: ~s\n", [Out]).
+73
View File
@@ -0,0 +1,73 @@
%%%=============================================================================
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Unit tests for the aeso_blake2 module
%%%
%%% In addition the aeso_blake2 module was compared to the C reference
%%% implementation by writing a QuickCheck property.
%%% @end
%%%=============================================================================
-module(aeso_blake2_tests).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
blake2b_test_() ->
{"Tests for BLAKE2b hash implementation",
[ fun() -> blake2b(Data) end || Data <- test_data_blake2b() ]}.
blake2b({Msg0, Key0, ExpectedOut0}) ->
Msg = mk_binary(Msg0),
Key = mk_binary(Key0),
ExpectedOut = mk_binary(ExpectedOut0),
Result = aeso_blake2:blake2b(byte_size(ExpectedOut), Msg, Key),
?assertEqual(Result, {ok, ExpectedOut}).
mk_binary(Bin) when is_binary(Bin) -> Bin;
mk_binary(HexStr) when is_list(HexStr) ->
<< << (erlang:list_to_integer([H], 16)):4 >> || H <- HexStr >>.
test_data_blake2b() ->
[ %% {Message, Key, ExpectedHash}
%% From Wikipedia
%% https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2
{<<>>,
<<>>,
"786A02F742015903C6C6FD852552D272912F4740E15847618A86E217F71F5419D25E1031AFEE585313896444934EB04B903A685B1448B755D56F701AFE9BE2CE"}
, {<<"The quick brown fox jumps over the lazy dog">>,
<<>>,
"A8ADD4BDDDFD93E4877D2746E62817B116364A1FA7BC148D95090BC7333B3673F82401CF7AA2E4CB1ECD90296E3F14CB5413F8ED77BE73045B13914CDCD6A918"}
%% From reference implementation testvectors
%% https://github.com/BLAKE2/BLAKE2/tree/master/testvectors
%%
%% Non-keyed
, {"00",
"",
"2FA3F686DF876995167E7C2E5D74C4C7B6E48F8068FE0E44208344D480F7904C36963E44115FE3EB2A3AC8694C28BCB4F5A0F3276F2E79487D8219057A506E4B"}
, {"0001",
"",
"1C08798DC641ABA9DEE435E22519A4729A09B2BFE0FF00EF2DCD8ED6F8A07D15EAF4AEE52BBF18AB5608A6190F70B90486C8A7D4873710B1115D3DEBBB4327B5"}
, {"00010203040506070809",
"",
"29102511D749DB3CC9B4E335FA1F5E8FACA8421D558F6A3F3321D50D044A248BA595CFC3EFD3D2ADC97334DA732413F5CBF4751C362BA1D53862AC1E8DABEEE8"}
%% Keyed
, {"",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"}
, {"00",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"}
, {"0001",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"}
, {"00010203040506070809",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd"}
].
-endif.
+102 -76
View File
@@ -28,17 +28,26 @@ simple_compile_test_() ->
end} || ContractName <- compilable_contracts() ] ++ end} || ContractName <- compilable_contracts() ] ++
[ {"Testing error messages of " ++ ContractName, [ {"Testing error messages of " ++ ContractName,
fun() -> fun() ->
{type_errors, Errors} = compile(ContractName), <<"Type errors\n",ErrorString/binary>> = compile(ContractName),
?assertEqual(lists:sort(ExpectedErrors), lists:sort(Errors)) check_errors(lists:sort(ExpectedErrors), ErrorString)
end} || end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ] {ContractName, ExpectedErrors} <- failing_contracts() ]
}. }.
check_errors(Expect, ErrorString) ->
%% This removes the final single \n as well.
Actual = binary:split(<<ErrorString/binary,$\n>>, <<"\n\n">>, [global,trim]),
case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
{Missing, Extra} -> ?assertEqual(Missing, Extra)
end.
compile(Name) -> compile(Name) ->
try String = aeso_test_utils:read_contract(Name),
aeso_compiler:from_string(aeso_test_utils:read_contract(Name), []) case aeso_compiler:from_string(String, []) of
catch _:{type_errors, _} = E -> {ok,Map} -> Map;
E {error,ErrorString} -> ErrorString
end. end.
%% compilable_contracts() -> [ContractName]. %% compilable_contracts() -> [ContractName].
@@ -68,77 +77,94 @@ compilable_contracts() ->
failing_contracts() -> failing_contracts() ->
[ {"name_clash", [ {"name_clash",
["Duplicate definitions of abort at\n - (builtin location)\n - line 14, column 3\n", [<<"Duplicate definitions of abort at\n"
"Duplicate definitions of double_def at\n - line 10, column 3\n - line 11, column 3\n", " - (builtin location)\n"
"Duplicate definitions of double_proto at\n - line 4, column 3\n - line 5, column 3\n", " - line 14, column 3">>,
"Duplicate definitions of proto_and_def at\n - line 7, column 3\n - line 8, column 3\n", <<"Duplicate definitions of double_def at\n"
"Duplicate definitions of put at\n - (builtin location)\n - line 15, column 3\n", " - line 10, column 3\n"
"Duplicate definitions of state at\n - (builtin location)\n - line 16, column 3\n"]} " - line 11, column 3">>,
<<"Duplicate definitions of double_proto at\n"
" - line 4, column 3\n"
" - line 5, column 3">>,
<<"Duplicate definitions of proto_and_def at\n"
" - line 7, column 3\n"
" - line 8, column 3">>,
<<"Duplicate definitions of put at\n"
" - (builtin location)\n"
" - line 15, column 3">>,
<<"Duplicate definitions of state at\n"
" - (builtin location)\n"
" - line 16, column 3">>]}
, {"type_errors", , {"type_errors",
["Unbound variable zz at line 17, column 21\n", [<<"Unbound variable zz at line 17, column 21">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and list(int)\n" " and list(int)\n"
"when checking the application at line 26, column 9 of\n" "when checking the application at line 26, column 9 of\n"
" (::) : (int, list(int)) => list(int)\n" " (::) : (int, list(int)) => list(int)\n"
"to arguments\n" "to arguments\n"
" x : int\n" " x : int\n"
" x : int\n", " x : int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the assignment of the field\n" "when checking the assignment of the field\n"
" x : map(string, string) (at line 9, column 46)\n" " x : map(string, string) (at line 9, column 46)\n"
"to the old value __x and the new value\n" "to the old value __x and the new value\n"
" __x {[\"foo\"] @ x = x + 1} : map(string, int)\n", " __x {[\"foo\"] @ x = x + 1} : map(string, int)">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and string\n" " and string\n"
"when checking the type of the expression at line 34, column 45\n" "when checking the type of the expression at line 34, column 45\n"
" 1 : int\n" " 1 : int\n"
"against the expected type\n" "against the expected type\n"
" string\n", " string">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 34, column 50\n" "when checking the type of the expression at line 34, column 50\n"
" \"bla\" : string\n" " \"bla\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 32, column 18\n" "when checking the type of the expression at line 32, column 18\n"
" \"x\" : string\n" " \"x\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 11, column 56\n" "when checking the type of the expression at line 11, column 56\n"
" \"foo\" : string\n" " \"foo\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and string\n" " and string\n"
"when comparing the types of the if-branches\n" "when comparing the types of the if-branches\n"
" - w : int (at line 38, column 13)\n" " - w : int (at line 38, column 13)\n"
" - z : string (at line 39, column 10)\n", " - z : string (at line 39, column 10)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from the projection of the field y (at line 22, column 38)\n", "arising from the projection of the field y (at line 22, column 38)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 21, column 42)\n", "arising from an assignment of the field y (at line 21, column 42)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 20, column 38)\n", "arising from an assignment of the field y (at line 20, column 38)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 19, column 35)\n", "arising from an assignment of the field y (at line 19, column 35)">>,
"Ambiguous record type with field y (at line 13, column 25) could be one of\n" <<"Ambiguous record type with field y (at line 13, column 25) could be one of\n"
" - r (at line 4, column 10)\n" " - r (at line 4, column 10)\n"
" - r' (at line 5, column 10)\n", " - r' (at line 5, column 10)">>,
"Record type r2 does not have field y (at line 15, column 22)\n", <<"Record type r2 does not have field y (at line 15, column 22)">>,
"Repeated name x in pattern\n" <<"The field z is missing when constructing an element of type r2 (at line 15, column 24)">>,
" x :: x (at line 26, column 7)\n", <<"Repeated name x in pattern\n"
"No record type with fields y, z (at line 14, column 22)\n"]} " x :: x (at line 26, column 7)">>,
<<"No record type with fields y, z (at line 14, column 22)">>]}
, {"init_type_error", , {"init_type_error",
["Cannot unify string\n" [<<"Cannot unify string\n"
" and map(int, int)\n" " and map(int, int)\n"
"when checking that 'init' returns a value of type 'state' at line 7, column 3\n"]} "when checking that 'init' returns a value of type 'state' at line 7, column 3">>]}
, {"missing_state_type", , {"missing_state_type",
["Cannot unify string\n" [<<"Cannot unify string\n"
" and ()\n" " and ()\n"
"when checking that 'init' returns a value of type 'state' at line 5, column 3\n"]} "when checking that 'init' returns a value of type 'state' at line 5, column 3">>]}
, {"missing_fields_in_record_expression",
[<<"The field x is missing when constructing an element of type r('a) (at line 7, column 40)">>,
<<"The field y is missing when constructing an element of type r(int) (at line 8, column 40)">>,
<<"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)">>]}
]. ].
-2
View File
@@ -36,8 +36,6 @@ contract AllSyntax =
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)}) (x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
(x, y :: _) => () (x, y :: _) => ()
function bitOperations(x, y) = bnot (0xff00 band x bsl 4 bxor 0xa5a5a5 bsr 4 bor y)
function mutual() = function mutual() =
let rec recFun(x : int) = mutFun(x) let rec recFun(x : int) = mutFun(x)
and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1) and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1)
@@ -0,0 +1,8 @@
contract MissingFieldsInRecordExpr =
record r('a) = {x : int, y : string, z : 'a}
type alias('a) = r('a)
function fail1() = { x = 0 }
function fail2(z : 'a) : r('a) = { y = "string", z = z }
function fail3() : alias(int) = { x = 0, z = 1 }
+13 -15
View File
@@ -5,7 +5,7 @@
contract MultiSig = contract MultiSig =
record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint } record pending_state = { yetNeeded : int, ownersDone : bits, index : int }
datatype event = datatype event =
Confirmation (address, hash) // of { .owner : Address, .operation : Hash } Confirmation (address, hash) // of { .owner : Address, .operation : Hash }
@@ -13,18 +13,18 @@ contract MultiSig =
| OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address } | OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address }
| OwnerAdded (address) // of { .newOwner : Address } | OwnerAdded (address) // of { .newOwner : Address }
| OwnerRemoved (address) // of { .removedOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address }
| ReqChanged (uint) // of { .newReq : uint } | ReqChanged (int) // of { .newReq : int }
let maxOwners : uint = 250 let maxOwners : int = 250
record state = { nRequired : uint record state = { nRequired : int
, nOwners : uint , nOwners : int
, owners : map(uint, address) , owners : map(int, address)
, ownerIndex : map(address, uint) , ownerIndex : map(address, int)
, pending : map(hash, pending_state) , pending : map(hash, pending_state)
, pendingIndex : list(address) } , pendingIndex : list(address) }
function init (owners : list(address), nRequired : uint) : state = function init (owners : list(address), nRequired : int) : state =
let n = length(owners) + 1 let n = length(owners) + 1
{ nRequired = nRequired, { nRequired = nRequired,
nOwners = n, nOwners = n,
@@ -39,10 +39,9 @@ contract MultiSig =
function revoke(operation : hash) = function revoke(operation : hash) =
let ownerIx = lookup(state.ownerIndex, caller()) let ownerIx = lookup(state.ownerIndex, caller())
let pending = lookup(state.pendingIndex, operation) let pending = lookup(state.pendingIndex, operation)
let ownerIxBit = 1 bsl (ownerIx - 1) let _ = require(Bits.test(pending.ownersDone, ownerIx))
let _ = require(pending.ownersDone band ownerIxBit > 0)
let pending' = pending { yetNeeded = pending.yetNeeded + 1 let pending' = pending { yetNeeded = pending.yetNeeded + 1
, ownersDone = pending.ownersDone - ownerIxBit } , ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) }
put(state{ pendingIndex.operator = pending' }) put(state{ pendingIndex.operator = pending' })
event(Revoke(caller, operation)) event(Revoke(caller, operation))
@@ -91,7 +90,7 @@ contract MultiSig =
, pendingIx = [] }, , pendingIx = [] },
event = [OwnerRemoved(oldOwner)] } event = [OwnerRemoved(oldOwner)] }
function changeRequirement(newReq : uint) = function changeRequirement(newReq : int) =
let _ = require(newReq =< state.nOwners) let _ = require(newReq =< state.nOwners)
switch(check_pending(callhash())) switch(check_pending(callhash()))
CheckFail(state') => { state = state' } CheckFail(state') => { state = state' }
@@ -102,7 +101,7 @@ contract MultiSig =
event = [ReqChanged(newReq)] } event = [ReqChanged(newReq)] }
function getOwner(ownerIx0 : uint) = function getOwner(ownerIx0 : int) =
lookup(state.owners, ownerIx0 + 1) lookup(state.owners, ownerIx0 + 1)
function isOwner(owner : address) = function isOwner(owner : address) =
@@ -116,8 +115,7 @@ contract MultiSig =
Some(pending) => Some(pending) =>
let _ = require(isOwner(owner)) let _ = require(isOwner(owner))
let ownerIx = lookup(state.ownerIndex, owner) let ownerIx = lookup(state.ownerIndex, owner)
let ownerIxBit = 1 bsl (ownerIx - 1) Bits.test(pending.ownersDone, ownerIx - 1)
(pending.ownersDone band ownerIxBit) != 0
/* Leave the rest for now... */ /* Leave the rest for now... */
-7
View File
@@ -1,5 +1,4 @@
// - + * / mod arithmetic operators // - + * / mod arithmetic operators
// bnot band bor bxor bsl bsr bitwise operators
// ! && || logical operators // ! && || logical operators
// == != < > =< >= comparison operators // == != < > =< >= comparison operators
// :: ++ list operators // :: ++ list operators
@@ -13,12 +12,6 @@ contract Operators =
"/" => a / b "/" => a / b
"mod" => a mod b "mod" => a mod b
"^" => a ^ b "^" => a ^ b
"bnot" => bnot a
"band" => a band b
"bor" => a bor b
"bxor" => a bxor b
"bsl" => a bsl b
"bsr" => a bsr b
function bool_op(a : bool, b : bool, op : string) = function bool_op(a : bool, b : bool, op : string) =
switch(op) switch(op)