Unverified Commit 0eb7b3be authored by Robert's avatar Robert Committed by GitHub

[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
parent a821b156
...@@ -50,15 +50,6 @@ static const char * const g_golang_keywords[] = { ...@@ -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,729 +59,732 @@ static std::string GoIdentity(const std::string &name) { ...@@ -68,729 +59,732 @@ static std::string GoIdentity(const std::string &name) {
return MakeCamel(name, false); return MakeCamel(name, false);
} }
// Most field accessors need to retrieve and test the field offset first, class GoGenerator : public BaseGenerator {
// this is the prefix code for that. public:
std::string OffsetPrefix(const FieldDef &field) { GoGenerator(const Parser &parser, const std::string &path,
return "{\n\to := flatbuffers.UOffsetT(rcv._tab.Offset(" + const std::string &file_name, const std::string &go_namespace)
NumToString(field.value.offset) + "))\n\tif o != 0 {\n"; : 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);
}
}
// Begin a class declaration. bool generate() {
static void BeginClass(const StructDef &struct_def, std::string *code_ptr) { std::string one_file_code;
std::string &code = *code_ptr; 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;
}
}
code += "type " + struct_def.name + " struct {\n\t"; 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;
}
}
// _ is reserved in flatbuffers field names, so no chance of name conflict: if (parser_.opts.one_file) {
code += "_tab "; std::string code = "";
code += struct_def.fixed ? "flatbuffers.Struct" : "flatbuffers.Table"; BeginFile(LastNamespacePart(go_namespace_), true, &code);
code += "\n}\n\n"; code += one_file_code;
} const std::string filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), code, false);
}
// Construct the name of the type alias for this enum. return true;
std::string GetEnumTypeName(const EnumDef &enum_def) { }
return GoIdentity(enum_def.name);
}
// Create a type for the enum values. private:
static void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { Namespace go_namespace_;
std::string &code = *code_ptr; Namespace *cur_name_space_;
code += "type " + GetEnumTypeName(enum_def) + " = "; std::set<const Namespace*> tracked_imported_namespaces_;
code += GenTypeBasic(enum_def.underlying_type) + "\n";
} // 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 enum code with a class declaration. // Begin a class declaration.
static void BeginEnum(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 += "const (\n";
}
// A single enum member. code += "type " + struct_def.name + " struct {\n\t";
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. // _ is reserved in flatbuffers field names, so no chance of name conflict:
static void EndEnum(std::string *code_ptr) { code += "_tab ";
std::string &code = *code_ptr; code += struct_def.fixed ? "flatbuffers.Struct" : "flatbuffers.Table";
code += ")\n\n"; code += "\n}\n\n";
} }
// Begin enum name code. // Construct the name of the type alias for this enum.
static void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { std::string GetEnumTypeName(const EnumDef &enum_def) {
std::string &code = *code_ptr; return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name));
code += "var EnumNames"; }
code += enum_def.name;
code += " = map[" + GetEnumTypeName(enum_def) + "]string{\n";
}
// A single enum name member. // Create a type for the enum values.
static void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) {
std::string *code_ptr) { std::string &code = *code_ptr;
std::string &code = *code_ptr; code += "type " + GetEnumTypeName(enum_def) + " = ";
code += "\t"; code += GenTypeBasic(enum_def.underlying_type) + "\n";
code += enum_def.name; }
code += ev.name;
code += ":\"";
code += ev.name;
code += "\",\n";
}
// End enum name code. // Begin enum code with a class declaration.
static void EndEnumNames(std::string *code_ptr) { void BeginEnum(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "}\n\n"; code += "const (\n";
} }
// Initialize a new struct or table from existing data. // A single enum member.
static void NewRootTypeFromBuffer(const StructDef &struct_def, 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 += "func GetRootAs"; code += enum_def.name;
code += struct_def.name; code += ev.name;
code += "(buf []byte, offset flatbuffers.UOffsetT) "; code += " ";
code += "*" + struct_def.name + ""; code += GetEnumTypeName(enum_def);
code += " {\n"; code += " = ";
code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; code += NumToString(ev.value) + "\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. // End enum code.
static void InitializeExisting(const StructDef &struct_def, void EndEnum(std::string *code_ptr) {
std::string *code_ptr) { std::string &code = *code_ptr;
std::string &code = *code_ptr; code += ")\n\n";
}
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 // Begin enum name code.
static void GenTableAccessor(const StructDef &struct_def, void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) {
std::string *code_ptr) { std::string &code = *code_ptr;
std::string &code = *code_ptr; code += "var EnumNames";
code += enum_def.name;
code += " = map[" + GetEnumTypeName(enum_def) + "]string{\n";
}
GenReceiver(struct_def, code_ptr); // A single enum name member.
code += " Table() flatbuffers.Table "; void EnumNameMember(const EnumDef &enum_def, const EnumVal ev,
code += "{\n"; std::string *code_ptr) {
std::string &code = *code_ptr;
code += "\t";
code += enum_def.name;
code += ev.name;
code += ":\"";
code += ev.name;
code += "\",\n";
}
if (struct_def.fixed) { // End enum name code.
code += "\treturn rcv._tab.Table\n"; void EndEnumNames(std::string *code_ptr) {
} else { std::string &code = *code_ptr;
code += "\treturn rcv._tab\n"; code += "}\n\n";
} }
code += "}\n\n";
}
// Get the length of a vector. // Initialize a new struct or table from existing data.
static void GetVectorLen(const StructDef &struct_def, const FieldDef &field, void NewRootTypeFromBuffer(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); code += "func GetRootAs";
code += " " + MakeCamel(field.name) + "Length("; code += struct_def.name;
code += ") int " + OffsetPrefix(field); code += "(buf []byte, offset flatbuffers.UOffsetT) ";
code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n"; code += "*" + struct_def.name + "";
code += "\treturn 0\n}\n\n"; 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";
}
// Get a [ubyte] vector as a byte slice. // Initialize an existing object with other data, to avoid an allocation.
static void GetUByteSlice(const StructDef &struct_def, const FieldDef &field, 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);
code += " " + MakeCamel(field.name) + "Bytes("; code += " Init(buf []byte, i flatbuffers.UOffsetT) ";
code += ") []byte " + OffsetPrefix(field); code += "{\n";
code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n"; code += "\trcv._tab.Bytes = buf\n";
code += "\treturn nil\n}\n\n"; code += "\trcv._tab.Pos = i\n";
} code += "}\n\n";
}
// Get the value of a struct's scalar. // Implement the table accessor
static void GetScalarFieldOfStruct(const StructDef &struct_def, void GenTableAccessor(const StructDef &struct_def, std::string *code_ptr) {
const FieldDef &field, std::string &code = *code_ptr;
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. GenReceiver(struct_def, code_ptr);
static void GetScalarFieldOfTable(const StructDef &struct_def, code += " Table() flatbuffers.Table ";
const FieldDef &field, code += "{\n";
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. if (struct_def.fixed) {
// Specific to Struct. code += "\treturn rcv._tab.Table\n";
static void GetStructFieldOfStruct(const StructDef &struct_def, } else {
const FieldDef &field, code += "\treturn rcv._tab\n";
std::string *code_ptr) { }
std::string &code = *code_ptr; code += "}\n\n";
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. // Get the length of a vector.
// Specific to Table. void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
static void GetStructFieldOfTable(const StructDef &struct_def, std::string *code_ptr) {
const FieldDef &field, std::string &code = *code_ptr;
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. GenReceiver(struct_def, code_ptr);
static void GetStringField(const StructDef &struct_def, const FieldDef &field, code += " " + MakeCamel(field.name) + "Length(";
std::string *code_ptr) { code += ") int " + OffsetPrefix(field);
std::string &code = *code_ptr; code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n";
GenReceiver(struct_def, code_ptr); code += "\treturn 0\n}\n\n";
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. // Get a [ubyte] vector as a byte slice.
static void GetUnionField(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;
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. GenReceiver(struct_def, code_ptr);
static void GetMemberOfVectorOfStruct(const StructDef &struct_def, code += " " + MakeCamel(field.name) + "Bytes(";
const FieldDef &field, code += ") []byte " + OffsetPrefix(field);
std::string *code_ptr) { code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n";
std::string &code = *code_ptr; code += "\treturn nil\n}\n\n";
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 // Get the value of a struct's scalar.
// argument to conveniently set the zero value for the result. void GetScalarFieldOfStruct(const StructDef &struct_def,
static 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; std::string getter = GenGetter(field.value.type);
auto vectortype = field.value.type.VectorType(); GenReceiver(struct_def, code_ptr);
code += " " + MakeCamel(field.name);
GenReceiver(struct_def, code_ptr); code += "() " + TypeName(field) + " {\n";
code += " " + MakeCamel(field.name); code += "\treturn " + getter;
code += "(j int) " + TypeName(field) + " "; code += "(rcv._tab.Pos + flatbuffers.UOffsetT(";
code += OffsetPrefix(field); code += NumToString(field.value.offset) + "))\n}\n";
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. // Get the value of a table's scalar.
static void BeginBuilderArgs(const StructDef &struct_def, void GetScalarFieldOfTable(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;
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";
}
if (code.substr(code.length() - 2) != "\n\n") { // Get a struct by initializing an existing struct.
// a previous mutate has not put an extra new line // Specific to Struct.
code += "\n"; 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";
} }
code += "func Create" + struct_def.name;
code += "(builder *flatbuffers.Builder";
}
// Recursively generate arguments for a constructor, to deal with nested // Get a struct by initializing an existing struct.
// structs. // Specific to Table.
static void StructBuilderArgs(const StructDef &struct_def, void GetStructFieldOfTable(const StructDef &struct_def,
const char *nameprefix, std::string *code_ptr) { const FieldDef &field,
for (auto it = struct_def.fields.vec.begin(); std::string *code_ptr) {
it != struct_def.fields.vec.end(); ++it) { std::string &code = *code_ptr;
auto &field = **it; GenReceiver(struct_def, code_ptr);
if (IsStruct(field.value.type)) { code += " " + MakeCamel(field.name);
// Generate arguments for a struct inside a struct. To ensure names code += "(obj *";
// don't clash, and to make it obvious these arguments are constructing code += TypeName(field);
// a nested struct, prefix the name with the field name. code += ") *" + TypeName(field) + " " + OffsetPrefix(field);
StructBuilderArgs(*field.value.type.struct_def, if (field.value.type.struct_def->fixed) {
(nameprefix + (field.name + "_")).c_str(), code_ptr); code += "\t\tx := o + rcv._tab.Pos\n";
} else { } else {
std::string &code = *code_ptr; code += "\t\tx := rcv._tab.Indirect(o + rcv._tab.Pos)\n";
code += std::string(", ") + nameprefix;
code += GoIdentity(field.name);
code += " " + GenTypeBasic(field.value.type);
} }
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";
} }
}
// End the creator function signature. // Get the value of a string.
static void EndBuilderArgs(std::string *code_ptr) { void GetStringField(const StructDef &struct_def,
std::string &code = *code_ptr; const FieldDef &field,
code += ") flatbuffers.UOffsetT {\n"; 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";
}
// Recursively generate struct construction statements and instert manual // Get the value of a vector's struct member.
// padding. void GetMemberOfVectorOfStruct(const StructDef &struct_def,
static void StructBuilderBody(const StructDef &struct_def, const FieldDef &field,
const char *nameprefix, std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", "; auto vectortype = field.value.type.VectorType();
code += NumToString(struct_def.bytesize) + ")\n";
for (auto it = struct_def.fields.vec.rbegin(); GenReceiver(struct_def, code_ptr);
it != struct_def.fields.vec.rend(); ++it) { code += " " + MakeCamel(field.name);
auto &field = **it; code += "(obj *" + TypeName(field);
if (field.padding) code += ", j int) bool " + OffsetPrefix(field);
code += "\tbuilder.Pad(" + NumToString(field.padding) + ")\n"; code += "\t\tx := rcv._tab.Vector(o)\n";
if (IsStruct(field.value.type)) { code += "\t\tx += flatbuffers.UOffsetT(j) * ";
StructBuilderBody(*field.value.type.struct_def, code += NumToString(InlineSize(vectortype)) + "\n";
(nameprefix + (field.name + "_")).c_str(), code_ptr); 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 { } else {
code += "\tbuilder.Prepend" + GenMethod(field) + "("; code += "\treturn 0\n";
code += nameprefix + GoIdentity(field.name) + ")\n";
} }
code += "}\n\n";
} }
}
static void EndBuilderBody(std::string *code_ptr) { // Begin the creator function signature.
std::string &code = *code_ptr; void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) {
code += "\treturn builder.Offset()\n"; std::string &code = *code_ptr;
code += "}\n";
}
// Get the value of a table's starting offset. if (code.substr(code.length() - 2) != "\n\n") {
static void GetStartOfTable(const StructDef &struct_def, // a previous mutate has not put an extra new line
std::string *code_ptr) { code += "\n";
std::string &code = *code_ptr; }
code += "func " + struct_def.name + "Start"; code += "func Create" + struct_def.name;
code += "(builder *flatbuffers.Builder) {\n"; code += "(builder *flatbuffers.Builder";
code += "\tbuilder.StartObject("; }
code += NumToString(struct_def.fields.vec.size());
code += ")\n}\n";
}
// Set the value of a table's field. // Recursively generate arguments for a constructor, to deal with nested
static void BuildFieldOfTable(const StructDef &struct_def, // structs.
const FieldDef &field, const size_t offset, void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
std::string *code_ptr) { std::string *code_ptr) {
std::string &code = *code_ptr; for (auto it = struct_def.fields.vec.begin();
code += "func " + struct_def.name + "Add" + MakeCamel(field.name); it != struct_def.fields.vec.end(); ++it) {
code += "(builder *flatbuffers.Builder, "; auto &field = **it;
code += GoIdentity(field.name) + " "; if (IsStruct(field.value.type)) {
if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { // Generate arguments for a struct inside a struct. To ensure names
code += "flatbuffers.UOffsetT"; // don't clash, and to make it obvious these arguments are constructing
} else { // a nested struct, prefix the name with the field name.
code += GenTypeBasic(field.value.type); StructBuilderArgs(*field.value.type.struct_def,
} (nameprefix + (field.name + "_")).c_str(), code_ptr);
code += ") {\n"; } else {
code += "\tbuilder.Prepend"; std::string &code = *code_ptr;
code += GenMethod(field) + "Slot("; code += std::string(", ") + nameprefix;
code += NumToString(offset) + ", "; code += GoIdentity(field.name);
if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) { code += " " + GenTypeBasic(field.value.type);
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. // End the creator function signature.
static void BuildVectorOfTable(const StructDef &struct_def, void EndBuilderArgs(std::string *code_ptr) {
const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr;
std::string &code = *code_ptr; code += ") flatbuffers.UOffsetT {\n";
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. // Recursively generate struct construction statements and instert manual
static void GetEndOffsetOnTable(const StructDef &struct_def, // padding.
std::string *code_ptr) { void StructBuilderBody(const StructDef &struct_def,
std::string &code = *code_ptr; const char *nameprefix, std::string *code_ptr) {
code += "func " + struct_def.name + "End"; std::string &code = *code_ptr;
code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT "; code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", ";
code += "{\n\treturn builder.EndObject()\n}\n"; 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";
}
}
}
// Generate the receiver for function signatures. void EndBuilderBody(std::string *code_ptr) {
static void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr;
std::string &code = *code_ptr; code += "\treturn builder.Offset()\n";
code += "func (rcv *" + struct_def.name + ")"; code += "}\n";
} }
// Generate a struct field getter, conditioned on its child type(s). // Get the value of a table's starting offset.
static void GenStructAccessor(const StructDef &struct_def, void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) {
const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr;
GenComment(field.doc_comment, code_ptr, nullptr, ""); code += "func " + struct_def.name + "Start";
if (IsScalar(field.value.type.base_type)) { code += "(builder *flatbuffers.Builder) {\n";
if (struct_def.fixed) { code += "\tbuilder.StartObject(";
GetScalarFieldOfStruct(struct_def, field, code_ptr); 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 { } else {
GetScalarFieldOfTable(struct_def, field, code_ptr); code += GenTypeBasic(field.value.type);
} }
} else { code += ") {\n";
switch (field.value.type.base_type) { code += "\tbuilder.Prepend";
case BASE_TYPE_STRUCT: code += GenMethod(field) + "Slot(";
if (struct_def.fixed) { code += NumToString(offset) + ", ";
GetStructFieldOfStruct(struct_def, field, code_ptr); if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
} else { code += "flatbuffers.UOffsetT";
GetStructFieldOfTable(struct_def, field, code_ptr); code += "(";
} code += GoIdentity(field.name) + ")";
break; } else {
case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break; code += GoIdentity(field.name);
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);
} }
code += ", " + GenConstant(field);
code += ")\n}\n";
} }
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
GetVectorLen(struct_def, field, code_ptr); // Set the value of one of the members of a table's vector.
if (field.value.type.element == BASE_TYPE_UCHAR) { void BuildVectorOfTable(const StructDef &struct_def,
GetUByteSlice(struct_def, field, code_ptr); 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";
} }
}
// Mutate the value of a struct's scalar. // Get the offset of the end of a table.
static void MutateScalarFieldOfStruct(const StructDef &struct_def, void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) {
const FieldDef &field, std::string &code = *code_ptr;
std::string *code_ptr) { code += "func " + struct_def.name + "End";
std::string &code = *code_ptr; code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT ";
std::string type = MakeCamel(GenTypeBasic(field.value.type)); code += "{\n\treturn builder.EndObject()\n}\n";
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. // Generate the receiver for function signatures.
static void MutateScalarFieldOfTable(const StructDef &struct_def, void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
const FieldDef &field, std::string &code = *code_ptr;
std::string *code_ptr) { code += "func (rcv *" + struct_def.name + ")";
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). // Generate a struct field getter, conditioned on its child type(s).
static void GenStructMutator(const StructDef &struct_def, const FieldDef &field, void GenStructAccessor(const StructDef &struct_def,
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)) {
if (struct_def.fixed) { if (struct_def.fixed) {
MutateScalarFieldOfStruct(struct_def, field, code_ptr); GetScalarFieldOfStruct(struct_def, field, code_ptr);
} else {
GetScalarFieldOfTable(struct_def, field, code_ptr);
}
} else { } else {
MutateScalarFieldOfTable(struct_def, field, code_ptr); 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);
}
} }
} }
}
// Generate table constructors, conditioned on its members' types. // Mutate the value of a struct's scalar.
static void GenTableBuilders(const StructDef &struct_def, void MutateScalarFieldOfStruct(const StructDef &struct_def,
std::string *code_ptr) { const FieldDef &field,
GetStartOfTable(struct_def, code_ptr); 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";
}
for (auto it = struct_def.fields.vec.begin(); // Mutate the value of a table's scalar.
it != struct_def.fields.vec.end(); ++it) { void MutateScalarFieldOfTable(const StructDef &struct_def,
auto &field = **it; const FieldDef &field,
if (field.deprecated) continue; 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";
}
auto offset = it - struct_def.fields.vec.begin(); // Generate a struct field setter, conditioned on its child type(s).
BuildFieldOfTable(struct_def, field, offset, code_ptr); void GenStructMutator(const StructDef &struct_def, const FieldDef &field,
if (field.value.type.base_type == BASE_TYPE_VECTOR) { std::string *code_ptr) {
BuildVectorOfTable(struct_def, field, 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);
}
} }
} }
GetEndOffsetOnTable(struct_def, 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);
// Generate struct or table methods. for (auto it = struct_def.fields.vec.begin();
static void GenStruct(const StructDef &struct_def, std::string *code_ptr) { it != struct_def.fields.vec.end(); ++it) {
if (struct_def.generated) return; auto &field = **it;
if (field.deprecated) continue;
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. auto offset = it - struct_def.fields.vec.begin();
static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { BuildFieldOfTable(struct_def, field, offset, code_ptr);
if (enum_def.generated) return; if (field.value.type.base_type == BASE_TYPE_VECTOR) {
BuildVectorOfTable(struct_def, field, code_ptr);
}
}
GenComment(enum_def.doc_comment, code_ptr, nullptr); GetEndOffsetOnTable(struct_def, code_ptr);
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); // Generate struct or table methods.
for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
++it) { if (struct_def.generated) return;
auto &ev = **it;
EnumNameMember(enum_def, ev, code_ptr); cur_name_space_ = struct_def.defined_namespace;
}
EndEnumNames(code_ptr); 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);
}
// Returns the function name that is able to read a value of the given type. // Generate builders
static std::string GenGetter(const Type &type) { if (struct_def.fixed) {
switch (type.base_type) { // create a struct constructor function
case BASE_TYPE_STRING: return "rcv._tab.ByteVector"; GenStructBuilder(struct_def, code_ptr);
case BASE_TYPE_UNION: return "rcv._tab.Union"; } else {
case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); // Create a set of functions that allow table construction.
default: return "rcv._tab.Get" + MakeCamel(GenTypeBasic(type)); GenTableBuilders(struct_def, code_ptr);
}
} }
}
// Returns the method name for use with add/put calls. // Generate enum declarations.
static std::string GenMethod(const FieldDef &field) { void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
return IsScalar(field.value.type.base_type) if (enum_def.generated) return;
? MakeCamel(GenTypeBasic(field.value.type))
: (IsStruct(field.value.type) ? "Struct" : "UOffsetT");
}
static std::string GenTypeBasic(const Type &type) { cur_name_space_ = enum_def.defined_namespace;
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) { GenComment(enum_def.doc_comment, code_ptr, nullptr);
switch (type.base_type) { GenEnumType(enum_def, code_ptr);
case BASE_TYPE_STRING: return "[]byte"; BeginEnum(code_ptr);
case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
case BASE_TYPE_STRUCT: return type.struct_def->name; ++it) {
case BASE_TYPE_UNION: auto &ev = **it;
// fall through GenComment(ev.doc_comment, code_ptr, nullptr, "\t");
default: return "*flatbuffers.Table"; EnumMember(enum_def, ev, code_ptr);
} }
} EndEnum(code_ptr);
static std::string GenTypeGet(const Type &type) { BeginEnumNames(enum_def, code_ptr);
if (type.enum_def != nullptr && !type.enum_def->is_union) { for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
return GetEnumTypeName(*type.enum_def); ++it) {
auto &ev = **it;
EnumNameMember(enum_def, ev, code_ptr);
}
EndEnumNames(code_ptr);
} }
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) { // Returns the function name that is able to read a value of the given type.
switch (field.value.type.base_type) { std::string GenGetter(const Type &type) {
case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; switch (type.base_type) {
default: return field.value.constant; 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));
}
} }
}
// Create a struct with a builder and the struct's arguments. // Returns the method name for use with add/put calls.
static void GenStructBuilder(const StructDef &struct_def, std::string GenMethod(const FieldDef &field) {
std::string *code_ptr) { return IsScalar(field.value.type.base_type)
BeginBuilderArgs(struct_def, code_ptr); ? MakeCamel(GenTypeBasic(field.value.type))
StructBuilderArgs(struct_def, "", code_ptr); : (IsStruct(field.value.type) ? "Struct" : "UOffsetT");
EndBuilderArgs(code_ptr); }
StructBuilderBody(struct_def, "", code_ptr); std::string GenTypeBasic(const Type &type) {
EndBuilderBody(code_ptr); 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];
}
class GoGenerator : public BaseGenerator { std::string GenTypePointer(const Type &type) {
public: switch (type.base_type) {
GoGenerator(const Parser &parser, const std::string &path, case BASE_TYPE_STRING: return "[]byte";
const std::string &file_name, const std::string &go_namespace) case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
: BaseGenerator(parser, path, file_name, "" /* not used*/, case BASE_TYPE_STRUCT: return WrapInNameSpaceAndTrack(*type.struct_def);
"" /* not used */) { case BASE_TYPE_UNION:
std::istringstream iss(go_namespace); // fall through
std::string component; default: return "*flatbuffers.Table";
while (std::getline(iss, component, '.')) {
go_namespace_.components.push_back(component);
} }
} }
bool generate() { std::string GenTypeGet(const Type &type) {
std::string one_file_code; if (type.enum_def != nullptr && !type.enum_def->is_union) {
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); return GetEnumTypeName(*type.enum_def);
++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;
}
} }
return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
}
for (auto it = parser_.structs_.vec.begin(); std::string TypeName(const FieldDef &field) {
it != parser_.structs_.vec.end(); ++it) { return GenTypeGet(field.value.type);
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 GenConstant(const FieldDef &field) {
std::string code = ""; switch (field.value.type.base_type) {
BeginFile(LastNamespacePart(go_namespace_), true, &code); case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";;
code += one_file_code; default: return field.value.constant;
const std::string filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), code, false);
} }
return true;
} }
private: // 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. // 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 { ...@@ -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 { ...@@ -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
......
...@@ -26,10 +26,13 @@ go_src=${go_path}/src ...@@ -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
......
...@@ -4,6 +4,8 @@ package Example ...@@ -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 { ...@@ -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
......
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
...@@ -17,7 +17,9 @@ ...@@ -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) { ...@@ -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{})) { ...@@ -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
......
...@@ -4,6 +4,8 @@ package NamespaceA ...@@ -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 { ...@@ -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
......
...@@ -4,6 +4,8 @@ package NamespaceA ...@@ -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 { ...@@ -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 { ...@@ -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
......
...@@ -4,6 +4,8 @@ package NamespaceC ...@@ -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 { ...@@ -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 { ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment