1%%% Copyright (C) 2017 Tomas Abrahamsson 2%%% 3%%% Author: Tomas Abrahamsson <tab@lysator.liu.se> 4%%% 5%%% This library is free software; you can redistribute it and/or 6%%% modify it under the terms of the GNU Lesser General Public 7%%% License as published by the Free Software Foundation; either 8%%% version 2.1 of the License, or (at your option) any later version. 9%%% 10%%% This library is distributed in the hope that it will be useful, 11%%% but WITHOUT ANY WARRANTY; without even the implied warranty of 12%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13%%% Lesser General Public License for more details. 14%%% 15%%% You should have received a copy of the GNU Lesser General Public 16%%% License along with this library; if not, write to the Free Software 17%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18%%% MA 02110-1301 USA 19 20%%% @doc Generation of message verifiers 21%%% @private 22 23-module(gpb_gen_verifiers). 24 25-export([format_verifiers_top_function/3]). 26-export([format_verifiers/3]). 27 28-include("../include/gpb.hrl"). 29-include("gpb_codegen.hrl"). 30-include("gpb_compile.hrl"). 31 32-import(gpb_lib, [replace_term/2, replace_tree/2, 33 splice_trees/2, repeat_clauses/2]). 34 35format_verifiers_top_function(Defs, AnRes, Opts) -> 36 case {gpb_lib:contains_messages(Defs), 37 gpb_lib:get_records_or_maps_by_opts(Opts)} of 38 {false, records} -> format_verifiers_top_no_msgs_r(); 39 {false, maps} -> format_verifiers_top_no_msgs_m(); 40 {true, _} -> format_verifiers_top_with_msgs(Defs, AnRes, Opts) 41 end. 42 43format_verifiers_top_no_msgs_r() -> 44 [?f("-spec verify_msg(_) -> no_return().~n", []), 45 gpb_codegen:format_fn( 46 verify_msg, 47 fun(Msg) -> call_self(Msg, []) end), 48 ?f("-spec verify_msg(_,_) -> no_return().~n", []), 49 gpb_codegen:format_fn( 50 verify_msg, 51 fun(Msg,_OptsOrMsgName) -> 52 mk_type_error(not_a_known_message, Msg, []) 53 end), 54 "\n", 55 ?f("-spec verify_msg(_,_,_) -> no_return().~n", []), 56 gpb_codegen:format_fn( 57 verify_msg, 58 fun(Msg,_MsgName,_Opts) -> 59 mk_type_error(not_a_known_message, Msg, []) 60 end), 61 "\n"]. 62 63format_verifiers_top_no_msgs_m() -> 64 [?f("-spec verify_msg(_,_) -> no_return().~n", []), 65 gpb_codegen:format_fn( 66 verify_msg, 67 fun(Msg, MsgName) -> call_self(Msg, MsgName, []) end), 68 ?f("-spec verify_msg(_,_,_) -> no_return().~n", []), 69 gpb_codegen:format_fn( 70 verify_msg, 71 fun(Msg, _MsgName, _Opts) -> 72 mk_type_error(not_a_known_message, Msg, []) 73 end), 74 "\n"]. 75 76format_verifiers_top_with_msgs(Defs, AnRes, Opts) -> 77 Mapping = gpb_lib:get_records_or_maps_by_opts(Opts), 78 [[gpb_codegen:format_fn( 79 verify_msg, 80 fun(Msg) when tuple_size(Msg) >= 1 -> 81 verify_msg(Msg, element(1, Msg), []); 82 (X) -> 83 mk_type_error(not_a_known_message, X, []) 84 end) || Mapping == records], 85 gpb_codegen:format_fn( 86 verify_msg, 87 fun(Msg, MsgName) when is_atom(MsgName) -> 88 call_self(Msg, MsgName, []); 89 ('Msg', Opts) when tuple_size('Msg') >= 1 -> 90 call_self('Msg', element(1,'Msg'), Opts); 91 ('X', _Opts) -> 92 mk_type_error(not_a_known_message, 'X', []) 93 end, 94 [repeat_clauses('Msg', [[replace_tree('Msg', ?expr(Msg))] 95 || Mapping == records]), 96 repeat_clauses('X', [[replace_tree('X', ?expr(X))] 97 || Mapping == records])]), 98 gpb_codegen:format_fn( 99 verify_msg, 100 fun(Msg, MsgName, Opts) -> 101 TrUserData = proplists:get_value(user_data, Opts), 102 case MsgName of 103 '<msg-name-match>' -> 104 '<verify-fn>'(Msg, [MsgName], TrUserData); 105 _ -> 106 mk_type_error(not_a_known_message, Msg, []) 107 end 108 end, 109 [repeat_clauses( 110 '<msg-name-match>', 111 [begin 112 DefaultVerifierFn = gpb_lib:mk_fn(v_msg_, MsgName), 113 ElemPath = [MsgName], 114 MVerifierFn = case gpb_gen_translators:has_translation( 115 ElemPath, verify, AnRes) of 116 {true, Transl} -> Transl; 117 false -> DefaultVerifierFn 118 end, 119 [replace_term('<msg-name-match>', MsgName), 120 replace_term('<verify-fn>', MVerifierFn), 121 replace_term('<MsgName>', MsgName)] 122 end 123 || {{msg, MsgName}, _MsgDef} <- Defs])])]. 124 125format_verifiers(Defs, AnRes, Opts) -> 126 [format_msg_verifiers(Defs, AnRes, Opts), 127 format_enum_verifiers(Defs, AnRes, Opts), 128 format_type_verifiers(AnRes, Opts), 129 format_map_verifiers(AnRes, Opts), 130 format_verifier_auxiliaries(Defs, Opts) 131 ]. 132 133format_msg_verifiers(Defs, AnRes, Opts) -> 134 [format_msg_verifier(MsgName, MsgDef, AnRes, Opts) 135 || {_Type, MsgName, MsgDef} <- gpb_lib:msgs_or_groups(Defs)]. 136 137format_msg_verifier(MsgName, MsgDef, AnRes, Opts) -> 138 FNames = gpb_lib:get_field_names(MsgDef), 139 FVars = [gpb_lib:var_f_n(I) || I <- lists:seq(1, length(FNames))], 140 MsgVar = ?expr(M), 141 {FieldMatching, NonOptKeys} = 142 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 143 records -> 144 {gpb_lib:mapping_match(MsgName, lists:zip(FNames, FVars), Opts), 145 FNames}; 146 #maps{unset_optional=present_undefined} -> 147 {gpb_lib:mapping_match(MsgName, lists:zip(FNames, FVars), Opts), 148 FNames}; 149 #maps{unset_optional=omitted} -> 150 FMap = gpb_lib:zip_for_non_opt_fields(MsgDef, FVars), 151 {?expr('mapmatch' = 'M', 152 [replace_tree('mapmatch', gpb_lib:map_match(FMap, Opts)), 153 replace_tree('M', MsgVar)]), 154 [K || {K, _} <- FMap]} 155 end, 156 ExtraneousFieldsChecks = 157 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 158 records -> 159 []; 160 #maps{unset_optional=present_undefined} -> 161 []; 162 #maps{unset_optional=omitted}=Mapping -> 163 Names = case Mapping of 164 #maps{oneof=tuples} -> 165 FNames; 166 #maps{oneof=flat} -> 167 field_names_oneofs_expanded(MsgDef) 168 end, 169 [?expr(lists:foreach( 170 fun('<Key>') -> 171 ok; 172 (OtherKey) -> 173 mk_type_error({extraneous_key, OtherKey}, 174 'M', Path) 175 end, 176 maps:keys('M')), 177 [repeat_clauses('<Key>', 178 [[replace_term('<Key>', Key)] 179 || Key <- Names]), 180 replace_tree('M', MsgVar)])] 181 end, 182 183 FnName = gpb_lib:mk_fn(v_msg_, MsgName), 184 TrUserDataVar = ?expr(TrUserData), 185 [gpb_lib:nowarn_unused_function(FnName, 3), 186 gpb_lib:nowarn_dialyzer_attr(FnName,3,Opts), 187 gpb_codegen:format_fn( 188 FnName, 189 fun('<msg-match>', '<Path>', 'TrUserData') -> 190 '<verify-fields>', 191 '<maybe-verify-no-extraneous-fields>', 192 ok; 193 ('<M>', Path, _TrUserData) when is_map('<M>') -> 194 mk_type_error( 195 {missing_fields, 'NonOptKeys'--maps:keys('<M>'), '<MsgName>'}, 196 '<M>', Path); 197 (X, Path, _TrUserData) -> 198 mk_type_error({expected_msg,'<MsgName>'}, X, Path) 199 end, 200 [replace_tree('<msg-match>', FieldMatching), 201 replace_tree('<Path>', if MsgDef == [], ExtraneousFieldsChecks == [] -> 202 ?expr(_Path); 203 true -> 204 ?expr(Path) 205 end), 206 replace_tree('TrUserData', if FNames /= [] -> TrUserDataVar; 207 FNames =:= [] -> ?expr(_) 208 end), 209 splice_trees('<verify-fields>', 210 field_verifiers(MsgName, MsgDef, FVars, MsgVar, 211 TrUserDataVar, 212 AnRes, Opts)), 213 splice_trees('<maybe-verify-no-extraneous-fields>', 214 ExtraneousFieldsChecks), 215 repeat_clauses('<M>', 216 case gpb_lib:get_records_or_maps_by_opts(Opts) of 217 records -> 218 []; % omit this clause 219 maps -> 220 [[replace_tree('<M>', ?expr(M)), 221 replace_term('NonOptKeys', NonOptKeys)]] 222 end), 223 replace_term('<MsgName>', MsgName)])]. 224 225field_names_oneofs_expanded(MsgDef) -> 226 gpb_lib:fold_msgdef_fields( 227 fun(#?gpb_field{name=FName}, Acc) -> [FName | Acc] end, 228 [], 229 MsgDef). 230 231field_verifiers(MsgName, Fields, FVars, MsgVar, TrUserDataVar, AnRes, Opts) -> 232 [field_verifier(MsgName, Field, FVar, MsgVar, TrUserDataVar, AnRes, Opts) 233 || {Field, FVar} <- lists:zip(Fields, FVars)]. 234 235field_verifier(MsgName, 236 #?gpb_field{name=FName, type=Type, occurrence=Occurrence}=Field, 237 FVar, MsgVar, TrUserDataVar, AnRes, Opts) -> 238 FVerifierFn = 239 case Type of 240 {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName); 241 {group,GName} -> gpb_lib:mk_fn(v_msg_, GName); 242 {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName); 243 {map,KT,VT} -> gpb_lib:mk_fn(v_, gpb_lib:map_type_to_msg_name( 244 KT,VT)); 245 Type -> gpb_lib:mk_fn(v_type_, Type) 246 end, 247 ElemPath = gpb_gen_translators:mk_elempath_elem(MsgName, Field, false), 248 FVerifierFn2 = gpb_gen_translators:find_translation(ElemPath, verify, 249 AnRes, FVerifierFn), 250 BReplacements = [replace_tree('<F>', FVar), 251 replace_term('<FName>', FName), 252 replace_term('<Type>', Type), 253 replace_tree('TrUserData', TrUserDataVar)], 254 Replacements = [replace_term('<verify-fn>', FVerifierFn2) 255 | BReplacements], 256 IsMapField = case Type of 257 {map,_,_} -> true; 258 _ -> false 259 end, 260 MElemPath = [MsgName, FName], 261 MTransl = gpb_gen_translators:has_translation(MElemPath, verify, AnRes), 262 HasTranslation = case MTransl of 263 {true, _} -> true; 264 false -> false 265 end, 266 case Occurrence of 267 required -> 268 %% FIXME: check especially for `undefined' 269 %% and if found, error out with required_field_not_set 270 %% specifying expected type 271 ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 'TrUserData'), 272 Replacements); 273 repeated when not IsMapField, HasTranslation -> 274 {true, RFVerifierFn} = MTransl, 275 RReplacements = [replace_term('<verify-fn>', RFVerifierFn) 276 | BReplacements], 277 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 278 records -> 279 ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 280 'TrUserData'), 281 RReplacements); 282 #maps{unset_optional=present_undefined} -> 283 ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 284 'TrUserData'), 285 RReplacements); 286 #maps{unset_optional=omitted} -> 287 ?expr(case 'M' of 288 '#{<FName> := <F>}' -> 289 '<verify-fn>'('<F>', ['<FName>' | Path], 290 'TrUserData'); 291 _ -> 292 ok 293 end, 294 [replace_tree('#{<FName> := <F>}', 295 gpb_lib:map_match([{FName, FVar}], 296 Opts)), 297 replace_tree('M', MsgVar) | RReplacements]) 298 end; 299 repeated when not IsMapField -> 300 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 301 records -> 302 ?expr(if is_list('<F>') -> 303 %% _ = [...] to avoid dialyzer error 304 %% "Expression produces a value of type 305 %% ['ok'], but this value is unmatched" 306 %% with the -Wunmatched_returns flag. 307 _ = ['<verify-fn>'(Elem, ['<FName>' | Path], 308 'TrUserData') 309 || Elem <- '<F>'], 310 ok; 311 true -> 312 mk_type_error( 313 {invalid_list_of, '<Type>'}, 314 '<F>', 315 ['<FName>' | Path]) 316 end, 317 Replacements); 318 #maps{unset_optional=present_undefined} -> 319 ?expr(if is_list('<F>') -> 320 %% _ = [...] to avoid dialyzer error 321 %% "Expression produces a value of type 322 %% ['ok'], but this value is unmatched" 323 %% with the -Wunmatched_returns flag. 324 _ = ['<verify-fn>'(Elem, ['<FName>' | Path], 325 'TrUserData') 326 || Elem <- '<F>'], 327 ok; 328 true -> 329 mk_type_error( 330 {invalid_list_of, '<Type>'}, 331 '<F>', 332 ['<FName>' | Path]) 333 end, 334 Replacements); 335 #maps{unset_optional=omitted} -> 336 ?expr(case 'M' of 337 '#{<FName> := <F>}' -> 338 if is_list('<F>') -> 339 %% _ = [...] to avoid dialyzer error 340 %% "Expression produces a value of type 341 %% ['ok'], but this value is unmatched" 342 %% with the -Wunmatched_returns flag. 343 _ = ['<verify-fn>'(Elem, ['<FName>' | Path], 344 'TrUserData') 345 || Elem <- '<F>'], 346 ok; 347 true -> 348 mk_type_error( 349 {invalid_list_of, '<Type>'}, 350 '<F>', 351 ['<FName>' | Path]) 352 end; 353 _ -> ok 354 end, 355 [replace_tree('#{<FName> := <F>}', 356 gpb_lib:map_match([{FName, FVar}], 357 Opts)), 358 replace_tree('M', MsgVar) | Replacements]) 359 end; 360 repeated when IsMapField -> 361 MFVerifierFn = gpb_gen_translators:find_translation( 362 MElemPath, verify, AnRes, FVerifierFn), 363 MReplacements = [replace_term('<verify-fn>', MFVerifierFn) 364 | BReplacements], 365 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 366 records -> 367 ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 368 'TrUserData'), 369 MReplacements); 370 #maps{unset_optional=present_undefined} -> 371 ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 372 'TrUserData'), 373 MReplacements); 374 #maps{unset_optional=omitted} -> 375 ?expr(case 'M' of 376 '#{<FName> := <F>}' -> 377 '<verify-fn>'('<F>', ['<FName>' | Path], 378 'TrUserData'); 379 _ -> 380 ok 381 end, 382 [replace_tree('#{<FName> := <F>}', 383 gpb_lib:map_match([{FName, FVar}], 384 Opts)), 385 replace_tree('M', MsgVar) | MReplacements]) 386 end; 387 optional -> 388 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 389 records -> 390 ?expr(if '<F>' == undefined -> ok; 391 true -> '<verify-fn>'('<F>', ['<FName>' | Path], 392 'TrUserData') 393 end, 394 Replacements); 395 #maps{unset_optional=present_undefined} -> 396 ?expr(if '<F>' == undefined -> ok; 397 true -> '<verify-fn>'('<F>', ['<FName>' | Path], 398 'TrUserData') 399 end, 400 Replacements); 401 #maps{unset_optional=omitted} -> 402 ?expr(case 'M' of 403 '#{<FName> := <F>}' -> 404 '<verify-fn>'('<F>', ['<FName>' | Path], 405 'TrUserData'); 406 _ -> 407 ok 408 end, 409 [replace_tree('#{<FName> := <F>}', 410 gpb_lib:map_match([{FName, FVar}], 411 Opts)), 412 replace_tree('M', MsgVar) | Replacements]) 413 end 414 end; 415field_verifier(MsgName, #gpb_oneof{name=FName, fields=OFields}, 416 FVar, MsgVar, TrUserDataVar, AnRes, Opts) -> 417 CfElemPath = [MsgName,FName], 418 case gpb_gen_translators:has_translation(CfElemPath, verify, AnRes) of 419 {true, Transl} -> 420 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 421 records -> 422 tr_field_oneof_present_undefined_verifier( 423 FName, FVar, Transl, TrUserDataVar); 424 #maps{unset_optional=present_undefined} -> 425 tr_field_oneof_present_undefined_verifier( 426 FName, FVar, Transl, TrUserDataVar); 427 #maps{unset_optional=omitted, oneof=tuples} -> 428 tr_field_oneof_omitted_tuples_verifier( 429 MsgVar, FName, FVar, Transl, TrUserDataVar, Opts); 430 #maps{unset_optional=omitted, oneof=flat} -> 431 %% translate on individual oneof fields instead 432 field_oneof_omitted_flat_verifier( 433 MsgName, FName, OFields, 434 FVar, MsgVar, TrUserDataVar, 435 AnRes, Opts) 436 end; 437 false -> 438 case gpb_lib:get_mapping_and_unset_by_opts(Opts) of 439 records -> 440 field_oneof_present_undefined_verifier( 441 MsgName, FName, OFields, 442 FVar, MsgVar, TrUserDataVar, 443 AnRes); 444 #maps{unset_optional=present_undefined} -> 445 field_oneof_present_undefined_verifier( 446 MsgName, FName, OFields, 447 FVar, MsgVar, TrUserDataVar, 448 AnRes); 449 #maps{unset_optional=omitted, oneof=tuples} -> 450 field_oneof_omitted_tuples_verifier( 451 MsgName, FName, OFields, 452 FVar, MsgVar, TrUserDataVar, 453 AnRes, Opts); 454 #maps{unset_optional=omitted, oneof=flat} -> 455 field_oneof_omitted_flat_verifier( 456 MsgName, FName, OFields, 457 FVar, MsgVar, TrUserDataVar, 458 AnRes, Opts) 459 end 460 end. 461 462field_oneof_present_undefined_verifier(MsgName, FName, OFields, 463 FVar, MsgVar, TrUserDataVar, 464 AnRes) -> 465 ?expr( 466 case '<F>' of 467 undefined -> 468 ok; 469 '<oneof-pattern>' -> 470 '<verify-fn>'('<OFVar>', ['<OFName>', '<FName>' | Path], 471 'TrUserData'); 472 _ -> 473 mk_type_error(invalid_oneof, '<F>', ['<FName>' | Path]) 474 end, 475 [replace_tree('<F>', FVar), 476 replace_term('<FName>', FName), 477 repeat_clauses( 478 '<oneof-pattern>', 479 [begin 480 FVerifierFn = 481 case Type of 482 {msg,FMsgName} -> 483 gpb_lib:mk_fn(v_msg_, FMsgName); 484 {enum,EnumName} -> 485 gpb_lib:mk_fn(v_enum_, EnumName); 486 Type -> 487 gpb_lib:mk_fn(v_type_, Type) 488 end, 489 ElemPath = gpb_gen_translators:mk_elempath_elem( 490 MsgName, F, {true, FName}), 491 FVerifierFn2 = gpb_gen_translators:find_translation( 492 ElemPath, verify, AnRes, 493 FVerifierFn), 494 OFVar = gpb_lib:prefix_var("O", FVar), 495 [replace_tree('M', MsgVar), 496 replace_tree('<oneof-pattern>', 497 ?expr({'<OFName>','<OFVar>'})), 498 replace_term('<verify-fn>', FVerifierFn2), 499 replace_tree('<OFVar>', OFVar), 500 replace_term('<OFName>', OFName), 501 replace_tree('TrUserData', TrUserDataVar)] 502 end 503 || #?gpb_field{name=OFName, type=Type}=F <- OFields])]). 504 505tr_field_oneof_present_undefined_verifier(FName, FVar, Transl, TrUserDataVar) -> 506 ?expr(if '<F>' =:= undefined -> 507 ok; 508 true -> 509 'Tr'('<F>', ['fname' | Path], 'TrUserData') 510 end, 511 [replace_tree('<F>', FVar), 512 replace_term('Tr', Transl), 513 replace_term('fname', FName), 514 replace_tree('TrUserData', TrUserDataVar)]). 515 516 517field_oneof_omitted_tuples_verifier(MsgName, FName, OFields, 518 FVar, MsgVar, TrUserDataVar, 519 AnRes, Opts) -> 520 ?expr( 521 case 'M' of 522 '<oneof-pattern>' -> 523 '<verify-fn>'('<OFVar>', ['<OFName>', '<FName>' | Path], 524 'TrUserData'); 525 '#{<FName> := <F>}' -> 526 mk_type_error(invalid_oneof, '<F>', ['<FName>' | Path]); 527 _ -> 528 ok 529 end, 530 [replace_tree('<F>', FVar), 531 replace_term('<FName>', FName), 532 replace_tree('M', MsgVar), 533 replace_tree('#{<FName> := <F>}', 534 gpb_lib:map_match([{FName, FVar}], Opts)), 535 repeat_clauses( 536 '<oneof-pattern>', 537 [begin 538 FVerifierFn = 539 case Type of 540 {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName); 541 {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName); 542 Type -> gpb_lib:mk_fn(v_type_, Type) 543 end, 544 ElemPath = gpb_gen_translators:mk_elempath_elem( 545 MsgName, F, {true, FName}), 546 FVerifierFn2 = gpb_gen_translators:find_translation( 547 ElemPath, verify, AnRes, 548 FVerifierFn), 549 OFVar = gpb_lib:prefix_var("O", FVar), 550 Trs1 = [replace_tree('<OFVar>', OFVar), 551 replace_term('<OFName>', OFName)], 552 OFPat = ?expr({'<OFName>','<OFVar>'}, Trs1), 553 [replace_tree('<oneof-pattern>', 554 gpb_lib:map_match([{FName, OFPat}], Opts)), 555 replace_term('<verify-fn>', FVerifierFn2), 556 replace_tree('TrUserData', TrUserDataVar) 557 | Trs1] 558 end 559 || #?gpb_field{name=OFName, type=Type}=F <- OFields])]). 560 561tr_field_oneof_omitted_tuples_verifier(MsgVar, FName, FVar, 562 Transl, TrUserDataVar, Opts) -> 563 ?expr(case 'M' of 564 '#{fname := F}' -> 565 'Tr'('F', ['fname' | Path], 'TrUserData'); 566 _ -> 567 ok 568 end, 569 [replace_tree('M', MsgVar), 570 replace_tree('#{fname := F}', gpb_lib:map_match([{FName, FVar}], 571 Opts)), 572 replace_term('Tr', Transl), 573 replace_tree('F', FVar), 574 replace_term('fname', FName), 575 replace_tree('TrUserData', TrUserDataVar)]). 576 577field_oneof_omitted_flat_verifier(MsgName, FName, OFields, 578 FVar, MsgVar, TrUserDataVar, 579 AnRes, Opts) -> 580 %% FIXME: Verify at most one oneof field is set 581 OFNames = gpb_lib:get_field_names(OFields), 582 ?expr( 583 case 'M' of 584 '#{OFName := OFVar}' -> 585 case maps:keys(maps:with('OFNames', 'M')) of 586 [_] -> 587 ok; 588 'OFDups' -> 589 mk_type_error({multiple_oneof_keys, 'OFDups', 'FName'}, 590 'M', ['FName' | Path]) 591 end, 592 'verify-fn'('OFVar', ['OFName' | Path], 'TrUserData'); 593 _ -> 594 ok 595 end, 596 [replace_tree('M', MsgVar), 597 replace_term('FName', FName), 598 replace_term('OFNames', OFNames), 599 repeat_clauses( 600 '#{OFName := OFVar}', 601 [begin 602 FVerifierFn = 603 case Type of 604 {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName); 605 {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName); 606 Type -> gpb_lib:mk_fn(v_type_, Type) 607 end, 608 ElemPath = gpb_gen_translators:mk_elempath_elem( 609 MsgName, F, {true, FName}), 610 FVerifierFn2 = gpb_gen_translators:find_translation( 611 ElemPath, verify, AnRes, 612 FVerifierFn), 613 OFVar = gpb_lib:prefix_var("O", FVar), 614 Trs1 = [replace_tree('OFVar', OFVar), 615 replace_term('OFName', OFName)], 616 [replace_tree('#{OFName := OFVar}', 617 gpb_lib:map_match([{OFName, OFVar}], Opts)), 618 replace_tree('OFDups', gpb_lib:prefix_var("OFDups", OFVar)), 619 replace_term('verify-fn', FVerifierFn2), 620 replace_tree('TrUserData', TrUserDataVar) 621 | Trs1] 622 end 623 || #?gpb_field{name=OFName, type=Type}=F <- OFields])]). 624 625format_enum_verifiers(Defs, #anres{used_types=UsedTypes}, Opts) -> 626 [format_enum_verifier(EnumName, Def, Opts) 627 || {{enum,EnumName}, Def} <- Defs, 628 gpb_lib:smember({enum, EnumName}, UsedTypes)]. 629 630format_enum_verifier(EnumName, EnumMembers, Opts) -> 631 FnName = gpb_lib:mk_fn(v_enum_, EnumName), 632 [gpb_lib:nowarn_unused_function(FnName, 3), 633 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 634 gpb_codegen:format_fn( 635 FnName, 636 fun('<sym>', _Path, _TrUserData) -> 637 ok; 638 (V, Path, TrUserData) when is_integer(V) -> 639 v_type_sint32(V, Path, TrUserData); 640 (X, Path, _TrUserData) -> 641 mk_type_error({invalid_enum, '<EnumName>'}, X, Path) 642 end, 643 [repeat_clauses('<sym>', [[replace_term('<sym>', EnumSym)] 644 || {EnumSym, _Value} <- EnumMembers]), 645 replace_term('<EnumName>', EnumName)])]. 646 647format_type_verifiers(#anres{used_types=UsedTypes}, Opts) -> 648 NeedSInt32 = (gpb_lib:smember(sint32, UsedTypes) orelse 649 gpb_lib:any_enum_field_exists(UsedTypes)), 650 NeedBool = gpb_lib:smember(bool, UsedTypes), 651 NeedFloat = gpb_lib:smember(float, UsedTypes), 652 NeedDouble = gpb_lib:smember(double, UsedTypes), 653 NeedString = gpb_lib:smember(string, UsedTypes), 654 NeedBytes = gpb_lib:smember(bytes, UsedTypes), 655 [[format_int_verifier(sint32, signed, 32, Opts) || NeedSInt32], 656 [format_int_verifier(Type, Signedness, Bits, Opts) 657 || {Type, Signedness, Bits} <- [{sint64, signed, 64}, 658 {int32, signed, 32}, 659 {int64, signed, 64}, 660 {uint32, unsigned, 32}, 661 {uint64, unsigned, 64}, 662 {fixed32, unsigned, 32}, 663 {fixed64, unsigned, 64}, 664 {sfixed32, signed, 32}, 665 {sfixed64, signed, 64}], 666 gpb_lib:smember(Type, UsedTypes)], 667 [format_bool_verifier(Opts) || NeedBool], 668 [format_float_verifier(float, Opts) || NeedFloat], 669 [format_float_verifier(double, Opts) || NeedDouble], 670 [format_string_verifier(Opts) || NeedString], 671 [format_bytes_verifier(Opts) || NeedBytes]]. 672 673format_int_verifier(IntType, Signedness, NumBits, Opts) -> 674 Min = case Signedness of 675 unsigned -> 0; 676 signed -> -(1 bsl (NumBits-1)) 677 end, 678 Max = case Signedness of 679 unsigned -> 1 bsl NumBits - 1; 680 signed -> 1 bsl (NumBits-1) - 1 681 end, 682 FnName = gpb_lib:mk_fn(v_type_, IntType), 683 [gpb_lib:nowarn_unused_function(FnName, 3), 684 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 685 gpb_codegen:format_fn( 686 FnName, 687 fun(N, _Path, _TrUserData) when '<Min>' =< N, N =< '<Max>' -> 688 ok; 689 (N, Path, _TrUserData) when is_integer(N) -> 690 mk_type_error({value_out_of_range, '<details>'}, N, Path); 691 (X, Path, _TrUserData) -> 692 mk_type_error({bad_integer, '<details>'}, X, Path) 693 end, 694 [replace_term('<Min>', Min), 695 replace_term('<Max>', Max), 696 splice_trees('<details>', [erl_syntax:atom(IntType), 697 erl_syntax:atom(Signedness), 698 erl_syntax:integer(NumBits)])])]. 699 700format_bool_verifier(Opts) -> 701 FnName = gpb_lib:mk_fn(v_type_, bool), 702 [gpb_lib:nowarn_unused_function(FnName, 3), 703 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 704 gpb_codegen:format_fn( 705 FnName, 706 fun(false, _Path, _TrUserData) -> ok; 707 (true, _Path, _TrUserData) -> ok; 708 (0, _Path, _TrUserData) -> ok; 709 (1, _Path, _TrUserData) -> ok; 710 (X, Path, _TrUserData) -> mk_type_error(bad_boolean_value, X, Path) 711 end)]. 712 713format_float_verifier(FlType, Opts) -> 714 BadTypeOfValue = list_to_atom(lists:concat(["bad_", FlType, "_value"])), 715 FnName = gpb_lib:mk_fn(v_type_, FlType), 716 [gpb_lib:nowarn_unused_function(FnName, 3), 717 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 718 gpb_codegen:format_fn( 719 FnName, 720 fun(N, _Path, _TrUserData) when is_float(N) -> ok; 721 %% It seems a float for the corresponding integer value is 722 %% indeed packed when doing <<Integer:32/little-float>>. 723 %% So let verify accept integers too. 724 %% When such a value is unpacked, we get a float. 725 (N, _Path, _TrUserData) when is_integer(N) -> ok; 726 (infinity, _Path, _TrUserData) -> ok; 727 ('-infinity', _Path, _TrUserData) -> ok; 728 (nan, _Path, _TrUserData) -> ok; 729 (X, Path, _TrUserData) -> 730 mk_type_error('<bad_x_value>', X, Path) 731 end, 732 [replace_term('<bad_x_value>', BadTypeOfValue)])]. 733 734format_string_verifier(Opts) -> 735 FnName = gpb_lib:mk_fn(v_type_, string), 736 [gpb_lib:nowarn_unused_function(FnName, 3), 737 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 738 gpb_codegen:format_fn( 739 FnName, 740 fun(S, Path, _TrUserData) when is_list(S); is_binary(S) -> 741 try unicode:characters_to_binary(S) of 742 B when is_binary(B) -> 743 ok; 744 {error, _, _} -> %% a non-UTF-8 binary 745 mk_type_error(bad_unicode_string, S, Path) 746 catch error:badarg -> 747 mk_type_error(bad_unicode_string, S, Path) 748 end; 749 (X, Path, _TrUserData) -> 750 mk_type_error(bad_unicode_string, X, Path) 751 end)]. 752 753format_bytes_verifier(Opts) -> 754 FnName = gpb_lib:mk_fn(v_type_, bytes), 755 [gpb_lib:nowarn_unused_function(FnName, 3), 756 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 757 gpb_codegen:format_fn( 758 FnName, 759 fun(B, _Path, _TrUserData) when is_binary(B) -> 760 ok; 761 (B, _Path, _TrUserData) when is_list(B) -> 762 ok; 763 (X, Path, _TrUserData) -> 764 mk_type_error(bad_binary_value, X, Path) 765 end)]. 766 767format_map_verifiers(#anres{map_types=MapTypes}=AnRes, Opts) -> 768 MapsOrTuples = gpb_lib:get_2tuples_or_maps_for_maptype_fields_by_opts(Opts), 769 [format_map_verifier(KeyType, ValueType, MapsOrTuples, AnRes, Opts) 770 || {KeyType,ValueType} <- sets:to_list(MapTypes)]. 771 772format_map_verifier(KeyType, ValueType, MapsOrTuples, AnRes, Opts) -> 773 MsgName = gpb_lib:map_type_to_msg_name(KeyType, ValueType), 774 FnName = gpb_lib:mk_fn(v_, MsgName), 775 KeyVerifierFn = gpb_lib:mk_fn(v_type_, KeyType), 776 ValueVerifierFn1 = case ValueType of 777 {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName); 778 {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName); 779 Type -> gpb_lib:mk_fn(v_type_, Type) 780 end, 781 ElemPath = [MsgName,value], 782 ValueVerifierFn2 = gpb_gen_translators:find_translation( 783 ElemPath, verify, AnRes, 784 ValueVerifierFn1), 785 [gpb_lib:nowarn_unused_function(FnName, 3), 786 gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts), 787 case MapsOrTuples of 788 '2tuples' -> 789 gpb_codegen:format_fn( 790 FnName, 791 fun(KVs, Path, TrUserData) when is_list(KVs) -> 792 [case X of 793 {Key, Value} -> 794 'VerifyKey'(Key, ['key' | Path], TrUserData), 795 'VerifyValue'(Value, ['value' | Path], 796 TrUserData); 797 _ -> 798 mk_type_error(invalid_key_value_tuple, X, Path) 799 end 800 || X <- KVs], 801 ok; 802 (X, Path, _TrUserData) -> 803 mk_type_error(invalid_list_of_key_value_tuples, X, Path) 804 end, 805 [replace_term('VerifyKey', KeyVerifierFn), 806 replace_term('VerifyValue', ValueVerifierFn2)]); 807 maps -> 808 gpb_codegen:format_fn( 809 FnName, 810 fun(M, Path, TrUserData) when is_map(M) -> 811 [begin 812 'VerifyKey'(Key, ['key' | Path], TrUserData), 813 'VerifyValue'(Value, ['value' | Path], 814 TrUserData) 815 end 816 || {Key, Value} <- maps:to_list(M)], 817 ok; 818 (X, Path, _TrUserData) -> 819 mk_type_error(invalid_map, X, Path) 820 end, 821 [replace_term('VerifyKey', KeyVerifierFn), 822 replace_term('VerifyValue', ValueVerifierFn2)]) 823 end]. 824 825format_verifier_auxiliaries(Defs, Opts) -> 826 [gpb_lib:nowarn_unused_function(mk_type_error, 3), 827 "-spec mk_type_error(_, _, list()) -> no_return().\n", 828 gpb_codegen:format_fn( 829 mk_type_error, 830 fun(Error, ValueSeen, Path) -> 831 Path2 = prettify_path(Path), 832 erlang:error({gpb_type_error, 833 {Error, [{value, ValueSeen},{path, Path2}]}}) 834 end), 835 "\n", 836 case gpb_lib:contains_messages(Defs) of 837 false -> 838 gpb_codegen:format_fn( 839 prettify_path, 840 fun([]) -> top_level end); 841 true -> 842 [gpb_lib:nowarn_unused_function(prettify_path, 1), 843 gpb_lib:nowarn_dialyzer_attr(prettify_path, 1, Opts), 844 case gpb_lib:target_has_lists_join(Opts) of 845 true -> 846 format_prettify_path_with_lists_join(); 847 false -> 848 format_prettify_path_with_string_join() 849 end] 850 end]. 851 852format_prettify_path_with_lists_join() -> 853 gpb_codegen:format_fn( 854 prettify_path, 855 fun([]) -> 856 top_level; 857 (PathR) -> 858 list_to_atom( 859 lists:append( 860 lists:join(".", lists:map(fun atom_to_list/1, 861 lists:reverse(PathR))))) 862 end). 863 864format_prettify_path_with_string_join() -> 865 gpb_codegen:format_fn( 866 prettify_path, 867 fun([]) -> 868 top_level; 869 (PathR) -> 870 list_to_atom(string:join(lists:map(fun atom_to_list/1, 871 lists:reverse(PathR)), 872 ".")) 873 end). 874