995 lines
41 KiB
Erlang
995 lines
41 KiB
Erlang
-module(aeso_tc_env).
|
|
|
|
%% Getters
|
|
-export([ contract_parents/1
|
|
, current_function/1
|
|
, in_guard/1
|
|
, in_pattern/1
|
|
, namespace/1
|
|
, stateful/1
|
|
, typevars/1
|
|
, unify_throws/1
|
|
, used_namespaces/1
|
|
, vars/1
|
|
, what/1
|
|
]).
|
|
|
|
-export([ field_info_field_t/1
|
|
, field_info_record_t/1
|
|
]).
|
|
|
|
-export([ scope_ann/1
|
|
, scope_consts/1
|
|
, scope_funs/1
|
|
, scope_kind/1
|
|
]).
|
|
|
|
%% Setters
|
|
-export([ set_contract_parents/2
|
|
, set_current_const/2
|
|
, set_current_function/2
|
|
, set_in_guard/2
|
|
, set_in_pattern/2
|
|
, set_stateful/2
|
|
, set_used_namespaces/2
|
|
, set_what/2
|
|
]).
|
|
|
|
-export([ push_scope/3
|
|
, pop_scope/1
|
|
, get_scope/2
|
|
, get_current_scope/1
|
|
, on_scopes/2
|
|
, switch_scope/2
|
|
, bind_var/3
|
|
, bind_vars/2
|
|
, bind_contract/3
|
|
, bind_state/1
|
|
, bind_fun/3
|
|
, bind_funs/2
|
|
, bind_tvars/2
|
|
, bind_type/4
|
|
, bind_consts/4
|
|
, bind_fields_append/4
|
|
]).
|
|
|
|
-export([ lookup_env/4
|
|
, lookup_name/3
|
|
, lookup_name/4
|
|
, lookup_type/2
|
|
, lookup_record_field/2
|
|
, lookup_record_field/3
|
|
, lookup_record_field_arity/4
|
|
]).
|
|
|
|
%% Env constructors
|
|
-export([ init_env/0
|
|
, init_env/1
|
|
, empty_env/0
|
|
]).
|
|
|
|
-export_type([env/0]).
|
|
|
|
-include("aeso_utils.hrl").
|
|
|
|
-record(field_info,
|
|
{ ann :: aeso_syntax:ann()
|
|
, field_t :: utype()
|
|
, record_t :: utype()
|
|
, kind :: contract | record }).
|
|
|
|
-type field_info() :: #field_info{}.
|
|
|
|
-type type_id() :: aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon().
|
|
|
|
-type typedef() :: {[aeso_syntax:tvar()], aeso_syntax:typedef() | {contract_t, [aeso_syntax:field_t()]}}
|
|
| {builtin, non_neg_integer()}.
|
|
|
|
-type namespace_alias() :: none | name().
|
|
-type namespace_parts() :: none | {for, [name()]} | {hiding, [name()]}.
|
|
-type used_namespaces() :: [{qname(), namespace_alias(), namespace_parts()}].
|
|
|
|
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
|
|
-type type_info() :: {aeso_syntax:ann(), typedef()}.
|
|
-type const_info() :: {aeso_syntax:ann(), type()}.
|
|
-type var_info() :: {aeso_syntax:ann(), utype()}.
|
|
|
|
-type fun_env() :: [{name(), fun_info()}].
|
|
-type type_env() :: [{name(), type_info()}].
|
|
-type const_env() :: [{name(), const_info()}].
|
|
|
|
-record(scope, { funs = [] :: fun_env()
|
|
, types = [] :: type_env()
|
|
, consts = [] :: const_env()
|
|
, kind = namespace :: namespace | contract
|
|
, ann = [{origin, system}] :: aeso_syntax:ann()
|
|
}).
|
|
|
|
-type scope() :: #scope{}.
|
|
|
|
-record(env,
|
|
{ scopes = #{ [] => #scope{}} :: #{ qname() => scope() }
|
|
, vars = [] :: [{name(), var_info()}]
|
|
, typevars = unrestricted :: unrestricted | [name()]
|
|
, fields = #{} :: #{ name() => [field_info()] } %% fields are global
|
|
, contract_parents = #{} :: #{ name() => [name()] }
|
|
, namespace = [] :: qname()
|
|
, used_namespaces = [] :: used_namespaces()
|
|
, in_pattern = false :: boolean()
|
|
, in_guard = false :: boolean()
|
|
, stateful = false :: boolean()
|
|
, unify_throws = true :: boolean()
|
|
, current_const = none :: none | aeso_syntax:id()
|
|
, current_function = none :: none | aeso_syntax:id()
|
|
, what = top :: top | namespace | contract | contract_interface
|
|
}).
|
|
|
|
-opaque env() :: #env{}.
|
|
|
|
%% -- Circular dependency ----------------------------------------------------
|
|
|
|
infer_const(A, B) -> aeso_ast_infer_types:infer_const(A, B).
|
|
|
|
%% -- Duplicated types -------------------------------------------------------
|
|
|
|
-type name() :: string().
|
|
-type qname() :: [string()].
|
|
-type type() :: aeso_syntax:type().
|
|
-type utype() :: aeso_ast_infer_types:utype().
|
|
-type typesig() :: aeso_ast_infer_types:typesig().
|
|
|
|
%% -- Duplicated macros ------------------------------------------------------
|
|
|
|
-define(CONSTRUCTOR_MOCK_NAME, "#__constructor__#").
|
|
|
|
%% -- Moved functions --------------------------------------------------------
|
|
|
|
name(A) -> aeso_tc_name_manip:name(A).
|
|
qname(A) -> aeso_tc_name_manip:qname(A).
|
|
qid(A, B) -> aeso_tc_name_manip:qid(A, B).
|
|
set_qname(A, B) -> aeso_tc_name_manip:set_qname(A, B).
|
|
|
|
%% -------
|
|
|
|
type_error(A) -> aeso_tc_errors:type_error(A).
|
|
|
|
%% -------
|
|
|
|
warn_potential_shadowing(A, B, C) -> aeso_tc_warnings:warn_potential_shadowing(A, B, C).
|
|
used_include(A) -> aeso_tc_warnings:used_include(A).
|
|
used_variable(A, B, C) -> aeso_tc_warnings:used_variable(A, B, C).
|
|
used_stateful(A) -> aeso_tc_warnings:used_stateful(A).
|
|
register_function_call(A, B) -> aeso_tc_warnings:register_function_call(A, B).
|
|
used_constant(A, B) -> aeso_tc_warnings:used_constant(A, B).
|
|
|
|
%% -------
|
|
|
|
get_option(A, B) -> aeso_tc_options:get_option(A, B).
|
|
when_warning(A, B) -> aeso_tc_options:when_warning(A, B).
|
|
|
|
%% -------
|
|
|
|
fresh_uvar(A) -> aeso_tc_fresh:fresh_uvar(A).
|
|
freshen_type(A, B) -> aeso_tc_fresh:freshen_type(A, B).
|
|
freshen_type_sig(A, B) -> aeso_tc_fresh:freshen_type_sig(A, B).
|
|
|
|
%% -- Getters ------------------------------------------------------------
|
|
|
|
contract_parents(#env{contract_parents = ContractParents}) ->
|
|
ContractParents.
|
|
|
|
current_function(#env{current_function = CurrentFunction}) ->
|
|
CurrentFunction.
|
|
|
|
in_guard(#env{in_guard = InGuard}) ->
|
|
InGuard.
|
|
|
|
in_pattern(#env{in_pattern = InPattern}) ->
|
|
InPattern.
|
|
|
|
namespace(#env{namespace = Namespace}) ->
|
|
Namespace.
|
|
|
|
stateful(#env{stateful = Stateful}) ->
|
|
Stateful.
|
|
|
|
typevars(#env{typevars = Typevars}) ->
|
|
Typevars.
|
|
|
|
unify_throws(#env{unify_throws = UnifyThrows}) ->
|
|
UnifyThrows.
|
|
|
|
used_namespaces(#env{used_namespaces = UsedNamespaces}) ->
|
|
UsedNamespaces.
|
|
|
|
vars(#env{vars = Vars}) ->
|
|
Vars.
|
|
|
|
what(#env{what = What}) ->
|
|
What.
|
|
|
|
%% -- Field Info Getters -------------------------------------------------
|
|
|
|
field_info_field_t(#field_info{field_t = FieldT}) ->
|
|
FieldT.
|
|
|
|
field_info_record_t(#field_info{record_t = RecordT}) ->
|
|
RecordT.
|
|
|
|
%% -- Scope Getters ------------------------------------------------------
|
|
|
|
scope_ann(#scope{ann = Ann}) ->
|
|
Ann.
|
|
|
|
scope_consts(#scope{consts = Consts}) ->
|
|
Consts.
|
|
|
|
scope_funs(#scope{funs = Funs}) ->
|
|
Funs.
|
|
|
|
scope_kind(#scope{kind = Kind}) ->
|
|
Kind.
|
|
|
|
%% -- Setters ------------------------------------------------------------
|
|
|
|
set_contract_parents(ContractParents, Env) ->
|
|
Env#env{contract_parents = ContractParents}.
|
|
|
|
set_current_const(CurrentConst, Env) ->
|
|
Env#env{current_const = CurrentConst}.
|
|
|
|
set_current_function(CurrentFunction, Env) ->
|
|
Env#env{current_function = CurrentFunction}.
|
|
|
|
set_in_guard(InGuard, Env) ->
|
|
Env#env{in_guard = InGuard}.
|
|
|
|
set_in_pattern(InPattern, Env) ->
|
|
Env#env{in_pattern = InPattern}.
|
|
|
|
set_stateful(Stateful, Env) ->
|
|
Env#env{stateful = Stateful}.
|
|
|
|
set_used_namespaces(UsedNamespaces, Env) ->
|
|
Env#env{used_namespaces = UsedNamespaces}.
|
|
|
|
set_what(What, Env) ->
|
|
Env#env{what = What}.
|
|
|
|
%% -- Environment manipulation -----------------------------------------------
|
|
|
|
-spec switch_scope(qname(), env()) -> env().
|
|
switch_scope(Scope, Env) ->
|
|
Env#env{namespace = Scope}.
|
|
|
|
-spec push_scope(namespace | contract, aeso_syntax:con(), env()) -> env().
|
|
push_scope(Kind, Con, Env) ->
|
|
Ann = aeso_syntax:get_ann(Con),
|
|
Name = name(Con),
|
|
New = Env#env.namespace ++ [Name],
|
|
Env#env{ namespace = New, scopes = (Env#env.scopes)#{ New => #scope{ kind = Kind, ann = Ann } } }.
|
|
|
|
-spec pop_scope(env()) -> env().
|
|
pop_scope(Env) ->
|
|
Env#env{ namespace = lists:droplast(Env#env.namespace) }.
|
|
|
|
-spec get_scope(env(), qname()) -> false | scope().
|
|
get_scope(#env{ scopes = Scopes }, Name) ->
|
|
maps:get(Name, Scopes, false).
|
|
|
|
-spec get_current_scope(env()) -> scope().
|
|
get_current_scope(#env{ namespace = NS, scopes = Scopes }) ->
|
|
maps:get(NS, Scopes).
|
|
|
|
-spec on_current_scope(env(), fun((scope()) -> scope())) -> env().
|
|
on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) ->
|
|
Scope = get_current_scope(Env),
|
|
Env#env{ scopes = Scopes#{ NS => Fun(Scope) } }.
|
|
|
|
-spec on_scopes(env(), fun((scope()) -> scope())) -> env().
|
|
on_scopes(Env = #env{ scopes = Scopes }, Fun) ->
|
|
Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }.
|
|
|
|
-spec bind_var(aeso_syntax:id(), utype(), env()) -> env().
|
|
bind_var({id, Ann, X}, T, Env) ->
|
|
when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Env, Ann, X) end),
|
|
Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }.
|
|
|
|
-spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env().
|
|
bind_vars([], Env) -> Env;
|
|
bind_vars([{X, T} | Vars], Env) ->
|
|
bind_vars(Vars, bind_var(X, T, Env)).
|
|
|
|
-spec bind_tvars([aeso_syntax:tvar()], env()) -> env().
|
|
bind_tvars(Xs, Env) ->
|
|
Env#env{ typevars = [X || {tvar, _, X} <- Xs] }.
|
|
|
|
-spec bind_fun(name(), type() | typesig(), env()) -> env().
|
|
bind_fun(X, Type, Env) ->
|
|
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)]}),
|
|
Env
|
|
end.
|
|
|
|
-spec force_bind_fun(name(), type() | typesig(), env()) -> env().
|
|
force_bind_fun(X, Type, Env = #env{ what = What }) ->
|
|
Ann = aeso_syntax:get_ann(Type),
|
|
NoCode = get_option(no_code, false),
|
|
Entry = if X == "init", What == contract, not NoCode ->
|
|
{reserved_init, Ann, Type};
|
|
What == contract; What == contract_interface -> {contract_fun, Ann, Type};
|
|
true -> {Ann, Type}
|
|
end,
|
|
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
|
|
Scope#scope{ funs = [{X, Entry} | Funs] }
|
|
end).
|
|
|
|
-spec bind_funs([{name(), type() | typesig()}], env()) -> env().
|
|
bind_funs([], Env) -> Env;
|
|
bind_funs([{Id, Type} | Rest], Env) ->
|
|
bind_funs(Rest, bind_fun(Id, Type, Env)).
|
|
|
|
-spec bind_type(name(), aeso_syntax:ann(), typedef(), env()) -> env().
|
|
bind_type(X, Ann, Def, Env) ->
|
|
on_current_scope(Env, fun(Scope = #scope{ types = Types }) ->
|
|
Scope#scope{ types = [{X, {Ann, Def}} | Types] }
|
|
end).
|
|
|
|
-spec bind_const(name(), aeso_syntax:ann(), type(), env()) -> env().
|
|
bind_const(X, Ann, Type, Env) ->
|
|
case lookup_env(Env, term, Ann, [X]) of
|
|
false ->
|
|
on_current_scope(Env, fun(Scope = #scope{ consts = Consts }) ->
|
|
Scope#scope{ consts = [{X, {Ann, Type}} | Consts] }
|
|
end);
|
|
_ ->
|
|
type_error({duplicate_definition, X, [Ann, aeso_syntax:get_ann(Type)]}),
|
|
Env
|
|
end.
|
|
|
|
-spec bind_consts(env(), #{ name() => aeso_syntax:decl() }, [{acyclic, name()} | {cyclic, [name()]}], [aeso_syntax:decl()]) ->
|
|
{env(), [aeso_syntax:decl()]}.
|
|
bind_consts(Env, _Consts, [], Acc) ->
|
|
{Env, lists:reverse(Acc)};
|
|
bind_consts(Env, Consts, [{cyclic, Xs} | _SCCs], _Acc) ->
|
|
ConstDecls = [ maps:get(X, Consts) || X <- Xs ],
|
|
type_error({mutually_recursive_constants, lists:reverse(ConstDecls)}),
|
|
{Env, []};
|
|
bind_consts(Env, Consts, [{acyclic, X} | SCCs], Acc) ->
|
|
case maps:get(X, Consts, undefined) of
|
|
Const = {letval, Ann, Id, _} ->
|
|
NewConst = {letval, _, {typed, _, _, Type}, _} = infer_const(Env, Const),
|
|
NewEnv = bind_const(name(Id), Ann, Type, Env),
|
|
bind_consts(NewEnv, Consts, SCCs, [NewConst | Acc]);
|
|
undefined ->
|
|
%% When a used id is not a letval, a type error will be thrown
|
|
bind_consts(Env, Consts, SCCs, Acc)
|
|
end.
|
|
|
|
%% Bind state primitives
|
|
-spec bind_state(env()) -> env().
|
|
bind_state(Env) ->
|
|
Ann = [{origin, system}],
|
|
Unit = {tuple_t, Ann, []},
|
|
State =
|
|
case lookup_type(Env, {id, Ann, "state"}) of
|
|
{S, _} -> {qid, Ann, S};
|
|
false -> Unit
|
|
end,
|
|
Env1 = bind_funs([{"state", State},
|
|
{"put", {type_sig, [stateful | Ann], none, [], [State], Unit}}], Env),
|
|
|
|
case lookup_type(Env, {id, Ann, "event"}) of
|
|
{E, _} ->
|
|
%% We bind Chain.event in a local 'Chain' namespace.
|
|
Event = {qid, Ann, E},
|
|
pop_scope(
|
|
bind_fun("event", {fun_t, Ann, [], [Event], Unit},
|
|
push_scope(namespace, {con, Ann, "Chain"}, Env1)));
|
|
false -> Env1
|
|
end.
|
|
|
|
%-spec bind_fields_append(env(), #{ name() => aeso_syntax:decl() }, type(), [aeso_syntax:field_t()]) -> env().
|
|
bind_fields_append(Env, _TypeMap, _, []) -> Env;
|
|
bind_fields_append(Env, TypeMap, RecTy, [{field_t, Ann, Id, Type} | Fields]) ->
|
|
Env1 = bind_field_append(name(Id), #field_info{ ann = Ann, kind = record, field_t = Type, record_t = RecTy }, Env),
|
|
bind_fields_append(Env1, TypeMap, RecTy, Fields).
|
|
|
|
-spec bind_field_append(name(), field_info(), env()) -> env().
|
|
bind_field_append(X, Info, Env = #env{ fields = Fields }) ->
|
|
Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields),
|
|
Env#env{ fields = Fields1 }.
|
|
|
|
-spec bind_field_update(name(), field_info(), env()) -> env().
|
|
bind_field_update(X, Info, Env = #env{ fields = Fields }) ->
|
|
Fields1 = maps:update_with(X, fun([_ | Infos]) -> [Info | Infos]; ([]) -> [Info] end, [Info], Fields),
|
|
Env#env{ fields = Fields1 }.
|
|
|
|
-spec bind_fields([{name(), field_info()}], typed | untyped, env()) -> env().
|
|
bind_fields([], _Typing, Env) -> Env;
|
|
bind_fields([{Id, Info} | Rest], Typing, Env) ->
|
|
NewEnv = case Typing of
|
|
untyped -> bind_field_append(Id, Info, Env);
|
|
typed -> bind_field_update(Id, Info, Env)
|
|
end,
|
|
bind_fields(Rest, Typing, NewEnv).
|
|
|
|
%% Contract entrypoints take three named arguments
|
|
%% gas : int = Call.gas_left()
|
|
%% value : int = 0
|
|
%% protected : bool = false
|
|
contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
|
Id = fun(X) -> {id, Ann, X} end,
|
|
Int = Id("int"),
|
|
Typed = fun(E, T) -> {typed, Ann, E, T} end,
|
|
Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end,
|
|
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
|
|
{fun_t, Ann, [], [], Int}),
|
|
[]}, Int)),
|
|
Named("value", Typed({int, Ann, 0}, Int)),
|
|
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
|
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
|
|
|
-spec bind_contract(typed | untyped, aeso_syntax:decl(), env()) -> env().
|
|
bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
|
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
|
Key = name(Id),
|
|
Sys = [{origin, system}],
|
|
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
|
|
Fields =
|
|
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
|
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
|
|
[ {field_t, AnnF, Entrypoint,
|
|
contract_call_type(
|
|
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
|
}
|
|
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
|
Name =/= "init"
|
|
] ++
|
|
%% Predefined fields
|
|
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++
|
|
[ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME},
|
|
contract_call_type(
|
|
case [ [TypeOrFresh(Arg) || Arg <- Args]
|
|
|| {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents,
|
|
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
|
++ [ Args
|
|
|| {fun_decl, AnnF, {id, _, "init"}, {fun_t, _, _, Args, _}} <- Contents,
|
|
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
|
++ [ Args
|
|
|| {fun_decl, AnnF, {id, _, "init"}, {type_sig, _, _, _, Args, _}} <- Contents,
|
|
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
|
of
|
|
[] -> {fun_t, [stateful,payable|Sys], [], [], {id, Sys, "void"}};
|
|
[Args] -> {fun_t, [stateful,payable|Sys], [], Args, {id, Sys, "void"}}
|
|
end
|
|
)
|
|
}
|
|
],
|
|
FieldInfo = [ {Entrypoint, #field_info{ ann = FieldAnn,
|
|
kind = contract,
|
|
field_t = Type,
|
|
record_t = Id }}
|
|
|| {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ],
|
|
bind_type(Key, Ann, {[], {contract_t, Fields}},
|
|
bind_fields(FieldInfo, Typing, Env)).
|
|
|
|
%% What scopes could a given name come from?
|
|
-spec possible_scopes(env(), qname()) -> [qname()].
|
|
possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, Name) ->
|
|
Qual = lists:droplast(Name),
|
|
NewQuals = case lists:filter(fun(X) -> element(2, X) == Qual end, UsedNamespaces) of
|
|
[] ->
|
|
[Qual];
|
|
Namespaces ->
|
|
lists:map(fun(X) -> element(1, X) end, Namespaces)
|
|
end,
|
|
Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ],
|
|
Ret2 = [ Namespace ++ Q || {Namespace, none, _} <- UsedNamespaces, Q <- NewQuals ],
|
|
lists:usort(Ret1 ++ Ret2).
|
|
|
|
-spec visible_in_used_namespaces(used_namespaces(), qname()) -> boolean().
|
|
visible_in_used_namespaces(UsedNamespaces, QName) ->
|
|
Qual = lists:droplast(QName),
|
|
Name = lists:last(QName),
|
|
case lists:filter(fun({Ns, _, _}) -> Qual == Ns end, UsedNamespaces) of
|
|
[] ->
|
|
true;
|
|
Namespaces ->
|
|
IsVisible = fun(Namespace) ->
|
|
case Namespace of
|
|
{_, _, {for, Names}} ->
|
|
lists:member(Name, Names);
|
|
{_, _, {hiding, Names}} ->
|
|
not lists:member(Name, Names);
|
|
_ ->
|
|
true
|
|
end
|
|
end,
|
|
lists:any(IsVisible, Namespaces)
|
|
end.
|
|
|
|
-spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}.
|
|
lookup_type(Env, Id) ->
|
|
lookup_env(Env, type, aeso_syntax:get_ann(Id), qname(Id)).
|
|
|
|
-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
|
|
{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, Ann, QName)], Res /= false] of
|
|
[] -> false;
|
|
[Res = {_, {AnnR, _}}] ->
|
|
when_warning(warn_unused_includes,
|
|
fun() ->
|
|
%% If a file is used from a different file, we
|
|
%% can then mark it as used
|
|
F1 = proplists:get_value(file, Ann, no_file),
|
|
F2 = proplists:get_value(file, AnnR, no_file),
|
|
if
|
|
F1 /= F2 ->
|
|
used_include(AnnR);
|
|
true ->
|
|
ok
|
|
end
|
|
end),
|
|
Res;
|
|
Many ->
|
|
type_error({ambiguous_name, qid(Ann, Name), [{qid, A, Q} || {Q, {A, _}} <- Many]}),
|
|
false
|
|
end
|
|
end.
|
|
|
|
-spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info() | type_info()}.
|
|
lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) ->
|
|
Qual = lists:droplast(QName),
|
|
Name = lists:last(QName),
|
|
QNameIsEvent = lists:suffix(["Chain", "event"], QName),
|
|
AllowPrivate = lists:prefix(Qual, Current),
|
|
%% Get the scope
|
|
case maps:get(Qual, Scopes, false) of
|
|
false -> false; %% TODO: return reason for not in scope
|
|
#scope{ funs = Funs, types = Types, consts = Consts, kind = ScopeKind } ->
|
|
Defs = case Kind of
|
|
type -> Types;
|
|
term -> Funs
|
|
end,
|
|
%% Look up the unqualified name
|
|
case proplists:get_value(Name, Defs, false) of
|
|
false ->
|
|
case proplists:get_value(Name, Consts, false) of
|
|
false ->
|
|
false;
|
|
Const when AllowPrivate; ScopeKind == namespace ->
|
|
{QName, Const};
|
|
Const ->
|
|
type_error({contract_treated_as_namespace_constant, Ann, QName}),
|
|
{QName, Const}
|
|
end;
|
|
{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
|
|
{contract_fun, Ann1, Type} when AllowPrivate orelse QNameIsEvent ->
|
|
{QName, {Ann1, Type}};
|
|
{contract_fun, Ann1, Type} ->
|
|
type_error({contract_treated_as_namespace_entrypoint, Ann, QName}),
|
|
{QName, {Ann1, Type}};
|
|
{Ann1, _} = E ->
|
|
%% Check that it's not private (or we can see private funs)
|
|
case not is_private(Ann1) orelse AllowPrivate of
|
|
true ->
|
|
case visible_in_used_namespaces(UsedNamespaces, QName) of
|
|
true -> {QName, E};
|
|
false -> false
|
|
end;
|
|
false -> false
|
|
end
|
|
end
|
|
end.
|
|
|
|
lookup_name(Env, As, Name) ->
|
|
lookup_name(Env, As, Name, []).
|
|
|
|
lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Options) ->
|
|
case lookup_env(Env, term, As, qname(Id)) of
|
|
false ->
|
|
type_error({unbound_variable, Id}),
|
|
{Id, fresh_uvar(As)};
|
|
{QId, {_, Ty}} ->
|
|
%% Variables and functions cannot be used when CurFn is `none`.
|
|
%% i.e. they cannot be used in toplevel constants
|
|
[ begin
|
|
when_warning(
|
|
warn_unused_variables,
|
|
fun() -> used_variable(NS, name(CurFn), QId) end),
|
|
when_warning(
|
|
warn_unused_functions,
|
|
fun() -> register_function_call(NS ++ qname(CurFn), QId) end)
|
|
end || CurFn =/= none ],
|
|
|
|
when_warning(warn_unused_constants, fun() -> used_constant(NS, QId) end),
|
|
|
|
Freshen = proplists:get_value(freshen, Options, false),
|
|
check_stateful(Env, Id, Ty),
|
|
Ty1 = case Ty of
|
|
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty);
|
|
_ when Freshen -> freshen_type(As, Ty);
|
|
_ -> Ty
|
|
end,
|
|
{set_qname(QId, Id), Ty1}
|
|
end.
|
|
|
|
check_stateful(#env{ in_guard = true }, Id, Type = {type_sig, _, _, _, _, _}) ->
|
|
case aeso_syntax:get_ann(stateful, Type, false) of
|
|
false -> ok;
|
|
true ->
|
|
type_error({stateful_not_allowed_in_guards, Id})
|
|
end;
|
|
check_stateful(#env{ stateful = false, current_function = Fun }, Id, Type = {type_sig, _, _, _, _, _}) ->
|
|
case aeso_syntax:get_ann(stateful, Type, false) of
|
|
false -> ok;
|
|
true ->
|
|
type_error({stateful_not_allowed, Id, Fun})
|
|
end;
|
|
check_stateful(#env{ current_function = Fun }, _Id, _Type) ->
|
|
when_warning(warn_unused_stateful, fun() -> used_stateful(Fun) end),
|
|
ok.
|
|
|
|
-spec lookup_record_field(env(), name()) -> [field_info()].
|
|
lookup_record_field(Env, FieldName) ->
|
|
maps:get(FieldName, Env#env.fields, []).
|
|
|
|
%% For 'create' or 'update' constraints we don't consider contract types.
|
|
-spec lookup_record_field(env(), name(), create | project | update) -> [field_info()].
|
|
lookup_record_field(Env, FieldName, Kind) ->
|
|
[ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName),
|
|
Kind == project orelse K /= contract ].
|
|
|
|
lookup_record_field_arity(Env, FieldName, Arity, Kind) ->
|
|
Fields = lookup_record_field(Env, FieldName, Kind),
|
|
[ Fld || Fld = #field_info{ field_t = FldType } <- Fields,
|
|
aeso_tc_type_utils:fun_arity(aeso_tc_type_utils:dereference_deep(FldType)) == Arity ].
|
|
|
|
is_private(Ann) -> proplists:get_value(private, Ann, false).
|
|
|
|
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
|
|
|
init_env() -> init_env([]).
|
|
|
|
init_env(_Options) -> global_env().
|
|
|
|
-spec empty_env() -> env().
|
|
empty_env() -> #env{}.
|
|
|
|
%% Environment containing language primitives
|
|
-spec global_env() -> env().
|
|
global_env() ->
|
|
Ann = [{origin, system}],
|
|
Int = {id, Ann, "int"},
|
|
Char = {id, Ann, "char"},
|
|
Bool = {id, Ann, "bool"},
|
|
String = {id, Ann, "string"},
|
|
Address = {id, Ann, "address"},
|
|
Hash = {id, Ann, "hash"},
|
|
Bits = {id, Ann, "bits"},
|
|
Bytes = fun(Len) -> {bytes_t, Ann, Len} end,
|
|
Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end,
|
|
Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end,
|
|
Unit = {tuple_t, Ann, []},
|
|
List = fun(T) -> {app_t, Ann, {id, Ann, "list"}, [T]} end,
|
|
Option = fun(T) -> {app_t, Ann, {id, Ann, "option"}, [T]} end,
|
|
Map = fun(A, B) -> {app_t, Ann, {id, Ann, "map"}, [A, B]} end,
|
|
Pair = fun(A, B) -> {tuple_t, Ann, [A, B]} end,
|
|
FunC = fun(C, Ts, T) -> {type_sig, Ann, C, [], Ts, T} end,
|
|
FunC1 = fun(C, S, T) -> {type_sig, Ann, C, [], [S], T} end,
|
|
Fun = fun(Ts, T) -> FunC(none, Ts, T) end,
|
|
Fun1 = fun(S, T) -> Fun([S], T) end,
|
|
FunCN = fun(C, Named, Normal, Ret) -> {type_sig, Ann, C, Named, Normal, Ret} end,
|
|
FunN = fun(Named, Normal, Ret) -> FunCN(none, Named, Normal, Ret) end,
|
|
%% Lambda = fun(Ts, T) -> {fun_t, Ann, [], Ts, T} end,
|
|
%% Lambda1 = fun(S, T) -> Lambda([S], T) end,
|
|
StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [], Ts, T} end,
|
|
TVar = fun(X) -> {tvar, Ann, "'" ++ X} end,
|
|
SignId = {id, Ann, "signature"},
|
|
SignDef = {bytes, Ann, <<0:64/unit:8>>},
|
|
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
|
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
|
TTL = {qid, Ann, ["Chain", "ttl"]},
|
|
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
|
AENSName = {qid, Ann, ["AENS", "name"]},
|
|
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
|
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
|
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
|
G1 = {tuple_t, Ann, [Fp, Fp, Fp]},
|
|
G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]},
|
|
GT = {tuple_t, Ann, lists:duplicate(12, Fp)},
|
|
Tx = {qid, Ann, ["Chain", "tx"]},
|
|
GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]},
|
|
BaseTx = {qid, Ann, ["Chain", "base_tx"]},
|
|
PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]},
|
|
|
|
FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end,
|
|
TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)},
|
|
{"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}],
|
|
TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]},
|
|
Stateful = fun(T) -> setelement(2, T, [stateful|element(2, T)]) end,
|
|
|
|
Fee = Int,
|
|
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
|
|
|
MkDefs = fun(Defs) -> [{X, {Ann, if is_integer(T) -> {builtin, T}; true -> T end}} || {X, T} <- Defs] end,
|
|
|
|
TopScope = #scope
|
|
{ funs = MkDefs(
|
|
%% Option constructors
|
|
[{"None", Option(A)},
|
|
{"Some", Fun1(A, Option(A))},
|
|
%% TTL constructors
|
|
{"RelativeTTL", Fun1(Int, TTL)},
|
|
{"FixedTTL", Fun1(Int, TTL)},
|
|
%% Abort/exit
|
|
{"abort", Fun1(String, A)},
|
|
{"exit", Fun1(String, A)},
|
|
{"require", Fun([Bool, String], Unit)}])
|
|
, types = MkDefs(
|
|
[{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0},
|
|
{"void", 0},
|
|
{"unit", {[], {alias_t, Unit}}},
|
|
{"hash", {[], {alias_t, Bytes(32)}}},
|
|
{"signature", {[], {alias_t, Bytes(64)}}},
|
|
{"bits", 0},
|
|
{"option", 1}, {"list", 1}, {"map", 2},
|
|
{"oracle", 2}, {"oracle_query", 2}
|
|
]) },
|
|
|
|
ChainScope = #scope
|
|
{ funs = MkDefs(
|
|
%% Spend transaction.
|
|
[{"spend", StateFun([Address, Int], Unit)},
|
|
%% Chain environment
|
|
{"balance", Fun1(Address, Int)},
|
|
{"block_hash", Fun1(Int, Option(Hash))},
|
|
{"coinbase", Address},
|
|
{"timestamp", Int},
|
|
{"block_height", Int},
|
|
{"difficulty", Int},
|
|
{"gas_limit", Int},
|
|
{"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))},
|
|
{"create", Stateful(
|
|
FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
|
|
], var_args, A))},
|
|
{"clone", Stateful(
|
|
FunN([ {named_arg_t, Ann, {id, Ann, "gas"}, Int,
|
|
{typed, Ann,
|
|
{app, Ann,
|
|
{typed, Ann, {qid, Ann, ["Call","gas_left"]},
|
|
aeso_tc_type_utils:typesig_to_fun_t(Fun([], Int))
|
|
},
|
|
[]}, Int
|
|
}}
|
|
, {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
|
|
, {named_arg_t, Ann, {id, Ann, "protected"}, Bool, {typed, Ann, {bool, Ann, false}, Bool}}
|
|
, {named_arg_t, Ann, {id, Ann, "ref"}, A, undefined}
|
|
], var_args, A))},
|
|
%% Tx constructors
|
|
{"GAMetaTx", Fun([Address, Int], GAMetaTx)},
|
|
{"PayingForTx", Fun([Address, Int], PayForTx)},
|
|
{"SpendTx", Fun([Address, Int, String], BaseTx)},
|
|
{"OracleRegisterTx", BaseTx},
|
|
{"OracleQueryTx", BaseTx},
|
|
{"OracleResponseTx", BaseTx},
|
|
{"OracleExtendTx", BaseTx},
|
|
{"NamePreclaimTx", BaseTx},
|
|
{"NameClaimTx", Fun([String], BaseTx)},
|
|
{"NameUpdateTx", Fun([Hash], BaseTx)},
|
|
{"NameRevokeTx", Fun([Hash], BaseTx)},
|
|
{"NameTransferTx", Fun([Address, Hash], BaseTx)},
|
|
{"ChannelCreateTx", Fun([Address], BaseTx)},
|
|
{"ChannelDepositTx", Fun([Address, Int], BaseTx)},
|
|
{"ChannelWithdrawTx", Fun([Address, Int], BaseTx)},
|
|
{"ChannelForceProgressTx", Fun([Address], BaseTx)},
|
|
{"ChannelCloseMutualTx", Fun([Address], BaseTx)},
|
|
{"ChannelCloseSoloTx", Fun([Address], BaseTx)},
|
|
{"ChannelSlashTx", Fun([Address], BaseTx)},
|
|
{"ChannelSettleTx", Fun([Address], BaseTx)},
|
|
{"ChannelSnapshotSoloTx", Fun([Address], BaseTx)},
|
|
{"ContractCreateTx", Fun([Int], BaseTx)},
|
|
{"ContractCallTx", Fun([Address, Int], BaseTx)},
|
|
{"GAAttachTx", BaseTx}
|
|
])
|
|
, types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}},
|
|
{"base_tx", 0},
|
|
{"paying_for_tx", 0}, {"ga_meta_tx", 0}]) },
|
|
|
|
ContractScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"address", Address},
|
|
{"creator", Address},
|
|
{"balance", Int}]) },
|
|
|
|
CallScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"origin", Address},
|
|
{"caller", Address},
|
|
{"value", Int},
|
|
{"gas_price", Int},
|
|
{"fee", Int},
|
|
{"gas_left", Fun([], Int)}])
|
|
},
|
|
|
|
OracleScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
|
{"expiry", Fun([Oracle(Q, R)], Fee)},
|
|
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
|
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
|
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
|
{"respond", SignFun([Oracle(Q, R), Query(Q, R), R], Unit)},
|
|
{"extend", SignFun([Oracle(Q, R), TTL], Unit)},
|
|
{"get_answer", Fun([Oracle(Q, R), Query(Q, R)], option_t(Ann, R))},
|
|
{"check", Fun([Oracle(Q, R)], Bool)},
|
|
{"check_query", Fun([Oracle(Q,R), Query(Q, R)], Bool)}]) },
|
|
|
|
AENSScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"resolve", Fun([String, String], option_t(Ann, A))},
|
|
{"preclaim", SignFun([Address, Hash], Unit)},
|
|
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
|
{"transfer", SignFun([Address, Address, String], Unit)},
|
|
{"revoke", SignFun([Address, String], Unit)},
|
|
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
|
|
{"lookup", Fun([String], option_t(Ann, AENSName))},
|
|
%% AENS pointee constructors
|
|
{"AccountPt", Fun1(Address, Pointee)},
|
|
{"OraclePt", Fun1(Address, Pointee)},
|
|
{"ContractPt", Fun1(Address, Pointee)},
|
|
{"ChannelPt", Fun1(Address, Pointee)},
|
|
%% Name object constructor
|
|
{"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)}
|
|
])
|
|
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
|
|
|
MapScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"from_list", Fun1(List(Pair(K, V)), Map(K, V))},
|
|
{"to_list", Fun1(Map(K, V), List(Pair(K, V)))},
|
|
{"lookup", Fun([K, Map(K, V)], Option(V))},
|
|
{"lookup_default", Fun([K, Map(K, V), V], V)},
|
|
{"delete", Fun([K, Map(K, V)], Map(K, V))},
|
|
{"member", Fun([K, Map(K, V)], Bool)},
|
|
{"size", Fun1(Map(K, V), Int)}]) },
|
|
|
|
%% Crypto/Curve operations
|
|
CryptoScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"verify_sig", Fun([Hash, Address, SignId], Bool)},
|
|
{"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)},
|
|
{"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)},
|
|
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
|
{"sha3", Fun1(A, Hash)},
|
|
{"sha256", Fun1(A, Hash)},
|
|
{"blake2b", Fun1(A, Hash)}]) },
|
|
|
|
%% Fancy BLS12-381 crypto operations
|
|
MCL_BLS12_381_Scope = #scope
|
|
{ funs = MkDefs(
|
|
[{"g1_neg", Fun1(G1, G1)},
|
|
{"g1_norm", Fun1(G1, G1)},
|
|
{"g1_valid", Fun1(G1, Bool)},
|
|
{"g1_is_zero", Fun1(G1, Bool)},
|
|
{"g1_add", Fun ([G1, G1], G1)},
|
|
{"g1_mul", Fun ([Fr, G1], G1)},
|
|
|
|
{"g2_neg", Fun1(G2, G2)},
|
|
{"g2_norm", Fun1(G2, G2)},
|
|
{"g2_valid", Fun1(G2, Bool)},
|
|
{"g2_is_zero", Fun1(G2, Bool)},
|
|
{"g2_add", Fun ([G2, G2], G2)},
|
|
{"g2_mul", Fun ([Fr, G2], G2)},
|
|
|
|
{"gt_inv", Fun1(GT, GT)},
|
|
{"gt_add", Fun ([GT, GT], GT)},
|
|
{"gt_mul", Fun ([GT, GT], GT)},
|
|
{"gt_pow", Fun ([GT, Fr], GT)},
|
|
{"gt_is_one", Fun1(GT, Bool)},
|
|
{"pairing", Fun ([G1, G2], GT)},
|
|
{"miller_loop", Fun ([G1, G2], GT)},
|
|
{"final_exp", Fun1(GT, GT)},
|
|
|
|
{"int_to_fr", Fun1(Int, Fr)},
|
|
{"int_to_fp", Fun1(Int, Fp)},
|
|
{"fr_to_int", Fun1(Fr, Int)},
|
|
{"fp_to_int", Fun1(Fp, Int)}
|
|
]),
|
|
types = MkDefs(
|
|
[{"fr", 0}, {"fp", 0}]) },
|
|
|
|
%% Authentication
|
|
AuthScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"tx_hash", Option(Hash)},
|
|
{"tx", Option(Tx)} ]) },
|
|
|
|
%% Strings
|
|
StringScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"length", Fun1(String, Int)},
|
|
{"concat", Fun([String, String], String)},
|
|
{"to_list", Fun1(String, List(Char))},
|
|
{"from_list", Fun1(List(Char), String)},
|
|
{"to_upper", Fun1(String, String)},
|
|
{"to_lower", Fun1(String, String)},
|
|
{"sha3", Fun1(String, Hash)},
|
|
{"sha256", Fun1(String, Hash)},
|
|
{"blake2b", Fun1(String, Hash)}
|
|
]) },
|
|
|
|
%% Chars
|
|
CharScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"to_int", Fun1(Char, Int)},
|
|
{"from_int", Fun1(Int, Option(Char))}]) },
|
|
|
|
%% Bits
|
|
BitsScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"set", Fun([Bits, Int], Bits)},
|
|
{"clear", Fun([Bits, Int], Bits)},
|
|
{"test", Fun([Bits, Int], Bool)},
|
|
{"sum", Fun1(Bits, Int)},
|
|
{"intersection", Fun([Bits, Bits], Bits)},
|
|
{"union", Fun([Bits, Bits], Bits)},
|
|
{"difference", Fun([Bits, Bits], Bits)},
|
|
{"none", Bits},
|
|
{"all", Bits}]) },
|
|
|
|
%% Bytes
|
|
BytesScope = #scope
|
|
{ funs = MkDefs(
|
|
[{"to_int", Fun1(Bytes(any), Int)},
|
|
{"to_str", Fun1(Bytes(any), String)},
|
|
{"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))},
|
|
{"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))}
|
|
]) },
|
|
|
|
%% Conversion
|
|
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
|
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
|
{"to_contract", FunC(address_to_contract, [Address], A)},
|
|
{"is_oracle", Fun1(Address, Bool)},
|
|
{"is_contract", Fun1(Address, Bool)},
|
|
{"is_payable", Fun1(Address, Bool)}]) },
|
|
|
|
|
|
#env{ scopes =
|
|
#{ [] => TopScope
|
|
, ["Chain"] => ChainScope
|
|
, ["Contract"] => ContractScope
|
|
, ["Call"] => CallScope
|
|
, ["Oracle"] => OracleScope
|
|
, ["AENS"] => AENSScope
|
|
, ["Map"] => MapScope
|
|
, ["Auth"] => AuthScope
|
|
, ["Crypto"] => CryptoScope
|
|
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
|
|
, ["StringInternal"] => StringScope
|
|
, ["Char"] => CharScope
|
|
, ["Bits"] => BitsScope
|
|
, ["Bytes"] => BytesScope
|
|
, ["Int"] => IntScope
|
|
, ["Address"] => AddressScope
|
|
}
|
|
, fields =
|
|
maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]}
|
|
|| {N, T} <- TxFlds ])
|
|
}.
|