aboutsummaryrefslogtreecommitdiff
path: root/flatcc/src/compiler/codegen_schema.c
diff options
context:
space:
mode:
Diffstat (limited to 'flatcc/src/compiler/codegen_schema.c')
-rw-r--r--flatcc/src/compiler/codegen_schema.c581
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;
+}