From 1c346af85edbd6a26a5ee5e2a61c1eaf350dae2e Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Mon, 17 Jun 2019 13:43:29 +0200 Subject: [PATCH 1/8] whitespaces --- src/aeso_compiler.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 4a4537a..71fd227 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -397,9 +397,9 @@ create_calldata(Code, Fun, Args, Options) -> case proplists:get_value(backend, Options, aevm) of aevm -> case check_call(Code, Fun, Args, Options) of - {ok, FunName, {ArgTypes, RetType}, VMArgs} -> + {ok, FunName, {ArgTypes, RetType}, VMArgs} -> aeb_aevm_abi:create_calldata(FunName, VMArgs, ArgTypes, RetType); - {error, _} = Err -> Err + {error, _} = Err -> Err end; fate -> case check_call(Code, Fun, Args, Options) of From d3ce5010d0d7f2de6dd347a136c5e4311cb9d05a Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 18 Jun 2019 13:46:27 +0200 Subject: [PATCH 2/8] Update tests --- test/aeso_abi_tests.erl | 6 ++++-- test/aeso_calldata_tests.erl | 38 ++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index a38a478..7d9542f 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -175,8 +175,10 @@ encode_decode_calldata(FunName, Types, Args, RetType) -> encode_decode_calldata_(Code, FunName, Args, RetType). encode_decode_calldata_(Code, FunName, Args, RetVMType) -> - {ok, Calldata, CalldataType, RetVMType1} = aeso_compiler:create_calldata(Code, FunName, Args), - ?assertEqual(RetVMType1, RetVMType), + {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args), + {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), + ?assertEqual(RetType, RetVMType), + CalldataType = {tuple, [word, {tuple, ArgTypes}]}, {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), case FunName of "init" -> diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index 83c5586..04b6965 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -16,18 +16,32 @@ %% are made on the output, just that it is a binary which indicates %% that the compilation worked. calldata_test_() -> - [ {"Testing the " ++ ContractName ++ " contract with the " ++ atom_to_list(Backend) ++ " backend", + [ {"Testing " ++ ContractName ++ " contract ", fun() -> - ContractString = aeso_test_utils:read_contract(ContractName), - Res = aeso_compiler:create_calldata(ContractString, Fun, Args, [{backend, Backend}]), - case Backend of - aevm -> - ?assertMatch({ok, _, _, _}, Res); - fate -> - ?assertMatch({ok, _}, Res) - end - end} || {ContractName, Fun, Args} <- compilable_contracts(), Backend <- [aevm, fate], - not lists:member(ContractName, not_yet_compilable(Backend))]. + ContractString = aeso_test_utils:read_contract(ContractName), + AevmExprs = + case not lists:member(ContractName, not_yet_compilable(aevm)) of + true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]); + false -> undefined + end, + FateExprs = + case not lists:member(ContractName, not_yet_compilable(fate)) of + true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); + false -> undefined + end, + case FateExprs == undefined orelse AevmExprs == undefined of + true -> ok; + false -> + ?assertEqual(FateExprs, AevmExprs) + end + end} || {ContractName, Fun, Args} <- compilable_contracts()]. + + +ast_exprs(ContractString, Fun, Args, Opts) -> + {ok, Data} = aeso_compiler:create_calldata(ContractString, Fun, Args, Opts), + {ok, Types, Exprs} = aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts), + ?assert(is_list(Exprs)), + Exprs. check_errors(Expect, ErrorString) -> %% This removes the final single \n as well. @@ -62,7 +76,7 @@ compilable_contracts() -> {"maps", "get_i", ["1", "{[1] = {x = 3, y = 4}, [2] = {x = 4, y = 5}}"]}, {"maps", "get_i", ["1", "{[1] = {x = 3, y = 4}, [2] = {x = 4, y = 5}, [3] = {x = 5, y = 6}}"]}, {"strings", "str_concat", ["\"test\"","\"me\""]}, - {"complex_types", "filter_some", ["[Some(1), Some(2), None]"]}, + {"complex_types", "filter_some", ["[Some(11), Some(12), None]"]}, {"complex_types", "init", ["ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ"]}, {"__call" "init", []}, {"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]}, From 46c746da1c7b2164f938df965c1efdad9fecb836 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 18 Jun 2019 13:47:35 +0200 Subject: [PATCH 3/8] Refactor string_to_code --- src/aeso_compiler.erl | 50 +++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 71fd227..7000c8c 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -99,7 +99,7 @@ from_string(Backend, ContractString, Options) -> end. from_string1(aevm, ContractString, Options) -> - #{icode := Icode} = string_to_icode(ContractString, Options), + #{icode := Icode} = string_to_code(ContractString, Options), TypeInfo = extract_type_info(Icode), Assembler = assemble(Icode, Options), pp_assembler(Assembler, Options), @@ -113,9 +113,7 @@ from_string1(aevm, ContractString, Options) -> type_info => TypeInfo }}; from_string1(fate, ContractString, Options) -> - Ast = parse(ContractString, Options), - TypedAst = aeso_ast_infer_types:infer(Ast, Options), - FCode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options), + #{fcode := FCode} = string_to_code(ContractString, Options), FateCode = aeso_fcode_to_fate:compile(FCode, Options), ByteCode = aeb_fate_code:serialize(FateCode, []), {ok, Version} = version(), @@ -126,30 +124,26 @@ from_string1(fate, ContractString, Options) -> fate_code => FateCode }}. --spec string_to_icode(string(), [option()]) -> map(). -string_to_icode(ContractString, Options) -> +-spec string_to_code(string(), [option()]) -> map(). +string_to_code(ContractString, Options) -> Ast = parse(ContractString, Options), pp_sophia_code(Ast, Options), pp_ast(Ast, Options), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), pp_typed_ast(TypedAst, Options), - Icode = ast_to_icode(TypedAst, Options), - pp_icode(Icode, Options), - #{ typed_ast => TypedAst, - type_env => TypeEnv, - icode => Icode }. - --spec string_to_fcode(string(), [option()]) -> map(). -string_to_fcode(ContractString, Options) -> - Ast = parse(ContractString, Options), - pp_sophia_code(Ast, Options), - pp_ast(Ast, Options), - {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), - pp_typed_ast(TypedAst, Options), - Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options), - #{ typed_ast => TypedAst, - type_env => TypeEnv, - fcode => Fcode }. + case proplists:get_value(backend, Options, aevm) of + aevm -> + Icode = ast_to_icode(TypedAst, Options), + pp_icode(Icode, Options), + #{ icode => Icode, + typed_ast => TypedAst, + type_env => TypeEnv}; + fate -> + Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options), + #{ fcode => Fcode, + typed_ast => TypedAst, + type_env => TypeEnv} + end. join_errors(Prefix, Errors, Pfun) -> Ess = [ Pfun(E) || E <- Errors ], @@ -187,10 +181,10 @@ check_call1(ContractString0, FunName, Args, Options) -> case proplists:get_value(backend, Options, aevm) of aevm -> %% First check the contract without the __call function - #{} = string_to_icode(ContractString0, Options), + #{} = string_to_code(ContractString0, Options), ContractString = insert_call_function(ContractString0, ?CALL_NAME, FunName, Args, Options), #{typed_ast := TypedAst, - icode := Icode} = string_to_icode(ContractString, Options), + icode := Icode} = string_to_code(ContractString, Options), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], RetVMType = case RetType of @@ -209,14 +203,14 @@ check_call1(ContractString0, FunName, Args, Options) -> {ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms}; fate -> %% First check the contract without the __call function - #{fcode := OrgFcode} = string_to_fcode(ContractString0, Options), + #{fcode := OrgFcode} = string_to_code(ContractString0, Options), FateCode = aeso_fcode_to_fate:compile(OrgFcode, []), %% collect all hashes and compute the first name without hash collision to SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), CallName = first_none_match(?CALL_NAME, SymbolHashes, lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)), ContractString = insert_call_function(ContractString0, CallName, FunName, Args, Options), - #{fcode := Fcode} = string_to_fcode(ContractString, Options), + #{fcode := Fcode} = string_to_code(ContractString, Options), CallArgs = arguments_of_body(CallName, FunName, Fcode), {ok, FunName, CallArgs} end @@ -295,7 +289,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) -> try #{ typed_ast := TypedAst, type_env := TypeEnv, - icode := Icode } = string_to_icode(ContractString, Options), + icode := Icode } = string_to_code(ContractString, Options), {ok, _, Type0} = get_decode_type(FunName, TypedAst), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), VmType = aeso_ast_to_icode:ast_typerep(Type, Icode), From 66528c8a6a1ae1fad136f0e32014f7db3152f8e5 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 18 Jun 2019 13:50:28 +0200 Subject: [PATCH 4/8] Move translate_vm to aeso_vm_decode --- src/aeso_compiler.erl | 137 +++++++++++++++-------------------- src/aeso_syntax.erl | 11 ++- src/aeso_vm_decode.erl | 115 +++++++++++++++++++++++++++++ test/aeso_calldata_tests.erl | 26 ++++--- 4 files changed, 194 insertions(+), 95 deletions(-) create mode 100644 src/aeso_vm_decode.erl diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 7000c8c..1cb91eb 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -16,9 +16,10 @@ , create_calldata/4 , version/0 , sophia_type_to_typerep/1 - , to_sophia_value/4 + , to_sophia_value/4 %% deprecated, need a backend , to_sophia_value/5 - , decode_calldata/3 + , decode_calldata/3 %% deprecated + , decode_calldata/4 , parse/2 ]). @@ -274,7 +275,7 @@ last_contract_indent(Decls) -> -spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) -> {ok, aeso_syntax:expr()} | {error, term()}. to_sophia_value(ContractString, Fun, ResType, Data) -> - to_sophia_value(ContractString, Fun, ResType, Data, []). + to_sophia_value(ContractString, Fun, ResType, Data, [{backend, aevm}]). -spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) -> {ok, aeso_syntax:expr()} | {error, term()}. @@ -296,7 +297,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) -> case aeb_heap:from_binary(VmType, Data) of {ok, VmValue} -> try - {ok, translate_vm_value(VmType, Type, VmValue)} + {ok, aeso_vm_decode:from_aevm(VmType, Type, VmValue)} catch throw:cannot_translate_to_sophia -> Type0Str = prettypr:format(aeso_pretty:type(Type0)), {error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n", @@ -320,62 +321,6 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) -> fun (E) -> io_lib:format("~p", [E]) end)} end. -address_literal(Type, N) -> {Type, [], <>}. - -%% TODO: somewhere else --spec translate_vm_value(aeb_aevm_data:type(), aeso_syntax:type(), aeb_aevm_data:data()) -> aeso_syntax:expr(). -translate_vm_value(word, {id, _, "address"}, N) -> address_literal(account_pubkey, N); -translate_vm_value(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N); -translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N); -translate_vm_value(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N); -translate_vm_value(word, {id, _, "int"}, N) -> <> = <>, {int, [], N1}; -translate_vm_value(word, {id, _, "bits"}, N) -> error({todo, bits, N}); -translate_vm_value(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; -translate_vm_value(word, {bytes_t, _, Len}, Val) when Len =< 32 -> - {bytes, [], <>}; -translate_vm_value({tuple, _}, {bytes_t, _, Len}, Val) -> - {bytes, [], binary:part(<< <> || W <- tuple_to_list(Val) >>, 0, Len)}; -translate_vm_value(string, {id, _, "string"}, S) -> {string, [], S}; -translate_vm_value({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, List) -> - {list, [], [translate_vm_value(VmType, Type, X) || X <- List]}; -translate_vm_value({option, VmType}, {app_t, _, {id, _, "option"}, [Type]}, Val) -> - case Val of - none -> {con, [], "None"}; - {some, X} -> {app, [], {con, [], "Some"}, [translate_vm_value(VmType, Type, X)]} - end; -translate_vm_value({variant, [[], [VmType]]}, {app_t, _, {id, _, "option"}, [Type]}, Val) -> - case Val of - {variant, 0, []} -> {con, [], "None"}; - {variant, 1, [X]} -> {app, [], {con, [], "Some"}, [translate_vm_value(VmType, Type, X)]} - end; -translate_vm_value({tuple, VmTypes}, {tuple_t, _, Types}, Val) - when length(VmTypes) == length(Types), - length(VmTypes) == tuple_size(Val) -> - {tuple, [], [translate_vm_value(VmType, Type, X) - || {VmType, Type, X} <- lists:zip3(VmTypes, Types, tuple_to_list(Val))]}; -translate_vm_value({tuple, VmTypes}, {record_t, Fields}, Val) - when length(VmTypes) == length(Fields), - length(VmTypes) == tuple_size(Val) -> - {record, [], [ {field, [], [{proj, [], FName}], translate_vm_value(VmType, FType, X)} - || {VmType, {field_t, _, FName, FType}, X} <- lists:zip3(VmTypes, Fields, tuple_to_list(Val)) ]}; -translate_vm_value({map, VmKeyType, VmValType}, {app_t, _, {id, _, "map"}, [KeyType, ValType]}, Map) - when is_map(Map) -> - {map, [], [ {translate_vm_value(VmKeyType, KeyType, Key), - translate_vm_value(VmValType, ValType, Val)} - || {Key, Val} <- maps:to_list(Map) ]}; -translate_vm_value({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args}) - when length(VmCons) == length(Cons), - length(VmCons) > Tag -> - VmTypes = lists:nth(Tag + 1, VmCons), - ConType = lists:nth(Tag + 1, Cons), - translate_vm_value(VmTypes, ConType, Args); -translate_vm_value(VmTypes, {constr_t, _, Con, Types}, Args) - when length(VmTypes) == length(Types), - length(VmTypes) == length(Args) -> - {app, [], Con, [ translate_vm_value(VmType, Type, Arg) - || {VmType, Type, Arg} <- lists:zip3(VmTypes, Types, Args) ]}; -translate_vm_value(_VmType, _Type, _Data) -> - throw(cannot_translate_to_sophia). -spec create_calldata(string(), string(), [string()]) -> {ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()} @@ -407,30 +352,59 @@ create_calldata(Code, Fun, Args, Options) -> {ok, [aeso_syntax:type()], [aeso_syntax:expr()]} | {error, term()}. decode_calldata(ContractString, FunName, Calldata) -> + decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). + +decode_calldata(ContractString, FunName, Calldata, Options) -> try - #{ typed_ast := TypedAst, - type_env := TypeEnv, - icode := Icode } = string_to_icode(ContractString, []), + Code = string_to_code(ContractString, Options), + #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, + {ok, Args, _} = get_decode_type(FunName, TypedAst), DropArg = fun({arg, _, _, T}) -> T; (T) -> T end, ArgTypes = lists:map(DropArg, Args), Type0 = {tuple_t, [], ArgTypes}, - Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), - VmType = aeso_ast_to_icode:ast_typerep(Type, Icode), - case aeb_heap:from_binary({tuple, [word, VmType]}, Calldata) of - {ok, {_, VmValue}} -> - try - {tuple, [], Values} = translate_vm_value(VmType, Type, VmValue), - {ok, ArgTypes, Values} - catch throw:cannot_translate_to_sophia -> - Type0Str = prettypr:format(aeso_pretty:type(Type0)), - {error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n", - [VmValue, VmType, Type0Str]))], - fun (E) -> E end)} + %% user defined data types such as variants needed to match against + Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), + case proplists:get_value(backend, Options, aevm) of + aevm -> + Icode = maps:get(icode, Code), + VmType = aeso_ast_to_icode:ast_typerep(Type, Icode), + case aeb_heap:from_binary({tuple, [word, VmType]}, Calldata) of + {ok, {_, VmValue}} -> + try + {tuple, [], Values} = aeso_vm_decode:from_aevm(VmType, Type, VmValue), + %% Values are Sophia expressions in AST format + {ok, ArgTypes, Values} + catch throw:cannot_translate_to_sophia -> + Type0Str = prettypr:format(aeso_pretty:type(Type0)), + {error, join_errors("Translation error", + [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n", + [VmValue, VmType, Type0Str]))], + fun (E) -> E end)} + end; + {error, _Err} -> + {error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))], + fun(E) -> E end)} end; - {error, _Err} -> - {error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))], - fun(E) -> E end)} + fate -> + case aeb_fate_abi:decode_calldata(FunName, Calldata) of + {ok, FateArgs} -> + try + {tuple_t, [], ArgTypes1} = Type, + AstArgs = [ aeso_vm_decode:from_fate(ArgType, FateArg) + || {ArgType, FateArg} <- lists:zip(ArgTypes1, FateArgs)], + {ok, ArgTypes, AstArgs} + catch throw:cannot_translate_to_sophia -> + Type0Str = prettypr:format(aeso_pretty:type(Type0)), + {error, join_errors("Translation error", + [lists:flatten(io_lib:format("Cannot translate fate value ~p\n of Sophia type ~s\n", + [FateArgs, Type0Str]))], + fun (E) -> E end)} + end; + {error, _} -> + {error, join_errors("Decode errors", ["Failed to decode binary"], + fun(E) -> E end)} + end end catch error:{parse_errors, Errors} -> @@ -445,7 +419,6 @@ decode_calldata(ContractString, FunName, Calldata) -> fun (E) -> io_lib:format("~p", [E]) end)} end. - get_arg_icode(Funs) -> case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of [Args] -> Args; @@ -471,7 +444,11 @@ get_decode_type(FunName, [{contract, _, _, Defs}]) -> (_) -> [] end, case lists:flatmap(GetType, Defs) of [{Args, Ret}] -> {ok, Args, Ret}; - [] -> {error, missing_function} + [] -> + case FunName of + "init" -> {ok, [], {id,[],"bool"}}; + _ -> {error, missing_function} + end end; get_decode_type(FunName, [_ | Contracts]) -> %% The __decode should be in the final contract diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index ddb95e5..7f45aa8 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -69,11 +69,11 @@ -type constant() :: {int, ann(), integer()} | {bool, ann(), true | false} - | {hash, ann(), binary()} - | {account_pubkey, binary()} - | {contract_pubkey, binary()} - | {oracle_pubkey, binary()} - | {oracle_query_id, binary()} + | {bytes, ann(), binary()} + | {account_pubkey, ann(), binary()} + | {contract_pubkey, ann(), binary()} + | {oracle_pubkey, ann(), binary()} + | {oracle_query_id, ann(), binary()} | {string, ann(), binary()} | {char, ann(), integer()}. @@ -148,4 +148,3 @@ get_ann(Key, Node, Default) -> qualify({con, Ann, N}, X) -> qualify({qcon, Ann, [N]}, X); qualify({qcon, _, NS}, {con, Ann, C}) -> {qcon, Ann, NS ++ [C]}; qualify({qcon, _, NS}, {id, Ann, X}) -> {qid, Ann, NS ++ [X]}. - diff --git a/src/aeso_vm_decode.erl b/src/aeso_vm_decode.erl new file mode 100644 index 0000000..e3d1c21 --- /dev/null +++ b/src/aeso_vm_decode.erl @@ -0,0 +1,115 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc Decoding aevm and fate data to AST +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(aeso_vm_decode). + +-export([ from_aevm/3, from_fate/2 ]). + +-include_lib("aebytecode/include/aeb_fate_data.hrl"). + +address_literal(Type, N) -> {Type, [], <>}. + +-spec from_aevm(aeb_aevm_data:type(), aeso_syntax:type(), aeb_aevm_data:data()) -> aeso_syntax:expr(). +from_aevm(word, {id, _, "address"}, N) -> address_literal(account_pubkey, N); +from_aevm(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N); +from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N); +from_aevm(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N); +from_aevm(word, {id, _, "int"}, N) -> <> = <>, {int, [], N1}; +from_aevm(word, {id, _, "bits"}, N) -> error({todo, bits, N}); +from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; +from_aevm(word, {bytes_t, _, Len}, Val) when Len =< 32 -> + <> = <>, + {bytes, [], <>}; +from_aevm({tuple, _}, {bytes_t, _, Len}, Val) -> + {bytes, [], binary:part(<< <> || W <- tuple_to_list(Val) >>, 0, Len)}; +from_aevm(string, {id, _, "string"}, S) -> {string, [], S}; +from_aevm({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, List) -> + {list, [], [from_aevm(VmType, Type, X) || X <- List]}; +from_aevm({variant, [[], [VmType]]}, {app_t, _, {id, _, "option"}, [Type]}, Val) -> + case Val of + {variant, 0, []} -> {con, [], "None"}; + {variant, 1, [X]} -> {app, [], {con, [], "Some"}, [from_aevm(VmType, Type, X)]} + end; +from_aevm({tuple, VmTypes}, {tuple_t, _, Types}, Val) + when length(VmTypes) == length(Types), + length(VmTypes) == tuple_size(Val) -> + {tuple, [], [from_aevm(VmType, Type, X) + || {VmType, Type, X} <- lists:zip3(VmTypes, Types, tuple_to_list(Val))]}; +from_aevm({tuple, VmTypes}, {record_t, Fields}, Val) + when length(VmTypes) == length(Fields), + length(VmTypes) == tuple_size(Val) -> + {record, [], [ {field, [], [{proj, [], FName}], from_aevm(VmType, FType, X)} + || {VmType, {field_t, _, FName, FType}, X} <- lists:zip3(VmTypes, Fields, tuple_to_list(Val)) ]}; +from_aevm({map, VmKeyType, VmValType}, {app_t, _, {id, _, "map"}, [KeyType, ValType]}, Map) + when is_map(Map) -> + {map, [], [ {from_aevm(VmKeyType, KeyType, Key), + from_aevm(VmValType, ValType, Val)} + || {Key, Val} <- maps:to_list(Map) ]}; +from_aevm({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args}) + when length(VmCons) == length(Cons), + length(VmCons) > Tag -> + VmTypes = lists:nth(Tag + 1, VmCons), + ConType = lists:nth(Tag + 1, Cons), + from_aevm(VmTypes, ConType, Args); +from_aevm(VmTypes, {constr_t, _, Con, Types}, Args) + when length(VmTypes) == length(Types), + length(VmTypes) == length(Args) -> + {app, [], Con, [ from_aevm(VmType, Type, Arg) + || {VmType, Type, Arg} <- lists:zip3(VmTypes, Types, Args) ]}; +from_aevm(_VmType, _Type, _Data) -> + throw(cannot_translate_to_sophia). + + +-spec from_fate(aeso_syntax:type(), aeb_fate_data:fate_type()) -> aeso_syntax:expr(). +from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_pubkey, [], Bin}; +from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin}; +from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin}; +from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; +from_fate({bytes_t, _, 32}, ?FATE_HASH(Bin)) -> {bytes, [], Bin}; +from_fate({bytes_t, _, 64}, ?FATE_SIGNATURE(Bin)) -> {bytes, [], Bin}; +from_fate({bytes_t, _, N}, Bin) when size(Bin) == N -> {bytes, [], Bin}; +from_fate({id, _, "bits"}, ?FATE_BITS(Bin)) -> error({todo, bits, Bin}); +from_fate({id, _, "int"}, N) when is_integer(N) -> {int, [], N}; +from_fate({id, _, "bool"}, B) when is_boolean(B) -> {bool, [], B}; +from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S}; +from_fate({app_t, _, {id, _, "list"}, [Type]}, List) when is_list(List) -> + {list, [], [from_fate(Type, X) || X <- List]}; +from_fate({app_t, _, {id, _, "option"}, [Type]}, Val) -> + case Val of + {variant, [0, 1], 0, {}} -> {con, [], "None"}; + {variant, [0, 1], 1, {X}} -> {app, [], {con, [], "Some"}, [from_fate(Type, X)]} + end; +from_fate({tuple_t, _, []}, ?FATE_UNIT) -> + {tuple, [], []}; +from_fate({tuple_t, _, Types}, ?FATE_TUPLE(Val)) + when length(Types) == tuple_size(Val) -> + {tuple, [], [from_fate(Type, X) + || {Type, X} <- lists:zip(Types, tuple_to_list(Val))]}; +from_fate({record_t, Fields}, ?FATE_TUPLE(Val)) + when length(Fields) == tuple_size(Val) -> + {record, [], [ {field, [], [{proj, [], FName}], from_fate(FType, X)} + || {{field_t, _, FName, FType}, X} <- lists:zip(Fields, tuple_to_list(Val)) ]}; +from_fate({app_t, _, {id, _, "map"}, [KeyType, ValType]}, Map) + when is_map(Map) -> + {map, [], [ {from_fate(KeyType, Key), + from_fate(ValType, Val)} + || {Key, Val} <- maps:to_list(Map) ]}; +from_fate({variant_t, Cons}, {variant, Ar, Tag, Args}) + when length(Cons) > Tag -> + ConType = lists:nth(Tag + 1, Cons), + Arity = lists:nth(Tag + 1, Ar), + case tuple_to_list(Args) of + ArgList when length(ArgList) == Arity -> + from_fate(ConType, tuple_to_list(Args)); + _ -> throw(cannot_translate_to_sophia) + end; +from_fate({constr_t, _, Con, Types}, Args) + when length(Types) == length(Args) -> + {app, [], Con, [ from_fate(Type, Arg) + || {Type, Arg} <- lists:zip(Types, Args) ]}; +from_fate(_Type, _Data) -> + throw(cannot_translate_to_sophia). diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index 04b6965..5fc9b1a 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -16,7 +16,7 @@ %% are made on the output, just that it is a binary which indicates %% that the compilation worked. calldata_test_() -> - [ {"Testing " ++ ContractName ++ " contract ", + [ {"Testing " ++ ContractName ++ " contract calling " ++ Fun, fun() -> ContractString = aeso_test_utils:read_contract(ContractName), AevmExprs = @@ -39,7 +39,7 @@ calldata_test_() -> ast_exprs(ContractString, Fun, Args, Opts) -> {ok, Data} = aeso_compiler:create_calldata(ContractString, Fun, Args, Opts), - {ok, Types, Exprs} = aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts), + {ok, _Types, Exprs} = aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts), ?assert(is_list(Exprs)), Exprs. @@ -59,7 +59,20 @@ compilable_contracts() -> [ {"identity", "init", []}, {"maps", "init", []}, - {"oracles", "init", []}, + {"funargs", "menot", ["false"]}, + {"funargs", "append", ["[\"false\", \" is\", \" not\", \" true\"]"]}, + %% TODO {"funargs", "bitsum", ["Bits.all"]}, + {"funargs", "read", ["{label = \"question 1\", result = 4}"]}, + {"funargs", "sjutton", ["#0011012003100011012003100011012003"]}, + {"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940" + "414243444546474849505152535455565758596061626364656667"]}, + {"funargs", "trettiotva", ["#0102030405060708091011121314151617181920212223242526272829303132"]}, + {"funargs", "find_oracle", ["ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5"]}, + {"funargs", "find_query", ["oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY"]}, + {"funargs", "traffic_light", ["Green"]}, + {"funargs", "traffic_light", ["Pantone(12)"]}, + {"funargs", "tuples", ["()"]}, + %% TODO {"funargs", "due", ["FixedTTL(1020)"]}, {"variant_types", "init", []}, {"basic_auth", "init", []}, {"address_literals", "init", []}, @@ -81,14 +94,9 @@ compilable_contracts() -> {"__call" "init", []}, {"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]}, {"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]} - ]. not_yet_compilable(fate) -> - ["oracles", %% Oracle.register - "events", - "address_literals", %% oracle_query_id literals - "address_chain" %% Oracle.check_query - ]; + ["address_chain"]; not_yet_compilable(aevm) -> ["__call"]. From ff1194357682a6fd3b277dcfe45a9ebf35ef440d Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 19 Jun 2019 12:27:05 +0200 Subject: [PATCH 5/8] Add test contract --- test/contracts/funargs.aes | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/contracts/funargs.aes diff --git a/test/contracts/funargs.aes b/test/contracts/funargs.aes new file mode 100644 index 0000000..808c1e8 --- /dev/null +++ b/test/contracts/funargs.aes @@ -0,0 +1,47 @@ + +contract FunctionArguments = + + function sum(n : int, m: int) = + n + m + + function append(xs : list(string)) = + switch(xs) + [] => "" + y :: ys => String.concat(y, append(ys)) + + function menot(b) = + !b + + function bitsum(b : bits) = + Bits.sum(b) + + record answer('a) = {label : string, result : 'a} + + function read(a : answer(int)) = + a.result + + function sjutton(b : bytes(17)) = + b + + function sextiosju(b : bytes(67)) = + b + + function trettiotva(b : bytes(32)) = + b + + function find_oracle(o : oracle(int, bool)) = + true + + function find_query(q : oracle_query(int, bool)) = + true + + datatype colour() = Green | Yellow | Red | Pantone(int) + + function traffic_light(c : colour) = + Red + + function tuples(t : ()) = + t + + function due(t : Chain.ttl) = + true From d571993405c67ff9d3dacb82d663d69a56d33219 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 19 Jun 2019 12:38:55 +0200 Subject: [PATCH 6/8] Fix type spec --- rebar.config | 2 +- rebar.lock | 2 +- src/aeso_compiler.erl | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rebar.config b/rebar.config index 29eeeac..569502c 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"9dfc5f4"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"f91c8fa"}}} , {getopt, "1.0.1"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}} diff --git a/rebar.lock b/rebar.lock index 9096a27..0cddda9 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"9dfc5f4f1d1a9676cb6b9577af74a4566cc3f3f4"}}, + {ref,"f91c8fabdd01cf911fb194862a50f9635c96c0e5"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 1cb91eb..c52b81a 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -329,8 +329,7 @@ create_calldata(Code, Fun, Args) -> create_calldata(Code, Fun, Args, [{backend, aevm}]). -spec create_calldata(string(), string(), [string()], [{atom(), any()}]) -> - {ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()} - | {ok, binary()} + {ok, binary()} | {error, term()}. create_calldata(Code, Fun, Args, Options) -> case proplists:get_value(backend, Options, aevm) of From 389e93167441284a90f6be0a74dda6c3e5a3f896 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 19 Jun 2019 13:35:08 +0200 Subject: [PATCH 7/8] Unit type instead of bool --- src/aeso_compiler.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index c52b81a..a07b955 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -445,7 +445,7 @@ get_decode_type(FunName, [{contract, _, _, Defs}]) -> [{Args, Ret}] -> {ok, Args, Ret}; [] -> case FunName of - "init" -> {ok, [], {id,[],"bool"}}; + "init" -> {ok, [], {tuple_t, [], []}}; _ -> {error, missing_function} end end; From c745827c53465190bd5119a3a5c36845d5e86cdb Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Thu, 20 Jun 2019 14:19:39 +0200 Subject: [PATCH 8/8] Update src/aeso_vm_decode.erl Co-Authored-By: Hans Svensson --- src/aeso_vm_decode.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_vm_decode.erl b/src/aeso_vm_decode.erl index e3d1c21..edc79f1 100644 --- a/src/aeso_vm_decode.erl +++ b/src/aeso_vm_decode.erl @@ -104,7 +104,7 @@ from_fate({variant_t, Cons}, {variant, Ar, Tag, Args}) Arity = lists:nth(Tag + 1, Ar), case tuple_to_list(Args) of ArgList when length(ArgList) == Arity -> - from_fate(ConType, tuple_to_list(Args)); + from_fate(ConType, ArgList); _ -> throw(cannot_translate_to_sophia) end; from_fate({constr_t, _, Con, Types}, Args)