Provide the ACI along with the bytecode #771
@ -14,6 +14,8 @@
|
|||||||
, contract_interface/2
|
, contract_interface/2
|
||||||
, contract_interface/3
|
, contract_interface/3
|
||||||
|
|
||||||
|
, from_typed_ast/2
|
||||||
|
|
||||||
, render_aci_json/1
|
, render_aci_json/1
|
||||||
|
|
||||||
, json_encode_expr/1
|
, json_encode_expr/1
|
||||||
@ -23,6 +25,8 @@
|
|||||||
-type json() :: jsx:json_term().
|
-type json() :: jsx:json_term().
|
||||||
-type json_text() :: binary().
|
-type json_text() :: binary().
|
||||||
|
|
||||||
|
-export_type([aci_type/0]).
|
||||||
|
|
||||||
%% External API
|
%% External API
|
||||||
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
|
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
|
||||||
file(Type, File) ->
|
file(Type, File) ->
|
||||||
@ -65,16 +69,18 @@ do_contract_interface(Type, ContractString, Options) ->
|
|||||||
try
|
try
|
||||||
Ast = aeso_compiler:parse(ContractString, Options),
|
Ast = aeso_compiler:parse(ContractString, Options),
|
||||||
%% io:format("~p\n", [Ast]),
|
%% io:format("~p\n", [Ast]),
|
||||||
TypedAst = aeso_ast_infer_types:infer(Ast, [dont_unfold]),
|
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold]),
|
||||||
%% io:format("~p\n", [TypedAst]),
|
%% io:format("~p\n", [TypedAst]),
|
||||||
JArray = [ encode_contract(C) || C <- TypedAst ],
|
from_typed_ast(Type, TypedAst)
|
||||||
|
catch
|
||||||
|
throw:{error, Errors} -> {error, Errors}
|
||||||
|
end.
|
||||||
|
|
||||||
|
from_typed_ast(Type, TypedAst) ->
|
||||||
|
JArray = [ encode_contract(C) || C <- TypedAst ],
|
||||||
case Type of
|
case Type of
|
||||||
json -> {ok, JArray};
|
json -> {ok, JArray};
|
||||||
string -> do_render_aci_json(JArray)
|
string -> do_render_aci_json(JArray)
|
||||||
end
|
|
||||||
catch
|
|
||||||
throw:{error, Errors} -> {error, Errors}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
||||||
|
@ -553,7 +553,7 @@ global_env() ->
|
|||||||
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
||||||
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
||||||
|
|
||||||
-spec infer(aeso_syntax:ast()) -> aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
|
-spec infer(aeso_syntax:ast()) -> {aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}.
|
||||||
infer(Contracts) ->
|
infer(Contracts) ->
|
||||||
infer(Contracts, []).
|
infer(Contracts, []).
|
||||||
|
|
||||||
@ -563,7 +563,7 @@ infer(Contracts) ->
|
|||||||
init_env(_Options) -> global_env().
|
init_env(_Options) -> global_env().
|
||||||
|
|
||||||
-spec infer(aeso_syntax:ast(), list(option())) ->
|
-spec infer(aeso_syntax:ast(), list(option())) ->
|
||||||
aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
|
{aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}.
|
||||||
infer([], Options) ->
|
infer([], Options) ->
|
||||||
create_type_errors(),
|
create_type_errors(),
|
||||||
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
|
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
|
||||||
@ -576,15 +576,15 @@ infer(Contracts, Options) ->
|
|||||||
ets_new(type_vars, [set]),
|
ets_new(type_vars, [set]),
|
||||||
check_modifiers(Env, Contracts),
|
check_modifiers(Env, Contracts),
|
||||||
{Env1, Decls} = infer1(Env, Contracts, [], Options),
|
{Env1, Decls} = infer1(Env, Contracts, [], Options),
|
||||||
{Env2, Decls2} =
|
{Env2, DeclsFolded, DeclsUnfolded} =
|
||||||
case proplists:get_value(dont_unfold, Options, false) of
|
case proplists:get_value(dont_unfold, Options, false) of
|
||||||
true -> {Env1, Decls};
|
true -> {Env1, Decls, Decls};
|
||||||
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
|
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
|
||||||
{E, unfold_record_types(E, Decls)}
|
{E, Decls, unfold_record_types(E, Decls)}
|
||||||
end,
|
end,
|
||||||
case proplists:get_value(return_env, Options, false) of
|
case proplists:get_value(return_env, Options, false) of
|
||||||
false -> Decls2;
|
false -> {DeclsFolded, DeclsUnfolded};
|
||||||
true -> {Env2, Decls2}
|
true -> {Env2, DeclsFolded, DeclsUnfolded}
|
||||||
end
|
end
|
||||||
after
|
after
|
||||||
clean_up_ets()
|
clean_up_ets()
|
||||||
|
@ -42,7 +42,8 @@
|
|||||||
| {backend, aevm | fate}
|
| {backend, aevm | fate}
|
||||||
| {include, {file_system, [string()]} |
|
| {include, {file_system, [string()]} |
|
||||||
{explicit_files, #{string() => binary()}}}
|
{explicit_files, #{string() => binary()}}}
|
||||||
| {src_file, string()}.
|
| {src_file, string()}
|
||||||
|
| {aci, aeso_aci:aci_type()}.
|
||||||
-type options() :: [option()].
|
-type options() :: [option()].
|
||||||
|
|
||||||
-export_type([ option/0
|
-export_type([ option/0
|
||||||
@ -116,7 +117,8 @@ from_string(Backend, ContractString, Options) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
from_string1(aevm, ContractString, Options) ->
|
from_string1(aevm, ContractString, Options) ->
|
||||||
#{icode := Icode} = string_to_code(ContractString, Options),
|
#{ icode := Icode
|
||||||
|
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
||||||
TypeInfo = extract_type_info(Icode),
|
TypeInfo = extract_type_info(Icode),
|
||||||
Assembler = assemble(Icode, Options),
|
Assembler = assemble(Icode, Options),
|
||||||
pp_assembler(aevm, Assembler, Options),
|
pp_assembler(aevm, Assembler, Options),
|
||||||
@ -124,47 +126,61 @@ from_string1(aevm, ContractString, Options) ->
|
|||||||
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
||||||
pp_bytecode(ByteCode, Options),
|
pp_bytecode(ByteCode, Options),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
{ok, #{byte_code => ByteCode,
|
Res = #{byte_code => ByteCode,
|
||||||
compiler_version => Version,
|
compiler_version => Version,
|
||||||
contract_source => ContractString,
|
contract_source => ContractString,
|
||||||
type_info => TypeInfo,
|
type_info => TypeInfo,
|
||||||
abi_version => aeb_aevm_abi:abi_version(),
|
abi_version => aeb_aevm_abi:abi_version(),
|
||||||
payable => maps:get(payable, Icode)
|
payable => maps:get(payable, Icode)
|
||||||
}};
|
},
|
||||||
|
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
|
||||||
from_string1(fate, ContractString, Options) ->
|
from_string1(fate, ContractString, Options) ->
|
||||||
#{fcode := FCode} = string_to_code(ContractString, Options),
|
#{ fcode := FCode
|
||||||
|
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
|
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
|
||||||
pp_assembler(fate, FateCode, Options),
|
pp_assembler(fate, FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
{ok, #{byte_code => ByteCode,
|
Res = #{byte_code => ByteCode,
|
||||||
compiler_version => Version,
|
compiler_version => Version,
|
||||||
contract_source => ContractString,
|
contract_source => ContractString,
|
||||||
type_info => [],
|
type_info => [],
|
||||||
fate_code => FateCode,
|
fate_code => FateCode,
|
||||||
abi_version => aeb_fate_abi:abi_version(),
|
abi_version => aeb_fate_abi:abi_version(),
|
||||||
payable => maps:get(payable, FCode)
|
payable => maps:get(payable, FCode)
|
||||||
}}.
|
},
|
||||||
|
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
||||||
|
|
||||||
|
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||||
|
case proplists:get_value(aci, Options) of
|
||||||
|
undefined ->
|
||||||
|
Result;
|
||||||
|
Type ->
|
||||||
|
{ok, Aci} = aeso_aci:from_typed_ast(Type, FoldedTypedAst),
|
||||||
|
maps:put(aci, Aci, Result)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec string_to_code(string(), options()) -> map().
|
-spec string_to_code(string(), options()) -> map().
|
||||||
string_to_code(ContractString, Options) ->
|
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 | Options]),
|
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||||
pp_typed_ast(TypedAst, Options),
|
pp_typed_ast(UnfoldedTypedAst, Options),
|
||||||
case proplists:get_value(backend, Options, aevm) of
|
case proplists:get_value(backend, Options, aevm) of
|
||||||
aevm ->
|
aevm ->
|
||||||
Icode = ast_to_icode(TypedAst, Options),
|
Icode = ast_to_icode(UnfoldedTypedAst, Options),
|
||||||
pp_icode(Icode, Options),
|
pp_icode(Icode, Options),
|
||||||
#{ icode => Icode
|
#{ icode => Icode
|
||||||
, typed_ast => TypedAst
|
, unfolded_typed_ast => UnfoldedTypedAst
|
||||||
|
, folded_typed_ast => FoldedTypedAst
|
||||||
, type_env => TypeEnv
|
, type_env => TypeEnv
|
||||||
, ast => Ast };
|
, ast => Ast };
|
||||||
fate ->
|
fate ->
|
||||||
Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options),
|
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
|
||||||
#{ fcode => Fcode
|
#{ fcode => Fcode
|
||||||
, typed_ast => TypedAst
|
, unfolded_typed_ast => UnfoldedTypedAst
|
||||||
|
, folded_typed_ast => FoldedTypedAst
|
||||||
, type_env => TypeEnv
|
, type_env => TypeEnv
|
||||||
, ast => Ast }
|
, ast => Ast }
|
||||||
end.
|
end.
|
||||||
@ -203,7 +219,7 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
%% First check the contract without the __call function
|
%% First check the contract without the __call function
|
||||||
#{ast := Ast} = string_to_code(ContractString0, Options),
|
#{ast := Ast} = string_to_code(ContractString0, Options),
|
||||||
ContractString = insert_call_function(Ast, ContractString0, ?CALL_NAME, FunName, Args),
|
ContractString = insert_call_function(Ast, ContractString0, ?CALL_NAME, FunName, Args),
|
||||||
#{typed_ast := TypedAst,
|
#{unfolded_typed_ast := TypedAst,
|
||||||
icode := Icode} = string_to_code(ContractString, Options),
|
icode := Icode} = string_to_code(ContractString, Options),
|
||||||
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
||||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
||||||
@ -313,7 +329,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
Code = string_to_code(ContractString, Options),
|
||||||
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||||
|
|
||||||
@ -389,7 +405,7 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
Code = string_to_code(ContractString, Options),
|
||||||
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||||
|
|
||||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||||
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||||
|
@ -11,7 +11,10 @@ test_contract(N) ->
|
|||||||
{Contract,MapACI,DecACI} = test_cases(N),
|
{Contract,MapACI,DecACI} = test_cases(N),
|
||||||
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
||||||
?assertEqual([MapACI], JSON),
|
?assertEqual([MapACI], JSON),
|
||||||
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)).
|
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)),
|
||||||
|
%% Check if the compiler provides correct aci
|
||||||
|
{ok,#{aci := JSON2}} = aeso_compiler:from_string(Contract, [{aci, json}]),
|
||||||
|
?assertEqual(JSON, JSON2).
|
||||||
|
|
||||||
test_cases(1) ->
|
test_cases(1) ->
|
||||||
Contract = <<"payable contract C =\n"
|
Contract = <<"payable contract C =\n"
|
||||||
@ -84,7 +87,7 @@ test_cases(3) ->
|
|||||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||||
{Contract,MapACI,DecACI}.
|
{Contract,MapACI,DecACI}.
|
||||||
|
|
||||||
%% Rounttrip
|
%% Roundtrip
|
||||||
aci_test_() ->
|
aci_test_() ->
|
||||||
[{"Testing ACI generation for " ++ ContractName,
|
[{"Testing ACI generation for " ++ ContractName,
|
||||||
fun() -> aci_test_contract(ContractName) end}
|
fun() -> aci_test_contract(ContractName) end}
|
||||||
@ -96,6 +99,8 @@ aci_test_contract(Name) ->
|
|||||||
String = aeso_test_utils:read_contract(Name),
|
String = aeso_test_utils:read_contract(Name),
|
||||||
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||||
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
||||||
|
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
|
||||||
|
?assertEqual(JSON, JSON1),
|
||||||
|
|
||||||
io:format("JSON:\n~p\n", [JSON]),
|
io:format("JSON:\n~p\n", [JSON]),
|
||||||
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
||||||
@ -122,4 +127,3 @@ check_stub(Stub, Options) ->
|
|||||||
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
|
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
|
||||||
error({parse_errors, Errs})
|
error({parse_errors, Errs})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user