Merge pull request #139 from aeternity/no-call-init

No calls to init-function (except when creating contract)
This commit is contained in:
Hans Svensson 2019-09-03 13:17:06 +02:00 committed by GitHub
commit f8cd3b87f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 42 additions and 37 deletions

View File

@ -2,7 +2,7 @@
{erl_opts, [debug_info]}. {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"} , {getopt, "1.0.1"}
, {eblake2, "1.0.0"} , {eblake2, "1.0.0"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", , {jsx, {git, "https://github.com/talentdeficit/jsx.git",

View File

@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"72b2a581d5a6d488a208331da88de1a488ac2da1"}}, {ref,"5e16b85ae2385bdc010f319e2f2717c09cac3621"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",

View File

@ -106,6 +106,7 @@
, in_pattern = false :: boolean() , in_pattern = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract
}). }).
-type env() :: #env{}. -type env() :: #env{}.
@ -162,7 +163,7 @@ check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) ->
-spec bind_fun(name(), type() | typesig(), env()) -> env(). -spec bind_fun(name(), type() | typesig(), env()) -> env().
bind_fun(X, Type, 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); false -> force_bind_fun(X, Type, Env);
{_QId, {Ann1, _}} -> {_QId, {Ann1, _}} ->
type_error({duplicate_definition, X, [Ann1, aeso_syntax:get_ann(Type)]}), 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(). -spec force_bind_fun(name(), type() | typesig(), env()) -> env().
force_bind_fun(X, Type, 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 }) -> on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
Scope#scope{ funs = [{X, {Ann, Type}} | Funs] } Scope#scope{ funs = [{X, Entry} | Funs] }
end). end).
-spec bind_funs([{name(), type() | typesig()}], env()) -> env(). -spec bind_funs([{name(), type() | typesig()}], env()) -> env().
@ -253,34 +259,32 @@ possible_scopes(#env{ namespace = Current}, Name) ->
Qual = lists:droplast(Name), Qual = lists:droplast(Name),
[ lists:sublist(Current, I) ++ Qual || I <- lists:seq(0, length(Current)) ]. [ 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()}. -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}.
lookup_type(Env, Id) -> 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()}; -spec lookup_env(env(), term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()};
(env(), type, qname()) -> false | {qname(), type_info()}. (env(), type, aeso_syntax:ann(), qname()) -> false | {qname(), type_info()}.
lookup_env(Env, Kind, Name) -> lookup_env(Env, Kind, Ann, Name) ->
Var = case Name of Var = case Name of
[X] when Kind == term -> proplists:get_value(X, Env#env.vars, false); [X] when Kind == term -> proplists:get_value(X, Env#env.vars, false);
_ -> false _ -> false
end, end,
case Var of case Var of
{Ann, Type} -> {Name, {Ann, Type}}; {Ann1, Type} -> {Name, {Ann1, Type}};
false -> false ->
Names = [ Qual ++ [lists:last(Name)] || Qual <- possible_scopes(Env, Name) ], 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; [] -> false;
[Res] -> Res; [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
end. end.
-spec lookup_env1(env(), type | term, qname()) -> false | {qname(), fun_info()}. -spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}.
lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) -> lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) ->
Qual = lists:droplast(QName), Qual = lists:droplast(QName),
Name = lists:last(QName), Name = lists:last(QName),
AllowPrivate = lists:prefix(Qual, Current), AllowPrivate = lists:prefix(Qual, Current),
@ -295,9 +299,12 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) ->
%% Look up the unqualified name %% Look up the unqualified name
case proplists:get_value(Name, Defs, false) of case proplists:get_value(Name, Defs, false) of
false -> false; 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) %% 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}; true -> {QName, E};
false -> false false -> false
end end
@ -532,7 +539,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
infer(Contracts) -> infer(Contracts) ->
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(). -spec init_env(list(option())) -> env().
init_env(_Options) -> global_env(). init_env(_Options) -> global_env().
@ -602,7 +609,8 @@ infer_constant({letval, Attrs,_Pattern, Type, E}) ->
%% infer_contract takes a proplist mapping global names to types, and %% infer_contract takes a proplist mapping global names to types, and
%% a list of definitions. %% a list of definitions.
-spec infer_contract(env(), contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -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; Kind = fun({type_def, _, _, _, _}) -> type;
({letfun, _, _, _, _, _}) -> function; ({letfun, _, _, _, _, _}) -> function;
({fun_decl, _, _, _}) -> prototype; ({fun_decl, _, _, _}) -> prototype;
@ -830,8 +838,8 @@ is_string_type({bytes_t, _, N}) -> N > 32;
is_string_type(_) -> false. is_string_type(_) -> false.
-spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return(). -spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return().
check_constructor_overlap(Env, Con = {con, _, Name}, NewType) -> check_constructor_overlap(Env, Con = {con, Ann, Name}, NewType) ->
case lookup_name(Env, Name) of case lookup_env(Env, term, Ann, Name) of
false -> ok; false -> ok;
{_, {Ann, Type}} -> {_, {Ann, Type}} ->
OldType = case Type of {type_sig, _, _, _, T} -> T; OldType = case Type of {type_sig, _, _, _, T} -> T;
@ -965,7 +973,7 @@ lookup_name(Env, As, Name) ->
lookup_name(Env, As, Name, []). lookup_name(Env, As, Name, []).
lookup_name(Env, As, Id, Options) -> lookup_name(Env, As, Id, Options) ->
case lookup_name(Env, qname(Id)) of case lookup_env(Env, term, As, qname(Id)) of
false -> false ->
type_error({unbound_variable, Id}), type_error({unbound_variable, Id}),
{Id, fresh_uvar(As)}; {Id, fresh_uvar(As)};

View File

@ -20,7 +20,7 @@
-type fun_name() :: {entrypoint, binary()} -type fun_name() :: {entrypoint, binary()}
| {local_fun, [string()]} | {local_fun, [string()]}
| init | event. | event.
-type var_name() :: string(). -type var_name() :: string().
-type sophia_name() :: [string()]. -type sophia_name() :: [string()].
@ -910,10 +910,9 @@ add_init_function(Env, StateType, Funs0) ->
false -> false ->
Funs = add_default_init_function(Env, StateType, Funs0), Funs = add_default_init_function(Env, StateType, Funs0),
InitName = {entrypoint, <<"init">>}, InitName = {entrypoint, <<"init">>},
InitFun = #{ args := InitArgs } = maps:get(InitName, Funs, none), InitFun = #{ body := InitBody} = maps:get(InitName, Funs),
Vars = [ {var, X} || {X, _} <- InitArgs ], Funs#{ InitName => InitFun#{ return => {tuple, []},
Funs#{ init => InitFun#{ return => {tuple, []}, body => {builtin, set_state, [InitBody]} } }
body => {builtin, set_state, [{def, InitName, Vars}]} } }
end. end.
add_default_init_function(_Env, StateType, Funs) -> 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(" =")]), pp_text(" : "), pp_ftype(Return), pp_text(" =")]),
prettypr:nest(2, pp_fexpr(Body))). prettypr:nest(2, pp_fexpr(Body))).
pp_fun_name(init) -> pp_text('INIT');
pp_fun_name(event) -> pp_text(event); pp_fun_name(event) -> pp_text(event);
pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E)); pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E));
pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")). pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")).

