[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
This commit is contained in:
Robert
2018-12-27 20:57:19 -05:00
committed by GitHub
parent a821b15634
commit 0eb7b3beb0
8 changed files with 815 additions and 699 deletions

View File

@@ -50,15 +50,6 @@ static const char * const g_golang_keywords[] = {
"for", "import", "return", "var", "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) { static std::string GoIdentity(const std::string &name) {
for (size_t i = 0; for (size_t i = 0;
i < sizeof(g_golang_keywords) / sizeof(g_golang_keywords[0]); i++) { i < sizeof(g_golang_keywords) / sizeof(g_golang_keywords[0]); i++) {
@@ -68,6 +59,62 @@ static std::string GoIdentity(const std::string &name) {
return MakeCamel(name, false); return MakeCamel(name, false);
} }
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 */),
cur_name_space_(nullptr) {
std::istringstream iss(go_namespace);
std::string component;
while (std::getline(iss, component, '.')) {
go_namespace_.components.push_back(component);
}
}
bool generate() {
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;
GenEnum(**it, &enumcode);
if (parser_.opts.one_file) {
one_file_code += enumcode;
} else {
if (!SaveType(**it, enumcode, false)) return false;
}
}
for (auto it = parser_.structs_.vec.begin();
it != parser_.structs_.vec.end(); ++it) {
tracked_imported_namespaces_.clear();
std::string declcode;
GenStruct(**it, &declcode);
if (parser_.opts.one_file) {
one_file_code += declcode;
} else {
if (!SaveType(**it, declcode, true)) return false;
}
}
if (parser_.opts.one_file) {
std::string code = "";
BeginFile(LastNamespacePart(go_namespace_), true, &code);
code += one_file_code;
const std::string filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), code, false);
}
return true;
}
private:
Namespace go_namespace_;
Namespace *cur_name_space_;
std::set<const Namespace*> tracked_imported_namespaces_;
// Most field accessors need to retrieve and test the field offset first, // Most field accessors need to retrieve and test the field offset first,
// this is the prefix code for that. // this is the prefix code for that.
std::string OffsetPrefix(const FieldDef &field) { std::string OffsetPrefix(const FieldDef &field) {
@@ -76,7 +123,7 @@ std::string OffsetPrefix(const FieldDef &field) {
} }
// Begin a class declaration. // Begin a class declaration.
static void BeginClass(const StructDef &struct_def, std::string *code_ptr) { void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "type " + struct_def.name + " struct {\n\t"; code += "type " + struct_def.name + " struct {\n\t";
@@ -89,24 +136,24 @@ static void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
// Construct the name of the type alias for this enum. // Construct the name of the type alias for this enum.
std::string GetEnumTypeName(const EnumDef &enum_def) { std::string GetEnumTypeName(const EnumDef &enum_def) {
return GoIdentity(enum_def.name); return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name));
} }
// Create a type for the enum values. // Create a type for the enum values.
static void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "type " + GetEnumTypeName(enum_def) + " = "; code += "type " + GetEnumTypeName(enum_def) + " = ";
code += GenTypeBasic(enum_def.underlying_type) + "\n"; code += GenTypeBasic(enum_def.underlying_type) + "\n";
} }
// Begin enum code with a class declaration. // Begin enum code with a class declaration.
static void BeginEnum(std::string *code_ptr) { void BeginEnum(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "const (\n"; code += "const (\n";
} }
// A single enum member. // A single enum member.
static void EnumMember(const EnumDef &enum_def, const EnumVal ev, void EnumMember(const EnumDef &enum_def, const EnumVal ev,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\t"; code += "\t";
@@ -119,13 +166,13 @@ static void EnumMember(const EnumDef &enum_def, const EnumVal ev,
} }
// End enum code. // End enum code.
static void EndEnum(std::string *code_ptr) { void EndEnum(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += ")\n\n"; code += ")\n\n";
} }
// Begin enum name code. // Begin enum name code.
static void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "var EnumNames"; code += "var EnumNames";
code += enum_def.name; code += enum_def.name;
@@ -133,7 +180,7 @@ static void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) {
} }
// A single enum name member. // A single enum name member.
static void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, void EnumNameMember(const EnumDef &enum_def, const EnumVal ev,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\t"; code += "\t";
@@ -145,13 +192,13 @@ static void EnumNameMember(const EnumDef &enum_def, const EnumVal ev,
} }
// End enum name code. // End enum name code.
static void EndEnumNames(std::string *code_ptr) { void EndEnumNames(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "}\n\n"; code += "}\n\n";
} }
// Initialize a new struct or table from existing data. // Initialize a new struct or table from existing data.
static void NewRootTypeFromBuffer(const StructDef &struct_def, void NewRootTypeFromBuffer(const StructDef &struct_def,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -168,8 +215,7 @@ static void NewRootTypeFromBuffer(const StructDef &struct_def,
} }
// Initialize an existing object with other data, to avoid an allocation. // Initialize an existing object with other data, to avoid an allocation.
static void InitializeExisting(const StructDef &struct_def, void InitializeExisting(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
@@ -181,8 +227,7 @@ static void InitializeExisting(const StructDef &struct_def,
} }
// Implement the table accessor // Implement the table accessor
static void GenTableAccessor(const StructDef &struct_def, void GenTableAccessor(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
@@ -198,7 +243,7 @@ static void GenTableAccessor(const StructDef &struct_def,
} }
// Get the length of a vector. // Get the length of a vector.
static void GetVectorLen(const StructDef &struct_def, const FieldDef &field, void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -210,7 +255,7 @@ static void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
} }
// Get a [ubyte] vector as a byte slice. // Get a [ubyte] vector as a byte slice.
static void GetUByteSlice(const StructDef &struct_def, const FieldDef &field, void GetUByteSlice(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -222,7 +267,7 @@ static void GetUByteSlice(const StructDef &struct_def, const FieldDef &field,
} }
// Get the value of a struct's scalar. // Get the value of a struct's scalar.
static void GetScalarFieldOfStruct(const StructDef &struct_def, void GetScalarFieldOfStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -236,7 +281,7 @@ static void GetScalarFieldOfStruct(const StructDef &struct_def,
} }
// Get the value of a table's scalar. // Get the value of a table's scalar.
static void GetScalarFieldOfTable(const StructDef &struct_def, void GetScalarFieldOfTable(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -252,7 +297,7 @@ static void GetScalarFieldOfTable(const StructDef &struct_def,
// Get a struct by initializing an existing struct. // Get a struct by initializing an existing struct.
// Specific to Struct. // Specific to Struct.
static void GetStructFieldOfStruct(const StructDef &struct_def, void GetStructFieldOfStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -272,7 +317,7 @@ static void GetStructFieldOfStruct(const StructDef &struct_def,
// Get a struct by initializing an existing struct. // Get a struct by initializing an existing struct.
// Specific to Table. // Specific to Table.
static void GetStructFieldOfTable(const StructDef &struct_def, void GetStructFieldOfTable(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -295,7 +340,8 @@ static void GetStructFieldOfTable(const StructDef &struct_def,
} }
// Get the value of a string. // Get the value of a string.
static void GetStringField(const StructDef &struct_def, const FieldDef &field, void GetStringField(const StructDef &struct_def,
const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
@@ -307,7 +353,7 @@ static void GetStringField(const StructDef &struct_def, const FieldDef &field,
} }
// Get the value of a union from an object. // Get the value of a union from an object.
static void GetUnionField(const StructDef &struct_def, const FieldDef &field, void GetUnionField(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
@@ -321,7 +367,7 @@ static void GetUnionField(const StructDef &struct_def, const FieldDef &field,
} }
// Get the value of a vector's struct member. // Get the value of a vector's struct member.
static void GetMemberOfVectorOfStruct(const StructDef &struct_def, void GetMemberOfVectorOfStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -345,7 +391,7 @@ static void GetMemberOfVectorOfStruct(const StructDef &struct_def,
// Get the value of a vector's non-struct member. Uses a named return // Get the value of a vector's non-struct member. Uses a named return
// argument to conveniently set the zero value for the result. // argument to conveniently set the zero value for the result.
static void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -371,8 +417,7 @@ static void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
} }
// Begin the creator function signature. // Begin the creator function signature.
static void BeginBuilderArgs(const StructDef &struct_def, void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
if (code.substr(code.length() - 2) != "\n\n") { if (code.substr(code.length() - 2) != "\n\n") {
@@ -385,8 +430,8 @@ static void BeginBuilderArgs(const StructDef &struct_def,
// Recursively generate arguments for a constructor, to deal with nested // Recursively generate arguments for a constructor, to deal with nested
// structs. // structs.
static void StructBuilderArgs(const StructDef &struct_def, void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
const char *nameprefix, std::string *code_ptr) { std::string *code_ptr) {
for (auto it = struct_def.fields.vec.begin(); for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) { it != struct_def.fields.vec.end(); ++it) {
auto &field = **it; auto &field = **it;
@@ -406,14 +451,14 @@ static void StructBuilderArgs(const StructDef &struct_def,
} }
// End the creator function signature. // End the creator function signature.
static void EndBuilderArgs(std::string *code_ptr) { void EndBuilderArgs(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += ") flatbuffers.UOffsetT {\n"; code += ") flatbuffers.UOffsetT {\n";
} }
// Recursively generate struct construction statements and instert manual // Recursively generate struct construction statements and instert manual
// padding. // padding.
static void StructBuilderBody(const StructDef &struct_def, void StructBuilderBody(const StructDef &struct_def,
const char *nameprefix, std::string *code_ptr) { const char *nameprefix, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", "; code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", ";
@@ -433,15 +478,14 @@ static void StructBuilderBody(const StructDef &struct_def,
} }
} }
static void EndBuilderBody(std::string *code_ptr) { void EndBuilderBody(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\treturn builder.Offset()\n"; code += "\treturn builder.Offset()\n";
code += "}\n"; code += "}\n";
} }
// Get the value of a table's starting offset. // Get the value of a table's starting offset.
static void GetStartOfTable(const StructDef &struct_def, void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "func " + struct_def.name + "Start"; code += "func " + struct_def.name + "Start";
code += "(builder *flatbuffers.Builder) {\n"; code += "(builder *flatbuffers.Builder) {\n";
@@ -451,9 +495,8 @@ static void GetStartOfTable(const StructDef &struct_def,
} }
// Set the value of a table's field. // Set the value of a table's field.
static void BuildFieldOfTable(const StructDef &struct_def, void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field,
const FieldDef &field, const size_t offset, const size_t offset, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "func " + struct_def.name + "Add" + MakeCamel(field.name); code += "func " + struct_def.name + "Add" + MakeCamel(field.name);
code += "(builder *flatbuffers.Builder, "; code += "(builder *flatbuffers.Builder, ";
@@ -479,7 +522,7 @@ static void BuildFieldOfTable(const StructDef &struct_def,
} }
// Set the value of one of the members of a table's vector. // Set the value of one of the members of a table's vector.
static void BuildVectorOfTable(const StructDef &struct_def, void BuildVectorOfTable(const StructDef &struct_def,
const FieldDef &field, std::string *code_ptr) { const FieldDef &field, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "func " + struct_def.name + "Start"; code += "func " + struct_def.name + "Start";
@@ -495,8 +538,7 @@ static void BuildVectorOfTable(const StructDef &struct_def,
} }
// Get the offset of the end of a table. // Get the offset of the end of a table.
static void GetEndOffsetOnTable(const StructDef &struct_def, void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "func " + struct_def.name + "End"; code += "func " + struct_def.name + "End";
code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT "; code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT ";
@@ -504,13 +546,13 @@ static void GetEndOffsetOnTable(const StructDef &struct_def,
} }
// Generate the receiver for function signatures. // Generate the receiver for function signatures.
static void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "func (rcv *" + struct_def.name + ")"; code += "func (rcv *" + struct_def.name + ")";
} }
// Generate a struct field getter, conditioned on its child type(s). // Generate a struct field getter, conditioned on its child type(s).
static void GenStructAccessor(const StructDef &struct_def, void GenStructAccessor(const StructDef &struct_def,
const FieldDef &field, std::string *code_ptr) { const FieldDef &field, std::string *code_ptr) {
GenComment(field.doc_comment, code_ptr, nullptr, ""); GenComment(field.doc_comment, code_ptr, nullptr, "");
if (IsScalar(field.value.type.base_type)) { if (IsScalar(field.value.type.base_type)) {
@@ -551,7 +593,7 @@ static void GenStructAccessor(const StructDef &struct_def,
} }
// Mutate the value of a struct's scalar. // Mutate the value of a struct's scalar.
static void MutateScalarFieldOfStruct(const StructDef &struct_def, void MutateScalarFieldOfStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -565,7 +607,7 @@ static void MutateScalarFieldOfStruct(const StructDef &struct_def,
} }
// Mutate the value of a table's scalar. // Mutate the value of a table's scalar.
static void MutateScalarFieldOfTable(const StructDef &struct_def, void MutateScalarFieldOfTable(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -579,7 +621,7 @@ static void MutateScalarFieldOfTable(const StructDef &struct_def,
} }
// Generate a struct field setter, conditioned on its child type(s). // Generate a struct field setter, conditioned on its child type(s).
static void GenStructMutator(const StructDef &struct_def, const FieldDef &field, void GenStructMutator(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
GenComment(field.doc_comment, code_ptr, nullptr, ""); GenComment(field.doc_comment, code_ptr, nullptr, "");
if (IsScalar(field.value.type.base_type)) { if (IsScalar(field.value.type.base_type)) {
@@ -592,8 +634,7 @@ static void GenStructMutator(const StructDef &struct_def, const FieldDef &field,
} }
// Generate table constructors, conditioned on its members' types. // Generate table constructors, conditioned on its members' types.
static void GenTableBuilders(const StructDef &struct_def, void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
GetStartOfTable(struct_def, code_ptr); GetStartOfTable(struct_def, code_ptr);
for (auto it = struct_def.fields.vec.begin(); for (auto it = struct_def.fields.vec.begin();
@@ -612,9 +653,11 @@ static void GenTableBuilders(const StructDef &struct_def,
} }
// Generate struct or table methods. // Generate struct or table methods.
static void GenStruct(const StructDef &struct_def, std::string *code_ptr) { void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
if (struct_def.generated) return; if (struct_def.generated) return;
cur_name_space_ = struct_def.defined_namespace;
GenComment(struct_def.doc_comment, code_ptr, nullptr); GenComment(struct_def.doc_comment, code_ptr, nullptr);
BeginClass(struct_def, code_ptr); BeginClass(struct_def, code_ptr);
if (!struct_def.fixed) { if (!struct_def.fixed) {
@@ -649,9 +692,11 @@ static void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
} }
// Generate enum declarations. // Generate enum declarations.
static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
if (enum_def.generated) return; if (enum_def.generated) return;
cur_name_space_ = enum_def.defined_namespace;
GenComment(enum_def.doc_comment, code_ptr, nullptr); GenComment(enum_def.doc_comment, code_ptr, nullptr);
GenEnumType(enum_def, code_ptr); GenEnumType(enum_def, code_ptr);
BeginEnum(code_ptr); BeginEnum(code_ptr);
@@ -673,7 +718,7 @@ static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
} }
// Returns the function name that is able to read a value of the given type. // Returns the function name that is able to read a value of the given type.
static std::string GenGetter(const Type &type) { std::string GenGetter(const Type &type) {
switch (type.base_type) { switch (type.base_type) {
case BASE_TYPE_STRING: return "rcv._tab.ByteVector"; case BASE_TYPE_STRING: return "rcv._tab.ByteVector";
case BASE_TYPE_UNION: return "rcv._tab.Union"; case BASE_TYPE_UNION: return "rcv._tab.Union";
@@ -683,13 +728,13 @@ static std::string GenGetter(const Type &type) {
} }
// Returns the method name for use with add/put calls. // Returns the method name for use with add/put calls.
static std::string GenMethod(const FieldDef &field) { std::string GenMethod(const FieldDef &field) {
return IsScalar(field.value.type.base_type) return IsScalar(field.value.type.base_type)
? MakeCamel(GenTypeBasic(field.value.type)) ? MakeCamel(GenTypeBasic(field.value.type))
: (IsStruct(field.value.type) ? "Struct" : "UOffsetT"); : (IsStruct(field.value.type) ? "Struct" : "UOffsetT");
} }
static std::string GenTypeBasic(const Type &type) { std::string GenTypeBasic(const Type &type) {
static const char *ctypename[] = { static const char *ctypename[] = {
// clang-format off // clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
@@ -702,29 +747,29 @@ static std::string GenTypeBasic(const Type &type) {
return ctypename[type.base_type]; return ctypename[type.base_type];
} }
static std::string GenTypePointer(const Type &type) { std::string GenTypePointer(const Type &type) {
switch (type.base_type) { switch (type.base_type) {
case BASE_TYPE_STRING: return "[]byte"; case BASE_TYPE_STRING: return "[]byte";
case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
case BASE_TYPE_STRUCT: return type.struct_def->name; case BASE_TYPE_STRUCT: return WrapInNameSpaceAndTrack(*type.struct_def);
case BASE_TYPE_UNION: case BASE_TYPE_UNION:
// fall through // fall through
default: return "*flatbuffers.Table"; default: return "*flatbuffers.Table";
} }
} }
static std::string GenTypeGet(const Type &type) { std::string GenTypeGet(const Type &type) {
if (type.enum_def != nullptr && !type.enum_def->is_union) { if (type.enum_def != nullptr && !type.enum_def->is_union) {
return GetEnumTypeName(*type.enum_def); return GetEnumTypeName(*type.enum_def);
} }
return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
} }
static std::string TypeName(const FieldDef &field) { std::string TypeName(const FieldDef &field) {
return GenTypeGet(field.value.type); return GenTypeGet(field.value.type);
} }
static std::string GenConstant(const FieldDef &field) { std::string GenConstant(const FieldDef &field) {
switch (field.value.type.base_type) { switch (field.value.type.base_type) {
case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";;
default: return field.value.constant; default: return field.value.constant;
@@ -732,8 +777,7 @@ static std::string GenConstant(const FieldDef &field) {
} }
// Create a struct with a builder and the struct's arguments. // Create a struct with a builder and the struct's arguments.
static void GenStructBuilder(const StructDef &struct_def, void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr) {
BeginBuilderArgs(struct_def, code_ptr); BeginBuilderArgs(struct_def, code_ptr);
StructBuilderArgs(struct_def, "", code_ptr); StructBuilderArgs(struct_def, "", code_ptr);
EndBuilderArgs(code_ptr); EndBuilderArgs(code_ptr);
@@ -741,56 +785,6 @@ static void GenStructBuilder(const StructDef &struct_def,
StructBuilderBody(struct_def, "", code_ptr); StructBuilderBody(struct_def, "", code_ptr);
EndBuilderBody(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 */) {
std::istringstream iss(go_namespace);
std::string component;
while (std::getline(iss, component, '.')) {
go_namespace_.components.push_back(component);
}
}
bool generate() {
std::string one_file_code;
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
++it) {
std::string enumcode;
go::GenEnum(**it, &enumcode);
if (parser_.opts.one_file) {
one_file_code += enumcode;
} else {
if (!SaveType(**it, enumcode, false)) return false;
}
}
for (auto it = parser_.structs_.vec.begin();
it != parser_.structs_.vec.end(); ++it) {
std::string declcode;
go::GenStruct(**it, &declcode);
if (parser_.opts.one_file) {
one_file_code += declcode;
} else {
if (!SaveType(**it, declcode, true)) return false;
}
}
if (parser_.opts.one_file) {
std::string code = "";
BeginFile(LastNamespacePart(go_namespace_), true, &code);
code += one_file_code;
const std::string filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), code, false);
}
return true;
}
private:
// Begin by declaring namespace and imports. // Begin by declaring namespace and imports.
void BeginFile(const std::string name_space_name, const bool needs_imports, void BeginFile(const std::string name_space_name, const bool needs_imports,
std::string *code_ptr) { std::string *code_ptr) {
@@ -804,6 +798,15 @@ class GoGenerator : public BaseGenerator {
} else { } else {
code += "\tflatbuffers \"github.com/google/flatbuffers/go\"\n"; 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"; code += ")\n\n";
} }
} }
@@ -822,7 +825,49 @@ class GoGenerator : public BaseGenerator {
return SaveFile(filename.c_str(), code, false); 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 } // namespace go

View File

@@ -26,10 +26,13 @@ go_src=${go_path}/src
# Copy flatbuffer Go files to their own package directories to compile the # Copy flatbuffer Go files to their own package directories to compile the
# test binary: # test binary:
mkdir -p ${go_src}/MyGame/Example 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}/github.com/google/flatbuffers/go
mkdir -p ${go_src}/flatbuffers_test 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/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 # do not compile the gRPC generated files, which are not tested by go_test.go
# below, but have their own test. # below, but have their own test.
rm ./go_gen/src/MyGame/Example/*_grpc.go rm ./go_gen/src/MyGame/Example/*_grpc.go

View File

@@ -4,6 +4,8 @@ package Example
import ( import (
flatbuffers "github.com/google/flatbuffers/go" flatbuffers "github.com/google/flatbuffers/go"
MyGame "MyGame"
) )
/// an example documentation comment: monster object /// an example documentation comment: monster object
@@ -516,12 +518,12 @@ func (rcv *Monster) VectorOfDoublesLength() int {
return 0 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(72))
if o != 0 { if o != 0 {
x := rcv._tab.Indirect(o + rcv._tab.Pos) x := rcv._tab.Indirect(o + rcv._tab.Pos)
if obj == nil { if obj == nil {
obj = new(InParentNamespace) obj = new(MyGame.InParentNamespace)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj

View File

@@ -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

View File

@@ -17,7 +17,9 @@
package main package main
import ( import (
mygame "MyGame" // refers to generated code
example "MyGame/Example" // refers to generated code example "MyGame/Example" // refers to generated code
"bytes" "bytes"
"flag" "flag"
"fmt" "fmt"
@@ -111,6 +113,9 @@ func TestAll(t *testing.T) {
// Check Builder.CreateByteVector // Check Builder.CreateByteVector
CheckCreateByteVector(t.Fatalf) CheckCreateByteVector(t.Fatalf)
// Check a parent namespace import
CheckParentNamespace(t.Fatalf)
// If the filename of the FlatBuffers file generated by the Java test // 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 // is given, check that Go code can read it, and that Go code
// generates an identical buffer when used to create the example data: // 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 // Include simple random number generator to ensure results will be the
// same cross platform. // same cross platform.
// http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator

View File

@@ -4,6 +4,8 @@ package NamespaceA
import ( import (
flatbuffers "github.com/google/flatbuffers/go" flatbuffers "github.com/google/flatbuffers/go"
NamespaceC "NamespaceC"
) )
type SecondTableInA struct { type SecondTableInA struct {
@@ -26,12 +28,12 @@ func (rcv *SecondTableInA) Table() flatbuffers.Table {
return rcv._tab 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 { if o != 0 {
x := rcv._tab.Indirect(o + rcv._tab.Pos) x := rcv._tab.Indirect(o + rcv._tab.Pos)
if obj == nil { if obj == nil {
obj = new(TableInC) obj = new(NamespaceC.TableInC)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj

View File

@@ -4,6 +4,8 @@ package NamespaceA
import ( import (
flatbuffers "github.com/google/flatbuffers/go" flatbuffers "github.com/google/flatbuffers/go"
NamespaceA__NamespaceB "NamespaceA/NamespaceB"
) )
type TableInFirstNS struct { type TableInFirstNS struct {
@@ -26,12 +28,12 @@ func (rcv *TableInFirstNS) Table() flatbuffers.Table {
return rcv._tab 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 { if o != 0 {
x := rcv._tab.Indirect(o + rcv._tab.Pos) x := rcv._tab.Indirect(o + rcv._tab.Pos)
if obj == nil { if obj == nil {
obj = new(TableInNestedNS) obj = new(NamespaceA__NamespaceB.TableInNestedNS)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj
@@ -51,12 +53,12 @@ func (rcv *TableInFirstNS) MutateFooEnum(n EnumInNestedNS) bool {
return rcv._tab.MutateInt8Slot(6, n) 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(8))
if o != 0 { if o != 0 {
x := o + rcv._tab.Pos x := o + rcv._tab.Pos
if obj == nil { if obj == nil {
obj = new(StructInNestedNS) obj = new(NamespaceA__NamespaceB.StructInNestedNS)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj

View File

@@ -4,6 +4,8 @@ package NamespaceC
import ( import (
flatbuffers "github.com/google/flatbuffers/go" flatbuffers "github.com/google/flatbuffers/go"
NamespaceA "NamespaceA"
) )
type TableInC struct { type TableInC struct {
@@ -26,12 +28,12 @@ func (rcv *TableInC) Table() flatbuffers.Table {
return rcv._tab 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 { if o != 0 {
x := rcv._tab.Indirect(o + rcv._tab.Pos) x := rcv._tab.Indirect(o + rcv._tab.Pos)
if obj == nil { if obj == nil {
obj = new(TableInFirstNS) obj = new(NamespaceA.TableInFirstNS)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj
@@ -39,12 +41,12 @@ func (rcv *TableInC) ReferToA1(obj *TableInFirstNS) *TableInFirstNS {
return nil 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)) o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
if o != 0 { if o != 0 {
x := rcv._tab.Indirect(o + rcv._tab.Pos) x := rcv._tab.Indirect(o + rcv._tab.Pos)
if obj == nil { if obj == nil {
obj = new(SecondTableInA) obj = new(NamespaceA.SecondTableInA)
} }
obj.Init(rcv._tab.Bytes, x) obj.Init(rcv._tab.Bytes, x)
return obj return obj