diff --git a/rebar.config b/rebar.config index 15fb233..b101255 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, "29b5ee3"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref, "7dd9c29"}}} , {getopt, "1.0.1"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}} diff --git a/rebar.lock b/rebar.lock index 4d651bc..dcdbc25 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"29b5ee3e68086e0f0170d3c70e92bbfa210cbae6"}}, + {ref,"7dd9c29cc075b52c8f966696e88c8a29fc296240"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index dc94a09..d296f1a 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -465,7 +465,7 @@ ast_body({app, _, {typed, _, {proj, _, {typed, _, Addr, {con, _, Contract}}, {id Gas = proplists:get_value("gas", ArgOpts ++ Defaults), Value = proplists:get_value("value", ArgOpts ++ Defaults), OutType = ast_typerep(OutT, Icode), - <> = aeb_abi:function_type_hash(list_to_binary(FunName), ArgType, OutType), + <> = aeb_aevm_abi:function_type_hash(list_to_binary(FunName), ArgType, OutType), %% The function is represented by its type hash (which includes the name) Fun = #integer{value = TypeHash}, #prim_call_contract{ @@ -679,7 +679,7 @@ prim_call(Prim, Amount, Args, ArgTypes, OutType) -> true -> PrimBin = binary:encode_unsigned(Prim), ArgType = {tuple, ArgTypes}, - <> = aeb_abi:function_type_hash(PrimBin, ArgType, OutType), + <> = aeb_aevm_abi:function_type_hash(PrimBin, ArgType, OutType), TH; false -> 0 diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index dbcfa0c..4ab53a8 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -12,7 +12,8 @@ , file/2 , from_string/2 , check_call/4 - , create_calldata/3 + , create_calldata/3 %% deprecated + , create_calldata/4 , version/0 , sophia_type_to_typerep/1 , to_sophia_value/4 @@ -32,6 +33,7 @@ | pp_icode | pp_assembler | pp_bytecode + | {backend, aevm | fate} | {include, {file_system, [string()]} | {explicit_files, #{string() => binary()}}} | {src_file, string()}. @@ -137,6 +139,18 @@ string_to_icode(ContractString, Options) -> type_env => TypeEnv, icode => Icode }. +-spec string_to_fcode(string(), [option()]) -> map(). +string_to_fcode(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]), + pp_typed_ast(TypedAst, Options), + Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options), + #{ typed_ast => TypedAst, + type_env => TypeEnv, + fcode => Fcode }. + join_errors(Prefix, Errors, Pfun) -> Ess = [ Pfun(E) || E <- Errors ], list_to_binary(string:join([Prefix|Ess], "\n")). @@ -150,14 +164,15 @@ join_errors(Prefix, Errors, Pfun) -> %% terms for the arguments. %% NOTE: Special treatment for "init" since it might be implicit and has %% a special return type (typerep, T) --spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]} | {error, term()} +-spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]} + | {ok, string(), [term()]} + | {error, term()} when Type :: term(). check_call(Source, "init" = FunName, Args, Options) -> - PatchFun = fun(T) -> {tuple, [typerep, T]} end, - case check_call(Source, FunName, Args, Options, PatchFun) of + case check_call1(Source, FunName, Args, Options) of Err = {error, _} when Args == [] -> %% Try with default init-function - case check_call(insert_init_function(Source, Options), FunName, Args, Options, PatchFun) of + case check_call1(insert_init_function(Source, Options), FunName, Args, Options) of {error, _} -> Err; %% The first error is most likely better... Res -> Res end; @@ -165,11 +180,12 @@ check_call(Source, "init" = FunName, Args, Options) -> Res end; check_call(Source, FunName, Args, Options) -> - PatchFun = fun(T) -> T end, - check_call(Source, FunName, Args, Options, PatchFun). + check_call1(Source, FunName, Args, Options). -check_call(ContractString0, FunName, Args, Options, PatchFun) -> +check_call1(ContractString0, FunName, Args, Options) -> try + case proplists:get_value(backend, Options, aevm) of + aevm -> %% First check the contract without the __call function #{} = string_to_icode(ContractString0, Options), ContractString = insert_call_function(ContractString0, FunName, Args, Options), @@ -185,7 +201,25 @@ check_call(ContractString0, FunName, Args, Options, PatchFun) -> ArgIcode = get_arg_icode(Funs), ArgTerms = [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ], - {ok, FunName, {ArgVMTypes, PatchFun(RetVMType)}, ArgTerms} + RetVMType1 = + case FunName of + "init" -> {tuple, [typerep, RetVMType]}; + _ -> RetVMType + end, + {ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms}; + fate -> + %% First check the contract without the __call function + #{fcode := OrgFcode} = string_to_fcode(ContractString0, Options), + FateCode = aeso_fcode_to_fate:compile(OrgFcode, []), + _SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), + %% TODO collect all hashes and compute the first name without hash collision to + %% be used as __callX + %% case aeb_fate_code:symbol_identifier(<<"__call">>) of + ContractString = insert_call_function(ContractString0, FunName, Args, Options), + #{fcode := Fcode} = string_to_fcode(ContractString, Options), + #{args := CallArgs} = maps:get({entrypoint, <<"__call">>}, maps:get(functions, Fcode)), + {ok, FunName, CallArgs} + end catch error:{parse_errors, Errors} -> {error, join_errors("Parse errors", Errors, fun (E) -> E end)}; @@ -199,6 +233,7 @@ check_call(ContractString0, FunName, Args, Options, PatchFun) -> fun (E) -> io_lib:format("~p", [E]) end)} end. + %% Add the __call function to a contract. -spec insert_call_function(string(), string(), [string()], options()) -> string(). insert_call_function(Code, FunName, Args, Options) -> @@ -337,10 +372,26 @@ translate_vm_value(_VmType, _Type, _Data) -> {ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()} | {error, term()}. create_calldata(Code, Fun, Args) -> - case check_call(Code, Fun, Args, []) of + create_calldata(Code, Fun, Args, [{backend, aevm}]). + +-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) -> + {ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()} + | {ok, binary()} + | {error, term()}. +create_calldata(Code, Fun, Args, Options) -> + case proplists:get_value(backend, Options, aevm) of + aevm -> + case check_call(Code, Fun, Args, Options) of {ok, FunName, {ArgTypes, RetType}, VMArgs} -> - aeb_abi:create_calldata(FunName, VMArgs, ArgTypes, RetType); + aeb_aevm_abi:create_calldata(FunName, VMArgs, ArgTypes, RetType); {error, _} = Err -> Err + end; + fate -> + case check_call(Code, Fun, Args, Options) of + {ok, FunName, FateArgs} -> + aeb_fate_abi:create_calldata(FunName, FateArgs); + {error, _} = Err -> Err + end end. -spec decode_calldata(string(), string(), binary()) -> @@ -467,7 +518,7 @@ to_bytecode([], _) -> []. extract_type_info(#{functions := Functions} =_Icode) -> ArgTypesOnly = fun(As) -> [ T || {_, T} <- As ] end, - TypeInfo = [aeb_abi:function_type_info(list_to_binary(lists:last(Name)), + TypeInfo = [aeb_aevm_abi:function_type_info(list_to_binary(lists:last(Name)), ArgTypesOnly(Args), TypeRep) || {Name, Attrs, Args,_Body, TypeRep} <- Functions, not is_tuple(Name), @@ -537,4 +588,3 @@ pos_error({no_file, Line, Pos}) -> pos_error({Line, Pos}); pos_error({File, Line, Pos}) -> io_lib:format("file ~s, line ~p, column ~p", [File, Line, Pos]). - diff --git a/src/aeso_icode_to_asm.erl b/src/aeso_icode_to_asm.erl index 1bf18a2..b9e6042 100644 --- a/src/aeso_icode_to_asm.erl +++ b/src/aeso_icode_to_asm.erl @@ -105,7 +105,7 @@ make_args(Args) -> fun_hash({FName, _, Args, _, TypeRep}) -> ArgType = {tuple, [T || {_, T} <- Args]}, - <> = aeb_abi:function_type_hash(list_to_binary(lists:last(FName)), ArgType, TypeRep), + <> = aeb_aevm_abi:function_type_hash(list_to_binary(lists:last(FName)), ArgType, TypeRep), {integer, Hash}. %% Expects two return addresses below N elements on the stack. Picks the top