diff options
Diffstat (limited to 'flatcc/src/compiler/codegen_schema.c')
-rw-r--r-- | flatcc/src/compiler/codegen_schema.c | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/flatcc/src/compiler/codegen_schema.c b/flatcc/src/compiler/codegen_schema.c new file mode 100644 index 0000000..d0c9fde --- /dev/null +++ b/flatcc/src/compiler/codegen_schema.c @@ -0,0 +1,581 @@ +#include <stdio.h> +#include <stdlib.h> +#include "flatcc/reflection/reflection_builder.h" +#include "symbols.h" +#include "parser.h" +#include "codegen.h" +#include "fileio.h" +/* Needed to store length prefix. */ +#include "catalog.h" + +#define BaseType(x) FLATBUFFERS_WRAP_NAMESPACE(reflection_BaseType, x) + +static flatbuffers_bool_t is_optional_type(fb_value_t type, int optional, int required) +{ + if (required) return 0; + if (optional) return 1; + if (type.type == vt_scalar_type) return 0; + if (type.type == vt_compound_type_ref && type.ct->symbol.kind == fb_is_enum) return 0; + return 1; +} + +static reflection_Type_ref_t export_type(flatcc_builder_t *B, fb_value_t type) +{ + fb_scalar_type_t st = fb_missing_type; + int32_t index = -1; + reflection_BaseType_enum_t base_type = BaseType(None); + reflection_BaseType_enum_t element = BaseType(None); + reflection_BaseType_enum_t primitive = BaseType(None); + uint16_t fixed_length = 0; + + switch (type.type) { + case vt_scalar_type: + st = type.st; + break; + case vt_vector_type: + st = type.st; + base_type = BaseType(Vector); + break; + case vt_vector_string_type: + element = BaseType(String); + base_type = BaseType(Vector); + break; + case vt_vector_compound_type_ref: + index = (int32_t)type.ct->export_index; + switch (type.ct->symbol.kind) { + case fb_is_enum: + st = type.ct->type.st; + base_type = BaseType(Vector); + break; + case fb_is_struct: + case fb_is_table: + base_type = BaseType(Vector); + element = BaseType(Obj); + break; + case fb_is_union: + base_type = BaseType(Vector); + element = BaseType(Union); + break; + default: + break; + } + break; + case vt_string_type: + base_type = BaseType(String); + break; + case vt_compound_type_ref: + index = (int32_t)type.ct->export_index; + switch (type.ct->symbol.kind) { + case fb_is_enum: + st = type.ct->type.st; + break; + case fb_is_struct: + case fb_is_table: + base_type = BaseType(Obj); + break; + case fb_is_union: + base_type = BaseType(Union); + break; + default: + index = -1; + break; + } + break; + case vt_fixed_array_type: + st = type.st; + base_type = BaseType(Array); + fixed_length = (uint16_t)type.len; + break; + case vt_fixed_array_string_type: + break; + element = BaseType(Byte); + base_type = BaseType(Array); + fixed_length = (uint16_t)type.len; + break; + case vt_fixed_array_compound_type_ref: + index = (int32_t)type.ct->export_index; + switch (type.ct->symbol.kind) { + case fb_is_enum: + st = type.ct->type.st; + break; + case fb_is_struct: + case fb_is_table: + element = BaseType(Obj); + break; + case fb_is_union: + element = BaseType(Union); + break; + default: + break; + } + base_type = BaseType(Array); + fixed_length = (uint16_t)type.len; + break; + default: + break; + } + /* If st is set, resolve scalar type and set it to base_type or element. */ + switch (st) { + case fb_missing_type: break; + case fb_ulong: primitive = BaseType(ULong); break; + case fb_uint: primitive = BaseType(UInt); break; + case fb_ushort: primitive = BaseType(UShort); break; + case fb_ubyte: primitive = BaseType(UByte); break; + case fb_bool: primitive = BaseType(Bool); break; + case fb_long: primitive = BaseType(Long); break; + case fb_int: primitive = BaseType(Int); break; + case fb_short: primitive = BaseType(Short); break; + case fb_byte: primitive = BaseType(Byte); break; + case fb_double: primitive = BaseType(Double); break; + case fb_float: primitive = BaseType(Float); break; + /* TODO: Googles flatc tool does not have char arrays so we use Byte as element type */ + case fb_char: primitive = BaseType(Byte); break; + default: break; + } + + if (base_type == BaseType(None)) { + base_type = primitive; + } else if (base_type == BaseType(Vector) || base_type == BaseType(Array)) { + if (element == BaseType(None)) { + element = primitive; + } + } + return reflection_Type_create(B, base_type, element, index, fixed_length); +} + +static void export_attributes(flatcc_builder_t *B, fb_metadata_t *m) +{ + for (; m; m = m->link) { + reflection_KeyValue_vec_push_start(B); + reflection_KeyValue_key_create_strn(B, m->ident->text, (size_t)m->ident->len); + if (m->value.type == vt_string) { + reflection_KeyValue_value_create_strn(B, m->value.s.s, (size_t)m->value.s.len); + } + reflection_KeyValue_vec_push_end(B); + } +} + +static void export_fields(flatcc_builder_t *B, fb_compound_type_t *ct) +{ + fb_symbol_t *sym; + fb_member_t *member; + flatbuffers_bool_t has_key, deprecated, required, optional, key_processed = 0; + int64_t default_integer; + double default_real; + + for (sym = ct->members; sym; sym = sym->link) { + member = (fb_member_t *)sym; + /* + * Unlike `flatc` we allow multiple keys in the parser, but + * there is no way to tell which key is default in the + * reflection schema because the fields are sorted, so we only + * export the default (first) key. + */ + has_key = !key_processed && (member->metadata_flags & fb_f_key) != 0; + required = (member->metadata_flags & fb_f_required) != 0; + default_integer = 0; + default_real = 0.0; + deprecated = (member->metadata_flags & fb_f_deprecated) != 0; + /* + * Flag is only set when `= null` is used in the schema, but + * non-scalar types are optional by default and therfore also + * true in the binary schema. + */ + optional = is_optional_type(member->type, !!(member->flags & fb_fm_optional), required); + + if ((member->type.type == vt_compound_type_ref || member->type.type == vt_vector_compound_type_ref) + && member->type.ct->symbol.kind == fb_is_union) { + reflection_Field_vec_push_start(B); + reflection_Field_name_start(B); + reflection_Field_name_append(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); + reflection_Field_name_append(B, "_type", 5); + reflection_Field_name_end(B); + switch(member->type.type) { + case vt_compound_type_ref: + reflection_Field_type_create(B, BaseType(UType), BaseType(None), -1, 0); + break; + case vt_vector_compound_type_ref: + reflection_Field_type_create(B, BaseType(Vector), BaseType(UType), -1, 0); + break; + } + reflection_Field_offset_add(B, (uint16_t)(member->id - 1 + 2) * sizeof(flatbuffers_voffset_t)); + reflection_Field_id_add(B, (uint16_t)(member->id - 1)); + reflection_Field_deprecated_add(B, deprecated); + reflection_Field_vec_push_end(B); + } + reflection_Field_vec_push_start(B); + reflection_Field_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); + reflection_Field_type_add(B, export_type(B, member->type)); + switch (ct->symbol.kind) { + case fb_is_table: + switch (member->value.type) { + case vt_uint: + default_integer = (int64_t)member->value.u; + break; + case vt_int: + default_integer = (int64_t)member->value.i; + break; + case vt_bool: + default_integer = (int64_t)member->value.b; + break; + case vt_float: + default_real = member->value.f; + break; + } + reflection_Field_default_integer_add(B, default_integer); + reflection_Field_default_real_add(B, default_real); + reflection_Field_id_add(B, (uint16_t)member->id); + reflection_Field_offset_add(B, (uint16_t)(member->id + 2) * sizeof(flatbuffers_voffset_t)); + reflection_Field_key_add(B, has_key); + reflection_Field_required_add(B, required); + reflection_Field_optional_add(B, optional); + break; + case fb_is_struct: + reflection_Field_offset_add(B, (uint16_t)member->offset); + break; + default: break; + } + /* Deprecated struct fields not supported by `flatc` but is here as an option. */ + reflection_Field_deprecated_add(B, deprecated); + if (member->metadata) { + reflection_Field_attributes_start(B); + export_attributes(B, member->metadata); + reflection_Field_attributes_end(B); + } + reflection_Field_vec_push_end(B); + key_processed |= has_key; + } +} + +/* `vec` is filled with references to the constructed objects. */ +static void export_objects(flatcc_builder_t *B, object_entry_t *objects, int nobjects, + reflection_Object_ref_t *object_map) +{ + int i, is_struct; + fb_compound_type_t *ct; + + for (i = 0; i < nobjects; ++i) { + ct = objects[i].ct; + reflection_Object_start(B); + reflection_Object_name_create_str(B, objects[i].name); + /* + * We can post sort-fields because the index is not used, unlike + * objects and enums. + */ + reflection_Object_fields_start(B); + export_fields(B, ct); + reflection_Object_fields_end(B); + is_struct = ct->symbol.kind == fb_is_struct; + if (is_struct) { + reflection_Object_bytesize_add(B, (int32_t)ct->size); + } + reflection_Object_is_struct_add(B, (flatbuffers_bool_t)is_struct); + reflection_Object_minalign_add(B, ct->align); + if (ct->metadata) { + reflection_Object_attributes_start(B); + export_attributes(B, ct->metadata); + reflection_Object_attributes_end(B); + } + object_map[i] = reflection_Object_end(B); + } + reflection_Schema_objects_create(B, object_map, (size_t)nobjects); +} + +static void export_enumval(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map) +{ + int is_union = object_map != 0; + + reflection_EnumVal_vec_push_start(B); + reflection_EnumVal_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); + if (is_union) { + if (member->type.type == vt_compound_type_ref) { + /* object is deprecated in favor of union_type to support mixed union types. */ + reflection_EnumVal_object_add(B, object_map[member->type.ct->export_index]); + } + reflection_EnumVal_union_type_add(B, export_type(B, member->type)); + } + reflection_EnumVal_value_add(B, (int64_t)member->value.u); + reflection_EnumVal_vec_push_end(B); +} + +static void export_enums(flatcc_builder_t *B, enum_entry_t *enums, int nenums, + reflection_Object_ref_t *object_map) +{ + int i, is_union; + fb_compound_type_t *ct; + fb_symbol_t *sym; + + reflection_Schema_enums_start(B); + for (i = 0; i < nenums; ++i) { + ct = enums[i].ct; + is_union = ct->symbol.kind == fb_is_union; + reflection_Enum_vec_push_start(B); + reflection_Enum_name_create_str(B, enums[i].name); + reflection_Enum_values_start(B); + for (sym = ct->members; sym; sym = sym->link) { + export_enumval(B, (fb_member_t *)sym, is_union ? object_map : 0); + } + reflection_Enum_values_end(B); + reflection_Enum_is_union_add(B, (flatbuffers_bool_t)is_union); + reflection_Enum_underlying_type_add(B, export_type(B, ct->type)); + if (ct->metadata) { + reflection_Enum_attributes_start(B); + export_attributes(B, ct->metadata); + reflection_Enum_attributes_end(B); + } + reflection_Enum_vec_push_end(B); + } + reflection_Schema_enums_end(B); +} + +static void export_root_type(flatcc_builder_t *B, fb_symbol_t * root_type, + reflection_Object_ref_t *object_map) +{ + fb_compound_type_t *ct; + if (root_type) { + /* + * We could also store a struct object here, but since the + * binrary schema says root_table, not root_type as in the text + * schema, it would be misleading. + */ + if (root_type->kind == fb_is_table) { + ct = (fb_compound_type_t *)root_type; + reflection_Schema_root_table_add(B, object_map[ct->export_index]); + } + } +} + +static void export_call(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map) +{ + reflection_RPCCall_vec_push_start(B); + reflection_RPCCall_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len); + reflection_RPCCall_request_add(B, object_map[member->req_type.ct->export_index]); + reflection_RPCCall_response_add(B, object_map[member->type.ct->export_index]); + if (member->metadata) { + reflection_RPCCall_attributes_start(B); + export_attributes(B, member->metadata); + reflection_RPCCall_attributes_end(B); + } + reflection_RPCCall_vec_push_end(B); +} + +static void export_services(flatcc_builder_t *B, service_entry_t *services, int nservices, + reflection_Object_ref_t *object_map) +{ + int i; + fb_compound_type_t *ct; + fb_symbol_t *sym; + + reflection_Schema_services_start(B); + for (i = 0; i < nservices; ++i) { + ct = services[i].ct; + reflection_Service_vec_push_start(B); + reflection_Service_name_create_str(B, services[i].name); + reflection_Service_calls_start(B); + for (sym = ct->members; sym; sym = sym->link) { + export_call(B, (fb_member_t *)sym, object_map); + } + reflection_Service_calls_end(B); + if (ct->metadata) { + reflection_Service_attributes_start(B); + export_attributes(B, ct->metadata); + reflection_Service_attributes_end(B); + } + reflection_Service_vec_push_end(B); + } + reflection_Schema_services_end(B); +} + +static int export_schema(flatcc_builder_t *B, fb_options_t *opts, fb_schema_t *S) +{ + catalog_t catalog; + reflection_Object_ref_t *object_map = 0; + + if (build_catalog(&catalog, S, opts->bgen_qualify_names, &S->root_schema->scope_index)) { + return -1; + } + + if (catalog.nobjects > 0 && !(object_map = malloc((size_t)catalog.nobjects * sizeof(object_map[0])))) { + clear_catalog(&catalog); + return -1; + } + + /* Build the schema. */ + + if (opts->bgen_length_prefix) { + reflection_Schema_start_as_root_with_size(B); + } else { + reflection_Schema_start_as_root(B); + } + if (S->file_identifier.type == vt_string) { + reflection_Schema_file_ident_create(B, + S->file_identifier.s.s, (size_t)S->file_identifier.s.len); + } + if (S->file_extension.type == vt_string) { + reflection_Schema_file_ext_create(B, + S->file_extension.s.s, (size_t)S->file_extension.s.len); + } + export_objects(B, catalog.objects, catalog.nobjects, object_map); + export_enums(B, catalog.enums, catalog.nenums, object_map); + export_root_type(B, S->root_type.type, object_map); + export_services(B, catalog.services, catalog.nservices, object_map); + + reflection_Schema_end_as_root(B); + + /* Clean up support datastructures. */ + + clear_catalog(&catalog); + if (object_map) { + free(object_map); + } + return 0; +} + +/* + * We do not not sort attributes because we would loose ordering + * information between different attributes, and between same named + * attributes because the sort is not stable. + * + * The C bindings has a scan interface that can find attributes + * in order of appearance. + * + * Field sorting is done on the finished buffer. + */ +static void sort_objects(void *buffer) +{ + size_t i; + reflection_Schema_table_t schema; + reflection_Object_vec_t objects; + reflection_Object_table_t object; + reflection_Field_vec_t fields; + reflection_Field_mutable_vec_t mfields; + + schema = reflection_Schema_as_root(buffer); + objects = reflection_Schema_objects(schema); + for (i = 0; i < reflection_Object_vec_len(objects); ++i) { + object = reflection_Object_vec_at(objects, i); + fields = reflection_Object_fields(object); + if (fields) { + mfields = (reflection_Field_mutable_vec_t)fields; + reflection_Field_vec_sort(mfields); + } + } +} + +static FILE *open_file(fb_options_t *opts, fb_schema_t *S) +{ + FILE *fp = 0; + char *path = 0, *ext = 0; + const char *prefix = opts->outpath ? opts->outpath : ""; + size_t len, prefix_len = strlen(prefix); + const char *name; + + name = S->basename; + len = strlen(name); + + ext = fb_create_path_ext(".", flatbuffers_extension); + /* We generally should not use cgen options here, but in this case it makes sense. */ + if (opts->gen_stdout) { + return stdout; + } + checkmem((path = fb_create_join_path_n(prefix, prefix_len, name, len, ext, 1))); + fp = fopen(path, "wb"); + if (!fp) { + fprintf(stderr, "error opening file for writing binary schema: %s\n", path); + } + free(path); + free(ext); + return fp; +} + +static void close_file(FILE *fp) +{ + if (fp && fp != stdout) { + fclose(fp); + } +} + +/* + * Normally enums are required to be ascending in the schema and + * therefore there is no need to sort enums. If not, we export them in + * the order defined anyway becuase there is no well-defined ordering + * and blindly sorting the content would just loose more information. + * + * In conclusion: find by enum value is only supported when enums are + * defined in consequtive order. + * + * refers to: `opts->ascending_enum` + * + * `size` must hold the maximum buffer size. + * Returns intput buffer if successful and updates size argument. + */ +void *fb_codegen_bfbs_to_buffer(fb_options_t *opts, fb_schema_t *S, void *buffer, size_t *size) +{ + flatcc_builder_t builder, *B; + + B = &builder; + flatcc_builder_init(B); + export_schema(B, opts, S); + if (!flatcc_builder_copy_buffer(B, buffer, *size)) { + goto done; + } + sort_objects(buffer); +done: + *size = flatcc_builder_get_buffer_size(B); + flatcc_builder_clear(B); + return buffer; +} + +/* + * Like to_buffer, but returns allocated buffer. + * Updates size argument with buffer size if not null. + * Returned buffer must be deallocatd with `free`. + * The buffer is malloc aligned which should suffice for reflection buffers. + */ +void *fb_codegen_bfbs_alloc_buffer(fb_options_t *opts, fb_schema_t *S, size_t *size) +{ + flatcc_builder_t builder, *B; + void *buffer = 0; + + B = &builder; + flatcc_builder_init(B); + if (export_schema(B, opts, S)) { + goto done; + } + if (!(buffer = flatcc_builder_finalize_buffer(B, size))) { + goto done; + } + sort_objects(buffer); +done: + flatcc_builder_clear(B); + return buffer; +} + +int fb_codegen_bfbs_to_file(fb_options_t *opts, fb_schema_t *S) +{ + void *buffer; + size_t size; + FILE *fp; + int ret = -1; + + fp = open_file(opts, S); + if (!fp) { + return -1; + } + buffer = fb_codegen_bfbs_alloc_buffer(opts, S, &size); + if (!buffer) { + fprintf(stderr, "failed to generate binary schema\n"); + goto done; + } + if (size != fwrite(buffer, 1, size, fp)) { + fprintf(stderr, "could not write binary schema to file\n"); + goto done; + } + ret = 0; +done: + if (buffer) { + free(buffer); + } + close_file(fp); + return ret; +} |