diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index b2fdfd4..30e1136 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -107,12 +107,30 @@ join_errors(Prefix, Errors, Pfun) -> -define(DECODE_NAME, "__decode"). %% Takes a string containing a contract with a declaration/prototype of a -%% function (foo, say) and a function __call() = foo(args) calling this +%% function (foo, say) and adds function __call() = foo(args) calling this %% function. Returns the name of the called functions, typereps and Erlang %% terms for the arguments. +%% NOTE: Special treatment for "init" since it might be implicit and has +%% a special return type (typerep, T) -spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()} when Type :: term(). -check_call(ContractString0, FunName, Args, Options) -> +check_call(Source, "init" = FunName, Args, Options) -> + PatchFun = fun(T) -> {tuple, [typerep, T]} end, + case check_call(Source, FunName, Args, Options, PatchFun) of + Err = {error, _} when Args == [] -> + %% Try with default init-function + case check_call(insert_init_function(Source, Options), FunName, Args, Options, PatchFun) of + {error, _} -> Err; %% The first error is most likely better... + Res -> Res + end; + Res -> + Res + end; +check_call(Source, FunName, Args, Options) -> + PatchFun = fun(T) -> T end, + check_call(Source, FunName, Args, Options, PatchFun). + +check_call(ContractString0, FunName, Args, Options, PatchFun) -> try ContractString = insert_call_function(ContractString0, FunName, Args, Options), Ast = parse(ContractString, Options), @@ -132,7 +150,7 @@ check_call(ContractString0, FunName, Args, Options) -> ArgIcode = get_arg_icode(Funs), ArgTerms = [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ], - {ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms} + {ok, FunName, {ArgVMTypes, PatchFun(RetVMType)}, ArgTerms} catch error:{parse_errors, Errors} -> {error, join_errors("Parse errors", Errors, fun (E) -> E end)}; @@ -158,6 +176,16 @@ insert_call_function(Code, FunName, Args, Options) -> "function __call() = ", FunName, "(", string:join(Args, ","), ")\n" ]). +-spec insert_init_function(string(), options()) -> string(). +insert_init_function(Code, Options) -> + Ast = parse(Code, Options), + Ind = last_contract_indent(Ast), + lists:flatten( + [ Code, + "\n\n", + lists:duplicate(Ind, " "), "function init() = ()\n" + ]). + last_contract_indent(Decls) -> case lists:last(Decls) of {_, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1; diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index f061a83..dee8317 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -93,20 +93,36 @@ encode_decode_sophia_string(SophiaType, String) -> end. calldata_test() -> - [42, <<"foobar">>] = encode_decode_calldata(["int", "string"], ["42", "\"foobar\""]), + [42, <<"foobar">>] = encode_decode_calldata("foo", ["int", "string"], ["42", "\"foobar\""]), Map = #{ <<"a">> => 4 }, [{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] = - encode_decode_calldata(["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]), + encode_decode_calldata("foo", ["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]), ok. -encode_decode_calldata(Types, Args) -> - Code = lists:flatten( +calldata_init_test() -> + encode_decode_calldata("init", ["int"], ["42"], {tuple, [typerep, word]}), + + Code = parameterized_contract("foo", ["int"]), + encode_decode_calldata_(Code, "init", [], {tuple, [typerep, {tuple, []}]}). + +parameterized_contract(FunName, Types) -> + lists:flatten( ["contract Dummy =\n", " type an_alias('a) = (string, 'a)\n" " record r = {x : an_alias(int), y : variant}\n" " datatype variant = Red | Blue(map(string, int))\n" - " function foo : (", string:join(Types, ", "), ") => int\n" ]), - {ok, Calldata, CalldataType, word} = aeso_compiler:create_calldata(Code, "foo", Args), + " function ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]). + +encode_decode_calldata(FunName, Types, Args) -> + encode_decode_calldata(FunName, Types, Args, word). + +encode_decode_calldata(FunName, Types, Args, RetType) -> + Code = parameterized_contract(FunName, Types), + 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, {_Hash, ArgTuple}} = aeso_heap:from_binary(CalldataType, Calldata), tuple_to_list(ArgTuple).