Skip to content

Commit 197c319

Browse files
committed
idl/gen_client: Don't specify argument values when they're equal to defaults
This enables client.ml to skip specifying an arbitrary number of rightmost arguments if they're all equal to their default values (since arguments are positional, once an argument is not skipped, no arguments to its left can be skipped). Generated code for e.g. host.disable looks like the following: let session_id = rpc_of_ref_session session_id in let host = rpc_of_ref_host host in let auto_enable = rpc_of_bool auto_enable in let needed_args, _ = List.fold_right2 (fun param default (acc, skipped)-> (* Since arguments are positional, we can only skip specifying an argument that's equal to its default value if all the arguments to its right were also not specified *) if skipped then (match default with | Some default_value when param = default_value -> (acc, true) | _ -> (param::acc, false)) else (param :: acc, false) ) [ session_id; host; auto_enable ] [ None; None; Some (Rpc.Bool true) ] ([], true) in rpc_wrapper rpc "host.disable" needed_args >>= fun x -> return (ignore x) This fixes an issue with client.ml always specifying values for new parameters that older server.ml did not know about (which happens during an RPU). Fixes: cf5be62 ("host.disable: Add auto_enabled parameter for persistency") Signed-off-by: Andrii Sultanov <[email protected]>
1 parent 0849336 commit 197c319

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

ocaml/idl/ocaml_backend/gen_client.ml

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,27 +92,34 @@ let ctor_fields (obj : obj) =
9292
(function {DT.qualifier= DT.StaticRO | DT.RW; _} -> true | _ -> false)
9393
(DU.fields_of_obj obj)
9494

95-
(* Compute a message parameter list from a message suitable for the client (only!) *)
96-
let args_of_message ?(expand_record = true) (obj : obj)
95+
(* Compute a list of message parameters and their default values from a
96+
message suitable for the client (only!) *)
97+
let args_of_message_with_default ?(expand_record = true) (obj : obj)
9798
({msg_tag= tag; _} as msg) =
9899
let arg_of_param = function
99-
| {param_type= Record x; _} -> (
100+
| {param_type= Record x; param_default= default; _} -> (
100101
match tag with
101102
| FromObject Make ->
102103
if x <> obj.DT.name then failwith "args_of_message" ;
103104
if expand_record then
104-
List.map param_of_field (ctor_fields obj)
105+
List.map
106+
(fun x -> (x, None))
107+
(List.map param_of_field (ctor_fields obj))
105108
else
106-
[custom _value (Record x)]
109+
[(custom _value (Record x), default)]
107110
| _ ->
108111
failwith "arg_of_param: encountered a Record in an unexpected place"
109112
)
110113
| p ->
111-
[of_param p]
114+
[(of_param p, p.param_default)]
112115
in
113-
let session = if msg.msg_session then [session] else [] in
116+
let session = if msg.msg_session then [(session, None)] else [] in
114117
List.concat (session :: List.map arg_of_param msg.msg_params)
115118

119+
(* Compute a message parameter list from a message suitable for the client (only!) *)
120+
let args_of_message ?(expand_record = true) obj x =
121+
List.map fst (args_of_message_with_default ~expand_record obj x)
122+
116123
let gen_module api : O.Module.t =
117124
(* Generate any additional helper functions for an operation here *)
118125
let helper_record_constructor ~sync (obj : obj) (x : message) =
@@ -148,7 +155,8 @@ let gen_module api : O.Module.t =
148155
in
149156
(* Convert an operation into a Let-binding *)
150157
let operation ~sync (obj : obj) (x : message) =
151-
let args = args_of_message obj x in
158+
let args_with_default = args_of_message_with_default obj x in
159+
let args = List.map fst args_with_default in
152160
let to_rpc (arg : O.param) =
153161
let binding = O.string_of_param arg in
154162
let converter = O.type_of_param arg in
@@ -172,6 +180,31 @@ let gen_module api : O.Module.t =
172180
else
173181
List.map O.string_of_param args
174182
in
183+
let defaults =
184+
List.map
185+
(fun (_, default_value) ->
186+
match default_value with
187+
| Some x ->
188+
Printf.sprintf "Some (%s)" (Datamodel_values.to_ocaml_string x)
189+
| None ->
190+
"None"
191+
)
192+
args_with_default
193+
in
194+
let rightmost_arg_default =
195+
Some true
196+
= List.fold_right
197+
(fun (_, x) rightmost_arg_default ->
198+
match rightmost_arg_default with
199+
| None when Option.is_some x ->
200+
Some true
201+
| Some true ->
202+
Some true
203+
| _ ->
204+
Some false
205+
)
206+
args_with_default None
207+
in
175208
let task = DT.Ref Datamodel_common._task in
176209
let from_xmlrpc t =
177210
match (x.msg_custom_marshaller, t, sync) with
@@ -203,15 +236,41 @@ let gen_module api : O.Module.t =
203236
(List.map to_rpc args
204237
@ [
205238
(if is_ctor then ctor_record else "")
239+
; ( if (not is_ctor) && rightmost_arg_default then
240+
(* Skip specifying arguments which are equal to their default
241+
values. This way, when a newer client talks to an older
242+
server that does not know about a new parameter, it can
243+
silently skip sending it, avoiding an error *)
244+
Printf.sprintf
245+
{|
246+
let needed_args, _ = List.fold_right2
247+
(fun param default (acc, skipped)->
248+
(* Since arguments are positional, we can only skip specifying an
249+
argument that's equal to its default value if all the arguments to
250+
its right were also not specified *)
251+
if skipped then
252+
(match default with
253+
| Some default_value when param = default_value -> (acc, true)
254+
| _ -> (param::acc, false))
255+
else
256+
(param :: acc, false)
257+
) [ %s ] [ %s ] ([], true)
258+
in
259+
|}
260+
(String.concat "; " rpc_args)
261+
(String.concat "; " defaults)
262+
else
263+
Printf.sprintf "let needed_args = [ %s ] in"
264+
(String.concat "; " rpc_args)
265+
)
206266
; Printf.sprintf
207-
"rpc_wrapper rpc %s [ %s ] >>= fun x -> return (%s x)"
267+
"rpc_wrapper rpc %s needed_args >>= fun x -> return (%s x)"
208268
( if sync then
209269
Printf.sprintf "\"%s\"" wire_name
210270
else
211271
Printf.sprintf {|(Printf.sprintf "%%s%s" AQ.async_qualifier)|}
212272
wire_name
213273
)
214-
(String.concat "; " rpc_args)
215274
(from_xmlrpc x.msg_result)
216275
]
217276
)
@@ -227,9 +286,6 @@ let gen_module api : O.Module.t =
227286
obj.messages
228287
in
229288
let fields = fields_of (operations @ helpers) in
230-
(*
231-
let fields = List.map (fun x -> O.Module.Let (operation ~sync obj x)) obj.messages in
232-
*)
233289
O.Module.make ~name:(OU.ocaml_of_obj_name obj.DT.name) ~elements:fields ()
234290
in
235291
let preamble =

quality-gate.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ verify-cert () {
2525
}
2626

2727
mli-files () {
28-
N=462
28+
N=461
2929
X="ocaml/tests"
3030
X+="|ocaml/quicktest"
3131
X+="|ocaml/message-switch/core_test"

0 commit comments

Comments
 (0)