Merge branch '105-on-failwith-and-using-format-json-ligo-return-exit-code-1-and-a-malformed-json-on-stderr' into 'dev'

Resolve "on failwith and using --format json ligo return exit code 1 and a malformed json on stderr"

Closes #105

See merge request ligolang/ligo!337
This commit is contained in:
Rémi Lesenechal 2020-01-17 16:37:03 +00:00
commit 481801ea91
7 changed files with 98 additions and 79 deletions

View File

@ -2,6 +2,10 @@
## [Unreleased] ## [Unreleased]
## [Failwith do not fail](https://gitlab.com/ligolang/ligo/merge_requests/337) - 2020-01-17
### Added
- running failing code in `ligo interpret`, `ligo dry-run`, `ligo run-function` will no longer be an error (return value : 0)
## [1899dfe8d7285580b3aa30fab933ed589f8f1bc5] - 2020-01-08 ## [1899dfe8d7285580b3aa30fab933ed589f8f1bc5] - 2020-01-08
### Added ### Added
- Partial application and OCaml-like currying behavior to CameLIGO & ReasonLIGO - Partial application and OCaml-like currying behavior to CameLIGO & ReasonLIGO

View File

@ -202,8 +202,13 @@ let interpret =
let%bind mini_c_exp = Compile.Of_typed.compile_expression typed_exp in let%bind mini_c_exp = Compile.Of_typed.compile_expression typed_exp in
let%bind compiled_exp = Compile.Of_mini_c.aggregate_and_compile_expression decl_list mini_c_exp in let%bind compiled_exp = Compile.Of_mini_c.aggregate_and_compile_expression decl_list mini_c_exp in
let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in
let%bind value = Run.run ~options compiled_exp.expr compiled_exp.expr_ty in let%bind runres = Run.run_expression ~options compiled_exp.expr compiled_exp.expr_ty in
let%bind simplified_output = Uncompile.uncompile_expression typed_exp.type_annotation value in match runres with
| Fail fail_res ->
let%bind failstring = Run.failwith_to_string fail_res in
ok @@ Format.asprintf "%s" failstring
| Success value' ->
let%bind simplified_output = Uncompile.uncompile_expression typed_exp.type_annotation value' in
ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output
in in
let term = let term =
@ -262,8 +267,12 @@ let dry_run =
let%bind args_michelson = Run.evaluate_expression compiled_params.expr compiled_params.expr_ty in let%bind args_michelson = Run.evaluate_expression compiled_params.expr compiled_params.expr_ty in
let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in
let%bind michelson_output = Run.run_contract ~options michelson_prg.expr michelson_prg.expr_ty args_michelson in let%bind runres = Run.run_contract ~options michelson_prg.expr michelson_prg.expr_ty args_michelson in
match runres with
| Fail fail_res ->
let%bind failstring = Run.failwith_to_string fail_res in
ok @@ Format.asprintf "%s" failstring
| Success michelson_output ->
let%bind simplified_output = Uncompile.uncompile_typed_program_entry_function_result typed_prg entry_point michelson_output in let%bind simplified_output = Uncompile.uncompile_typed_program_entry_function_result typed_prg entry_point michelson_output in
ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output
in in
@ -289,7 +298,12 @@ let run_function =
let%bind michelson = Compile.Of_mini_c.aggregate_and_compile_expression mini_c_prg compiled_applied in let%bind michelson = Compile.Of_mini_c.aggregate_and_compile_expression mini_c_prg compiled_applied in
let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in
let%bind michelson_output = Run.run ~options michelson.expr michelson.expr_ty in let%bind runres = Run.run_expression ~options michelson.expr michelson.expr_ty in
match runres with
| Fail fail_res ->
let%bind failstring = Run.failwith_to_string fail_res in
ok @@ Format.asprintf "%s" failstring
| Success michelson_output ->
let%bind simplified_output = Uncompile.uncompile_typed_program_entry_function_result typed_prg entry_point michelson_output in let%bind simplified_output = Uncompile.uncompile_typed_program_entry_function_result typed_prg entry_point michelson_output in
ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output
in in
@ -308,8 +322,8 @@ let evaluate_value =
let%bind (exp,_) = Mini_c.get_entry mini_c entry_point in let%bind (exp,_) = Mini_c.get_entry mini_c entry_point in
let%bind compiled = Compile.Of_mini_c.aggregate_and_compile_expression mini_c exp in let%bind compiled = Compile.Of_mini_c.aggregate_and_compile_expression mini_c exp in
let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in
let%bind michelson_output = Run.run ~options compiled.expr compiled.expr_ty in let%bind michelson_output = Run.run_no_failwith ~options compiled.expr compiled.expr_ty in
let%bind simplified_output = Uncompile.uncompile_typed_program_entry_expression_result typed_prg entry_point michelson_output in let%bind simplified_output = Uncompile.uncompile_typed_program_entry_function_result typed_prg entry_point michelson_output in
ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output ok @@ Format.asprintf "%a\n" Ast_simplified.PP.expression simplified_output
in in
let term = let term =

