diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index d50e7e9..7689555 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -261,6 +261,11 @@ decls_to_fcode(Env, Decls) -> -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). decl_to_fcode(Env, {type_decl, _, _, _}) -> Env; +decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, Ann, {id, _, Name}, _}) -> + case proplists:get_value(no_code, maps:get(options, Env, []), false) of + false -> fcode_error({missing_definition, Name, lists:keydelete(entrypoint, 1, Ann)}); + true -> Env + end; decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env; decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) -> typedef_to_fcode(Env, Name, Args, Def); @@ -1001,6 +1006,8 @@ add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no func add_fun_env(Env = #{ fun_env := FunEnv }, Decls) -> Entry = fun({letfun, Ann, {id, _, Name}, Args, _, _}) -> [{qname(Env, Name), {make_fun_name(Env, Ann, Name), length(Args)}}]; + ({fun_decl, Ann, {id, _, Name}, {fun_t, _, _, ArgTypes, _}}) -> + [{qname(Env, Name), {make_fun_name(Env, Ann, Name), length(ArgTypes)}}]; (_) -> [] end, FunEnv1 = maps:from_list(lists:flatmap(Entry, Decls)), Env#{ fun_env := maps:merge(FunEnv, FunEnv1) }. diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 779173a..e611f8f 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -21,18 +21,19 @@ convert_typed(TypedTree, Options) -> {contract, _, {con, _, Con}, _} -> Con; _ -> gen_error(last_declaration_must_be_contract) end, - Icode = code(TypedTree, aeso_icode:set_name(Name, aeso_icode:new(Options))), + NewIcode = aeso_icode:set_name(Name, aeso_icode:new(Options)), + Icode = code(TypedTree, NewIcode, Options), deadcode_elimination(Icode). -code([{contract, _Attribs, Con, Code}|Rest], Icode) -> +code([{contract, _Attribs, Con, Code}|Rest], Icode, Options) -> NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)), - code(Rest, NewIcode); -code([{namespace, _Ann, Name, Code}|Rest], Icode) -> - %% TODO: nested namespaces + code(Rest, NewIcode, Options); +code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) -> + %% TODO: nested namespaces NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Name, Icode)), - code(Rest, NewIcode); -code([], Icode) -> - add_default_init_function(add_builtins(Icode)). + code(Rest, NewIcode, Options); +code([], Icode, Options) -> + add_default_init_function(add_builtins(Icode), Options). %% Generate error on correct format. @@ -40,10 +41,12 @@ gen_error(Error) -> error({code_errors, [Error]}). %% Create default init function (only if state is unit). -add_default_init_function(Icode = #{functions := Funs, state_type := State}) -> +add_default_init_function(Icode = #{functions := Funs, state_type := State}, Options) -> + NoCode = proplists:get_value(no_code, Options, false), {_, _, QInit} = aeso_icode:qualify({id, [], "init"}, Icode), case lists:keymember(QInit, 1, Funs) of true -> Icode; + false when NoCode -> Icode; false when State /= {tuple, []} -> gen_error(missing_init_function); false -> diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index f2f1ef0..cd464d7 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -35,6 +35,7 @@ | pp_icode | pp_assembler | pp_bytecode + | no_code | {backend, aevm | fate} | {include, {file_system, [string()]} | {explicit_files, #{string() => binary()}}} @@ -134,7 +135,7 @@ from_string1(fate, ContractString, Options) -> fate_code => FateCode }}. --spec string_to_code(string(), [option()]) -> map(). +-spec string_to_code(string(), options()) -> map(). string_to_code(ContractString, Options) -> Ast = parse(ContractString, Options), pp_sophia_code(Ast, Options), @@ -361,7 +362,8 @@ create_calldata(Code, Fun, Args) -> -spec create_calldata(string(), string(), [string()], [{atom(), any()}]) -> {ok, binary()} | {error, term()}. -create_calldata(Code, Fun, Args, Options) -> +create_calldata(Code, Fun, Args, Options0) -> + Options = [no_code | Options0], case proplists:get_value(backend, Options, aevm) of aevm -> case check_call(Code, Fun, Args, Options) of @@ -383,7 +385,8 @@ create_calldata(Code, Fun, Args, Options) -> decode_calldata(ContractString, FunName, Calldata) -> decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). -decode_calldata(ContractString, FunName, Calldata, Options) -> +decode_calldata(ContractString, FunName, Calldata, Options0) -> + Options = [no_code | Options0], try Code = string_to_code(ContractString, Options), #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index 5fc9b1a..a68f3b9 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -36,10 +36,34 @@ calldata_test_() -> end end} || {ContractName, Fun, Args} <- compilable_contracts()]. +calldata_aci_test_() -> + [ {"Testing " ++ ContractName ++ " contract calling " ++ Fun, + fun() -> + ContractString = aeso_test_utils:read_contract(ContractName), + {ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString), + ContractACI = binary_to_list(ContractACIBin), + io:format("ACI:\n~s\n", [ContractACIBin]), + AevmExprs = + case not lists:member(ContractName, not_yet_compilable(aevm)) of + true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}]); + false -> undefined + end, + FateExprs = + case not lists:member(ContractName, not_yet_compilable(fate)) of + true -> ast_exprs(ContractACI, 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), + {ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)), + {ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)), ?assert(is_list(Exprs)), Exprs. @@ -93,10 +117,12 @@ compilable_contracts() -> {"complex_types", "init", ["ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ"]}, {"__call" "init", []}, {"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]}, - {"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]} + {"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]}, + {"stub", "foo", ["42"]}, + {"stub", "foo", ["-42"]} ]. not_yet_compilable(fate) -> - ["address_chain"]; + []; not_yet_compilable(aevm) -> - ["__call"]. + []. diff --git a/test/contracts/stub.aes b/test/contracts/stub.aes new file mode 100644 index 0000000..a1cf3b3 --- /dev/null +++ b/test/contracts/stub.aes @@ -0,0 +1,2 @@ +contract Stub = + entrypoint foo : (int) => int