From 0eb7b3beb037748bf5b469e4df9db862c4833e35 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 27 Dec 2018 20:57:19 -0500 Subject: [PATCH] [Go] Namespaced imports fix (#5097) Track and emit required FlatBuffers namespace imports in generated Go code. Update Go code generator by moving most functionality into the generator class, to facilitate namespace tracking. (Note that the git diff in this combined commit may appear large due to this refactoring, but very little code was actually changed.) Update Go code generator by tracking namespace imports when generating FlatBuffers code. Update Go code generator by emitting package imports to correctly reference code in other FlatBuffers namespaces. Create Go test that checks the usage of InParentNamespace objects (as defined in the example schema). Create Docker test that checks the Go language port. Fixes #4883 Fixes #3927 Individual commits: * remove "static" from soon-to-be method functions * move almost all functions into class as methods * set current namespace and emit package names if needed * track imported namespaces * parent namespaces work * docker test for go ^1.11 * update base image name for go docker test * remove cerr debugging * formatting fixes * re-run generate_code.sh * explicitly test namespace imports and usage --- src/idl_gen_go.cpp | 1419 +++++++++-------- tests/GoTest.sh | 3 + tests/MyGame/Example/Monster.go | 6 +- .../languages/Dockerfile.testing.golang.1_11 | 7 + tests/go_test.go | 53 + .../NamespaceA/SecondTableInA.go | 6 +- .../NamespaceA/TableInFirstNS.go | 10 +- tests/namespace_test/NamespaceC/TableInC.go | 10 +- 8 files changed, 815 insertions(+), 699 deletions(-) create mode 100644 tests/docker/languages/Dockerfile.testing.golang.1_11 diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index fe4939edf..4a4029360 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -50,15 +50,6 @@ static const char * const g_golang_keywords[] = { "for", "import", "return", "var", }; -static std::string GenGetter(const Type &type); -static std::string GenMethod(const FieldDef &field); -static std::string GenConstant(const FieldDef &field); -static void GenStructBuilder(const StructDef &struct_def, - std::string *code_ptr); -static void GenReceiver(const StructDef &struct_def, std::string *code_ptr); -static std::string GenTypeBasic(const Type &type); -static std::string GenTypeGet(const Type &type); -static std::string TypeName(const FieldDef &field); static std::string GoIdentity(const std::string &name) { for (size_t i = 0; i < sizeof(g_golang_keywords) / sizeof(g_golang_keywords[0]); i++) { @@ -68,686 +59,13 @@ static std::string GoIdentity(const std::string &name) { return MakeCamel(name, false); } -// Most field accessors need to retrieve and test the field offset first, -// this is the prefix code for that. -std::string OffsetPrefix(const FieldDef &field) { - return "{\n\to := flatbuffers.UOffsetT(rcv._tab.Offset(" + - NumToString(field.value.offset) + "))\n\tif o != 0 {\n"; -} - -// Begin a class declaration. -static void BeginClass(const StructDef &struct_def, std::string *code_ptr) { - std::string &code = *code_ptr; - - code += "type " + struct_def.name + " struct {\n\t"; - - // _ is reserved in flatbuffers field names, so no chance of name conflict: - code += "_tab "; - code += struct_def.fixed ? "flatbuffers.Struct" : "flatbuffers.Table"; - code += "\n}\n\n"; -} - -// Construct the name of the type alias for this enum. -std::string GetEnumTypeName(const EnumDef &enum_def) { - return GoIdentity(enum_def.name); -} - -// Create a type for the enum values. -static void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "type " + GetEnumTypeName(enum_def) + " = "; - code += GenTypeBasic(enum_def.underlying_type) + "\n"; -} - -// Begin enum code with a class declaration. -static void BeginEnum(std::string *code_ptr) { - std::string &code = *code_ptr; - code += "const (\n"; -} - -// A single enum member. -static void EnumMember(const EnumDef &enum_def, const EnumVal ev, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "\t"; - code += enum_def.name; - code += ev.name; - code += " "; - code += GetEnumTypeName(enum_def); - code += " = "; - code += NumToString(ev.value) + "\n"; -} - -// End enum code. -static void EndEnum(std::string *code_ptr) { - std::string &code = *code_ptr; - code += ")\n\n"; -} - -// Begin enum name code. -static void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "var EnumNames"; - code += enum_def.name; - code += " = map[" + GetEnumTypeName(enum_def) + "]string{\n"; -} - -// A single enum name member. -static void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "\t"; - code += enum_def.name; - code += ev.name; - code += ":\""; - code += ev.name; - code += "\",\n"; -} - -// End enum name code. -static void EndEnumNames(std::string *code_ptr) { - std::string &code = *code_ptr; - code += "}\n\n"; -} - -// Initialize a new struct or table from existing data. -static void NewRootTypeFromBuffer(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - - code += "func GetRootAs"; - code += struct_def.name; - code += "(buf []byte, offset flatbuffers.UOffsetT) "; - code += "*" + struct_def.name + ""; - code += " {\n"; - code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; - code += "\tx := &" + struct_def.name + "{}\n"; - code += "\tx.Init(buf, n+offset)\n"; - code += "\treturn x\n"; - code += "}\n\n"; -} - -// Initialize an existing object with other data, to avoid an allocation. -static void InitializeExisting(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - - GenReceiver(struct_def, code_ptr); - code += " Init(buf []byte, i flatbuffers.UOffsetT) "; - code += "{\n"; - code += "\trcv._tab.Bytes = buf\n"; - code += "\trcv._tab.Pos = i\n"; - code += "}\n\n"; -} - -// Implement the table accessor -static void GenTableAccessor(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - - GenReceiver(struct_def, code_ptr); - code += " Table() flatbuffers.Table "; - code += "{\n"; - - if (struct_def.fixed) { - code += "\treturn rcv._tab.Table\n"; - } else { - code += "\treturn rcv._tab\n"; - } - code += "}\n\n"; -} - -// Get the length of a vector. -static void GetVectorLen(const StructDef &struct_def, const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name) + "Length("; - code += ") int " + OffsetPrefix(field); - code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n"; - code += "\treturn 0\n}\n\n"; -} - -// Get a [ubyte] vector as a byte slice. -static void GetUByteSlice(const StructDef &struct_def, const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name) + "Bytes("; - code += ") []byte " + OffsetPrefix(field); - code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n"; - code += "\treturn nil\n}\n\n"; -} - -// Get the value of a struct's scalar. -static void GetScalarFieldOfStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - std::string getter = GenGetter(field.value.type); - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "() " + TypeName(field) + " {\n"; - code += "\treturn " + getter; - code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; - code += NumToString(field.value.offset) + "))\n}\n"; -} - -// Get the value of a table's scalar. -static void GetScalarFieldOfTable(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - std::string getter = GenGetter(field.value.type); - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "() " + TypeName(field) + " "; - code += OffsetPrefix(field) + "\t\treturn " + getter; - code += "(o + rcv._tab.Pos)\n\t}\n"; - code += "\treturn " + GenConstant(field) + "\n"; - code += "}\n\n"; -} - -// Get a struct by initializing an existing struct. -// Specific to Struct. -static void GetStructFieldOfStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "(obj *" + TypeName(field); - code += ") *" + TypeName(field); - code += " {\n"; - code += "\tif obj == nil {\n"; - code += "\t\tobj = new(" + TypeName(field) + ")\n"; - code += "\t}\n"; - code += "\tobj.Init(rcv._tab.Bytes, rcv._tab.Pos+"; - code += NumToString(field.value.offset) + ")"; - code += "\n\treturn obj\n"; - code += "}\n"; -} - -// Get a struct by initializing an existing struct. -// Specific to Table. -static void GetStructFieldOfTable(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "(obj *"; - code += TypeName(field); - code += ") *" + TypeName(field) + " " + OffsetPrefix(field); - if (field.value.type.struct_def->fixed) { - code += "\t\tx := o + rcv._tab.Pos\n"; - } else { - code += "\t\tx := rcv._tab.Indirect(o + rcv._tab.Pos)\n"; - } - code += "\t\tif obj == nil {\n"; - code += "\t\t\tobj = new(" + TypeName(field) + ")\n"; - code += "\t\t}\n"; - code += "\t\tobj.Init(rcv._tab.Bytes, x)\n"; - code += "\t\treturn obj\n\t}\n\treturn nil\n"; - code += "}\n\n"; -} - -// Get the value of a string. -static void GetStringField(const StructDef &struct_def, const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "() " + TypeName(field) + " "; - code += OffsetPrefix(field) + "\t\treturn " + GenGetter(field.value.type); - code += "(o + rcv._tab.Pos)\n\t}\n\treturn nil\n"; - code += "}\n\n"; -} - -// Get the value of a union from an object. -static void GetUnionField(const StructDef &struct_def, const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name) + "("; - code += "obj " + TypeName(field) + ") bool "; - code += OffsetPrefix(field); - code += "\t\t" + GenGetter(field.value.type); - code += "(obj, o)\n\t\treturn true\n\t}\n"; - code += "\treturn false\n"; - code += "}\n\n"; -} - -// Get the value of a vector's struct member. -static void GetMemberOfVectorOfStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - auto vectortype = field.value.type.VectorType(); - - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "(obj *" + TypeName(field); - code += ", j int) bool " + OffsetPrefix(field); - code += "\t\tx := rcv._tab.Vector(o)\n"; - code += "\t\tx += flatbuffers.UOffsetT(j) * "; - code += NumToString(InlineSize(vectortype)) + "\n"; - if (!(vectortype.struct_def->fixed)) { - code += "\t\tx = rcv._tab.Indirect(x)\n"; - } - code += "\t\tobj.Init(rcv._tab.Bytes, x)\n"; - code += "\t\treturn true\n\t}\n"; - code += "\treturn false\n"; - code += "}\n\n"; -} - -// Get the value of a vector's non-struct member. Uses a named return -// argument to conveniently set the zero value for the result. -static void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - auto vectortype = field.value.type.VectorType(); - - GenReceiver(struct_def, code_ptr); - code += " " + MakeCamel(field.name); - code += "(j int) " + TypeName(field) + " "; - code += OffsetPrefix(field); - code += "\t\ta := rcv._tab.Vector(o)\n"; - code += "\t\treturn " + GenGetter(field.value.type) + "("; - code += "a + flatbuffers.UOffsetT(j*"; - code += NumToString(InlineSize(vectortype)) + "))\n"; - code += "\t}\n"; - if (vectortype.base_type == BASE_TYPE_STRING) { - code += "\treturn nil\n"; - } else if (vectortype.base_type == BASE_TYPE_BOOL) { - code += "\treturn false\n"; - } else { - code += "\treturn 0\n"; - } - code += "}\n\n"; -} - -// Begin the creator function signature. -static void BeginBuilderArgs(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - - if (code.substr(code.length() - 2) != "\n\n") { - // a previous mutate has not put an extra new line - code += "\n"; - } - code += "func Create" + struct_def.name; - code += "(builder *flatbuffers.Builder"; -} - -// Recursively generate arguments for a constructor, to deal with nested -// structs. -static void StructBuilderArgs(const StructDef &struct_def, - const char *nameprefix, std::string *code_ptr) { - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (IsStruct(field.value.type)) { - // Generate arguments for a struct inside a struct. To ensure names - // don't clash, and to make it obvious these arguments are constructing - // a nested struct, prefix the name with the field name. - StructBuilderArgs(*field.value.type.struct_def, - (nameprefix + (field.name + "_")).c_str(), code_ptr); - } else { - std::string &code = *code_ptr; - code += std::string(", ") + nameprefix; - code += GoIdentity(field.name); - code += " " + GenTypeBasic(field.value.type); - } - } -} - -// End the creator function signature. -static void EndBuilderArgs(std::string *code_ptr) { - std::string &code = *code_ptr; - code += ") flatbuffers.UOffsetT {\n"; -} - -// Recursively generate struct construction statements and instert manual -// padding. -static void StructBuilderBody(const StructDef &struct_def, - const char *nameprefix, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", "; - code += NumToString(struct_def.bytesize) + ")\n"; - for (auto it = struct_def.fields.vec.rbegin(); - it != struct_def.fields.vec.rend(); ++it) { - auto &field = **it; - if (field.padding) - code += "\tbuilder.Pad(" + NumToString(field.padding) + ")\n"; - if (IsStruct(field.value.type)) { - StructBuilderBody(*field.value.type.struct_def, - (nameprefix + (field.name + "_")).c_str(), code_ptr); - } else { - code += "\tbuilder.Prepend" + GenMethod(field) + "("; - code += nameprefix + GoIdentity(field.name) + ")\n"; - } - } -} - -static void EndBuilderBody(std::string *code_ptr) { - std::string &code = *code_ptr; - code += "\treturn builder.Offset()\n"; - code += "}\n"; -} - -// Get the value of a table's starting offset. -static void GetStartOfTable(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "func " + struct_def.name + "Start"; - code += "(builder *flatbuffers.Builder) {\n"; - code += "\tbuilder.StartObject("; - code += NumToString(struct_def.fields.vec.size()); - code += ")\n}\n"; -} - -// Set the value of a table's field. -static void BuildFieldOfTable(const StructDef &struct_def, - const FieldDef &field, const size_t offset, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "func " + struct_def.name + "Add" + MakeCamel(field.name); - code += "(builder *flatbuffers.Builder, "; - code += GoIdentity(field.name) + " "; - if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { - code += "flatbuffers.UOffsetT"; - } else { - code += GenTypeBasic(field.value.type); - } - code += ") {\n"; - code += "\tbuilder.Prepend"; - code += GenMethod(field) + "Slot("; - code += NumToString(offset) + ", "; - if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { - code += "flatbuffers.UOffsetT"; - code += "("; - code += GoIdentity(field.name) + ")"; - } else { - code += GoIdentity(field.name); - } - code += ", " + GenConstant(field); - code += ")\n}\n"; -} - -// Set the value of one of the members of a table's vector. -static void BuildVectorOfTable(const StructDef &struct_def, - const FieldDef &field, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "func " + struct_def.name + "Start"; - code += MakeCamel(field.name); - code += "Vector(builder *flatbuffers.Builder, numElems int) "; - code += "flatbuffers.UOffsetT {\n\treturn builder.StartVector("; - auto vector_type = field.value.type.VectorType(); - auto alignment = InlineAlignment(vector_type); - auto elem_size = InlineSize(vector_type); - code += NumToString(elem_size); - code += ", numElems, " + NumToString(alignment); - code += ")\n}\n"; -} - -// Get the offset of the end of a table. -static void GetEndOffsetOnTable(const StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "func " + struct_def.name + "End"; - code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT "; - code += "{\n\treturn builder.EndObject()\n}\n"; -} - -// Generate the receiver for function signatures. -static void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "func (rcv *" + struct_def.name + ")"; -} - -// Generate a struct field getter, conditioned on its child type(s). -static void GenStructAccessor(const StructDef &struct_def, - const FieldDef &field, std::string *code_ptr) { - GenComment(field.doc_comment, code_ptr, nullptr, ""); - if (IsScalar(field.value.type.base_type)) { - if (struct_def.fixed) { - GetScalarFieldOfStruct(struct_def, field, code_ptr); - } else { - GetScalarFieldOfTable(struct_def, field, code_ptr); - } - } else { - switch (field.value.type.base_type) { - case BASE_TYPE_STRUCT: - if (struct_def.fixed) { - GetStructFieldOfStruct(struct_def, field, code_ptr); - } else { - GetStructFieldOfTable(struct_def, field, code_ptr); - } - break; - case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break; - case BASE_TYPE_VECTOR: { - auto vectortype = field.value.type.VectorType(); - if (vectortype.base_type == BASE_TYPE_STRUCT) { - GetMemberOfVectorOfStruct(struct_def, field, code_ptr); - } else { - GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr); - } - break; - } - case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break; - default: FLATBUFFERS_ASSERT(0); - } - } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { - GetVectorLen(struct_def, field, code_ptr); - if (field.value.type.element == BASE_TYPE_UCHAR) { - GetUByteSlice(struct_def, field, code_ptr); - } - } -} - -// Mutate the value of a struct's scalar. -static void MutateScalarFieldOfStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - std::string type = MakeCamel(GenTypeBasic(field.value.type)); - std::string setter = "rcv._tab.Mutate" + type; - GenReceiver(struct_def, code_ptr); - code += " Mutate" + MakeCamel(field.name); - code += "(n " + TypeName(field) + ") bool {\n\treturn " + setter; - code += "(rcv._tab.Pos+flatbuffers.UOffsetT("; - code += NumToString(field.value.offset) + "), n)\n}\n\n"; -} - -// Mutate the value of a table's scalar. -static void MutateScalarFieldOfTable(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { - std::string &code = *code_ptr; - std::string type = MakeCamel(GenTypeBasic(field.value.type)); - std::string setter = "rcv._tab.Mutate" + type + "Slot"; - GenReceiver(struct_def, code_ptr); - code += " Mutate" + MakeCamel(field.name); - code += "(n " + TypeName(field) + ") bool {\n\treturn "; - code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; - code += "}\n\n"; -} - -// Generate a struct field setter, conditioned on its child type(s). -static void GenStructMutator(const StructDef &struct_def, const FieldDef &field, - std::string *code_ptr) { - GenComment(field.doc_comment, code_ptr, nullptr, ""); - if (IsScalar(field.value.type.base_type)) { - if (struct_def.fixed) { - MutateScalarFieldOfStruct(struct_def, field, code_ptr); - } else { - MutateScalarFieldOfTable(struct_def, field, code_ptr); - } - } -} - -// Generate table constructors, conditioned on its members' types. -static void GenTableBuilders(const StructDef &struct_def, - std::string *code_ptr) { - GetStartOfTable(struct_def, code_ptr); - - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) continue; - - auto offset = it - struct_def.fields.vec.begin(); - BuildFieldOfTable(struct_def, field, offset, code_ptr); - if (field.value.type.base_type == BASE_TYPE_VECTOR) { - BuildVectorOfTable(struct_def, field, code_ptr); - } - } - - GetEndOffsetOnTable(struct_def, code_ptr); -} - -// Generate struct or table methods. -static void GenStruct(const StructDef &struct_def, std::string *code_ptr) { - if (struct_def.generated) return; - - GenComment(struct_def.doc_comment, code_ptr, nullptr); - BeginClass(struct_def, code_ptr); - if (!struct_def.fixed) { - // Generate a special accessor for the table that has been declared as - // the root type. - NewRootTypeFromBuffer(struct_def, code_ptr); - } - // Generate the Init method that sets the field in a pre-existing - // accessor object. This is to allow object reuse. - InitializeExisting(struct_def, code_ptr); - // Generate _tab accessor - GenTableAccessor(struct_def, code_ptr); - - // Generate struct fields accessors - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) continue; - - GenStructAccessor(struct_def, field, code_ptr); - GenStructMutator(struct_def, field, code_ptr); - } - - // Generate builders - if (struct_def.fixed) { - // create a struct constructor function - GenStructBuilder(struct_def, code_ptr); - } else { - // Create a set of functions that allow table construction. - GenTableBuilders(struct_def, code_ptr); - } -} - -// Generate enum declarations. -static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { - if (enum_def.generated) return; - - GenComment(enum_def.doc_comment, code_ptr, nullptr); - GenEnumType(enum_def, code_ptr); - BeginEnum(code_ptr); - for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); - ++it) { - auto &ev = **it; - GenComment(ev.doc_comment, code_ptr, nullptr, "\t"); - EnumMember(enum_def, ev, code_ptr); - } - EndEnum(code_ptr); - - BeginEnumNames(enum_def, code_ptr); - for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); - ++it) { - auto &ev = **it; - EnumNameMember(enum_def, ev, code_ptr); - } - EndEnumNames(code_ptr); -} - -// Returns the function name that is able to read a value of the given type. -static std::string GenGetter(const Type &type) { - switch (type.base_type) { - case BASE_TYPE_STRING: return "rcv._tab.ByteVector"; - case BASE_TYPE_UNION: return "rcv._tab.Union"; - case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); - default: return "rcv._tab.Get" + MakeCamel(GenTypeBasic(type)); - } -} - -// Returns the method name for use with add/put calls. -static std::string GenMethod(const FieldDef &field) { - return IsScalar(field.value.type.base_type) - ? MakeCamel(GenTypeBasic(field.value.type)) - : (IsStruct(field.value.type) ? "Struct" : "UOffsetT"); -} - -static std::string GenTypeBasic(const Type &type) { - static const char *ctypename[] = { - // clang-format off - #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ - #GTYPE, - FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) - #undef FLATBUFFERS_TD - // clang-format on - }; - return ctypename[type.base_type]; -} - -static std::string GenTypePointer(const Type &type) { - switch (type.base_type) { - case BASE_TYPE_STRING: return "[]byte"; - case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); - case BASE_TYPE_STRUCT: return type.struct_def->name; - case BASE_TYPE_UNION: - // fall through - default: return "*flatbuffers.Table"; - } -} - -static std::string GenTypeGet(const Type &type) { - if (type.enum_def != nullptr && !type.enum_def->is_union) { - return GetEnumTypeName(*type.enum_def); - } - return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); -} - -static std::string TypeName(const FieldDef &field) { - return GenTypeGet(field.value.type); -} - -static std::string GenConstant(const FieldDef &field) { - switch (field.value.type.base_type) { - case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; - default: return field.value.constant; - } -} - -// Create a struct with a builder and the struct's arguments. -static void GenStructBuilder(const StructDef &struct_def, - std::string *code_ptr) { - BeginBuilderArgs(struct_def, code_ptr); - StructBuilderArgs(struct_def, "", code_ptr); - EndBuilderArgs(code_ptr); - - StructBuilderBody(struct_def, "", code_ptr); - EndBuilderBody(code_ptr); -} - class GoGenerator : public BaseGenerator { public: GoGenerator(const Parser &parser, const std::string &path, const std::string &file_name, const std::string &go_namespace) : BaseGenerator(parser, path, file_name, "" /* not used*/, - "" /* not used */) { + "" /* not used */), + cur_name_space_(nullptr) { std::istringstream iss(go_namespace); std::string component; while (std::getline(iss, component, '.')) { @@ -759,8 +77,9 @@ class GoGenerator : public BaseGenerator { std::string one_file_code; for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); ++it) { + tracked_imported_namespaces_.clear(); std::string enumcode; - go::GenEnum(**it, &enumcode); + GenEnum(**it, &enumcode); if (parser_.opts.one_file) { one_file_code += enumcode; } else { @@ -770,8 +89,9 @@ class GoGenerator : public BaseGenerator { for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { + tracked_imported_namespaces_.clear(); std::string declcode; - go::GenStruct(**it, &declcode); + GenStruct(**it, &declcode); if (parser_.opts.one_file) { one_file_code += declcode; } else { @@ -791,6 +111,680 @@ class GoGenerator : public BaseGenerator { } private: + Namespace go_namespace_; + Namespace *cur_name_space_; + std::set tracked_imported_namespaces_; + + // Most field accessors need to retrieve and test the field offset first, + // this is the prefix code for that. + std::string OffsetPrefix(const FieldDef &field) { + return "{\n\to := flatbuffers.UOffsetT(rcv._tab.Offset(" + + NumToString(field.value.offset) + "))\n\tif o != 0 {\n"; + } + + // Begin a class declaration. + void BeginClass(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + code += "type " + struct_def.name + " struct {\n\t"; + + // _ is reserved in flatbuffers field names, so no chance of name conflict: + code += "_tab "; + code += struct_def.fixed ? "flatbuffers.Struct" : "flatbuffers.Table"; + code += "\n}\n\n"; + } + + // Construct the name of the type alias for this enum. + std::string GetEnumTypeName(const EnumDef &enum_def) { + return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name)); + } + + // Create a type for the enum values. + void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "type " + GetEnumTypeName(enum_def) + " = "; + code += GenTypeBasic(enum_def.underlying_type) + "\n"; + } + + // Begin enum code with a class declaration. + void BeginEnum(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "const (\n"; + } + + // A single enum member. + void EnumMember(const EnumDef &enum_def, const EnumVal ev, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += "\t"; + code += enum_def.name; + code += ev.name; + code += " "; + code += GetEnumTypeName(enum_def); + code += " = "; + code += NumToString(ev.value) + "\n"; + } + + // End enum code. + void EndEnum(std::string *code_ptr) { + std::string &code = *code_ptr; + code += ")\n\n"; + } + + // Begin enum name code. + void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "var EnumNames"; + code += enum_def.name; + code += " = map[" + GetEnumTypeName(enum_def) + "]string{\n"; + } + + // A single enum name member. + void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, + std::string *code_ptr) { + std::string &code = *code_ptr; + code += "\t"; + code += enum_def.name; + code += ev.name; + code += ":\""; + code += ev.name; + code += "\",\n"; + } + + // End enum name code. + void EndEnumNames(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "}\n\n"; + } + + // Initialize a new struct or table from existing data. + void NewRootTypeFromBuffer(const StructDef &struct_def, + std::string *code_ptr) { + std::string &code = *code_ptr; + + code += "func GetRootAs"; + code += struct_def.name; + code += "(buf []byte, offset flatbuffers.UOffsetT) "; + code += "*" + struct_def.name + ""; + code += " {\n"; + code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; + code += "\tx := &" + struct_def.name + "{}\n"; + code += "\tx.Init(buf, n+offset)\n"; + code += "\treturn x\n"; + code += "}\n\n"; + } + + // Initialize an existing object with other data, to avoid an allocation. + void InitializeExisting(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + GenReceiver(struct_def, code_ptr); + code += " Init(buf []byte, i flatbuffers.UOffsetT) "; + code += "{\n"; + code += "\trcv._tab.Bytes = buf\n"; + code += "\trcv._tab.Pos = i\n"; + code += "}\n\n"; + } + + // Implement the table accessor + void GenTableAccessor(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + GenReceiver(struct_def, code_ptr); + code += " Table() flatbuffers.Table "; + code += "{\n"; + + if (struct_def.fixed) { + code += "\treturn rcv._tab.Table\n"; + } else { + code += "\treturn rcv._tab\n"; + } + code += "}\n\n"; + } + + // Get the length of a vector. + void GetVectorLen(const StructDef &struct_def, const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name) + "Length("; + code += ") int " + OffsetPrefix(field); + code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n"; + code += "\treturn 0\n}\n\n"; + } + + // Get a [ubyte] vector as a byte slice. + void GetUByteSlice(const StructDef &struct_def, const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name) + "Bytes("; + code += ") []byte " + OffsetPrefix(field); + code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n"; + code += "\treturn nil\n}\n\n"; + } + + // Get the value of a struct's scalar. + void GetScalarFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string getter = GenGetter(field.value.type); + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "() " + TypeName(field) + " {\n"; + code += "\treturn " + getter; + code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; + code += NumToString(field.value.offset) + "))\n}\n"; + } + + // Get the value of a table's scalar. + void GetScalarFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string getter = GenGetter(field.value.type); + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "() " + TypeName(field) + " "; + code += OffsetPrefix(field) + "\t\treturn " + getter; + code += "(o + rcv._tab.Pos)\n\t}\n"; + code += "\treturn " + GenConstant(field) + "\n"; + code += "}\n\n"; + } + + // Get a struct by initializing an existing struct. + // Specific to Struct. + void GetStructFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "(obj *" + TypeName(field); + code += ") *" + TypeName(field); + code += " {\n"; + code += "\tif obj == nil {\n"; + code += "\t\tobj = new(" + TypeName(field) + ")\n"; + code += "\t}\n"; + code += "\tobj.Init(rcv._tab.Bytes, rcv._tab.Pos+"; + code += NumToString(field.value.offset) + ")"; + code += "\n\treturn obj\n"; + code += "}\n"; + } + + // Get a struct by initializing an existing struct. + // Specific to Table. + void GetStructFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "(obj *"; + code += TypeName(field); + code += ") *" + TypeName(field) + " " + OffsetPrefix(field); + if (field.value.type.struct_def->fixed) { + code += "\t\tx := o + rcv._tab.Pos\n"; + } else { + code += "\t\tx := rcv._tab.Indirect(o + rcv._tab.Pos)\n"; + } + code += "\t\tif obj == nil {\n"; + code += "\t\t\tobj = new(" + TypeName(field) + ")\n"; + code += "\t\t}\n"; + code += "\t\tobj.Init(rcv._tab.Bytes, x)\n"; + code += "\t\treturn obj\n\t}\n\treturn nil\n"; + code += "}\n\n"; + } + + // Get the value of a string. + void GetStringField(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "() " + TypeName(field) + " "; + code += OffsetPrefix(field) + "\t\treturn " + GenGetter(field.value.type); + code += "(o + rcv._tab.Pos)\n\t}\n\treturn nil\n"; + code += "}\n\n"; + } + + // Get the value of a union from an object. + void GetUnionField(const StructDef &struct_def, const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name) + "("; + code += "obj " + TypeName(field) + ") bool "; + code += OffsetPrefix(field); + code += "\t\t" + GenGetter(field.value.type); + code += "(obj, o)\n\t\treturn true\n\t}\n"; + code += "\treturn false\n"; + code += "}\n\n"; + } + + // Get the value of a vector's struct member. + void GetMemberOfVectorOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + auto vectortype = field.value.type.VectorType(); + + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "(obj *" + TypeName(field); + code += ", j int) bool " + OffsetPrefix(field); + code += "\t\tx := rcv._tab.Vector(o)\n"; + code += "\t\tx += flatbuffers.UOffsetT(j) * "; + code += NumToString(InlineSize(vectortype)) + "\n"; + if (!(vectortype.struct_def->fixed)) { + code += "\t\tx = rcv._tab.Indirect(x)\n"; + } + code += "\t\tobj.Init(rcv._tab.Bytes, x)\n"; + code += "\t\treturn true\n\t}\n"; + code += "\treturn false\n"; + code += "}\n\n"; + } + + // Get the value of a vector's non-struct member. Uses a named return + // argument to conveniently set the zero value for the result. + void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + auto vectortype = field.value.type.VectorType(); + + GenReceiver(struct_def, code_ptr); + code += " " + MakeCamel(field.name); + code += "(j int) " + TypeName(field) + " "; + code += OffsetPrefix(field); + code += "\t\ta := rcv._tab.Vector(o)\n"; + code += "\t\treturn " + GenGetter(field.value.type) + "("; + code += "a + flatbuffers.UOffsetT(j*"; + code += NumToString(InlineSize(vectortype)) + "))\n"; + code += "\t}\n"; + if (vectortype.base_type == BASE_TYPE_STRING) { + code += "\treturn nil\n"; + } else if (vectortype.base_type == BASE_TYPE_BOOL) { + code += "\treturn false\n"; + } else { + code += "\treturn 0\n"; + } + code += "}\n\n"; + } + + // Begin the creator function signature. + void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + if (code.substr(code.length() - 2) != "\n\n") { + // a previous mutate has not put an extra new line + code += "\n"; + } + code += "func Create" + struct_def.name; + code += "(builder *flatbuffers.Builder"; + } + + // Recursively generate arguments for a constructor, to deal with nested + // structs. + void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix, + std::string *code_ptr) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (IsStruct(field.value.type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the field name. + StructBuilderArgs(*field.value.type.struct_def, + (nameprefix + (field.name + "_")).c_str(), code_ptr); + } else { + std::string &code = *code_ptr; + code += std::string(", ") + nameprefix; + code += GoIdentity(field.name); + code += " " + GenTypeBasic(field.value.type); + } + } + } + + // End the creator function signature. + void EndBuilderArgs(std::string *code_ptr) { + std::string &code = *code_ptr; + code += ") flatbuffers.UOffsetT {\n"; + } + + // Recursively generate struct construction statements and instert manual + // padding. + void StructBuilderBody(const StructDef &struct_def, + const char *nameprefix, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", "; + code += NumToString(struct_def.bytesize) + ")\n"; + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); ++it) { + auto &field = **it; + if (field.padding) + code += "\tbuilder.Pad(" + NumToString(field.padding) + ")\n"; + if (IsStruct(field.value.type)) { + StructBuilderBody(*field.value.type.struct_def, + (nameprefix + (field.name + "_")).c_str(), code_ptr); + } else { + code += "\tbuilder.Prepend" + GenMethod(field) + "("; + code += nameprefix + GoIdentity(field.name) + ")\n"; + } + } + } + + void EndBuilderBody(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "\treturn builder.Offset()\n"; + code += "}\n"; + } + + // Get the value of a table's starting offset. + void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func " + struct_def.name + "Start"; + code += "(builder *flatbuffers.Builder) {\n"; + code += "\tbuilder.StartObject("; + code += NumToString(struct_def.fields.vec.size()); + code += ")\n}\n"; + } + + // Set the value of a table's field. + void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field, + const size_t offset, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func " + struct_def.name + "Add" + MakeCamel(field.name); + code += "(builder *flatbuffers.Builder, "; + code += GoIdentity(field.name) + " "; + if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { + code += "flatbuffers.UOffsetT"; + } else { + code += GenTypeBasic(field.value.type); + } + code += ") {\n"; + code += "\tbuilder.Prepend"; + code += GenMethod(field) + "Slot("; + code += NumToString(offset) + ", "; + if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { + code += "flatbuffers.UOffsetT"; + code += "("; + code += GoIdentity(field.name) + ")"; + } else { + code += GoIdentity(field.name); + } + code += ", " + GenConstant(field); + code += ")\n}\n"; + } + + // Set the value of one of the members of a table's vector. + void BuildVectorOfTable(const StructDef &struct_def, + const FieldDef &field, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func " + struct_def.name + "Start"; + code += MakeCamel(field.name); + code += "Vector(builder *flatbuffers.Builder, numElems int) "; + code += "flatbuffers.UOffsetT {\n\treturn builder.StartVector("; + auto vector_type = field.value.type.VectorType(); + auto alignment = InlineAlignment(vector_type); + auto elem_size = InlineSize(vector_type); + code += NumToString(elem_size); + code += ", numElems, " + NumToString(alignment); + code += ")\n}\n"; + } + + // Get the offset of the end of a table. + void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func " + struct_def.name + "End"; + code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT "; + code += "{\n\treturn builder.EndObject()\n}\n"; + } + + // Generate the receiver for function signatures. + void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func (rcv *" + struct_def.name + ")"; + } + + // Generate a struct field getter, conditioned on its child type(s). + void GenStructAccessor(const StructDef &struct_def, + const FieldDef &field, std::string *code_ptr) { + GenComment(field.doc_comment, code_ptr, nullptr, ""); + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + GetScalarFieldOfStruct(struct_def, field, code_ptr); + } else { + GetScalarFieldOfTable(struct_def, field, code_ptr); + } + } else { + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: + if (struct_def.fixed) { + GetStructFieldOfStruct(struct_def, field, code_ptr); + } else { + GetStructFieldOfTable(struct_def, field, code_ptr); + } + break; + case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break; + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + GetMemberOfVectorOfStruct(struct_def, field, code_ptr); + } else { + GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr); + } + break; + } + case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break; + default: FLATBUFFERS_ASSERT(0); + } + } + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + GetVectorLen(struct_def, field, code_ptr); + if (field.value.type.element == BASE_TYPE_UCHAR) { + GetUByteSlice(struct_def, field, code_ptr); + } + } + } + + // Mutate the value of a struct's scalar. + void MutateScalarFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool {\n\treturn " + setter; + code += "(rcv._tab.Pos+flatbuffers.UOffsetT("; + code += NumToString(field.value.offset) + "), n)\n}\n\n"; + } + + // Mutate the value of a table's scalar. + void MutateScalarFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type + "Slot"; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool {\n\treturn "; + code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; + code += "}\n\n"; + } + + // Generate a struct field setter, conditioned on its child type(s). + void GenStructMutator(const StructDef &struct_def, const FieldDef &field, + std::string *code_ptr) { + GenComment(field.doc_comment, code_ptr, nullptr, ""); + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + MutateScalarFieldOfStruct(struct_def, field, code_ptr); + } else { + MutateScalarFieldOfTable(struct_def, field, code_ptr); + } + } + } + + // Generate table constructors, conditioned on its members' types. + void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) { + GetStartOfTable(struct_def, code_ptr); + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + auto offset = it - struct_def.fields.vec.begin(); + BuildFieldOfTable(struct_def, field, offset, code_ptr); + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + BuildVectorOfTable(struct_def, field, code_ptr); + } + } + + GetEndOffsetOnTable(struct_def, code_ptr); + } + + // Generate struct or table methods. + void GenStruct(const StructDef &struct_def, std::string *code_ptr) { + if (struct_def.generated) return; + + cur_name_space_ = struct_def.defined_namespace; + + GenComment(struct_def.doc_comment, code_ptr, nullptr); + BeginClass(struct_def, code_ptr); + if (!struct_def.fixed) { + // Generate a special accessor for the table that has been declared as + // the root type. + NewRootTypeFromBuffer(struct_def, code_ptr); + } + // Generate the Init method that sets the field in a pre-existing + // accessor object. This is to allow object reuse. + InitializeExisting(struct_def, code_ptr); + // Generate _tab accessor + GenTableAccessor(struct_def, code_ptr); + + // Generate struct fields accessors + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + GenStructAccessor(struct_def, field, code_ptr); + GenStructMutator(struct_def, field, code_ptr); + } + + // Generate builders + if (struct_def.fixed) { + // create a struct constructor function + GenStructBuilder(struct_def, code_ptr); + } else { + // Create a set of functions that allow table construction. + GenTableBuilders(struct_def, code_ptr); + } + } + + // Generate enum declarations. + void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { + if (enum_def.generated) return; + + cur_name_space_ = enum_def.defined_namespace; + + GenComment(enum_def.doc_comment, code_ptr, nullptr); + GenEnumType(enum_def, code_ptr); + BeginEnum(code_ptr); + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + GenComment(ev.doc_comment, code_ptr, nullptr, "\t"); + EnumMember(enum_def, ev, code_ptr); + } + EndEnum(code_ptr); + + BeginEnumNames(enum_def, code_ptr); + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + EnumNameMember(enum_def, ev, code_ptr); + } + EndEnumNames(code_ptr); + } + + // Returns the function name that is able to read a value of the given type. + std::string GenGetter(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_STRING: return "rcv._tab.ByteVector"; + case BASE_TYPE_UNION: return "rcv._tab.Union"; + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); + default: return "rcv._tab.Get" + MakeCamel(GenTypeBasic(type)); + } + } + + // Returns the method name for use with add/put calls. + std::string GenMethod(const FieldDef &field) { + return IsScalar(field.value.type.base_type) + ? MakeCamel(GenTypeBasic(field.value.type)) + : (IsStruct(field.value.type) ? "Struct" : "UOffsetT"); + } + + std::string GenTypeBasic(const Type &type) { + static const char *ctypename[] = { + // clang-format off + #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ + #GTYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD + // clang-format on + }; + return ctypename[type.base_type]; + } + + std::string GenTypePointer(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_STRING: return "[]byte"; + case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); + case BASE_TYPE_STRUCT: return WrapInNameSpaceAndTrack(*type.struct_def); + case BASE_TYPE_UNION: + // fall through + default: return "*flatbuffers.Table"; + } + } + + std::string GenTypeGet(const Type &type) { + if (type.enum_def != nullptr && !type.enum_def->is_union) { + return GetEnumTypeName(*type.enum_def); + } + return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); + } + + std::string TypeName(const FieldDef &field) { + return GenTypeGet(field.value.type); + } + + std::string GenConstant(const FieldDef &field) { + switch (field.value.type.base_type) { + case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; + default: return field.value.constant; + } + } + + // Create a struct with a builder and the struct's arguments. + void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) { + BeginBuilderArgs(struct_def, code_ptr); + StructBuilderArgs(struct_def, "", code_ptr); + EndBuilderArgs(code_ptr); + + StructBuilderBody(struct_def, "", code_ptr); + EndBuilderBody(code_ptr); + } // Begin by declaring namespace and imports. void BeginFile(const std::string name_space_name, const bool needs_imports, std::string *code_ptr) { @@ -804,6 +798,15 @@ class GoGenerator : public BaseGenerator { } else { code += "\tflatbuffers \"github.com/google/flatbuffers/go\"\n"; } + if (tracked_imported_namespaces_.size() > 0) { + code += "\n"; + for (auto it = tracked_imported_namespaces_.begin(); + it != tracked_imported_namespaces_.end(); + ++it) { + code += "\t" + NamespaceImportName(*it) + " \"" + \ + NamespaceImportPath(*it) + "\"\n"; + } + } code += ")\n\n"; } } @@ -822,7 +825,49 @@ class GoGenerator : public BaseGenerator { return SaveFile(filename.c_str(), code, false); } - Namespace go_namespace_; + // Create the full name of the imported namespace (format: A__B__C). + std::string NamespaceImportName(const Namespace *ns) { + std::string s = ""; + for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { + if (s.size() == 0) { + s += *it; + } else { + s += "__" + *it; + } + } + return s; + } + + // Create the full path for the imported namespace (format: A/B/C). + std::string NamespaceImportPath(const Namespace *ns) { + std::string s = ""; + for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { + if (s.size() == 0) { + s += *it; + } else { + s += "/" + *it; + } + } + return s; + } + + // Ensure that a type is prefixed with its go package import name if it is + // used outside of its namespace. + std::string WrapInNameSpaceAndTrack(const Namespace *ns, + const std::string &name) { + if (CurrentNameSpace() == ns) return name; + + tracked_imported_namespaces_.insert(ns); + + std::string import_name = NamespaceImportName(ns); + return import_name + "." + name; + } + + std::string WrapInNameSpaceAndTrack(const Definition &def) { + return WrapInNameSpaceAndTrack(def.defined_namespace, def.name); + } + + const Namespace *CurrentNameSpace() const { return cur_name_space_; } }; } // namespace go diff --git a/tests/GoTest.sh b/tests/GoTest.sh index 88e7a3f70..f50b91df1 100755 --- a/tests/GoTest.sh +++ b/tests/GoTest.sh @@ -26,10 +26,13 @@ go_src=${go_path}/src # Copy flatbuffer Go files to their own package directories to compile the # test binary: mkdir -p ${go_src}/MyGame/Example +mkdir -p ${go_src}/MyGame/Example2 mkdir -p ${go_src}/github.com/google/flatbuffers/go mkdir -p ${go_src}/flatbuffers_test +cp -a MyGame/*.go ./go_gen/src/MyGame/ cp -a MyGame/Example/*.go ./go_gen/src/MyGame/Example/ +cp -a MyGame/Example2/*.go ./go_gen/src/MyGame/Example2/ # do not compile the gRPC generated files, which are not tested by go_test.go # below, but have their own test. rm ./go_gen/src/MyGame/Example/*_grpc.go diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index 622e083ae..30b64a08c 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -4,6 +4,8 @@ package Example import ( flatbuffers "github.com/google/flatbuffers/go" + + MyGame "MyGame" ) /// an example documentation comment: monster object @@ -516,12 +518,12 @@ func (rcv *Monster) VectorOfDoublesLength() int { return 0 } -func (rcv *Monster) ParentNamespaceTest(obj *InParentNamespace) *InParentNamespace { +func (rcv *Monster) ParentNamespaceTest(obj *MyGame.InParentNamespace) *MyGame.InParentNamespace { o := flatbuffers.UOffsetT(rcv._tab.Offset(72)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { - obj = new(InParentNamespace) + obj = new(MyGame.InParentNamespace) } obj.Init(rcv._tab.Bytes, x) return obj diff --git a/tests/docker/languages/Dockerfile.testing.golang.1_11 b/tests/docker/languages/Dockerfile.testing.golang.1_11 new file mode 100644 index 000000000..81707ea58 --- /dev/null +++ b/tests/docker/languages/Dockerfile.testing.golang.1_11 @@ -0,0 +1,7 @@ +FROM golang:1.11-stretch as base +WORKDIR /code +ADD . . +RUN cp flatc_debian_stretch flatc +WORKDIR /code/tests +RUN go version +RUN ./GoTest.sh diff --git a/tests/go_test.go b/tests/go_test.go index f93788097..ec6aaeffa 100644 --- a/tests/go_test.go +++ b/tests/go_test.go @@ -17,7 +17,9 @@ package main import ( + mygame "MyGame" // refers to generated code example "MyGame/Example" // refers to generated code + "bytes" "flag" "fmt" @@ -111,6 +113,9 @@ func TestAll(t *testing.T) { // Check Builder.CreateByteVector CheckCreateByteVector(t.Fatalf) + // Check a parent namespace import + CheckParentNamespace(t.Fatalf) + // If the filename of the FlatBuffers file generated by the Java test // is given, check that Go code can read it, and that Go code // generates an identical buffer when used to create the example data: @@ -1444,6 +1449,54 @@ func CheckCreateByteVector(fail func(string, ...interface{})) { } } +func CheckParentNamespace(fail func(string, ...interface{})) { + var empty, nonempty []byte + + // create monster with an empty parent namespace field + { + builder := flatbuffers.NewBuilder(0) + + example.MonsterStart(builder) + m := example.MonsterEnd(builder) + builder.Finish(m) + + empty = make([]byte, len(builder.FinishedBytes())) + copy(empty, builder.FinishedBytes()) + } + + // create monster with a non-empty parent namespace field + { + builder := flatbuffers.NewBuilder(0) + mygame.InParentNamespaceStart(builder) + pn := mygame.InParentNamespaceEnd(builder) + + example.MonsterStart(builder) + example.MonsterAddParentNamespaceTest(builder, pn) + m := example.MonsterEnd(builder) + + builder.Finish(m) + + nonempty = make([]byte, len(builder.FinishedBytes())) + copy(nonempty, builder.FinishedBytes()) + } + + // read monster with empty parent namespace field + { + m := example.GetRootAsMonster(empty, 0) + if m.ParentNamespaceTest(nil) != nil { + fail("expected nil ParentNamespaceTest for empty field") + } + } + + // read monster with non-empty parent namespace field + { + m := example.GetRootAsMonster(nonempty, 0) + if m.ParentNamespaceTest(nil) == nil { + fail("expected non-nil ParentNamespaceTest for non-empty field") + } + } +} + // Include simple random number generator to ensure results will be the // same cross platform. // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator diff --git a/tests/namespace_test/NamespaceA/SecondTableInA.go b/tests/namespace_test/NamespaceA/SecondTableInA.go index 3fea3a82a..6dd1eeff8 100644 --- a/tests/namespace_test/NamespaceA/SecondTableInA.go +++ b/tests/namespace_test/NamespaceA/SecondTableInA.go @@ -4,6 +4,8 @@ package NamespaceA import ( flatbuffers "github.com/google/flatbuffers/go" + + NamespaceC "NamespaceC" ) type SecondTableInA struct { @@ -26,12 +28,12 @@ func (rcv *SecondTableInA) Table() flatbuffers.Table { return rcv._tab } -func (rcv *SecondTableInA) ReferToC(obj *TableInC) *TableInC { +func (rcv *SecondTableInA) ReferToC(obj *NamespaceC.TableInC) *NamespaceC.TableInC { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { - obj = new(TableInC) + obj = new(NamespaceC.TableInC) } obj.Init(rcv._tab.Bytes, x) return obj diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.go b/tests/namespace_test/NamespaceA/TableInFirstNS.go index 2b2373c13..877e2d445 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.go +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.go @@ -4,6 +4,8 @@ package NamespaceA import ( flatbuffers "github.com/google/flatbuffers/go" + + NamespaceA__NamespaceB "NamespaceA/NamespaceB" ) type TableInFirstNS struct { @@ -26,12 +28,12 @@ func (rcv *TableInFirstNS) Table() flatbuffers.Table { return rcv._tab } -func (rcv *TableInFirstNS) FooTable(obj *TableInNestedNS) *TableInNestedNS { +func (rcv *TableInFirstNS) FooTable(obj *NamespaceA__NamespaceB.TableInNestedNS) *NamespaceA__NamespaceB.TableInNestedNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { - obj = new(TableInNestedNS) + obj = new(NamespaceA__NamespaceB.TableInNestedNS) } obj.Init(rcv._tab.Bytes, x) return obj @@ -51,12 +53,12 @@ func (rcv *TableInFirstNS) MutateFooEnum(n EnumInNestedNS) bool { return rcv._tab.MutateInt8Slot(6, n) } -func (rcv *TableInFirstNS) FooStruct(obj *StructInNestedNS) *StructInNestedNS { +func (rcv *TableInFirstNS) FooStruct(obj *NamespaceA__NamespaceB.StructInNestedNS) *NamespaceA__NamespaceB.StructInNestedNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { x := o + rcv._tab.Pos if obj == nil { - obj = new(StructInNestedNS) + obj = new(NamespaceA__NamespaceB.StructInNestedNS) } obj.Init(rcv._tab.Bytes, x) return obj diff --git a/tests/namespace_test/NamespaceC/TableInC.go b/tests/namespace_test/NamespaceC/TableInC.go index 5cd30da83..59b3e484e 100644 --- a/tests/namespace_test/NamespaceC/TableInC.go +++ b/tests/namespace_test/NamespaceC/TableInC.go @@ -4,6 +4,8 @@ package NamespaceC import ( flatbuffers "github.com/google/flatbuffers/go" + + NamespaceA "NamespaceA" ) type TableInC struct { @@ -26,12 +28,12 @@ func (rcv *TableInC) Table() flatbuffers.Table { return rcv._tab } -func (rcv *TableInC) ReferToA1(obj *TableInFirstNS) *TableInFirstNS { +func (rcv *TableInC) ReferToA1(obj *NamespaceA.TableInFirstNS) *NamespaceA.TableInFirstNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { - obj = new(TableInFirstNS) + obj = new(NamespaceA.TableInFirstNS) } obj.Init(rcv._tab.Bytes, x) return obj @@ -39,12 +41,12 @@ func (rcv *TableInC) ReferToA1(obj *TableInFirstNS) *TableInFirstNS { return nil } -func (rcv *TableInC) ReferToA2(obj *SecondTableInA) *SecondTableInA { +func (rcv *TableInC) ReferToA2(obj *NamespaceA.SecondTableInA) *NamespaceA.SecondTableInA { o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { - obj = new(SecondTableInA) + obj = new(NamespaceA.SecondTableInA) } obj.Init(rcv._tab.Bytes, x) return obj