View File

@ -8,7 +8,7 @@ let error_suggest: string = "\n If you're not sure how to fix this error, you ca
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/ * Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt * Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new * Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog'" * Check the changelog by running 'ligo changelog'\n"
let toplevel ~(display_format : display_format) (x : string result) : unit Term.ret = let toplevel ~(display_format : display_format) (x : string result) : unit Term.ret =
match x with match x with

View File

@ -964,18 +964,14 @@ let%expect_test _ =
* Check the changelog by running 'ligo changelog' |}] * Check the changelog by running 'ligo changelog' |}]
let%expect_test _ = let%expect_test _ =
run_ligo_bad [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ] ; run_ligo_good [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ] ;
[%expect {| [%expect {|
ligo: Execution failed: {"value":"some_string","type":"string"} failwith("some_string") |}]
let%expect_test _ =
If you're not sure how to fix this error, you can run_ligo_good [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ; "--format=json" ] ;
do one of the following: [%expect {|
{"status":"ok","content":"failwith(\"some_string\")"} |}]
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}]
let%expect_test _ = let%expect_test _ =
run_ligo_bad [ "compile-contract" ; contract "bad_address_format.religo" ; "main" ] ; run_ligo_bad [ "compile-contract" ; contract "bad_address_format.religo" ; "main" ] ;
@ -1024,15 +1020,7 @@ let%expect_test _ =
let%expect_test _ = let%expect_test _ =
(* TODO should not be bad? *) (* TODO should not be bad? *)
run_ligo_bad [ "dry-run" ; contract "subtle_nontail_fail.mligo" ; "main" ; "()" ; "()" ] ; run_ligo_good [ "dry-run" ; contract "subtle_nontail_fail.mligo" ; "main" ; "()" ; "()" ] ;
[%expect {| [%expect {|
ligo: error of execution failwith("This contract always fails") |}]
If you're not sure how to fix this error, you can
do one of the following:
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}]

View File

@ -57,14 +57,14 @@ let result_pp_hr f out (r : _ result) =
| Ok (s , _) -> Format.fprintf out "%a" f s | Ok (s , _) -> Format.fprintf out "%a" f s
| Error e -> Format.fprintf out "%a" (error_pp ~dev:false) (e ()) | Error e -> Format.fprintf out "%a" (error_pp ~dev:false) (e ())
let string_result_pp_hr = result_pp_hr (fun out s -> Format.fprintf out "%s" s) let string_result_pp_hr = result_pp_hr (fun out s -> Format.fprintf out "%s\n" s)
let result_pp_dev f out (r : _ result) = let result_pp_dev f out (r : _ result) =
match r with match r with
| Ok (s , _) -> Format.fprintf out "%a" f s | Ok (s , _) -> Format.fprintf out "%a" f s
| Error e -> Format.fprintf out "%a" (error_pp ~dev:true) (e ()) | Error e -> Format.fprintf out "%a" (error_pp ~dev:true) (e ())
let string_result_pp_dev = result_pp_hr (fun out s -> Format.fprintf out "%s" s) let string_result_pp_dev = result_pp_hr (fun out s -> Format.fprintf out "%s\n" s)
let json_pp out x = Format.fprintf out "%s" (J.to_string x) let json_pp out x = Format.fprintf out "%s" (J.to_string x)
@ -75,10 +75,10 @@ let string_result_pp_json out (r : string result) =
]) in ]) in
match r with match r with
| Ok (x , _) -> ( | Ok (x , _) -> (
Format.fprintf out "%a" json_pp (status_json "ok" (`String x)) Format.fprintf out "%a\n" json_pp (status_json "ok" (`String x))
) )
| Error e -> ( | Error e -> (
Format.fprintf out "%a" json_pp (status_json "error" (e ())) Format.fprintf out "%a\n" json_pp (status_json "error" (e ()))
) )
type display_format = [ type display_format = [

View File

@ -9,32 +9,40 @@ module Errors = struct
let message () = "only bytes, string or int are printable" in let message () = "only bytes, string or int are printable" in
error title message error title message
let failwith data_str type_str () = let failwith str () =
let title () = "Execution failed" in let title () = "Execution failed" in
let message () = "" in let message () = "" in
let data = [ let data = [
("value" , fun () -> Format.asprintf "%s" data_str); ("value" , fun () -> Format.asprintf "%s" str);
("type" , fun () -> Format.asprintf "%s" type_str);
] in ] in
error ~data title message error ~data title message
end end
type options = Memory_proto_alpha.options
type run_res = type options = Memory_proto_alpha.options
| Success of ex_typed_value
| Fail of Memory_proto_alpha.Protocol.Script_repr.expr
type run_failwith_res = type run_failwith_res =
| Failwith_int of int | Failwith_int of int
| Failwith_string of string | Failwith_string of string
| Failwith_bytes of bytes | Failwith_bytes of bytes
type run_res =
| Success of ex_typed_value
| Fail of run_failwith_res
type dry_run_options = type dry_run_options =
{ amount : string ; { amount : string ;
predecessor_timestamp : string option ; predecessor_timestamp : string option ;
sender : string option ; sender : string option ;
source : string option } source : string option }
let failwith_to_string (f:run_failwith_res) : string result =
let%bind str = match f with
| Failwith_int i -> ok @@ string_of_int i
| Failwith_string s -> ok @@ Format.asprintf "\"%s\"" (String.escaped s)
| Failwith_bytes b ->
ok @@ Format.asprintf "0X%a" Hex.pp (Hex.of_bytes b) in
ok @@ Format.asprintf "failwith(%s)" str
let make_dry_run_options (opts : dry_run_options) : options result = let make_dry_run_options (opts : dry_run_options) : options result =
let open Proto_alpha_utils.Trace in let open Proto_alpha_utils.Trace in
let open Proto_alpha_utils.Memory_proto_alpha in let open Proto_alpha_utils.Memory_proto_alpha in
@ -88,7 +96,7 @@ let fetch_lambda_types (contract_ty:ex_ty) =
| Ex_ty (Lambda_t (in_ty, out_ty, _)) -> ok (Ex_ty in_ty, Ex_ty out_ty) | Ex_ty (Lambda_t (in_ty, out_ty, _)) -> ok (Ex_ty in_ty, Ex_ty out_ty)
| _ -> simple_fail "failed to fetch lambda types" | _ -> simple_fail "failed to fetch lambda types"
let run_contract ?options (exp:Michelson.t) (exp_type:ex_ty) (input_michelson:Michelson.t) : ex_typed_value result = let run_contract ?options (exp:Michelson.t) (exp_type:ex_ty) (input_michelson:Michelson.t) : run_res result =
let open! Tezos_raw_protocol_005_PsBabyM1 in let open! Tezos_raw_protocol_005_PsBabyM1 in
let%bind (Ex_ty input_ty, Ex_ty output_ty) = fetch_lambda_types exp_type in let%bind (Ex_ty input_ty, Ex_ty output_ty) = fetch_lambda_types exp_type in
let%bind input = let%bind input =
@ -105,11 +113,18 @@ let run_contract ?options (exp:Michelson.t) (exp_type:ex_ty) (input_michelson:Mi
Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@ Trace.trace_tzresult_lwt (simple_error "error parsing program code") @@
Memory_proto_alpha.parse_michelson_fail ~top_level exp ty_stack_before ty_stack_after in Memory_proto_alpha.parse_michelson_fail ~top_level exp ty_stack_before ty_stack_after in
let open! Memory_proto_alpha.Protocol.Script_interpreter in let open! Memory_proto_alpha.Protocol.Script_interpreter in
let%bind (Item(output, Empty)) = let%bind res =
Trace.trace_tzresult_lwt (simple_error "error of execution") @@ Trace.trace_tzresult_lwt (simple_error "error of execution") @@
Memory_proto_alpha.interpret ?options descr Memory_proto_alpha.failure_interpret ?options descr (Item(input, Empty)) in
(Item(input, Empty)) in match res with
ok (Ex_typed_value (output_ty, output)) | Memory_proto_alpha.Succeed stack ->
let (Item(output, Empty)) = stack in
ok @@ Success (Ex_typed_value (output_ty, output))
| Memory_proto_alpha.Fail expr -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims expr with
| Int (_ , i) -> ok @@ Fail (Failwith_int (Z.to_int i))
| String (_ , s) -> ok @@ Fail (Failwith_string s)
| Bytes (_, s) -> ok @@ Fail (Failwith_bytes s)
| _ -> fail @@ Errors.unknown_failwith_type () )
let run_expression ?options (exp:Michelson.t) (exp_type:ex_ty) : run_res result = let run_expression ?options (exp:Michelson.t) (exp_type:ex_ty) : run_res result =
let open! Tezos_raw_protocol_005_PsBabyM1 in let open! Tezos_raw_protocol_005_PsBabyM1 in
@ -129,30 +144,28 @@ let run_expression ?options (exp:Michelson.t) (exp_type:ex_ty) : run_res result
| Memory_proto_alpha.Succeed stack -> | Memory_proto_alpha.Succeed stack ->
let (Item(output, Empty)) = stack in let (Item(output, Empty)) = stack in
ok @@ Success (Ex_typed_value (exp_type', output)) ok @@ Success (Ex_typed_value (exp_type', output))
| Memory_proto_alpha.Fail expr -> | Memory_proto_alpha.Fail expr -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims expr with
ok (Fail expr) | Int (_ , i) -> ok @@ Fail (Failwith_int (Z.to_int i))
| String (_ , s) -> ok @@ Fail (Failwith_string s)
let run ?options (exp:Michelson.t) (exp_type:ex_ty) : ex_typed_value result = | Bytes (_, s) -> ok @@ Fail (Failwith_bytes s)
let%bind expr = run_expression ?options exp exp_type in
match expr with
| Success res -> ok res
| Fail res -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims res with
| Int (_ , i) -> fail @@ Errors.failwith (Z.to_string i) "int" ()
| String (_ , s) -> fail @@ Errors.failwith s "string" ()
| Bytes (_, s) -> fail @@ Errors.failwith (Bytes.to_string s) "bytes" ()
| _ -> fail @@ Errors.unknown_failwith_type () ) | _ -> fail @@ Errors.unknown_failwith_type () )
let run_failwith ?options (exp:Michelson.t) (exp_type:ex_ty) : run_failwith_res result = let run_failwith ?options (exp:Michelson.t) (exp_type:ex_ty) : run_failwith_res result =
let%bind expr = run_expression ?options exp exp_type in let%bind expr = run_expression ?options exp exp_type in
match expr with match expr with
| Fail res -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims res with | Success _ -> simple_fail "An error of execution was expected"
| Int (_ , i) -> ok (Failwith_int (Z.to_int i)) | Fail res -> ok res
| String (_ , s) -> ok (Failwith_string s)
| Bytes (_, b) -> ok (Failwith_bytes b) let run_no_failwith ?options (exp:Michelson.t) (exp_type:ex_ty) : ex_typed_value result =
| _ -> simple_fail "Unknown failwith type" ) let%bind expr = run_expression ?options exp exp_type in
| _ -> simple_fail "An error of execution was expected" match expr with
| Success tval -> ok tval
| Fail _ -> simple_fail "Unexpected error of execution"
let evaluate_expression ?options exp exp_type = let evaluate_expression ?options exp exp_type =
let%bind etv = run ?options exp exp_type in let%bind etv = run_expression ?options exp exp_type in
ex_value_ty_to_michelson etv match etv with
| Success etv' -> ex_value_ty_to_michelson etv'
| Fail res ->
let%bind str = failwith_to_string res in
fail @@ Errors.failwith str ()

View File

@ -93,7 +93,7 @@ let run_typed_program_with_simplified_input ?options
(program: Ast_typed.program) (entry_point: string) (program: Ast_typed.program) (entry_point: string)
(input: Ast_simplified.expression) : Ast_simplified.expression result = (input: Ast_simplified.expression) : Ast_simplified.expression result =
let%bind michelson_program = typed_program_with_simplified_input_to_michelson program entry_point input in let%bind michelson_program = typed_program_with_simplified_input_to_michelson program entry_point input in
let%bind michelson_output = Ligo.Run.Of_michelson.run ?options michelson_program.expr michelson_program.expr_ty in let%bind michelson_output = Ligo.Run.Of_michelson.run_no_failwith ?options michelson_program.expr michelson_program.expr_ty in
Uncompile.uncompile_typed_program_entry_function_result program entry_point michelson_output Uncompile.uncompile_typed_program_entry_function_result program entry_point michelson_output
let expect ?options program entry_point input expecter = let expect ?options program entry_point input expecter =
@ -147,7 +147,7 @@ let expect_evaluate program entry_point expecter =
let%bind mini_c = Ligo.Compile.Of_typed.compile program in let%bind mini_c = Ligo.Compile.Of_typed.compile program in
let%bind (exp,_) = Mini_c.get_entry mini_c entry_point in let%bind (exp,_) = Mini_c.get_entry mini_c entry_point in
let%bind michelson_value = Ligo.Compile.Of_mini_c.aggregate_and_compile_expression mini_c exp in let%bind michelson_value = Ligo.Compile.Of_mini_c.aggregate_and_compile_expression mini_c exp in
let%bind res_michelson = Ligo.Run.Of_michelson.run michelson_value.expr michelson_value.expr_ty in let%bind res_michelson = Ligo.Run.Of_michelson.run_no_failwith michelson_value.expr michelson_value.expr_ty in
let%bind res_simpl = Uncompile.uncompile_typed_program_entry_expression_result program entry_point res_michelson in let%bind res_simpl = Uncompile.uncompile_typed_program_entry_expression_result program entry_point res_michelson in
expecter res_simpl expecter res_simpl