codegen_schema.c (19802B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "flatcc/reflection/reflection_builder.h" 4 #include "symbols.h" 5 #include "parser.h" 6 #include "codegen.h" 7 #include "fileio.h" 8 /* Needed to store length prefix. */ 9 #include "catalog.h" 10 11 #define BaseType(x) FLATBUFFERS_WRAP_NAMESPACE(reflection_BaseType, x) 12 13 static flatbuffers_bool_t is_optional_type(fb_value_t type, int optional, int required) 14 { 15 if (required) return 0; 16 if (optional) return 1; 17 if (type.type == vt_scalar_type) return 0; 18 if (type.type == vt_compound_type_ref && type.ct->symbol.kind == fb_is_enum) return 0; 19 return 1; 20 } 21 22 static reflection_Type_ref_t export_type(flatcc_builder_t *B, fb_value_t type) 23 { 24 fb_scalar_type_t st = fb_missing_type; 25 int32_t index = -1; 26 reflection_BaseType_enum_t base_type = BaseType(None); 27 reflection_BaseType_enum_t element = BaseType(None); 28 reflection_BaseType_enum_t primitive = BaseType(None); 29 uint16_t fixed_length = 0; 30 31 switch (type.type) { 32 case vt_scalar_type: 33 st = type.st; 34 break; 35 case vt_vector_type: 36 st = type.st; 37 base_type = BaseType(Vector); 38 break; 39 case vt_vector_string_type: 40 element = BaseType(String); 41 base_type = BaseType(Vector); 42 break; 43 case vt_vector_compound_type_ref: 44 index = (int32_t)type.ct->export_index; 45 switch (type.ct->symbol.kind) { 46 case fb_is_enum: 47 st = type.ct->type.st; 48 base_type = BaseType(Vector); 49 break; 50 case fb_is_struct: 51 case fb_is_table: 52 base_type = BaseType(Vector); 53 element = BaseType(Obj); 54 break; 55 case fb_is_union: 56 base_type = BaseType(Vector); 57 element = BaseType(Union); 58 break; 59 default: 60 break; 61 } 62 break; 63 case vt_string_type: 64 base_type = BaseType(String); 65 break; 66 case vt_compound_type_ref: 67 index = (int32_t)type.ct->export_index; 68 switch (type.ct->symbol.kind) { 69 case fb_is_enum: 70 st = type.ct->type.st; 71 break; 72 case fb_is_struct: 73 case fb_is_table: 74 base_type = BaseType(Obj); 75 break; 76 case fb_is_union: 77 base_type = BaseType(Union); 78 break; 79 default: 80 index = -1; 81 break; 82 } 83 break; 84 case vt_fixed_array_type: 85 st = type.st; 86 base_type = BaseType(Array); 87 fixed_length = (uint16_t)type.len; 88 break; 89 case vt_fixed_array_string_type: 90 break; 91 element = BaseType(Byte); 92 base_type = BaseType(Array); 93 fixed_length = (uint16_t)type.len; 94 break; 95 case vt_fixed_array_compound_type_ref: 96 index = (int32_t)type.ct->export_index; 97 switch (type.ct->symbol.kind) { 98 case fb_is_enum: 99 st = type.ct->type.st; 100 break; 101 case fb_is_struct: 102 case fb_is_table: 103 element = BaseType(Obj); 104 break; 105 case fb_is_union: 106 element = BaseType(Union); 107 break; 108 default: 109 break; 110 } 111 base_type = BaseType(Array); 112 fixed_length = (uint16_t)type.len; 113 break; 114 default: 115 break; 116 } 117 /* If st is set, resolve scalar type and set it to base_type or element. */ 118 switch (st) { 119 case fb_missing_type: break; 120 case fb_ulong: primitive = BaseType(ULong); break; 121 case fb_uint: primitive = BaseType(UInt); break; 122 case fb_ushort: primitive = BaseType(UShort); break; 123 case fb_ubyte: primitive = BaseType(UByte); break; 124 case fb_bool: primitive = BaseType(Bool); break; 125 case fb_long: primitive = BaseType(Long); break; 126 case fb_int: primitive = BaseType(Int); break; 127 case fb_short: primitive = BaseType(Short); break; 128 case fb_byte: primitive = BaseType(Byte); break; 129 case fb_double: primitive = BaseType(Double); break; 130 case fb_float: primitive = BaseType(Float); break; 131 /* TODO: Googles flatc tool does not have char arrays so we use Byte as element type */ 132 case fb_char: primitive = BaseType(Byte); break; 133 default: break; 134 } 135 136 if (base_type == BaseType(None)) { 137 base_type = primitive; 138 } else if (base_type == BaseType(Vector) || base_type == BaseType(Array)) { 139 if (element == BaseType(None)) { 140 element = primitive; 141 } 142 } 143 return reflection_Type_create(B, base_type, element, index, fixed_length); 144 } 145 146 static void export_attributes(flatcc_builder_t *B, fb_metadata_t *m) 147 { 148 for (; m; m = m->link) { 149 reflection_KeyValue_vec_push_start(B); 150 reflection_KeyValue_key_create_strn(B, m->ident->text, (size_t)m->ident->len); 151 if (m->value.type == vt_string) { 152 reflection_KeyValue_value_create_strn(B, m->value.s.s, (size_t)m->value.s.len); 153 } 154 reflection_KeyValue_vec_push_end(B); 155 } 156 } 157 158 static void export_fields(flatcc_builder_t *B, fb_compound_type_t *ct) 159 { 160 fb_symbol_t *sym; 161 fb_member_t *member; 162 flatbuffers_bool_t has_key, deprecated, required, optional, key_processed = 0; 163 int64_t default_integer; 164 double default_real; 165 166 for (sym = ct->members; sym; sym = sym->link) { 167 member = (fb_member_t *)sym; 168 /* 169 * Unlike `flatc` we allow multiple keys in the parser, but 170 * there is no way to tell which key is default in the 171 * reflection schema because the fields are sorted, so we only 172 * export the default (first) key. 173 */ 174 has_key = !key_processed && (member->metadata_flags & fb_f_key) != 0; 175 required = (member->metadata_flags & fb_f_required) != 0; 176 default_integer = 0; 177 default_real = 0.0; 178 deprecated = (member->metadata_flags & fb_f_deprecated) != 0; 179 /* 180 * Flag is only set when `= null` is used in the schema, but 181 * non-scalar types are optional by default and therfore also 182 * true in the binary schema. 183 */ 184 optional = is_optional_type(member->type, !!(member->flags & fb_fm_optional), required); 185 186 if ((member->type.type == vt_compound_type_ref || member->type.type == vt_vector_compound_type_ref) 187 && member->type.ct->symbol.kind == fb_is_union) { 188 reflection_Field_vec_push_start(B); 189 reflection_Field_name_start(B); 190 reflection_Field_name_append(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); 191 reflection_Field_name_append(B, "_type", 5); 192 reflection_Field_name_end(B); 193 switch(member->type.type) { 194 case vt_compound_type_ref: 195 reflection_Field_type_create(B, BaseType(UType), BaseType(None), -1, 0); 196 break; 197 case vt_vector_compound_type_ref: 198 reflection_Field_type_create(B, BaseType(Vector), BaseType(UType), -1, 0); 199 break; 200 } 201 reflection_Field_offset_add(B, (uint16_t)(member->id - 1 + 2) * sizeof(flatbuffers_voffset_t)); 202 reflection_Field_id_add(B, (uint16_t)(member->id - 1)); 203 reflection_Field_deprecated_add(B, deprecated); 204 reflection_Field_vec_push_end(B); 205 } 206 reflection_Field_vec_push_start(B); 207 reflection_Field_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); 208 reflection_Field_type_add(B, export_type(B, member->type)); 209 switch (ct->symbol.kind) { 210 case fb_is_table: 211 switch (member->value.type) { 212 case vt_uint: 213 default_integer = (int64_t)member->value.u; 214 break; 215 case vt_int: 216 default_integer = (int64_t)member->value.i; 217 break; 218 case vt_bool: 219 default_integer = (int64_t)member->value.b; 220 break; 221 case vt_float: 222 default_real = member->value.f; 223 break; 224 } 225 reflection_Field_default_integer_add(B, default_integer); 226 reflection_Field_default_real_add(B, default_real); 227 reflection_Field_id_add(B, (uint16_t)member->id); 228 reflection_Field_offset_add(B, (uint16_t)(member->id + 2) * sizeof(flatbuffers_voffset_t)); 229 reflection_Field_key_add(B, has_key); 230 reflection_Field_required_add(B, required); 231 reflection_Field_optional_add(B, optional); 232 break; 233 case fb_is_struct: 234 reflection_Field_offset_add(B, (uint16_t)member->offset); 235 break; 236 default: break; 237 } 238 /* Deprecated struct fields not supported by `flatc` but is here as an option. */ 239 reflection_Field_deprecated_add(B, deprecated); 240 if (member->metadata) { 241 reflection_Field_attributes_start(B); 242 export_attributes(B, member->metadata); 243 reflection_Field_attributes_end(B); 244 } 245 reflection_Field_vec_push_end(B); 246 key_processed |= has_key; 247 } 248 } 249 250 /* `vec` is filled with references to the constructed objects. */ 251 static void export_objects(flatcc_builder_t *B, object_entry_t *objects, int nobjects, 252 reflection_Object_ref_t *object_map) 253 { 254 int i, is_struct; 255 fb_compound_type_t *ct; 256 257 for (i = 0; i < nobjects; ++i) { 258 ct = objects[i].ct; 259 reflection_Object_start(B); 260 reflection_Object_name_create_str(B, objects[i].name); 261 /* 262 * We can post sort-fields because the index is not used, unlike 263 * objects and enums. 264 */ 265 reflection_Object_fields_start(B); 266 export_fields(B, ct); 267 reflection_Object_fields_end(B); 268 is_struct = ct->symbol.kind == fb_is_struct; 269 if (is_struct) { 270 reflection_Object_bytesize_add(B, (int32_t)ct->size); 271 } 272 reflection_Object_is_struct_add(B, (flatbuffers_bool_t)is_struct); 273 reflection_Object_minalign_add(B, ct->align); 274 if (ct->metadata) { 275 reflection_Object_attributes_start(B); 276 export_attributes(B, ct->metadata); 277 reflection_Object_attributes_end(B); 278 } 279 object_map[i] = reflection_Object_end(B); 280 } 281 reflection_Schema_objects_create(B, object_map, (size_t)nobjects); 282 } 283 284 static void export_enumval(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map) 285 { 286 int is_union = object_map != 0; 287 288 reflection_EnumVal_vec_push_start(B); 289 reflection_EnumVal_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); 290 if (is_union) { 291 if (member->type.type == vt_compound_type_ref) { 292 /* object is deprecated in favor of union_type to support mixed union types. */ 293 reflection_EnumVal_object_add(B, object_map[member->type.ct->export_index]); 294 } 295 reflection_EnumVal_union_type_add(B, export_type(B, member->type)); 296 } 297 reflection_EnumVal_value_add(B, (int64_t)member->value.u); 298 reflection_EnumVal_vec_push_end(B); 299 } 300 301 static void export_enums(flatcc_builder_t *B, enum_entry_t *enums, int nenums, 302 reflection_Object_ref_t *object_map) 303 { 304 int i, is_union; 305 fb_compound_type_t *ct; 306 fb_symbol_t *sym; 307 308 reflection_Schema_enums_start(B); 309 for (i = 0; i < nenums; ++i) { 310 ct = enums[i].ct; 311 is_union = ct->symbol.kind == fb_is_union; 312 reflection_Enum_vec_push_start(B); 313 reflection_Enum_name_create_str(B, enums[i].name); 314 reflection_Enum_values_start(B); 315 for (sym = ct->members; sym; sym = sym->link) { 316 export_enumval(B, (fb_member_t *)sym, is_union ? object_map : 0); 317 } 318 reflection_Enum_values_end(B); 319 reflection_Enum_is_union_add(B, (flatbuffers_bool_t)is_union); 320 reflection_Enum_underlying_type_add(B, export_type(B, ct->type)); 321 if (ct->metadata) { 322 reflection_Enum_attributes_start(B); 323 export_attributes(B, ct->metadata); 324 reflection_Enum_attributes_end(B); 325 } 326 reflection_Enum_vec_push_end(B); 327 } 328 reflection_Schema_enums_end(B); 329 } 330 331 static void export_root_type(flatcc_builder_t *B, fb_symbol_t * root_type, 332 reflection_Object_ref_t *object_map) 333 { 334 fb_compound_type_t *ct; 335 if (root_type) { 336 /* 337 * We could also store a struct object here, but since the 338 * binrary schema says root_table, not root_type as in the text 339 * schema, it would be misleading. 340 */ 341 if (root_type->kind == fb_is_table) { 342 ct = (fb_compound_type_t *)root_type; 343 reflection_Schema_root_table_add(B, object_map[ct->export_index]); 344 } 345 } 346 } 347 348 static void export_call(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map) 349 { 350 reflection_RPCCall_vec_push_start(B); 351 reflection_RPCCall_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); 352 reflection_RPCCall_request_add(B, object_map[member->req_type.ct->export_index]); 353 reflection_RPCCall_response_add(B, object_map[member->type.ct->export_index]); 354 if (member->metadata) { 355 reflection_RPCCall_attributes_start(B); 356 export_attributes(B, member->metadata); 357 reflection_RPCCall_attributes_end(B); 358 } 359 reflection_RPCCall_vec_push_end(B); 360 } 361 362 static void export_services(flatcc_builder_t *B, service_entry_t *services, int nservices, 363 reflection_Object_ref_t *object_map) 364 { 365 int i; 366 fb_compound_type_t *ct; 367 fb_symbol_t *sym; 368 369 reflection_Schema_services_start(B); 370 for (i = 0; i < nservices; ++i) { 371 ct = services[i].ct; 372 reflection_Service_vec_push_start(B); 373 reflection_Service_name_create_str(B, services[i].name); 374 reflection_Service_calls_start(B); 375 for (sym = ct->members; sym; sym = sym->link) { 376 export_call(B, (fb_member_t *)sym, object_map); 377 } 378 reflection_Service_calls_end(B); 379 if (ct->metadata) { 380 reflection_Service_attributes_start(B); 381 export_attributes(B, ct->metadata); 382 reflection_Service_attributes_end(B); 383 } 384 reflection_Service_vec_push_end(B); 385 } 386 reflection_Schema_services_end(B); 387 } 388 389 static int export_schema(flatcc_builder_t *B, fb_options_t *opts, fb_schema_t *S) 390 { 391 catalog_t catalog; 392 reflection_Object_ref_t *object_map = 0; 393 394 if (build_catalog(&catalog, S, opts->bgen_qualify_names, &S->root_schema->scope_index)) { 395 return -1; 396 } 397 398 if (catalog.nobjects > 0 && !(object_map = malloc((size_t)catalog.nobjects * sizeof(object_map[0])))) { 399 clear_catalog(&catalog); 400 return -1; 401 } 402 403 /* Build the schema. */ 404 405 if (opts->bgen_length_prefix) { 406 reflection_Schema_start_as_root_with_size(B); 407 } else { 408 reflection_Schema_start_as_root(B); 409 } 410 if (S->file_identifier.type == vt_string) { 411 reflection_Schema_file_ident_create(B, 412 S->file_identifier.s.s, (size_t)S->file_identifier.s.len); 413 } 414 if (S->file_extension.type == vt_string) { 415 reflection_Schema_file_ext_create(B, 416 S->file_extension.s.s, (size_t)S->file_extension.s.len); 417 } 418 export_objects(B, catalog.objects, catalog.nobjects, object_map); 419 export_enums(B, catalog.enums, catalog.nenums, object_map); 420 export_root_type(B, S->root_type.type, object_map); 421 export_services(B, catalog.services, catalog.nservices, object_map); 422 423 reflection_Schema_end_as_root(B); 424 425 /* Clean up support datastructures. */ 426 427 clear_catalog(&catalog); 428 if (object_map) { 429 free(object_map); 430 } 431 return 0; 432 } 433 434 /* 435 * We do not not sort attributes because we would loose ordering 436 * information between different attributes, and between same named 437 * attributes because the sort is not stable. 438 * 439 * The C bindings has a scan interface that can find attributes 440 * in order of appearance. 441 * 442 * Field sorting is done on the finished buffer. 443 */ 444 static void sort_objects(void *buffer) 445 { 446 size_t i; 447 reflection_Schema_table_t schema; 448 reflection_Object_vec_t objects; 449 reflection_Object_table_t object; 450 reflection_Field_vec_t fields; 451 reflection_Field_mutable_vec_t mfields; 452 453 schema = reflection_Schema_as_root(buffer); 454 objects = reflection_Schema_objects(schema); 455 for (i = 0; i < reflection_Object_vec_len(objects); ++i) { 456 object = reflection_Object_vec_at(objects, i); 457 fields = reflection_Object_fields(object); 458 if (fields) { 459 mfields = (reflection_Field_mutable_vec_t)fields; 460 reflection_Field_vec_sort(mfields); 461 } 462 } 463 } 464 465 static FILE *open_file(fb_options_t *opts, fb_schema_t *S) 466 { 467 FILE *fp = 0; 468 char *path = 0, *ext = 0; 469 const char *prefix = opts->outpath ? opts->outpath : ""; 470 size_t len, prefix_len = strlen(prefix); 471 const char *name; 472 473 name = S->basename; 474 len = strlen(name); 475 476 ext = fb_create_path_ext(".", flatbuffers_extension); 477 /* We generally should not use cgen options here, but in this case it makes sense. */ 478 if (opts->gen_stdout) { 479 return stdout; 480 } 481 checkmem((path = fb_create_join_path_n(prefix, prefix_len, name, len, ext, 1))); 482 fp = fopen(path, "wb"); 483 if (!fp) { 484 fprintf(stderr, "error opening file for writing binary schema: %s\n", path); 485 } 486 free(path); 487 free(ext); 488 return fp; 489 } 490 491 static void close_file(FILE *fp) 492 { 493 if (fp && fp != stdout) { 494 fclose(fp); 495 } 496 } 497 498 /* 499 * Normally enums are required to be ascending in the schema and 500 * therefore there is no need to sort enums. If not, we export them in 501 * the order defined anyway becuase there is no well-defined ordering 502 * and blindly sorting the content would just loose more information. 503 * 504 * In conclusion: find by enum value is only supported when enums are 505 * defined in consequtive order. 506 * 507 * refers to: `opts->ascending_enum` 508 * 509 * `size` must hold the maximum buffer size. 510 * Returns intput buffer if successful and updates size argument. 511 */ 512 void *fb_codegen_bfbs_to_buffer(fb_options_t *opts, fb_schema_t *S, void *buffer, size_t *size) 513 { 514 flatcc_builder_t builder, *B; 515 516 B = &builder; 517 flatcc_builder_init(B); 518 export_schema(B, opts, S); 519 if (!flatcc_builder_copy_buffer(B, buffer, *size)) { 520 goto done; 521 } 522 sort_objects(buffer); 523 done: 524 *size = flatcc_builder_get_buffer_size(B); 525 flatcc_builder_clear(B); 526 return buffer; 527 } 528 529 /* 530 * Like to_buffer, but returns allocated buffer. 531 * Updates size argument with buffer size if not null. 532 * Returned buffer must be deallocatd with `free`. 533 * The buffer is malloc aligned which should suffice for reflection buffers. 534 */ 535 void *fb_codegen_bfbs_alloc_buffer(fb_options_t *opts, fb_schema_t *S, size_t *size) 536 { 537 flatcc_builder_t builder, *B; 538 void *buffer = 0; 539 540 B = &builder; 541 flatcc_builder_init(B); 542 if (export_schema(B, opts, S)) { 543 goto done; 544 } 545 if (!(buffer = flatcc_builder_finalize_buffer(B, size))) { 546 goto done; 547 } 548 sort_objects(buffer); 549 done: 550 flatcc_builder_clear(B); 551 return buffer; 552 } 553 554 int fb_codegen_bfbs_to_file(fb_options_t *opts, fb_schema_t *S) 555 { 556 void *buffer; 557 size_t size; 558 FILE *fp; 559 int ret = -1; 560 561 fp = open_file(opts, S); 562 if (!fp) { 563 return -1; 564 } 565 buffer = fb_codegen_bfbs_alloc_buffer(opts, S, &size); 566 if (!buffer) { 567 fprintf(stderr, "failed to generate binary schema\n"); 568 goto done; 569 } 570 if (size != fwrite(buffer, 1, size, fp)) { 571 fprintf(stderr, "could not write binary schema to file\n"); 572 goto done; 573 } 574 ret = 0; 575 done: 576 if (buffer) { 577 free(buffer); 578 } 579 close_file(fp); 580 return ret; 581 }