View File

@ -144,7 +144,7 @@ string_to_code(ContractString, Options) ->
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
pp_sophia_code(Ast, Options), pp_sophia_code(Ast, Options),
pp_ast(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), pp_typed_ast(TypedAst, Options),
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->

View File

@ -143,7 +143,6 @@ compile(FCode, Options) ->
make_function_id(X) -> make_function_id(X) ->
aeb_fate_code:symbol_identifier(make_function_name(X)). aeb_fate_code:symbol_identifier(make_function_name(X)).
make_function_name(init) -> <<"INIT">>;
make_function_name(event) -> <<"Chain.event">>; make_function_name(event) -> <<"Chain.event">>;
make_function_name({entrypoint, Name}) -> Name; make_function_name({entrypoint, Name}) -> Name;
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).

View File

@ -80,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) ->
, " record r = {x : an_alias(int), y : variant}\n" , " record r = {x : an_alias(int), y : variant}\n"
, " datatype variant = Red | Blue(map(string, int))\n" , " datatype variant = Red | Blue(map(string, int))\n"
, " entrypoint foo : arg_type => arg_type\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]} -> {ok, _, {[Type], _}, [Arg]} ->
io:format("Type ~p~n", [Type]), io:format("Type ~p~n", [Type]),
Data = encode(Arg), 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} -> {ok, Sophia} ->
lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))]));
{error, Err} -> {error, Err} ->
@ -152,7 +152,7 @@ oracle_test() ->
" Oracle.get_question(o, q)\n", " Oracle.get_question(o, q)\n",
{ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} =
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], []), "oq_1111111111111111111111111111113AFEFpt5"], [no_code]),
ok. ok.
@ -174,7 +174,7 @@ encode_decode_calldata(FunName, Types, Args, RetType) ->
encode_decode_calldata_(Code, FunName, Args, RetVMType) -> encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
{ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []), {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), ?assertEqual(RetType, RetVMType),
CalldataType = {tuple, [word, {tuple, ArgTypes}]}, CalldataType = {tuple, [word, {tuple, ArgTypes}]},
{ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata),