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