diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 4cdf902..d305875 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -267,15 +267,16 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) -> bind_contract({Contract, Ann, Id, Contents}, Env) when ?IS_CONTRACT_HEAD(Contract) -> Key = name(Id), + io:format("BIND CONTRACT ~p\n", [Id]), Sys = [{origin, system}], Fields = [ {field_t, AnnF, Entrypoint, contract_call_type(Type)} || {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++ [ {field_t, AnnF, Entrypoint, contract_call_type( - {fun_t, AnnF, [], [ArgT || ArgT <- if is_list(Args) -> Args; true -> [Args] end], RetT}) + {fun_t, AnnF, [], [ArgT || {typed, _, _, ArgT} <- if is_list(Args) -> Args; true -> [Args] end], RetT}) } - || {letfun, AnnF, Entrypoint, _Named, Args, {typed, _, _, RetT}} <- Contents + || {letfun, AnnF, Entrypoint, Args, _Type, {typed, _, _, RetT}} <- Contents ] ++ %% Predefined fields [ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ], @@ -1595,6 +1596,7 @@ infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) -> {fun_t, _, NamedArgsT, var_args, RetT} = FunType0, GasCapMock = {named_arg_t, Ann, {id, Ann, "gas"}, {id, Ann, "int"}, {int, Ann, 0}}, ProtectedMock = {named_arg_t, Ann, {id, Ann, "protected"}, {id, Ann, "bool"}, {bool, Ann, false}}, + check_contract_construction(Env, RetT, Fun, [GasCapMock, ProtectedMock|NamedArgsT], ArgTypes, RetT), {fun_t, Ann, NamedArgsT, ArgTypes, RetT}; {qid, _, ["Chain", "clone"]} -> @@ -1618,7 +1620,6 @@ check_contract_construction(Env, ContractT, Fun, NamedArgsT, ArgTypes, RetT) -> InitT = fresh_uvar(Ann), unify(Env, InitT, {fun_t, Ann, NamedArgsT, ArgTypes, fresh_uvar(Ann)}, {checking_init_args, Ann, ContractT, ArgTypes}), unify(Env, RetT, ContractT, {return_contract, Fun, ContractT}), - io:format("DEREF: ~p\n", [dereference_deep(InitT)]), constrain([ #field_constraint{ record_t = unfold_types_in_type(Env, ContractT), field = {id, Ann, "init"}, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index d658309..e6b290b 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -55,6 +55,7 @@ | {oracle_pubkey, binary()} | {oracle_query_id, binary()} | {bool, false | true} + | {contract_code, string()} %% for CREATE, by name | {typerep, ftype()}. -type fexpr() :: {lit, flit()} @@ -167,15 +168,24 @@ %% and produces Fate intermediate code. -spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> fcode(). ast_to_fcode(Code, Options) -> - Verbose = lists:member(pp_fcode, Options), init_fresh_names(), - FCode1 = to_fcode(init_env(Options), Code), + {Env1, FCode1} = to_fcode(init_env(Options), Code), + FCode2 = optimize(FCode1, Options), + Env2 = Env1#{ child_con_env := + maps:map( + fun (_, FC) -> optimize(FC, Options) end, + maps:get(child_con_env, Env1) + )}, + clear_fresh_names(), + {Env2, FCode2}. + +optimize(FCode1, Options) -> + Verbose = lists:member(pp_fcode, Options), [io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose], FCode2 = optimize_fcode(FCode1), [ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ], FCode3 = lambda_lift(FCode2), [ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ], - clear_fresh_names(), FCode3. %% -- Environment ------------------------------------------------------------ @@ -337,7 +347,7 @@ to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest]) functions => add_init_function(Env1, Con, StateType, add_event_function(Env1, EventType, Funs)) }, case Contract of - contract_main -> Rest = [], ConFcode; + contract_main -> Rest = [], {Env1, ConFcode}; contract_child -> Env2 = add_child_con(Env1, Name, ConFcode), to_fcode(Env2, Rest) @@ -696,7 +706,7 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) -> end; %% Function calls -expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) -> +expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) -> Args1 = get_named_args(NamedArgsT, Args), FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1], case expr_to_fcode(Env, Fun) of @@ -708,6 +718,14 @@ expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, FInitArgsT = {typerep, {tuple, [type_to_fcode(Env, T) || T <- ArgsT]}}, builtin_to_fcode(state_layout(Env), chain_clone, [{lit, FInitArgsT}|FArgs]) end; + {builtin_u, chain_create, _Ar} -> + case {ArgsT, Type} of + {var_args, _} -> fcode_error({var_args_not_set, FunE}); + {_, {con, _, Contract}} -> + FInitArgsT = {typerep, {tuple, [type_to_fcode(Env, T) || T <- ArgsT]}}, + builtin_to_fcode(state_layout(Env), chain_clone, [{lit, {contract_code, Contract}}, {lit, FInitArgsT}|FArgs]); + {_, _} -> fcode_error({not_a_contract_type, Type}) + end; {builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs); {def_u, F, _Ar} -> {def, F, FArgs}; {remote_u, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs}; diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 930c70c..6a3c576 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -138,8 +138,9 @@ from_string1(aevm, ContractString, Options) -> {ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}; from_string1(fate, ContractString, Options) -> #{ fcode := FCode + , fcode_env := #{child_con_env := ChildContracts} , folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options), - FateCode = aeso_fcode_to_fate:compile(FCode, Options), + FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options), pp_assembler(fate, FateCode, Options), ByteCode = aeb_fate_code:serialize(FateCode, []), {ok, Version} = version(), @@ -179,8 +180,9 @@ string_to_code(ContractString, Options) -> , type_env => TypeEnv , ast => Ast }; fate -> - Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options), + {Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options), #{ fcode => Fcode + , fcode_env => Env , unfolded_typed_ast => UnfoldedTypedAst , folded_typed_ast => FoldedTypedAst , type_env => TypeEnv diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index a525dcc..cf25dd7 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -9,7 +9,7 @@ %%%------------------------------------------------------------------- -module(aeso_fcode_to_fate). --export([compile/2, term_to_fate/1]). +-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]). -ifdef(TEST). -export([optimize_fun/4, to_basic_blocks/1]). @@ -45,7 +45,7 @@ -define(s(N), {store, N}). -define(void, {var, 9999}). --record(env, { contract, vars = [], locals = [], current_function, tailpos = true }). +-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}). %% -- Debugging -------------------------------------------------------------- @@ -70,9 +70,11 @@ code_error(Err) -> %% @doc Main entry point. compile(FCode, Options) -> + compile(#{}, FCode, Options). +compile(ChildContracts, FCode, Options) -> #{ contract_name := ContractName, functions := Functions } = FCode, - SFuns = functions_to_scode(ContractName, Functions, Options), + SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options), SFuns1 = optimize_scode(SFuns, Options), FateCode = to_basic_blocks(SFuns1), ?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), @@ -85,19 +87,20 @@ make_function_name(event) -> <<"Chain.event">>; make_function_name({entrypoint, Name}) -> Name; make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). -functions_to_scode(ContractName, Functions, Options) -> +functions_to_scode(ChildContracts, ContractName, Functions, Options) -> FunNames = maps:keys(Functions), maps:from_list( - [ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)} + [ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)} || {Name, #{args := Args, body := Body, attrs := Attrs, return := Type}} <- maps:to_list(Functions)]). -function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) -> +function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) -> {ArgTypes, ResType1} = typesig_to_scode(Args, ResType), Attrs = Attrs0 -- [stateful], %% Only track private and payable from here. - SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body), + Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options), + SCode = to_scode(Env, Body), {Attrs, {ArgTypes, ResType1}, SCode}. -define(tvars, '$tvars'). @@ -142,11 +145,13 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts). %% -- Environment functions -- -init_env(ContractName, FunNames, Name, Args) -> +init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) -> #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], contract = ContractName, + child_contracts = ChildContracts, locals = FunNames, current_function = Name, + options = Options, tailpos = true }. next_var(#env{ vars = Vars }) -> @@ -169,7 +174,7 @@ lookup_var(#env{vars = Vars}, X) -> %% -- The compiler -- -lit_to_fate(L) -> +lit_to_fate(Env, L) -> case L of {int, N} -> aeb_fate_data:make_integer(N); {string, S} -> aeb_fate_data:make_string(S); @@ -179,63 +184,79 @@ lit_to_fate(L) -> {contract_pubkey, K} -> aeb_fate_data:make_contract(K); {oracle_pubkey, K} -> aeb_fate_data:make_oracle(K); {oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H); + {contract_code, C} -> + FCode = maps:get(C, Env#env.child_contracts), + SCode = compile(Env#env.child_contracts, FCode, Env#env.options), + ByteCode = aeb_fate_code:serialize(SCode, []), + {ok, Version} = aeso_compiler:version(), + Code = #{byte_code => ByteCode, + compiler_version => Version, + contract_source => "child_contract_src_placeholder", + type_info => [], + fate_code => SCode, + abi_version => aeb_fate_abi:abi_version(), + payable => maps:get(payable, FCode) + }, + Serialized = aeser_contract_code:serialize(Code), + aeb_fate_data:make_contract_bytearray(Serialized); {typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T)) end. -term_to_fate(E) -> term_to_fate(#{}, E). +term_to_fate(E) -> term_to_fate(#env{}, #{}, E). +term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E). -term_to_fate(_Env, {lit, L}) -> - lit_to_fate(L); +term_to_fate(GlobEnv, _Env, {lit, L}) -> + lit_to_fate(GlobEnv, L); %% negative literals are parsed as 0 - N -term_to_fate(_Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) -> +term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) -> aeb_fate_data:make_integer(-N); -term_to_fate(_Env, nil) -> +term_to_fate(_GlobEnv, _Env, nil) -> aeb_fate_data:make_list([]); -term_to_fate(Env, {op, '::', [Hd, Tl]}) -> +term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) -> %% The Tl will translate into a list, because FATE lists are just lists - [term_to_fate(Env, Hd) | term_to_fate(Env, Tl)]; -term_to_fate(Env, {tuple, As}) -> - aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(Env, A) || A<-As])); -term_to_fate(Env, {con, Ar, I, As}) -> - FateAs = [ term_to_fate(Env, A) || A <- As ], + [term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)]; +term_to_fate(GlobEnv, Env, {tuple, As}) -> + aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As])); +term_to_fate(GlobEnv, Env, {con, Ar, I, As}) -> + FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ], aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)); -term_to_fate(_Env, {builtin, bits_all, []}) -> +term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) -> aeb_fate_data:make_bits(-1); -term_to_fate(_Env, {builtin, bits_none, []}) -> +term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) -> aeb_fate_data:make_bits(0); -term_to_fate(_Env, {op, bits_set, [B, I]}) -> - {bits, N} = term_to_fate(B), - J = term_to_fate(I), +term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) -> + {bits, N} = term_to_fate(GlobEnv, B), + J = term_to_fate(GlobEnv, I), {bits, N bor (1 bsl J)}; -term_to_fate(_Env, {op, bits_clear, [B, I]}) -> - {bits, N} = term_to_fate(B), - J = term_to_fate(I), +term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) -> + {bits, N} = term_to_fate(GlobEnv, B), + J = term_to_fate(GlobEnv, I), {bits, N band bnot (1 bsl J)}; -term_to_fate(Env, {'let', X, E, Body}) -> - Env1 = Env#{ X => term_to_fate(Env, E) }, - term_to_fate(Env1, Body); -term_to_fate(Env, {var, X}) -> +term_to_fate(GlobEnv, Env, {'let', X, E, Body}) -> + Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) }, + term_to_fate(GlobEnv, Env1, Body); +term_to_fate(_GlobEnv, Env, {var, X}) -> case maps:get(X, Env, undefined) of undefined -> throw(not_a_fate_value); V -> V end; -term_to_fate(_Env, {builtin, map_empty, []}) -> +term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) -> aeb_fate_data:make_map(#{}); -term_to_fate(Env, {op, map_set, [M, K, V]}) -> - Map = term_to_fate(Env, M), - Map#{term_to_fate(Env, K) => term_to_fate(Env, V)}; -term_to_fate(_Env, _) -> +term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) -> + Map = term_to_fate(GlobEnv, Env, M), + Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)}; +term_to_fate(_GlobEnv, _Env, _) -> throw(not_a_fate_value). to_scode(Env, T) -> - try term_to_fate(T) of + try term_to_fate(Env, T) of V -> [push(?i(V))] catch throw:not_a_fate_value -> to_scode1(Env, T) end. -to_scode1(_Env, {lit, L}) -> - [push(?i(lit_to_fate(L)))]; +to_scode1(Env, {lit, L}) -> + [push(?i(lit_to_fate(Env, L)))]; to_scode1(_Env, nil) -> [aeb_fate_ops:nil(?a)]; @@ -564,10 +585,9 @@ builtin_to_scode(Env, chain_clone, [Contract, TypeRep, Value, GasCap, Prot | InitArgs] ); builtin_to_scode(Env, chain_create, - [_GasCap = {con,[0,1],0,[]}, Prot, Value, Contract | InitArgs]) -> - TypeRep = xd, - call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a), - [Contract, TypeRep, Value, Prot | InitArgs] + [ Code, TypeRep | InitArgs]) -> + call_to_scode(Env, aeb_fate_ops:create(?a, ?a, ?a), + [Code, TypeRep | InitArgs] ). diff --git a/test/contracts/test.aes b/test/contracts/test.aes index 6431d88..e22cfb6 100644 --- a/test/contracts/test.aes +++ b/test/contracts/test.aes @@ -1,9 +1,20 @@ // If you need a quick compiler test — this file is for your playground -contract Chuj = - type state = bool -// entrypoint init : (int, bool) => void - entrypoint init(x : int, y : bool) = if(x < 0) abort("xD") else true +contract IntegerAdder = + entrypoint init() = unit + entrypoint addIntegers(x, y) = x + y -main contract Test = - stateful entrypoint kek() = - Chain.create(value=3, 123, 555) : Chuj +contract IntegerAdderHolder = + type state = IntegerAdder + entrypoint init() = Chain.create() : IntegerAdder + entrypoint get() = state + +namespace IntegerAdderFactory = + function new() = + let i = Chain.create() : IntegerAdderHolder + i.get() + +main contract EnterpriseContract = + entrypoint calculateSomething(x) = + let adder = IntegerAdderFactory.new() + adder.addIntegers(x, 2137) + \ No newline at end of file