From 58ab771dff8750b02bf9c199d4adde3165e177a5 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 3 Sep 2019 09:01:11 +0200 Subject: [PATCH 1/3] Make `init` do the state updates in FATE (instead of a new INIT function) --- src/aeso_ast_to_fcode.erl | 10 ++++------ src/aeso_fcode_to_fate.erl | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 29473e7..82c276b 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -20,7 +20,7 @@ -type fun_name() :: {entrypoint, binary()} | {local_fun, [string()]} - | init | event. + | event. -type var_name() :: string(). -type sophia_name() :: [string()]. @@ -910,10 +910,9 @@ add_init_function(Env, StateType, Funs0) -> false -> Funs = add_default_init_function(Env, StateType, Funs0), InitName = {entrypoint, <<"init">>}, - InitFun = #{ args := InitArgs } = maps:get(InitName, Funs, none), - Vars = [ {var, X} || {X, _} <- InitArgs ], - Funs#{ init => InitFun#{ return => {tuple, []}, - body => {builtin, set_state, [{def, InitName, Vars}]} } } + InitFun = #{ body := InitBody} = maps:get(InitName, Funs), + Funs#{ InitName => InitFun#{ return => {tuple, []}, + body => {builtin, set_state, [InitBody]} } } end. add_default_init_function(_Env, StateType, Funs) -> @@ -1460,7 +1459,6 @@ pp_fun(Name, #{ args := Args, return := Return, body := Body }) -> pp_text(" : "), pp_ftype(Return), pp_text(" =")]), prettypr:nest(2, pp_fexpr(Body))). -pp_fun_name(init) -> pp_text('INIT'); pp_fun_name(event) -> pp_text(event); pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E)); pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")). diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 304ffdb..9412c79 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -143,7 +143,6 @@ compile(FCode, Options) -> make_function_id(X) -> aeb_fate_code:symbol_identifier(make_function_name(X)). -make_function_name(init) -> <<"INIT">>; make_function_name(event) -> <<"Chain.event">>; make_function_name({entrypoint, Name}) -> Name; make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). From 470970d937eea273bf066cd023f688e854e5ddba Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 3 Sep 2019 09:47:19 +0200 Subject: [PATCH 2/3] Disallow calling `init` from inside the contract --- src/aeso_ast_infer_types.erl | 55 +++++++++++++++++++++--------------- src/aeso_compiler.erl | 2 +- test/aeso_abi_tests.erl | 8 +++--- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index ea9c1ae..b80ecfc 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -106,6 +106,7 @@ , in_pattern = false :: boolean() , stateful = false :: boolean() , current_function = none :: none | aeso_syntax:id() + , what = top :: top | namespace | contract }). -type env() :: #env{}. @@ -162,7 +163,7 @@ check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) -> -spec bind_fun(name(), type() | typesig(), env()) -> env(). bind_fun(X, Type, Env) -> - case lookup_name(Env, [X]) of + case lookup_env(Env, term, [], [X]) of false -> force_bind_fun(X, Type, Env); {_QId, {Ann1, _}} -> type_error({duplicate_definition, X, [Ann1, aeso_syntax:get_ann(Type)]}), @@ -171,9 +172,14 @@ bind_fun(X, Type, Env) -> -spec force_bind_fun(name(), type() | typesig(), env()) -> env(). force_bind_fun(X, Type, Env) -> - Ann = aeso_syntax:get_ann(Type), + Ann = aeso_syntax:get_ann(Type), + NoCode = get_option(no_code, false), + Entry = case X == "init" andalso Env#env.what == contract andalso not NoCode of + true -> {reserved_init, Ann, Type}; + false -> {Ann, Type} + end, on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> - Scope#scope{ funs = [{X, {Ann, Type}} | Funs] } + Scope#scope{ funs = [{X, Entry} | Funs] } end). -spec bind_funs([{name(), type() | typesig()}], env()) -> env(). @@ -253,34 +259,32 @@ possible_scopes(#env{ namespace = Current}, Name) -> Qual = lists:droplast(Name), [ lists:sublist(Current, I) ++ Qual || I <- lists:seq(0, length(Current)) ]. --spec lookup_name(env(), qname()) -> false | {qname(), fun_info()}. -lookup_name(Env, Name) -> - lookup_env(Env, term, Name). - -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. lookup_type(Env, Id) -> - lookup_env(Env, type, qname(Id)). + lookup_env(Env, type, aeso_syntax:get_ann(Id), qname(Id)). --spec lookup_env(env(), term, qname()) -> false | {qname(), fun_info()}; - (env(), type, qname()) -> false | {qname(), type_info()}. -lookup_env(Env, Kind, Name) -> +-spec lookup_env(env(), term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}; + (env(), type, aeso_syntax:ann(), qname()) -> false | {qname(), type_info()}. +lookup_env(Env, Kind, Ann, Name) -> Var = case Name of [X] when Kind == term -> proplists:get_value(X, Env#env.vars, false); _ -> false end, case Var of - {Ann, Type} -> {Name, {Ann, Type}}; + {Ann1, Type} -> {Name, {Ann1, Type}}; false -> Names = [ Qual ++ [lists:last(Name)] || Qual <- possible_scopes(Env, Name) ], - case [ Res || QName <- Names, Res <- [lookup_env1(Env, Kind, QName)], Res /= false] of + case [ Res || QName <- Names, Res <- [lookup_env1(Env, Kind, Ann, QName)], Res /= false] of [] -> false; [Res] -> Res; - Many -> type_error({ambiguous_name, [{qid, Ann, Q} || {Q, {Ann, _}} <- Many]}) + Many -> + type_error({ambiguous_name, [{qid, A, Q} || {Q, {A, _}} <- Many]}), + false end end. --spec lookup_env1(env(), type | term, qname()) -> false | {qname(), fun_info()}. -lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) -> +-spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}. +lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) -> Qual = lists:droplast(QName), Name = lists:last(QName), AllowPrivate = lists:prefix(Qual, Current), @@ -295,9 +299,12 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) -> %% Look up the unqualified name case proplists:get_value(Name, Defs, false) of false -> false; - {Ann, _} = E -> + {reserved_init, Ann1, Type} -> + type_error({cannot_call_init_function, Ann}), + {QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error + {Ann1, _} = E -> %% Check that it's not private (or we can see private funs) - case not is_private(Ann) orelse AllowPrivate of + case not is_private(Ann1) orelse AllowPrivate of true -> {QName, E}; false -> false end @@ -532,7 +539,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}. infer(Contracts) -> infer(Contracts, []). --type option() :: return_env | dont_unfold. +-type option() :: return_env | dont_unfold | no_code | term(). -spec init_env(list(option())) -> env(). init_env(_Options) -> global_env(). @@ -541,6 +548,7 @@ init_env(_Options) -> global_env(). infer(Contracts, Options) -> ets_init(), %% Init the ETS table state try + io:format("Options = ~p\n", [Options]), Env = init_env(Options), create_options(Options), ets_new(type_vars, [set]), @@ -602,7 +610,8 @@ infer_constant({letval, Attrs,_Pattern, Type, E}) -> %% infer_contract takes a proplist mapping global names to types, and %% a list of definitions. -spec infer_contract(env(), contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -infer_contract(Env, What, Defs) -> +infer_contract(Env0, What, Defs) -> + Env = Env0#env{ what = What }, Kind = fun({type_def, _, _, _, _}) -> type; ({letfun, _, _, _, _, _}) -> function; ({fun_decl, _, _, _}) -> prototype; @@ -830,8 +839,8 @@ is_string_type({bytes_t, _, N}) -> N > 32; is_string_type(_) -> false. -spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return(). -check_constructor_overlap(Env, Con = {con, _, Name}, NewType) -> - case lookup_name(Env, Name) of +check_constructor_overlap(Env, Con = {con, Ann, Name}, NewType) -> + case lookup_env(Env, term, Ann, Name) of false -> ok; {_, {Ann, Type}} -> OldType = case Type of {type_sig, _, _, _, T} -> T; @@ -965,7 +974,7 @@ lookup_name(Env, As, Name) -> lookup_name(Env, As, Name, []). lookup_name(Env, As, Id, Options) -> - case lookup_name(Env, qname(Id)) of + case lookup_env(Env, term, As, qname(Id)) of false -> type_error({unbound_variable, Id}), {Id, fresh_uvar(As)}; diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index eeba90c..014d17b 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -144,7 +144,7 @@ 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]), + {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]), pp_typed_ast(TypedAst, Options), case proplists:get_value(backend, Options, aevm) of aevm -> diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index c6ce469..3042f92 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -80,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) -> , " record r = {x : an_alias(int), y : variant}\n" , " datatype variant = Red | Blue(map(string, int))\n" , " entrypoint foo : arg_type => arg_type\n" ], - case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of + case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_code]) of {ok, _, {[Type], _}, [Arg]} -> io:format("Type ~p~n", [Type]), Data = encode(Arg), - case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, []) of + case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_code]) of {ok, Sophia} -> lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); {error, Err} -> @@ -152,7 +152,7 @@ oracle_test() -> " Oracle.get_question(o, q)\n", {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", - "oq_1111111111111111111111111111113AFEFpt5"], []), + "oq_1111111111111111111111111111113AFEFpt5"], [no_code]), ok. @@ -174,7 +174,7 @@ encode_decode_calldata(FunName, Types, Args, RetType) -> encode_decode_calldata_(Code, FunName, Args, RetVMType) -> {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []), - {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), + {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_code]), ?assertEqual(RetType, RetVMType), CalldataType = {tuple, [word, {tuple, ArgTypes}]}, {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), From f0c728ef1edf5d75c48943a75b46762d10f91b06 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Tue, 3 Sep 2019 12:22:26 +0200 Subject: [PATCH 3/3] set aebytecode commit --- rebar.config | 2 +- rebar.lock | 2 +- src/aeso_ast_infer_types.erl | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index ac0d491..8d9c9aa 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,"72b2a58"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"5e16b85"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index 4e9247b..03d8ad6 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"72b2a581d5a6d488a208331da88de1a488ac2da1"}}, + {ref,"5e16b85ae2385bdc010f319e2f2717c09cac3621"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index b80ecfc..24fda40 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -548,7 +548,6 @@ init_env(_Options) -> global_env(). infer(Contracts, Options) -> ets_init(), %% Init the ETS table state try - io:format("Options = ~p\n", [Options]), Env = init_env(Options), create_options(Options), ets_new(type_vars, [set]),