diff --git a/internal/generator/flatbuffersc/reflection/AdvancedFeatures.go b/internal/generator/flatbuffersc/reflection/AdvancedFeatures.go new file mode 100644 index 00000000..2b23bc8d --- /dev/null +++ b/internal/generator/flatbuffersc/reflection/AdvancedFeatures.go @@ -0,0 +1,35 @@ +// Code generated by the FlatBuffers compiler. DO NOT EDIT. + +package reflection + +import "strconv" + +type AdvancedFeatures uint64 + +const ( + AdvancedFeaturesAdvancedArrayFeatures AdvancedFeatures = 1 + AdvancedFeaturesAdvancedUnionFeatures AdvancedFeatures = 2 + AdvancedFeaturesOptionalScalars AdvancedFeatures = 4 + AdvancedFeaturesDefaultVectorsAndStrings AdvancedFeatures = 8 +) + +var EnumNamesAdvancedFeatures = map[AdvancedFeatures]string{ + AdvancedFeaturesAdvancedArrayFeatures: "AdvancedArrayFeatures", + AdvancedFeaturesAdvancedUnionFeatures: "AdvancedUnionFeatures", + AdvancedFeaturesOptionalScalars: "OptionalScalars", + AdvancedFeaturesDefaultVectorsAndStrings: "DefaultVectorsAndStrings", +} + +var EnumValuesAdvancedFeatures = map[string]AdvancedFeatures{ + "AdvancedArrayFeatures": AdvancedFeaturesAdvancedArrayFeatures, + "AdvancedUnionFeatures": AdvancedFeaturesAdvancedUnionFeatures, + "OptionalScalars": AdvancedFeaturesOptionalScalars, + "DefaultVectorsAndStrings": AdvancedFeaturesDefaultVectorsAndStrings, +} + +func (v AdvancedFeatures) String() string { + if s, ok := EnumNamesAdvancedFeatures[v]; ok { + return s + } + return "AdvancedFeatures(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/internal/generator/flatbuffersc/reflection/BaseType.go b/internal/generator/flatbuffersc/reflection/BaseType.go index 2fdc5dbe..43751e88 100644 --- a/internal/generator/flatbuffersc/reflection/BaseType.go +++ b/internal/generator/flatbuffersc/reflection/BaseType.go @@ -7,66 +7,69 @@ import "strconv" type BaseType int8 const ( - BaseTypeNone BaseType = 0 - BaseTypeUType BaseType = 1 - BaseTypeBool BaseType = 2 - BaseTypeByte BaseType = 3 - BaseTypeUByte BaseType = 4 - BaseTypeShort BaseType = 5 - BaseTypeUShort BaseType = 6 - BaseTypeInt BaseType = 7 - BaseTypeUInt BaseType = 8 - BaseTypeLong BaseType = 9 - BaseTypeULong BaseType = 10 - BaseTypeFloat BaseType = 11 - BaseTypeDouble BaseType = 12 - BaseTypeString BaseType = 13 - BaseTypeVector BaseType = 14 - BaseTypeObj BaseType = 15 - BaseTypeUnion BaseType = 16 - BaseTypeArray BaseType = 17 + BaseTypeNone BaseType = 0 + BaseTypeUType BaseType = 1 + BaseTypeBool BaseType = 2 + BaseTypeByte BaseType = 3 + BaseTypeUByte BaseType = 4 + BaseTypeShort BaseType = 5 + BaseTypeUShort BaseType = 6 + BaseTypeInt BaseType = 7 + BaseTypeUInt BaseType = 8 + BaseTypeLong BaseType = 9 + BaseTypeULong BaseType = 10 + BaseTypeFloat BaseType = 11 + BaseTypeDouble BaseType = 12 + BaseTypeString BaseType = 13 + BaseTypeVector BaseType = 14 + BaseTypeObj BaseType = 15 + BaseTypeUnion BaseType = 16 + BaseTypeArray BaseType = 17 + BaseTypeMaxBaseType BaseType = 18 ) var EnumNamesBaseType = map[BaseType]string{ - BaseTypeNone: "None", - BaseTypeUType: "UType", - BaseTypeBool: "Bool", - BaseTypeByte: "Byte", - BaseTypeUByte: "UByte", - BaseTypeShort: "Short", - BaseTypeUShort: "UShort", - BaseTypeInt: "Int", - BaseTypeUInt: "UInt", - BaseTypeLong: "Long", - BaseTypeULong: "ULong", - BaseTypeFloat: "Float", - BaseTypeDouble: "Double", - BaseTypeString: "String", - BaseTypeVector: "Vector", - BaseTypeObj: "Obj", - BaseTypeUnion: "Union", - BaseTypeArray: "Array", + BaseTypeNone: "None", + BaseTypeUType: "UType", + BaseTypeBool: "Bool", + BaseTypeByte: "Byte", + BaseTypeUByte: "UByte", + BaseTypeShort: "Short", + BaseTypeUShort: "UShort", + BaseTypeInt: "Int", + BaseTypeUInt: "UInt", + BaseTypeLong: "Long", + BaseTypeULong: "ULong", + BaseTypeFloat: "Float", + BaseTypeDouble: "Double", + BaseTypeString: "String", + BaseTypeVector: "Vector", + BaseTypeObj: "Obj", + BaseTypeUnion: "Union", + BaseTypeArray: "Array", + BaseTypeMaxBaseType: "MaxBaseType", } var EnumValuesBaseType = map[string]BaseType{ - "None": BaseTypeNone, - "UType": BaseTypeUType, - "Bool": BaseTypeBool, - "Byte": BaseTypeByte, - "UByte": BaseTypeUByte, - "Short": BaseTypeShort, - "UShort": BaseTypeUShort, - "Int": BaseTypeInt, - "UInt": BaseTypeUInt, - "Long": BaseTypeLong, - "ULong": BaseTypeULong, - "Float": BaseTypeFloat, - "Double": BaseTypeDouble, - "String": BaseTypeString, - "Vector": BaseTypeVector, - "Obj": BaseTypeObj, - "Union": BaseTypeUnion, - "Array": BaseTypeArray, + "None": BaseTypeNone, + "UType": BaseTypeUType, + "Bool": BaseTypeBool, + "Byte": BaseTypeByte, + "UByte": BaseTypeUByte, + "Short": BaseTypeShort, + "UShort": BaseTypeUShort, + "Int": BaseTypeInt, + "UInt": BaseTypeUInt, + "Long": BaseTypeLong, + "ULong": BaseTypeULong, + "Float": BaseTypeFloat, + "Double": BaseTypeDouble, + "String": BaseTypeString, + "Vector": BaseTypeVector, + "Obj": BaseTypeObj, + "Union": BaseTypeUnion, + "Array": BaseTypeArray, + "MaxBaseType": BaseTypeMaxBaseType, } func (v BaseType) String() string { diff --git a/internal/generator/flatbuffersc/reflection/Enum.go b/internal/generator/flatbuffersc/reflection/Enum.go index 565edf39..cde53dcc 100644 --- a/internal/generator/flatbuffersc/reflection/Enum.go +++ b/internal/generator/flatbuffersc/reflection/Enum.go @@ -17,6 +17,13 @@ func GetRootAsEnum(buf []byte, offset flatbuffers.UOffsetT) *Enum { return x } +func GetSizePrefixedRootAsEnum(buf []byte, offset flatbuffers.UOffsetT) *Enum { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Enum{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Enum) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/EnumVal.go b/internal/generator/flatbuffersc/reflection/EnumVal.go index a0bfcc83..dba2ab7a 100644 --- a/internal/generator/flatbuffersc/reflection/EnumVal.go +++ b/internal/generator/flatbuffersc/reflection/EnumVal.go @@ -17,6 +17,13 @@ func GetRootAsEnumVal(buf []byte, offset flatbuffers.UOffsetT) *EnumVal { return x } +func GetSizePrefixedRootAsEnumVal(buf []byte, offset flatbuffers.UOffsetT) *EnumVal { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &EnumVal{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *EnumVal) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/Field.go b/internal/generator/flatbuffersc/reflection/Field.go index 1a791575..60858db9 100644 --- a/internal/generator/flatbuffersc/reflection/Field.go +++ b/internal/generator/flatbuffersc/reflection/Field.go @@ -17,6 +17,13 @@ func GetRootAsField(buf []byte, offset flatbuffers.UOffsetT) *Field { return x } +func GetSizePrefixedRootAsField(buf []byte, offset flatbuffers.UOffsetT) *Field { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Field{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Field) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i @@ -168,8 +175,20 @@ func (rcv *Field) DocumentationLength() int { return 0 } +func (rcv *Field) Optional() bool { + o := flatbuffers.UOffsetT(rcv._tab.Offset(26)) + if o != 0 { + return rcv._tab.GetBool(o + rcv._tab.Pos) + } + return false +} + +func (rcv *Field) MutateOptional(n bool) bool { + return rcv._tab.MutateBoolSlot(26, n) +} + func FieldStart(builder *flatbuffers.Builder) { - builder.StartObject(11) + builder.StartObject(12) } func FieldAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(name), 0) @@ -210,6 +229,9 @@ func FieldAddDocumentation(builder *flatbuffers.Builder, documentation flatbuffe func FieldStartDocumentationVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(4, numElems, 4) } +func FieldAddOptional(builder *flatbuffers.Builder, optional bool) { + builder.PrependBoolSlot(11, optional, false) +} func FieldEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/internal/generator/flatbuffersc/reflection/KeyValue.go b/internal/generator/flatbuffersc/reflection/KeyValue.go index 1bfcf0cc..6d538ce7 100644 --- a/internal/generator/flatbuffersc/reflection/KeyValue.go +++ b/internal/generator/flatbuffersc/reflection/KeyValue.go @@ -17,6 +17,13 @@ func GetRootAsKeyValue(buf []byte, offset flatbuffers.UOffsetT) *KeyValue { return x } +func GetSizePrefixedRootAsKeyValue(buf []byte, offset flatbuffers.UOffsetT) *KeyValue { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &KeyValue{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *KeyValue) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/Object.go b/internal/generator/flatbuffersc/reflection/Object.go index f83af024..6baa4475 100644 --- a/internal/generator/flatbuffersc/reflection/Object.go +++ b/internal/generator/flatbuffersc/reflection/Object.go @@ -17,6 +17,13 @@ func GetRootAsObject(buf []byte, offset flatbuffers.UOffsetT) *Object { return x } +func GetSizePrefixedRootAsObject(buf []byte, offset flatbuffers.UOffsetT) *Object { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Object{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Object) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/RPCCall.go b/internal/generator/flatbuffersc/reflection/RPCCall.go index 0ecaeb9c..9d2b057a 100644 --- a/internal/generator/flatbuffersc/reflection/RPCCall.go +++ b/internal/generator/flatbuffersc/reflection/RPCCall.go @@ -17,6 +17,13 @@ func GetRootAsRPCCall(buf []byte, offset flatbuffers.UOffsetT) *RPCCall { return x } +func GetSizePrefixedRootAsRPCCall(buf []byte, offset flatbuffers.UOffsetT) *RPCCall { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &RPCCall{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *RPCCall) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/Schema.go b/internal/generator/flatbuffersc/reflection/Schema.go index 0be53c6c..ad0c3118 100644 --- a/internal/generator/flatbuffersc/reflection/Schema.go +++ b/internal/generator/flatbuffersc/reflection/Schema.go @@ -17,6 +17,13 @@ func GetRootAsSchema(buf []byte, offset flatbuffers.UOffsetT) *Schema { return x } +func GetSizePrefixedRootAsSchema(buf []byte, offset flatbuffers.UOffsetT) *Schema { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Schema{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Schema) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i @@ -115,8 +122,20 @@ func (rcv *Schema) ServicesLength() int { return 0 } +func (rcv *Schema) AdvancedFeatures() AdvancedFeatures { + o := flatbuffers.UOffsetT(rcv._tab.Offset(16)) + if o != 0 { + return AdvancedFeatures(rcv._tab.GetUint64(o + rcv._tab.Pos)) + } + return 0 +} + +func (rcv *Schema) MutateAdvancedFeatures(n AdvancedFeatures) bool { + return rcv._tab.MutateUint64Slot(16, uint64(n)) +} + func SchemaStart(builder *flatbuffers.Builder) { - builder.StartObject(6) + builder.StartObject(7) } func SchemaAddObjects(builder *flatbuffers.Builder, objects flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(objects), 0) @@ -145,6 +164,9 @@ func SchemaAddServices(builder *flatbuffers.Builder, services flatbuffers.UOffse func SchemaStartServicesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(4, numElems, 4) } +func SchemaAddAdvancedFeatures(builder *flatbuffers.Builder, advancedFeatures AdvancedFeatures) { + builder.PrependUint64Slot(6, uint64(advancedFeatures), 0) +} func SchemaEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/internal/generator/flatbuffersc/reflection/Service.go b/internal/generator/flatbuffersc/reflection/Service.go index b03c4f50..29f3fdb3 100644 --- a/internal/generator/flatbuffersc/reflection/Service.go +++ b/internal/generator/flatbuffersc/reflection/Service.go @@ -17,6 +17,13 @@ func GetRootAsService(buf []byte, offset flatbuffers.UOffsetT) *Service { return x } +func GetSizePrefixedRootAsService(buf []byte, offset flatbuffers.UOffsetT) *Service { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Service{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Service) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/internal/generator/flatbuffersc/reflection/Type.go b/internal/generator/flatbuffersc/reflection/Type.go index 9bbf7a70..d1d1f65c 100644 --- a/internal/generator/flatbuffersc/reflection/Type.go +++ b/internal/generator/flatbuffersc/reflection/Type.go @@ -17,6 +17,13 @@ func GetRootAsType(buf []byte, offset flatbuffers.UOffsetT) *Type { return x } +func GetSizePrefixedRootAsType(buf []byte, offset flatbuffers.UOffsetT) *Type { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Type{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Type) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/scripts/update-flatbuffersc-reflection.sh b/scripts/update-flatbuffersc-reflection.sh index 9be139b2..e800344b 100755 --- a/scripts/update-flatbuffersc-reflection.sh +++ b/scripts/update-flatbuffersc-reflection.sh @@ -24,7 +24,7 @@ fi mkdir -pv "${tmpDir}" printf "\n******** Generating Go code ********\n" -${flatc} --go -o "${tmpDir}" "${repoDir}"/third_party/flatbuffers-c-bridge/cmake-build/_deps/flatbuffers-*-src/reflection/reflection.fbs +${flatc} --go -o "${tmpDir}" "${repoDir}"/third_party/flatbuffers-c-bridge/third_party/flatbuffers/reflection/reflection.fbs echo "Generated files:" ls "${tmpDir}/reflection" diff --git a/third_party/flatbuffers-c-bridge/CMakeLists.txt b/third_party/flatbuffers-c-bridge/CMakeLists.txt index 3ec46257..9e387f05 100644 --- a/third_party/flatbuffers-c-bridge/CMakeLists.txt +++ b/third_party/flatbuffers-c-bridge/CMakeLists.txt @@ -24,7 +24,7 @@ add_library(${PROJECT_NAME} STATIC ${FLATBUFFERS_SRC_DIR}/src/idl_gen_kotlin.cpp ${FLATBUFFERS_SRC_DIR}/src/idl_gen_go.cpp ${FLATBUFFERS_SRC_DIR}/src/idl_gen_java.cpp - ${FLATBUFFERS_SRC_DIR}/src/idl_gen_js_ts.cpp + ${FLATBUFFERS_SRC_DIR}/src/idl_gen_ts.cpp ${FLATBUFFERS_SRC_DIR}/src/idl_gen_php.cpp ${FLATBUFFERS_SRC_DIR}/src/idl_gen_python.cpp ${FLATBUFFERS_SRC_DIR}/src/idl_gen_lobster.cpp @@ -45,10 +45,11 @@ add_library(${PROJECT_NAME} STATIC ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/java_generator.h ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/java_generator.cc ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/python_generator.h - ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/python_private_generator.h ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/python_generator.cc ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/swift_generator.h ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/swift_generator.cc + ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/ts_generator.h + ${FLATBUFFERS_SRC_DIR}/grpc/src/compiler/ts_generator.cc ) target_link_libraries(${PROJECT_NAME} PUBLIC flatbuffers) target_include_directories(${PROJECT_NAME} PRIVATE include ${FLATBUFFERS_SRC_DIR}/grpc) diff --git a/third_party/flatbuffers-c-bridge/src/flatc_main.cpp b/third_party/flatbuffers-c-bridge/src/flatc_main.cpp index 76c8870a..71ff04ed 100644 --- a/third_party/flatbuffers-c-bridge/src/flatc_main.cpp +++ b/third_party/flatbuffers-c-bridge/src/flatc_main.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /* +/* * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. * https://objectbox.io * @@ -31,7 +31,8 @@ * You should have received a copy of the GNU General Public License * along with ObjectBox Generator. If not, see . */ -// This file is heavily based on flatbuffers src/flatc_main.cpp - it exposes the main() function +// This file is heavily based on flatbuffers src/flatc_main.cpp - it exposes the +// main() function #include #include @@ -41,80 +42,100 @@ #include "utils.h" namespace { -static void Warn(const flatbuffers::FlatCompiler* flatc, const std::string& warn, bool show_exe_name) { - printf("flatc warning: %s\n", warn.c_str()); +static void Warn(const flatbuffers::FlatCompiler *flatc, + const std::string &warn, bool show_exe_name) { + printf("flatc warning: %s\n", warn.c_str()); } -static void Error(const flatbuffers::FlatCompiler* flatc, const std::string& err, bool usage, bool show_exe_name) { - printf("flatc error: %s\n", err.c_str()); - if (usage && flatc) { - printf("%s", flatc->GetUsageString("").c_str()); - } - throw std::runtime_error(err); +static void Error(const flatbuffers::FlatCompiler *flatc, + const std::string &err, bool usage, bool show_exe_name) { + printf("flatc error: %s\n", err.c_str()); + if (usage && flatc) { + printf("%s", flatc->GetUsageString("").c_str()); + } + throw std::runtime_error(err); } -} // namespace +} // namespace namespace flatbuffers { void LogCompilerWarn(const std::string &warn) { - Warn(static_cast(nullptr), warn, true); + Warn(static_cast(nullptr), warn, true); } void LogCompilerError(const std::string &err) { - Error(static_cast(nullptr), err, false, - true); + Error(static_cast(nullptr), err, false, + true); } -} // namespace flatbuffers +} // namespace flatbuffers -int fbs_flatc(const char** args, size_t count, const char** out_error) { - int code = 1; - runCpp(out_error, [&]() { - const flatbuffers::FlatCompiler::Generator generators[] = { - {flatbuffers::GenerateBinary, "-b", "--binary", "binary", false, nullptr, flatbuffers::IDLOptions::kBinary, - "Generate wire format binaries for any data definitions", flatbuffers::BinaryMakeRule}, - {flatbuffers::GenerateTextFile, "-t", "--json", "text", false, nullptr, flatbuffers::IDLOptions::kJson, - "Generate text output for any data definitions", flatbuffers::TextMakeRule}, - {flatbuffers::GenerateCPP, "-c", "--cpp", "C++", true, flatbuffers::GenerateCppGRPC, - flatbuffers::IDLOptions::kCpp, "Generate C++ headers for tables/structs", flatbuffers::CPPMakeRule}, - {flatbuffers::GenerateGo, "-g", "--go", "Go", true, flatbuffers::GenerateGoGRPC, - flatbuffers::IDLOptions::kGo, "Generate Go files for tables/structs", nullptr}, - {flatbuffers::GenerateJava, "-j", "--java", "Java", true, flatbuffers::GenerateJavaGRPC, - flatbuffers::IDLOptions::kJava, "Generate Java classes for tables/structs", - flatbuffers::JavaCSharpMakeRule}, - {flatbuffers::GenerateJSTS, "-s", "--js", "JavaScript", true, nullptr, flatbuffers::IDLOptions::kJs, - "Generate JavaScript code for tables/structs", flatbuffers::JSTSMakeRule}, - {flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr, flatbuffers::IDLOptions::kDart, - "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule}, - {flatbuffers::GenerateJSTS, "-T", "--ts", "TypeScript", true, nullptr, flatbuffers::IDLOptions::kTs, - "Generate TypeScript code for tables/structs", flatbuffers::JSTSMakeRule}, - {flatbuffers::GenerateCSharp, "-n", "--csharp", "C#", true, nullptr, flatbuffers::IDLOptions::kCSharp, - "Generate C# classes for tables/structs", flatbuffers::JavaCSharpMakeRule}, - {flatbuffers::GeneratePython, "-p", "--python", "Python", true, flatbuffers::GeneratePythonGRPC, - flatbuffers::IDLOptions::kPython, "Generate Python files for tables/structs", nullptr}, - {flatbuffers::GenerateLobster, nullptr, "--lobster", "Lobster", true, nullptr, - flatbuffers::IDLOptions::kLobster, "Generate Lobster files for tables/structs", nullptr}, - {flatbuffers::GenerateLua, "-l", "--lua", "Lua", true, nullptr, flatbuffers::IDLOptions::kLua, - "Generate Lua files for tables/structs", nullptr}, - {flatbuffers::GenerateRust, "-r", "--rust", "Rust", true, nullptr, flatbuffers::IDLOptions::kRust, - "Generate Rust files for tables/structs", flatbuffers::RustMakeRule}, - {flatbuffers::GeneratePhp, nullptr, "--php", "PHP", true, nullptr, flatbuffers::IDLOptions::kPhp, - "Generate PHP files for tables/structs", nullptr}, - {flatbuffers::GenerateKotlin, nullptr, "--kotlin", "Kotlin", true, nullptr, - flatbuffers::IDLOptions::kKotlin, "Generate Kotlin classes for tables/structs", nullptr}, - {flatbuffers::GenerateJsonSchema, nullptr, "--jsonschema", "JsonSchema", true, nullptr, - flatbuffers::IDLOptions::kJsonSchema, "Generate Json schema", nullptr}, - {flatbuffers::GenerateSwift, nullptr, "--swift", "swift", true, flatbuffers::GenerateSwiftGRPC, - flatbuffers::IDLOptions::kSwift, "Generate Swift files for tables/structs", nullptr}, - }; +int fbs_flatc(const char **args, size_t count, const char **out_error) { + int code = 1; + runCpp(out_error, [&]() { + const flatbuffers::FlatCompiler::Generator generators[] = { + {flatbuffers::GenerateBinary, "-b", "--binary", "binary", false, + nullptr, flatbuffers::IDLOptions::kBinary, + "Generate wire format binaries for any data definitions", + flatbuffers::BinaryMakeRule}, + {flatbuffers::GenerateTextFile, "-t", "--json", "text", false, nullptr, + flatbuffers::IDLOptions::kJson, + "Generate text output for any data definitions", + flatbuffers::TextMakeRule}, + {flatbuffers::GenerateCPP, "-c", "--cpp", "C++", true, + flatbuffers::GenerateCppGRPC, flatbuffers::IDLOptions::kCpp, + "Generate C++ headers for tables/structs", flatbuffers::CPPMakeRule}, + {flatbuffers::GenerateGo, "-g", "--go", "Go", true, + flatbuffers::GenerateGoGRPC, flatbuffers::IDLOptions::kGo, + "Generate Go files for tables/structs", nullptr}, + {flatbuffers::GenerateJava, "-j", "--java", "Java", true, + flatbuffers::GenerateJavaGRPC, flatbuffers::IDLOptions::kJava, + "Generate Java classes for tables/structs", + flatbuffers::JavaCSharpMakeRule}, + {flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr, + flatbuffers::IDLOptions::kDart, + "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule}, + {flatbuffers::GenerateTS, "-T", "--ts", "TypeScript", true, + flatbuffers::GenerateTSGRPC, flatbuffers::IDLOptions::kTs, + "Generate TypeScript code for tables/structs", + flatbuffers::TSMakeRule}, + {flatbuffers::GenerateCSharp, "-n", "--csharp", "C#", true, nullptr, + flatbuffers::IDLOptions::kCSharp, + "Generate C# classes for tables/structs", + flatbuffers::JavaCSharpMakeRule}, + {flatbuffers::GeneratePython, "-p", "--python", "Python", true, + flatbuffers::GeneratePythonGRPC, flatbuffers::IDLOptions::kPython, + "Generate Python files for tables/structs", nullptr}, + {flatbuffers::GenerateLobster, nullptr, "--lobster", "Lobster", true, + nullptr, flatbuffers::IDLOptions::kLobster, + "Generate Lobster files for tables/structs", nullptr}, + {flatbuffers::GenerateLua, "-l", "--lua", "Lua", true, nullptr, + flatbuffers::IDLOptions::kLua, "Generate Lua files for tables/structs", + nullptr}, + {flatbuffers::GenerateRust, "-r", "--rust", "Rust", true, nullptr, + flatbuffers::IDLOptions::kRust, + "Generate Rust files for tables/structs", flatbuffers::RustMakeRule}, + {flatbuffers::GeneratePhp, nullptr, "--php", "PHP", true, nullptr, + flatbuffers::IDLOptions::kPhp, "Generate PHP files for tables/structs", + nullptr}, + {flatbuffers::GenerateKotlin, nullptr, "--kotlin", "Kotlin", true, + nullptr, flatbuffers::IDLOptions::kKotlin, + "Generate Kotlin classes for tables/structs", nullptr}, + {flatbuffers::GenerateJsonSchema, nullptr, "--jsonschema", "JsonSchema", + true, nullptr, flatbuffers::IDLOptions::kJsonSchema, + "Generate Json schema", nullptr}, + {flatbuffers::GenerateSwift, nullptr, "--swift", "swift", true, + flatbuffers::GenerateSwiftGRPC, flatbuffers::IDLOptions::kSwift, + "Generate Swift files for tables/structs", nullptr}, + }; - flatbuffers::FlatCompiler::InitParams params; - params.generators = generators; - params.num_generators = sizeof(generators) / sizeof(generators[0]); - params.warn_fn = Warn; - params.error_fn = Error; + flatbuffers::FlatCompiler::InitParams params; + params.generators = generators; + params.num_generators = sizeof(generators) / sizeof(generators[0]); + params.warn_fn = Warn; + params.error_fn = Error; - flatbuffers::FlatCompiler flatc(params); - code = flatc.Compile(count, args); - }); - fflush(stdout); - fflush(stderr); - return code; + flatbuffers::FlatCompiler flatc(params); + code = flatc.Compile(count, args); + }); + fflush(stdout); + fflush(stderr); + return code; } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers.cmake b/third_party/flatbuffers-c-bridge/third_party/flatbuffers.cmake index 2a76bdcc..54a807fd 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers.cmake +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers.cmake @@ -1,6 +1,6 @@ # Cmake FetchContent is nice to use but not supported in pre-v3.11 cmake. # Instead we keep a copy of some FB sources in this repository. -# Current version: 1.12.0 +# Current version: 2.0.0 set(FLATBUFFERS_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/flatbuffers) diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/README.md b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/README.md index 544651d6..685003f9 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/README.md +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/README.md @@ -22,6 +22,12 @@ the GRPC libraries for this to compile. This test will build using the 5. `cmake -DFLATBUFFERS_BUILD_GRPCTEST=ON -DGRPC_INSTALL_PATH=${GRPC_INSTALL_PATH} -DPROTOBUF_DOWNLOAD_PATH=${PROTOBUF_DOWNLOAD_PATH} ..` 6. `make` +For Bazel users: + +```shell +$bazel test src/compiler/... +``` + ## Running FlatBuffer gRPC tests ### Linux @@ -29,3 +35,9 @@ the GRPC libraries for this to compile. This test will build using the 1. `ln -s ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.6 ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.1` 2. `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${GRPC_INSTALL_PATH}/lib` 3. `make test ARGS=-V` + +For Bazel users: + +```shell +$bazel test tests/... +``` \ No newline at end of file diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/BUILD b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/BUILD deleted file mode 100644 index 96a78584..00000000 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/BUILD +++ /dev/null @@ -1,107 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") - -package( - default_visibility = ["//visibility:public"], -) - -filegroup( - name = "common_headers", - srcs = [ - "config.h", - "schema_interface.h", - ], -) - -cc_library( - name = "cpp_generator", - srcs = [ - "cpp_generator.cc", - ], - hdrs = [ - "cpp_generator.h", - ":common_headers", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - deps = [ - "//:flatbuffers", - ], -) - -cc_library( - name = "go_generator", - srcs = [ - "go_generator.cc", - ], - hdrs = [ - "go_generator.h", - ":common_headers", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - deps = [ - "//:flatbuffers", - ], -) - -cc_library( - name = "java_generator", - srcs = [ - "java_generator.cc", - ], - hdrs = [ - "java_generator.h", - ":common_headers", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - deps = [ - "//:flatbuffers", - ], -) - -cc_library( - name = "python_generator", - hdrs = [ - "python_generator.h", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - deps = [ - ":python_generator_private", - ], -) - -cc_library( - name = "python_generator_private", - srcs = [ - "python_generator.cc", - ], - hdrs = [ - "python_generator.h", - "python_private_generator.h", - ":common_headers", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - visibility = ["//visibility:private"], - deps = [ - "//:flatbuffers", - ], -) - -cc_library( - name = "swift_generator", - srcs = [ - "swift_generator.cc", - ], - hdrs = [ - "swift_generator.h", - ":common_headers", - ], - include_prefix = "src/compiler", - strip_include_prefix = "/grpc/src/compiler", - deps = [ - "//:flatbuffers", - ], -) \ No newline at end of file diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/cpp_generator.cc b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/cpp_generator.cc index 6cfd22e3..8dd40883 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/cpp_generator.cc +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/cpp_generator.cc @@ -144,15 +144,15 @@ grpc::string GetHeaderIncludes(grpc_generator::File *file, std::map vars; static const char *headers_strs[] = { - "grpc++/impl/codegen/async_stream.h", - "grpc++/impl/codegen/async_unary_call.h", - "grpc++/impl/codegen/method_handler_impl.h", - "grpc++/impl/codegen/proto_utils.h", - "grpc++/impl/codegen/rpc_method.h", - "grpc++/impl/codegen/service_type.h", - "grpc++/impl/codegen/status.h", - "grpc++/impl/codegen/stub_options.h", - "grpc++/impl/codegen/sync_stream.h"}; + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/async_unary_call.h", + "grpcpp/impl/codegen/method_handler.h", + "grpcpp/impl/codegen/proto_utils.h", + "grpcpp/impl/codegen/rpc_method.h", + "grpcpp/impl/codegen/service_type.h", + "grpcpp/impl/codegen/status.h", + "grpcpp/impl/codegen/stub_options.h", + "grpcpp/impl/codegen/sync_stream.h"}; std::vector headers(headers_strs, array_end(headers_strs)); PrintIncludes(printer.get(), headers, params); printer->Print(vars, "\n"); @@ -1179,14 +1179,14 @@ grpc::string GetSourceIncludes(grpc_generator::File *file, std::map vars; static const char *headers_strs[] = { - "grpc++/impl/codegen/async_stream.h", - "grpc++/impl/codegen/async_unary_call.h", - "grpc++/impl/codegen/channel_interface.h", - "grpc++/impl/codegen/client_unary_call.h", - "grpc++/impl/codegen/method_handler_impl.h", - "grpc++/impl/codegen/rpc_service_method.h", - "grpc++/impl/codegen/service_type.h", - "grpc++/impl/codegen/sync_stream.h"}; + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/async_unary_call.h", + "grpcpp/impl/codegen/channel_interface.h", + "grpcpp/impl/codegen/client_unary_call.h", + "grpcpp/impl/codegen/method_handler.h", + "grpcpp/impl/codegen/rpc_service_method.h", + "grpcpp/impl/codegen/service_type.h", + "grpcpp/impl/codegen/sync_stream.h"}; std::vector headers(headers_strs, array_end(headers_strs)); PrintIncludes(printer.get(), headers, params); @@ -1604,8 +1604,8 @@ grpc::string GetMockIncludes(grpc_generator::File *file, std::map vars; static const char *headers_strs[] = { - "grpc++/impl/codegen/async_stream.h", - "grpc++/impl/codegen/sync_stream.h", + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/sync_stream.h", "gmock/gmock.h", }; std::vector headers(headers_strs, array_end(headers_strs)); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/go_generator.cc b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/go_generator.cc index 604828d6..d646451a 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/go_generator.cc +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/go_generator.cc @@ -70,6 +70,17 @@ grpc::string exportName(grpc::string s) { return s; } +void GenerateError(grpc_generator::Printer *printer, + std::map vars, + const bool multiple_return = true) { + printer->Print(vars, "if $Error_Check$ {\n"); + printer->Indent(); + vars["Return"] = multiple_return ? "nil, err" : "err"; + printer->Print(vars, "return $Return$\n"); + printer->Outdent(); + printer->Print("}\n"); +} + // Generates imports for the service void GenerateImports(grpc_generator::File *file, grpc_generator::Printer *printer, std::map vars) { @@ -78,14 +89,13 @@ void GenerateImports(grpc_generator::File *file, grpc_generator::Printer *printe printer->Print("//If you make any local changes, they will be lost\n"); printer->Print(vars, "//source: $filename$\n\n"); printer->Print(vars, "package $Package$\n\n"); - if (file->additional_headers() != "") { - printer->Print(file->additional_headers().c_str()); - printer->Print("\n\n"); - } printer->Print("import (\n"); printer->Indent(); printer->Print(vars, "$context$ \"context\"\n"); + printer->Print("flatbuffers \"github.com/google/flatbuffers/go\"\n"); printer->Print(vars, "$grpc$ \"google.golang.org/grpc\"\n"); + printer->Print("\"google.golang.org/grpc/codes\"\n"); + printer->Print("\"google.golang.org/grpc/status\"\n"); printer->Outdent(); printer->Print(")\n\n"); } @@ -93,15 +103,15 @@ void GenerateImports(grpc_generator::File *file, grpc_generator::Printer *printe // Generates Server method signature source void GenerateServerMethodSignature(const grpc_generator::Method *method, grpc_generator::Printer *printer, std::map vars) { - vars["Method"] = exportName(method->name()); + vars["Method"] = exportName(method->name()); vars["Request"] = method->get_input_type_name(); vars["Response"] = (vars["CustomMethodIO"] == "") ? method->get_output_type_name() : vars["CustomMethodIO"]; if (method->NoStreaming()) { - printer->Print(vars, "$Method$($context$.Context, *$Request$) (*$Response$, error)"); + printer->Print(vars, "$Method$($context$.Context, *$Request$) (*$Response$, error)$Ending$"); } else if (ServerOnlyStreaming(method)) { - printer->Print(vars, "$Method$(*$Request$, $Service$_$Method$Server) error"); + printer->Print(vars, "$Method$(*$Request$, $Service$_$Method$Server) error$Ending$"); } else { - printer->Print(vars, "$Method$($Service$_$Method$Server) error"); + printer->Print(vars, "$Method$($Service$_$Method$Server) error$Ending$"); } } @@ -110,28 +120,36 @@ void GenerateServerMethod(const grpc_generator::Method *method, grpc_generator:: vars["Method"] = exportName(method->name()); vars["Request"] = method->get_input_type_name(); vars["Response"] = (vars["CustomMethodIO"] == "") ? method->get_output_type_name() : vars["CustomMethodIO"]; - vars["FullMethodName"] = "/" + vars["ServicePrefix"] + "." + vars["Service"] + "/" + vars["Method"]; + vars["FullMethodName"] = "/" + vars["ServicePrefix"] + vars["Service"] + "/" + vars["Method"]; vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler"; if (method->NoStreaming()) { printer->Print(vars, "func $Handler$(srv interface{}, ctx $context$.Context,\n\tdec func(interface{}) error, interceptor $grpc$.UnaryServerInterceptor) (interface{}, error) {\n"); printer->Indent(); printer->Print(vars, "in := new($Request$)\n"); - printer->Print("if err := dec(in); err != nil { return nil, err }\n"); - printer->Print(vars, "if interceptor == nil { return srv.($Service$Server).$Method$(ctx, in) }\n"); + vars["Error_Check"] = "err := dec(in); err != nil"; + GenerateError(printer, vars); + printer->Print("if interceptor == nil {\n"); + printer->Indent(); + printer->Print(vars, "return srv.($Service$Server).$Method$(ctx, in)\n"); + printer->Outdent(); + printer->Print("}\n"); printer->Print(vars, "info := &$grpc$.UnaryServerInfo{\n"); printer->Indent(); - printer->Print("Server: srv,\n"); + printer->Print("Server: srv,\n"); printer->Print(vars, "FullMethod: \"$FullMethodName$\",\n"); printer->Outdent(); - printer->Print("}\n\n"); + printer->Print("}\n"); + printer->Outdent(); + printer->Print("\n"); + printer->Indent(); printer->Print(vars, "handler := func(ctx $context$.Context, req interface{}) (interface{}, error) {\n"); printer->Indent(); - printer->Print(vars, "return srv.($Service$Server).$Method$(ctx, req.(* $Request$))\n"); + printer->Print(vars, "return srv.($Service$Server).$Method$(ctx, req.(*$Request$))\n"); printer->Outdent(); printer->Print("}\n"); printer->Print("return interceptor(ctx, in, info, handler)\n"); printer->Outdent(); - printer->Print("}\n\n"); + printer->Print("}\n"); return; } vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Server"; @@ -139,7 +157,8 @@ void GenerateServerMethod(const grpc_generator::Method *method, grpc_generator:: printer->Indent(); if (ServerOnlyStreaming(method)) { printer->Print(vars, "m := new($Request$)\n"); - printer->Print(vars, "if err := stream.RecvMsg(m); err != nil { return err }\n"); + vars["Error_Check"] = "err := stream.RecvMsg(m); err != nil"; + GenerateError(printer, vars, false); printer->Print(vars, "return srv.($Service$Server).$Method$(m, &$StreamType${stream})\n"); } else { printer->Print(vars, "return srv.($Service$Server).$Method$(&$StreamType${stream})\n"); @@ -151,16 +170,16 @@ void GenerateServerMethod(const grpc_generator::Method *method, grpc_generator:: bool genRecv = method->BidiStreaming() || ClientOnlyStreaming(method); bool genSendAndClose = ClientOnlyStreaming(method); - printer->Print(vars, "type $Service$_$Method$Server interface { \n"); + printer->Print(vars, "type $Service$_$Method$Server interface {\n"); printer->Indent(); if (genSend) { - printer->Print(vars, "Send(* $Response$) error\n"); + printer->Print(vars, "Send(*$Response$) error\n"); } if (genRecv) { - printer->Print(vars, "Recv() (* $Request$, error)\n"); + printer->Print(vars, "Recv() (*$Request$, error)\n"); } if (genSendAndClose) { - printer->Print(vars, "SendAndClose(* $Response$) error\n"); + printer->Print(vars, "SendAndClose(*$Response$) error\n"); } printer->Print(vars, "$grpc$.ServerStream\n"); printer->Outdent(); @@ -183,7 +202,8 @@ void GenerateServerMethod(const grpc_generator::Method *method, grpc_generator:: printer->Print(vars, "func (x *$StreamType$) Recv() (*$Request$, error) {\n"); printer->Indent(); printer->Print(vars, "m := new($Request$)\n"); - printer->Print("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }\n"); + vars["Error_Check"] = "err := x.ServerStream.RecvMsg(m); err != nil"; + GenerateError(printer, vars); printer->Print("return m, nil\n"); printer->Outdent(); printer->Print("}\n\n"); @@ -206,43 +226,47 @@ void GenerateClientMethodSignature(const grpc_generator::Method *method, grpc_ge if (ClientOnlyStreaming(method) || method->BidiStreaming()) { vars["Request"] = ""; } - vars["Response"] = "* " + method->get_output_type_name(); + vars["Response"] = "*" + method->get_output_type_name(); if (ClientOnlyStreaming(method) || method->BidiStreaming() || ServerOnlyStreaming(method)) { vars["Response"] = vars["Service"] + "_" + vars["Method"] + "Client" ; } - printer->Print(vars, "$Method$(ctx $context$.Context$Request$, \n\topts... $grpc$.CallOption) ($Response$, error)"); + printer->Print(vars, "$Method$(ctx $context$.Context$Request$,\n\topts ...$grpc$.CallOption) ($Response$, error)$Ending$"); } // Generates Client method source void GenerateClientMethod(const grpc_generator::Method *method, grpc_generator::Printer *printer, std::map vars) { printer->Print(vars, "func (c *$ServiceUnexported$Client) "); + vars["Ending"] = " {\n"; GenerateClientMethodSignature(method, printer, vars); - printer->Print(" {\n"); printer->Indent(); vars["Method"] = exportName(method->name()); vars["Request"] = (vars["CustomMethodIO"] == "") ? method->get_input_type_name() : vars["CustomMethodIO"]; vars["Response"] = method->get_output_type_name(); - vars["FullMethodName"] = "/" + vars["ServicePrefix"] + "." + vars["Service"] + "/" + vars["Method"]; + vars["FullMethodName"] = "/" + vars["ServicePrefix"] + vars["Service"] + "/" + vars["Method"]; if (method->NoStreaming()) { printer->Print(vars, "out := new($Response$)\n"); - printer->Print(vars, "err := $grpc$.Invoke(ctx, \"$FullMethodName$\", in, out, c.cc, opts...)\n"); - printer->Print("if err != nil { return nil, err }\n"); + printer->Print(vars, "err := c.cc.Invoke(ctx, \"$FullMethodName$\", in, out, opts...)\n"); + vars["Error_Check"] = "err != nil"; + GenerateError(printer, vars); printer->Print("return out, nil\n"); printer->Outdent(); printer->Print("}\n\n"); return; } vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Client"; - printer->Print(vars, "stream, err := $grpc$.NewClientStream(ctx, &$MethodDesc$, c.cc, \"$FullMethodName$\", opts...)\n"); - printer->Print("if err != nil { return nil, err }\n"); + printer->Print(vars, "stream, err := c.cc.NewStream(ctx, &$MethodDesc$, \"$FullMethodName$\", opts...)\n"); + vars["Error_Check"] = "err != nil"; + GenerateError(printer, vars); printer->Print(vars, "x := &$StreamType${stream}\n"); if (ServerOnlyStreaming(method)) { - printer->Print("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }\n"); - printer->Print("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }\n"); + vars["Error_Check"] = "err := x.ClientStream.SendMsg(in); err != nil"; + GenerateError(printer, vars); + vars["Error_Check"] = "err := x.ClientStream.CloseSend(); err != nil"; + GenerateError(printer, vars); } - printer->Print("return x,nil\n"); + printer->Print("return x, nil\n"); printer->Outdent(); printer->Print("}\n\n"); @@ -267,7 +291,7 @@ void GenerateClientMethod(const grpc_generator::Method *method, grpc_generator:: printer->Print("}\n\n"); //Stream Client - printer->Print(vars, "type $StreamType$ struct{\n"); + printer->Print(vars, "type $StreamType$ struct {\n"); printer->Indent(); printer->Print(vars, "$grpc$.ClientStream\n"); printer->Outdent(); @@ -285,7 +309,8 @@ void GenerateClientMethod(const grpc_generator::Method *method, grpc_generator:: printer->Print(vars, "func (x *$StreamType$) Recv() (*$Response$, error) {\n"); printer->Indent(); printer->Print(vars, "m := new($Response$)\n"); - printer->Print("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }\n"); + vars["Error_Check"] = "err := x.ClientStream.RecvMsg(m); err != nil"; + GenerateError(printer, vars); printer->Print("return m, nil\n"); printer->Outdent(); printer->Print("}\n\n"); @@ -294,9 +319,11 @@ void GenerateClientMethod(const grpc_generator::Method *method, grpc_generator:: if (genCloseAndRecv) { printer->Print(vars, "func (x *$StreamType$) CloseAndRecv() (*$Response$, error) {\n"); printer->Indent(); - printer->Print("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }\n"); - printer->Print(vars, "m := new ($Response$)\n"); - printer->Print("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }\n"); + vars["Error_Check"] = "err := x.ClientStream.CloseSend(); err != nil"; + GenerateError(printer, vars); + printer->Print(vars, "m := new($Response$)\n"); + vars["Error_Check"] = "err := x.ClientStream.RecvMsg(m); err != nil"; + GenerateError(printer, vars); printer->Print("return m, nil\n"); printer->Outdent(); printer->Print("}\n\n"); @@ -309,11 +336,11 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri vars["Service"] = exportName(service->name()); // Client Interface printer->Print(vars, "// Client API for $Service$ service\n"); - printer->Print(vars, "type $Service$Client interface{\n"); + printer->Print(vars, "type $Service$Client interface {\n"); printer->Indent(); + vars["Ending"] = "\n"; for (int i = 0; i < service->method_count(); i++) { GenerateClientMethodSignature(service->method(i).get(), printer, vars); - printer->Print("\n"); } printer->Outdent(); printer->Print("}\n\n"); @@ -322,12 +349,12 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri vars["ServiceUnexported"] = unexportName(vars["Service"]); printer->Print(vars, "type $ServiceUnexported$Client struct {\n"); printer->Indent(); - printer->Print(vars, "cc *$grpc$.ClientConn\n"); + printer->Print(vars, "cc $grpc$.ClientConnInterface\n"); printer->Outdent(); printer->Print("}\n\n"); // NewClient - printer->Print(vars, "func New$Service$Client(cc *$grpc$.ClientConn) $Service$Client {\n"); + printer->Print(vars, "func New$Service$Client(cc $grpc$.ClientConnInterface) $Service$Client {\n"); printer->Indent(); printer->Print(vars, "return &$ServiceUnexported$Client{cc}"); printer->Outdent(); @@ -351,15 +378,41 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri printer->Print(vars, "// Server API for $Service$ service\n"); printer->Print(vars, "type $Service$Server interface {\n"); printer->Indent(); + vars["Ending"] = "\n"; for (int i = 0; i < service->method_count(); i++) { GenerateServerMethodSignature(service->method(i).get(), printer, vars); - printer->Print("\n"); } + printer->Print(vars, "mustEmbedUnimplemented$Service$Server()\n"); printer->Outdent(); printer->Print("}\n\n"); + printer->Print(vars, "type Unimplemented$Service$Server struct {\n"); + printer->Print("}\n\n"); + + vars["Ending"] = " {\n"; + for (int i = 0; i < service->method_count(); i++) { + auto method = service->method(i); + vars["Method"] = exportName(method->name()); + vars["Nil"] = method->NoStreaming() ? "nil, " : ""; + printer->Print(vars, "func (Unimplemented$Service$Server) "); + GenerateServerMethodSignature(method.get(), printer, vars); + printer->Indent(); + printer->Print(vars, "return $Nil$status.Errorf(codes.Unimplemented, \"method $Method$ not implemented\")\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print("\n"); + } + + printer->Print(vars, "func (Unimplemented$Service$Server) mustEmbedUnimplemented$Service$Server() {}"); + printer->Print("\n\n"); + + printer->Print(vars, "type Unsafe$Service$Server interface {\n"); + printer->Indent(); + printer->Print(vars, "mustEmbedUnimplemented$Service$Server()\n"); + printer->Outdent(); + printer->Print("}\n\n"); // Server registration. - printer->Print(vars, "func Register$Service$Server(s *$grpc$.Server, srv $Service$Server) {\n"); + printer->Print(vars, "func Register$Service$Server(s $grpc$.ServiceRegistrar, srv $Service$Server) {\n"); printer->Indent(); printer->Print(vars, "s.RegisterService(&$ServiceDesc$, srv)\n"); printer->Outdent(); @@ -367,26 +420,25 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri for (int i = 0; i < service->method_count(); i++) { GenerateServerMethod(service->method(i).get(), printer, vars); - printer->Print("\n"); } //Service Descriptor printer->Print(vars, "var $ServiceDesc$ = $grpc$.ServiceDesc{\n"); printer->Indent(); - printer->Print(vars, "ServiceName: \"$ServicePrefix$.$Service$\",\n"); + printer->Print(vars, "ServiceName: \"$ServicePrefix$$Service$\",\n"); printer->Print(vars, "HandlerType: (*$Service$Server)(nil),\n"); printer->Print(vars, "Methods: []$grpc$.MethodDesc{\n"); printer->Indent(); for (int i = 0; i < service->method_count(); i++) { auto method = service->method(i); - vars["Method"] = method->name(); + vars["Method"] = exportName(method->name()); vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler"; if (method->NoStreaming()) { printer->Print("{\n"); printer->Indent(); printer->Print(vars, "MethodName: \"$Method$\",\n"); - printer->Print(vars, "Handler: $Handler$, \n"); + printer->Print(vars, "Handler: $Handler$,\n"); printer->Outdent(); printer->Print("},\n"); } @@ -397,13 +449,13 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri printer->Indent(); for (int i = 0; i < service->method_count(); i++) { auto method = service->method(i); - vars["Method"] = method->name(); + vars["Method"] = exportName(method->name()); vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler"; if (!method->NoStreaming()) { printer->Print("{\n"); printer->Indent(); - printer->Print(vars, "StreamName: \"$Method$\",\n"); - printer->Print(vars, "Handler: $Handler$, \n"); + printer->Print(vars, "StreamName: \"$Method$\",\n"); + printer->Print(vars, "Handler: $Handler$,\n"); if (ClientOnlyStreaming(method.get())) { printer->Print("ClientStreams: true,\n"); } else if (ServerOnlyStreaming(method.get())) { @@ -419,7 +471,7 @@ void GenerateService(const grpc_generator::Service *service, grpc_generator::Pri printer->Outdent(); printer->Print("},\n"); printer->Outdent(); - printer->Print("}\n\n"); + printer->Print("}\n"); } @@ -429,11 +481,14 @@ grpc::string GenerateServiceSource(grpc_generator::File *file, const grpc_generator::Service *service, grpc_go_generator::Parameters *parameters) { grpc::string out; - auto p = file->CreatePrinter(&out); + auto p = file->CreatePrinter(&out, '\t'); + p->SetIndentationSize(1); auto printer = p.get(); std::map vars; vars["Package"] = parameters->package_name; vars["ServicePrefix"] = parameters->service_prefix; + if (!parameters->service_prefix.empty()) + vars["ServicePrefix"].append("."); vars["grpc"] = "grpc"; vars["context"] = "context"; GenerateImports(file, printer, vars); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.cc b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.cc index 3fcf7ea4..8108db45 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.cc +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.cc @@ -16,608 +16,133 @@ * */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include -#include #include "flatbuffers/util.h" #include "src/compiler/python_generator.h" -#include "src/compiler/python_private_generator.h" - -using std::make_pair; -using std::map; -using std::pair; -using std::replace; -using std::tuple; -using std::vector; -using std::set; namespace grpc_python_generator { -grpc::string generator_file_name; - -typedef map StringMap; -typedef vector StringVector; -typedef tuple StringPair; -typedef set StringPairSet; - -// Provides RAII indentation handling. Use as: -// { -// IndentScope raii_my_indent_var_name_here(my_py_printer); -// // constructor indented my_py_printer -// ... -// // destructor called at end of scope, un-indenting my_py_printer -// } -class IndentScope { - public: - explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) { - printer_->Indent(); - } - - ~IndentScope() { printer_->Outdent(); } - - private: - grpc_generator::Printer* printer_; -}; +grpc::string GenerateMethodType(const grpc_generator::Method *method) { -inline grpc::string StringReplace(grpc::string str, const grpc::string& from, - const grpc::string& to, bool replace_all) { - size_t pos = 0; + if (method->NoStreaming()) + return "unary_unary"; - do { - pos = str.find(from, pos); - if (pos == grpc::string::npos) { - break; - } - str.replace(pos, from.length(), to); - pos += to.length(); - } while (replace_all); + if (method->ServerStreaming()) + return "unary_stream"; - return str; -} - -inline grpc::string StringReplace(grpc::string str, const grpc::string& from, - const grpc::string& to) { - return StringReplace(str, from, to, true); -} + if (method->ClientStreaming()) + return "stream_unary"; -grpc::string ModuleName(const grpc::string& filename, - const grpc::string& import_prefix) { - grpc::string basename = flatbuffers::StripExtension(filename); - basename = StringReplace(basename, "-", "_"); - basename = StringReplace(basename, "/", "."); - return import_prefix + basename + "_fb"; + return "stream_stream"; } -grpc::string ModuleAlias(const grpc::string& filename, - const grpc::string& import_prefix) { - grpc::string module_name = ModuleName(filename, import_prefix); - // We can't have dots in the module name, so we replace each with _dot_. - // But that could lead to a collision between a.b and a_dot_b, so we also - // duplicate each underscore. - module_name = StringReplace(module_name, "_", "__"); - module_name = StringReplace(module_name, ".", "_dot_"); - return module_name; -} - -PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config_, - const grpc_generator::File* file_) - : config(config_), file(file_) {} - -void PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service, - grpc_generator::Printer* out) { - StringMap service_dict; - service_dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(service_dict, "class Beta$Service$Servicer(object):\n"); - { - IndentScope raii_class_indent(out); - out->Print( - "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" - "\nIt is recommended to use the GA API (classes and functions in this\n" - "file not marked beta) for all further purposes. This class was " - "generated\n" - "only to ease transition from grpcio<0.15.0 to " - "grpcio>=0.15.0.\"\"\"\n"); - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - grpc::string arg_name = - method->ClientStreaming() ? "request_iterator" : "request"; - StringMap method_dict; - method_dict["Method"] = method->name(); - method_dict["ArgName"] = arg_name; - out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n"); - { - IndentScope raii_method_indent(out); - out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n"); - } - } - } -} +grpc::string GenerateMethodInput(const grpc_generator::Method *method) { -void PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service, - grpc_generator::Printer* out) { - StringMap service_dict; - service_dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(service_dict, "class Beta$Service$Stub(object):\n"); - { - IndentScope raii_class_indent(out); - out->Print( - "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" - "\nIt is recommended to use the GA API (classes and functions in this\n" - "file not marked beta) for all further purposes. This class was " - "generated\n" - "only to ease transition from grpcio<0.15.0 to " - "grpcio>=0.15.0.\"\"\"\n"); - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - grpc::string arg_name = - method->ClientStreaming() ? "request_iterator" : "request"; - StringMap method_dict; - method_dict["Method"] = method->name(); - method_dict["ArgName"] = arg_name; - out->Print(method_dict, - "def $Method$(self, $ArgName$, timeout, metadata=None, " - "with_call=False, protocol_options=None):\n"); - { - IndentScope raii_method_indent(out); - out->Print("raise NotImplementedError()\n"); - } - if (!method->ServerStreaming()) { - out->Print(method_dict, "$Method$.future = None\n"); - } - } - } -} + if (method->NoStreaming() || method->ServerStreaming()) + return "self, request, context"; -void PrivateGenerator::PrintBetaServerFactory( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out) { - StringMap service_dict; - service_dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(service_dict, - "def beta_create_$Service$_server(servicer, pool=None, " - "pool_size=None, default_timeout=None, maximum_timeout=None):\n"); - { - IndentScope raii_create_server_indent(out); - out->Print( - "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" - "\nIt is recommended to use the GA API (classes and functions in this\n" - "file not marked beta) for all further purposes. This function was\n" - "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" - "\"\"\"\n"); - StringMap method_implementation_constructors; - StringMap input_message_modules_and_classes; - StringMap output_message_modules_and_classes; - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - const grpc::string method_implementation_constructor = - grpc::string(method->ClientStreaming() ? "stream_" : "unary_") + - grpc::string(method->ServerStreaming() ? "stream_" : "unary_") + - "inline"; - grpc::string input_message_module_and_class = method->get_fb_builder(); - grpc::string output_message_module_and_class = method->get_fb_builder(); - method_implementation_constructors.insert( - make_pair(method->name(), method_implementation_constructor)); - input_message_modules_and_classes.insert( - make_pair(method->name(), input_message_module_and_class)); - output_message_modules_and_classes.insert( - make_pair(method->name(), output_message_module_and_class)); - } - StringMap method_dict; - method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; -// out->Print("request_deserializers = {\n"); -// for (StringMap::iterator name_and_input_module_class_pair = -// input_message_modules_and_classes.begin(); -// name_and_input_module_class_pair != -// input_message_modules_and_classes.end(); -// name_and_input_module_class_pair++) { -// method_dict["MethodName"] = name_and_input_module_class_pair->first; -// method_dict["InputTypeModuleAndClass"] = -// name_and_input_module_class_pair->second; -// IndentScope raii_indent(out); -// out->Print(method_dict, -// "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " -// "$InputTypeModuleAndClass$.FromString,\n"); -// } -// out->Print("}\n"); -// out->Print("response_serializers = {\n"); -// for (StringMap::iterator name_and_output_module_class_pair = -// output_message_modules_and_classes.begin(); -// name_and_output_module_class_pair != -// output_message_modules_and_classes.end(); -// name_and_output_module_class_pair++) { -// method_dict["MethodName"] = name_and_output_module_class_pair->first; -// method_dict["OutputTypeModuleAndClass"] = -// name_and_output_module_class_pair->second; -// IndentScope raii_indent(out); -// out->Print(method_dict, -// "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " -// "$OutputTypeModuleAndClass$.SerializeToString,\n"); -// } -// out->Print("}\n"); - out->Print("method_implementations = {\n"); - for (StringMap::iterator name_and_implementation_constructor = - method_implementation_constructors.begin(); - name_and_implementation_constructor != - method_implementation_constructors.end(); - name_and_implementation_constructor++) { - method_dict["Method"] = name_and_implementation_constructor->first; - method_dict["Constructor"] = name_and_implementation_constructor->second; - IndentScope raii_descriptions_indent(out); - const grpc::string method_name = - name_and_implementation_constructor->first; - out->Print(method_dict, - "(\'$PackageQualifiedServiceName$\', \'$Method$\'): " - "face_utilities.$Constructor$(servicer.$Method$),\n"); - } - out->Print("}\n"); - out->Print( - "server_options = beta_implementations.server_options(" - "thread_pool=pool, thread_pool_size=pool_size, " - "default_timeout=default_timeout, " - "maximum_timeout=maximum_timeout)\n"); - out->Print( - "return beta_implementations.server(method_implementations, " - "options=server_options)\n"); - //"request_deserializers=request_deserializers, " - //"response_serializers=response_serializers, " - } + return "self, request_iterator, context"; } -void PrivateGenerator::PrintBetaStubFactory( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out) { - StringMap dict; - dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(dict, - "def beta_create_$Service$_stub(channel, host=None," - " metadata_transformer=None, pool=None, pool_size=None):\n"); - { - IndentScope raii_create_server_indent(out); - out->Print( - "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" - "\nIt is recommended to use the GA API (classes and functions in this\n" - "file not marked beta) for all further purposes. This function was\n" - "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" - "\"\"\"\n"); - StringMap method_cardinalities; - StringMap input_message_modules_and_classes; - StringMap output_message_modules_and_classes; - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - const grpc::string method_cardinality = - grpc::string(method->ClientStreaming() ? "STREAM" : "UNARY") + - "_" + - grpc::string(method->ServerStreaming() ? "STREAM" : "UNARY"); - grpc::string input_message_module_and_class = method->get_fb_builder(); - grpc::string output_message_module_and_class = method->get_fb_builder(); - method_cardinalities.insert( - make_pair(method->name(), method_cardinality)); - input_message_modules_and_classes.insert( - make_pair(method->name(), input_message_module_and_class)); - output_message_modules_and_classes.insert( - make_pair(method->name(), output_message_module_and_class)); - } - StringMap method_dict; - method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; -// out->Print("request_serializers = {\n"); -// for (StringMap::iterator name_and_input_module_class_pair = -// input_message_modules_and_classes.begin(); -// name_and_input_module_class_pair != -// input_message_modules_and_classes.end(); -// name_and_input_module_class_pair++) { -// method_dict["MethodName"] = name_and_input_module_class_pair->first; -// method_dict["InputTypeModuleAndClass"] = -// name_and_input_module_class_pair->second; -// IndentScope raii_indent(out); -// out->Print(method_dict, -// "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " -// "$InputTypeModuleAndClass$.SerializeToString,\n"); -// } -// out->Print("}\n"); -// out->Print("response_deserializers = {\n"); -// for (StringMap::iterator name_and_output_module_class_pair = -// output_message_modules_and_classes.begin(); -// name_and_output_module_class_pair != -// output_message_modules_and_classes.end(); -// name_and_output_module_class_pair++) { -// method_dict["MethodName"] = name_and_output_module_class_pair->first; -// method_dict["OutputTypeModuleAndClass"] = -// name_and_output_module_class_pair->second; -// IndentScope raii_indent(out); -// out->Print(method_dict, -// "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " -// "$OutputTypeModuleAndClass$.FromString,\n"); -// } -// out->Print("}\n"); - out->Print("cardinalities = {\n"); - for (StringMap::iterator name_and_cardinality = - method_cardinalities.begin(); - name_and_cardinality != method_cardinalities.end(); - name_and_cardinality++) { - method_dict["Method"] = name_and_cardinality->first; - method_dict["Cardinality"] = name_and_cardinality->second; - IndentScope raii_descriptions_indent(out); - out->Print(method_dict, - "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n"); - } - out->Print("}\n"); - out->Print( - "stub_options = beta_implementations.stub_options(" - "host=host, metadata_transformer=metadata_transformer, " - "thread_pool=pool, thread_pool_size=pool_size)\n"); - out->Print(method_dict, - "return beta_implementations.dynamic_stub(channel, " - "\'$PackageQualifiedServiceName$\', " - "cardinalities, options=stub_options)\n"); - // "request_serializers=request_serializers, " - //"response_deserializers=response_deserializers, " - } -} +void GenerateStub(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "class $ServiceName$Stub(object):\n"); + printer->Indent(); + printer->Print("\"\"\" Interface exported by the server. \"\"\""); + printer->Print("\n\n"); + printer->Print("def __init__(self, channel):\n"); + printer->Indent(); + printer->Print("\"\"\" Constructor. \n\n"); + printer->Print("Args: \n"); + printer->Print("channel: A grpc.Channel. \n"); + printer->Print("\"\"\"\n\n"); -void PrivateGenerator::PrintStub( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out) { - StringMap dict; - dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(dict, "class $Service$Stub(object):\n"); - { - IndentScope raii_class_indent(out); - out->Print("\n"); - out->Print("def __init__(self, channel):\n"); - { - IndentScope raii_init_indent(out); - out->Print("\"\"\"Constructor.\n"); - out->Print("\n"); - out->Print("Args:\n"); - { - IndentScope raii_args_indent(out); - out->Print("channel: A grpc.Channel.\n"); - } - out->Print("\"\"\"\n"); - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - grpc::string multi_callable_constructor = - grpc::string(method->ClientStreaming() ? "stream" : "unary") + - "_" + - grpc::string(method->ServerStreaming() ? "stream" : "unary"); - grpc::string request_module_and_class = method->get_fb_builder(); - grpc::string response_module_and_class = method->get_fb_builder(); - StringMap method_dict; - method_dict["Method"] = method->name(); - method_dict["MultiCallableConstructor"] = multi_callable_constructor; - out->Print(method_dict, - "self.$Method$ = channel.$MultiCallableConstructor$(\n"); - { - method_dict["PackageQualifiedService"] = - package_qualified_service_name; - method_dict["RequestModuleAndClass"] = request_module_and_class; - method_dict["ResponseModuleAndClass"] = response_module_and_class; - IndentScope raii_first_attribute_indent(out); - IndentScope raii_second_attribute_indent(out); - out->Print(method_dict, "'/$PackageQualifiedService$/$Method$',\n"); - out->Print(method_dict,"\n"); - out->Print( - method_dict,"\n"); - out->Print(")\n"); - } - } - } + for (int j = 0; j < service->method_count(); j++) { + auto method = service->method(j); + vars["MethodName"] = method->name(); + vars["MethodType"] = GenerateMethodType(&*method); + printer->Print(vars, "self.$MethodName$ = channel.$MethodType$(\n"); + printer->Indent(); + printer->Print(vars, "\"/$PATH$$ServiceName$/$MethodName$\"\n"); + printer->Print(")\n"); + printer->Outdent(); + printer->Print("\n"); } -} - -void PrivateGenerator::PrintServicer(const grpc_generator::Service* service, - grpc_generator::Printer* out) { - StringMap service_dict; - service_dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(service_dict, "class $Service$Servicer(object):\n"); - { - IndentScope raii_class_indent(out); - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - grpc::string arg_name = - method->ClientStreaming() ? "request_iterator" : "request"; - StringMap method_dict; - method_dict["Method"] = method->name(); - method_dict["ArgName"] = arg_name; - out->Print("\n"); - out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n"); - { - IndentScope raii_method_indent(out); - out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n"); - out->Print("context.set_details('Method not implemented!')\n"); - out->Print("raise NotImplementedError('Method not implemented!')\n"); - } - } + printer->Outdent(); + printer->Outdent(); + printer->Print("\n"); +} + +void GenerateServicer(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "class $ServiceName$Servicer(object):\n"); + printer->Indent(); + printer->Print("\"\"\" Interface exported by the server. \"\"\""); + printer->Print("\n\n"); + + for (int j = 0; j < service->method_count(); j++) { + auto method = service->method(j); + vars["MethodName"] = method->name(); + vars["MethodInput"] = GenerateMethodInput(&*method); + printer->Print(vars, "def $MethodName$($MethodInput$):\n"); + printer->Indent(); + printer->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n"); + printer->Print("context.set_details('Method not implemented!')\n"); + printer->Print("raise NotImplementedError('Method not implemented!')\n"); + printer->Outdent(); + printer->Print("\n\n"); } -} - -void PrivateGenerator::PrintAddServicerToServer( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out) { - StringMap service_dict; - service_dict["Service"] = service->name(); - out->Print("\n\n"); - out->Print(service_dict, - "def add_$Service$Servicer_to_server(servicer, server):\n"); - { - IndentScope raii_class_indent(out); - out->Print("rpc_method_handlers = {\n"); - { - IndentScope raii_dict_first_indent(out); - IndentScope raii_dict_second_indent(out); - for (int i = 0; i < service->method_count(); ++i) { - auto method = service->method(i); - grpc::string method_handler_constructor = - grpc::string(method->ClientStreaming() ? "stream" : "unary") + - "_" + - grpc::string(method->ServerStreaming() ? "stream" : "unary") + - "_rpc_method_handler"; - grpc::string request_module_and_class = method->get_fb_builder(); - grpc::string response_module_and_class = method->get_fb_builder(); - StringMap method_dict; - method_dict["Method"] = method->name(); - method_dict["MethodHandlerConstructor"] = method_handler_constructor; - method_dict["RequestModuleAndClass"] = request_module_and_class; - method_dict["ResponseModuleAndClass"] = response_module_and_class; - out->Print(method_dict, - "'$Method$': grpc.$MethodHandlerConstructor$(\n"); - { - IndentScope raii_call_first_indent(out); - IndentScope raii_call_second_indent(out); - out->Print(method_dict, "servicer.$Method$,\n"); - out->Print( - method_dict,"\n"); - out->Print( - method_dict, - "\n"); - } - out->Print("),\n"); - } - } - StringMap method_dict; - method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; - out->Print("}\n"); - out->Print("generic_handler = grpc.method_handlers_generic_handler(\n"); - { - IndentScope raii_call_first_indent(out); - IndentScope raii_call_second_indent(out); - out->Print(method_dict, - "'$PackageQualifiedServiceName$', rpc_method_handlers)\n"); - } - out->Print("server.add_generic_rpc_handlers((generic_handler,))\n"); + printer->Outdent(); + printer->Print("\n"); + +} + +void GenerateRegister(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "def add_$ServiceName$Servicer_to_server(servicer, server):\n"); + printer->Indent(); + printer->Print("rpc_method_handlers = {\n"); + printer->Indent(); + for (int j = 0; j < service->method_count(); j++) { + auto method = service->method(j); + vars["MethodName"] = method->name(); + vars["MethodType"] = GenerateMethodType(&*method); + printer->Print(vars, "'$MethodName$': grpc.$MethodType$_rpc_method_handler(\n"); + printer->Indent(); + printer->Print(vars, "servicer.$MethodName$\n"); + printer->Outdent(); + printer->Print("),\n"); } -} - -void PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) { - StringMap var; - var["Package"] = config.beta_package_root; - out->Print(var, - "from $Package$ import implementations as beta_implementations\n"); - out->Print(var, "from $Package$ import interfaces as beta_interfaces\n"); - out->Print("from grpc.framework.common import cardinality\n"); - out->Print( - "from grpc.framework.interfaces.face import utilities as " - "face_utilities\n"); -} - -void PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) { - StringMap var; - var["Package"] = config.grpc_package_root; - out->Print(var, "import $Package$\n"); - out->Print("\n"); - StringPairSet imports_set; - for (int i = 0; i < file->service_count(); ++i) { - auto service = file->service(i); - for (int j = 0; j < service->method_count(); ++j) { - auto method = service.get()->method(j); - - grpc::string input_type_file_name = method->get_fb_builder(); - grpc::string input_module_name = - ModuleName(input_type_file_name, config.import_prefix); - grpc::string input_module_alias = - ModuleAlias(input_type_file_name, config.import_prefix); - imports_set.insert( - std::make_tuple(input_module_name, input_module_alias)); - - grpc::string output_type_file_name = method->get_fb_builder(); - grpc::string output_module_name = - ModuleName(output_type_file_name, config.import_prefix); - grpc::string output_module_alias = - ModuleAlias(output_type_file_name, config.import_prefix); - imports_set.insert( - std::make_tuple(output_module_name, output_module_alias)); - } - } - - for (StringPairSet::iterator it = imports_set.begin(); - it != imports_set.end(); ++it) { - var["ModuleName"] = std::get<0>(*it); - var["ModuleAlias"] = std::get<1>(*it); - out->Print(var, "import $ModuleName$ as $ModuleAlias$\n"); - } -} - -void PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) { - grpc::string package = file->package(); - if (!package.empty()) { - package = package.append("."); - } - - out->Print(file->additional_headers().c_str()); - - for (int i = 0; i < file->service_count(); ++i) { - auto service = file->service(i); - - grpc::string package_qualified_service_name = package + service->name(); - PrintStub(package_qualified_service_name, service.get(), out); - PrintServicer(service.get(), out); - PrintAddServicerToServer(package_qualified_service_name, service.get(), - out); - } -} - -void PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) { - grpc::string package = file->package(); - if (!package.empty()) { - package = package.append("."); - } - for (int i = 0; i < file->service_count(); ++i) { - auto service = file->service(i); - - grpc::string package_qualified_service_name = package + service->name(); - PrintBetaServicer(service.get(), out); - PrintBetaStub(service.get(), out); - PrintBetaServerFactory(package_qualified_service_name, service.get(), out); - PrintBetaStubFactory(package_qualified_service_name, service.get(), out); - } -} - -grpc::string PrivateGenerator::GetGrpcServices() { + printer->Outdent(); + printer->Print("}\n"); + printer->Print(vars, "generic_handler = grpc.method_handlers_generic_handler(\n"); + printer->Indent(); + printer->Print(vars, "'$PATH$$ServiceName$', rpc_method_handlers)\n"); + printer->Outdent(); + printer->Print("server.add_generic_rpc_handlers((generic_handler,))"); + printer->Outdent(); + printer->Print("\n"); +} + +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service) { grpc::string output; - { - // Scope the output stream so it closes and finalizes output to the string. - auto out = file->CreatePrinter(&output); - out->Print( - "# Generated by the gRPC Python protocol compiler plugin. " - "DO NOT EDIT!\n"); - StringMap var; - var["Package"] = config.grpc_package_root; - out->Print(var, "import $Package$\n"); - PrintGAServices(out.get()); - out->Print("try:\n"); - { - IndentScope raii_dict_try_indent(out.get()); - out->Print( - "# THESE ELEMENTS WILL BE DEPRECATED.\n" - "# Please use the generated *_pb2_grpc.py files instead.\n"); - out->Print(var, "import $Package$\n"); - PrintBetaPreamble(out.get()); - PrintGAServices(out.get()); - PrintBetaServices(out.get()); - } - out->Print("except ImportError:\n"); - { - IndentScope raii_dict_except_indent(out.get()); - out->Print("pass"); - } - } + std::map vars; + vars["PATH"] = file->package(); + if (!file->package().empty()) { vars["PATH"].append("."); } + vars["ServiceName"] = service->name(); + auto printer = file->CreatePrinter(&output); + GenerateStub(service, &*printer, &vars); + GenerateServicer(service, &*printer, &vars); + GenerateRegister(service, &*printer, &vars); return output; } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.h index d92cb025..4f8f5cc8 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_generator.h @@ -21,20 +21,13 @@ #include +#include "src/compiler/config.h" #include "src/compiler/schema_interface.h" namespace grpc_python_generator { -// Data pertaining to configuration of the generator with respect to anything -// that may be used internally at Google. -struct GeneratorConfiguration { - grpc::string grpc_package_root; - // TODO(https://github.com/grpc/grpc/issues/8622): Drop this. - grpc::string beta_package_root; - // TODO(https://github.com/google/protobuf/issues/888): Drop this. - grpc::string import_prefix; -}; - +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service); } // namespace grpc_python_generator #endif // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_private_generator.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_private_generator.h deleted file mode 100644 index 30ba0d7b..00000000 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/python_private_generator.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H -#define GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H - -#include -#include - -#include "src/compiler/python_generator.h" -#include "src/compiler/schema_interface.h" - -namespace grpc_python_generator { - -// Tucks all generator state in an anonymous namespace away from -// PythonGrpcGenerator and the header file, mostly to encourage future changes -// to not require updates to the grpcio-tools C++ code part. Assumes that it is -// only ever used from a single thread. -struct PrivateGenerator { - const GeneratorConfiguration& config; - const grpc_generator::File* file; - - PrivateGenerator(const GeneratorConfiguration& config, - const grpc_generator::File* file); - - grpc::string GetGrpcServices(); - - private: - void PrintPreamble(grpc_generator::Printer* out); - void PrintBetaPreamble(grpc_generator::Printer* out); - void PrintGAServices(grpc_generator::Printer* out); - void PrintBetaServices(grpc_generator::Printer* out); - - void PrintAddServicerToServer( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out); - void PrintServicer(const grpc_generator::Service* service, - grpc_generator::Printer* out); - void PrintStub(const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, - grpc_generator::Printer* out); - - void PrintBetaServicer(const grpc_generator::Service* service, - grpc_generator::Printer* out); - void PrintBetaServerFactory( - const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, grpc_generator::Printer* out); - void PrintBetaStub(const grpc_generator::Service* service, - grpc_generator::Printer* out); - void PrintBetaStubFactory(const grpc::string& package_qualified_service_name, - const grpc_generator::Service* service, - grpc_generator::Printer* out); -}; - -} // namespace grpc_python_generator - -#endif // GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/schema_interface.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/schema_interface.h index 9611642e..04494981 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/schema_interface.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/schema_interface.h @@ -62,7 +62,9 @@ struct Method : public CommentHolder { grpc::string *str, grpc::string generator_file_name, bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0; + virtual std::vector get_input_namespace_parts() const = 0; virtual grpc::string get_input_type_name() const = 0; + virtual std::vector get_output_namespace_parts() const = 0; virtual grpc::string get_output_type_name() const = 0; virtual grpc::string get_fb_builder() const = 0; @@ -77,7 +79,9 @@ struct Method : public CommentHolder { struct Service : public CommentHolder { virtual ~Service() {} + virtual std::vector namespace_parts() const = 0; virtual grpc::string name() const = 0; + virtual bool is_internal() const = 0; virtual int method_count() const = 0; virtual std::unique_ptr method(int i) const = 0; @@ -89,6 +93,7 @@ struct Printer { virtual void Print(const std::map &vars, const char *template_string) = 0; virtual void Print(const char *string) = 0; + virtual void SetIndentationSize(const int size) = 0; virtual void Indent() = 0; virtual void Outdent() = 0; }; @@ -107,7 +112,8 @@ struct File : public CommentHolder { virtual int service_count() const = 0; virtual std::unique_ptr service(int i) const = 0; - virtual std::unique_ptr CreatePrinter(grpc::string *str) const = 0; + virtual std::unique_ptr CreatePrinter( + grpc::string *str, const char indentation_type = ' ') const = 0; }; } // namespace grpc_generator diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/swift_generator.cc b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/swift_generator.cc index 7997278b..403a803e 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/swift_generator.cc +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/swift_generator.cc @@ -29,111 +29,208 @@ namespace grpc_swift_generator { -grpc::string GenerateMessage(const grpc::string &name) { - return "Message<" + name + ">"; +std::string WrapInNameSpace(const std::vector &components, + const grpc::string &name) { + std::string qualified_name; + for (auto it = components.begin(); it != components.end(); ++it) + qualified_name += *it + "_"; + return qualified_name + name; +} + +grpc::string GenerateMessage(const std::vector &components, + const grpc::string &name) { + return "Message<" + WrapInNameSpace(components, name) + ">"; } // MARK: - Client -grpc::string GenerateClientFuncName(const grpc_generator::Method *method) { +void GenerateClientFuncName(const grpc_generator::Method *method, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; if (method->NoStreaming()) { - return "$GenAccess$ func $MethodName$(_ request: $Input$" - ", callOptions: CallOptions?$isNil$) -> UnaryCall<$Input$,$Output$>"; + printer->Print(vars, + " $GenAccess$func $MethodName$(\n" + " _ request: $Input$\n" + " , callOptions: CallOptions?$isNil$\n" + " ) -> UnaryCall<$Input$, $Output$>"); + return; } - if (method->ClientStreaming()) { - return "$GenAccess$ func $MethodName$" - "(callOptions: CallOptions?$isNil$) -> " - "ClientStreamingCall<$Input$,$Output$>"; + if (method->ServerStreaming()) { + printer->Print(vars, + " $GenAccess$func $MethodName$(\n" + " _ request: $Input$\n" + " , callOptions: CallOptions?$isNil$,\n" + " handler: @escaping ($Output$) -> Void\n" + " ) -> ServerStreamingCall<$Input$, $Output$>"); + return; } - if (method->ServerStreaming()) { - return "$GenAccess$ func $MethodName$(_ request: $Input$" - ", callOptions: CallOptions?$isNil$, handler: @escaping ($Output$" - ") -> Void) -> ServerStreamingCall<$Input$, $Output$>"; + if (method->ClientStreaming()) { + printer->Print(vars, + " $GenAccess$func $MethodName$(\n" + " callOptions: CallOptions?$isNil$\n" + " ) -> ClientStreamingCall<$Input$, $Output$>"); + return; } - return "$GenAccess$ func $MethodName$" - "(callOptions: CallOptions?$isNil$, handler: @escaping ($Output$" - ") -> Void) -> BidirectionalStreamingCall<$Input$, $Output$>"; + + printer->Print(vars, + " $GenAccess$func $MethodName$(\n" + " callOptions: CallOptions?$isNil$,\n" + " handler: @escaping ($Output$ ) -> Void\n" + " ) -> BidirectionalStreamingCall<$Input$, $Output$>"); } -grpc::string GenerateClientFuncBody(const grpc_generator::Method *method) { +void GenerateClientFuncBody(const grpc_generator::Method *method, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + vars["Interceptor"] = + "interceptors: self.interceptors?.make$MethodName$Interceptors() ?? []"; if (method->NoStreaming()) { - return "return self.makeUnaryCall(path: " - "\"/$PATH$$ServiceName$/$MethodName$\", request: request, " - "callOptions: callOptions ?? self.defaultCallOptions)"; + printer->Print( + vars, + " return self.makeUnaryCall(\n" + " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" + " request: request,\n" + " callOptions: callOptions ?? self.defaultCallOptions,\n" + " $Interceptor$\n" + " )\n"); + return; } - if (method->ClientStreaming()) { - return "return self.makeClientStreamingCall(path: " - "\"/$PATH$$ServiceName$/$MethodName$\", callOptions: callOptions ?? " - "self.defaultCallOptions)"; + if (method->ServerStreaming()) { + printer->Print( + vars, + " return self.makeServerStreamingCall(\n" + " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" + " request: request,\n" + " callOptions: callOptions ?? self.defaultCallOptions,\n" + " $Interceptor$,\n" + " handler: handler\n" + " )\n"); + return; } - if (method->ServerStreaming()) { - return "return self.makeServerStreamingCall(path: " - "\"/$PATH$$ServiceName$/$MethodName$\", request: request, " - "callOptions: callOptions ?? self.defaultCallOptions, handler: " - "handler)"; + if (method->ClientStreaming()) { + printer->Print( + vars, + " return self.makeClientStreamingCall(\n" + " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" + " callOptions: callOptions ?? self.defaultCallOptions,\n" + " $Interceptor$\n" + " )\n"); + return; } - return "return self.makeBidirectionalStreamingCall(path: " - "\"/$PATH$$ServiceName$/$MethodName$\", callOptions: callOptions ?? " - "self.defaultCallOptions, handler: handler)"; + printer->Print(vars, + " return self.makeBidirectionalStreamingCall(\n" + " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" + " callOptions: callOptions ?? self.defaultCallOptions,\n" + " $Interceptor$,\n" + " handler: handler\n" + " )\n"); } void GenerateClientProtocol(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; - printer->Print(vars, "$ACCESS$ protocol $ServiceName$Service {\n"); + printer->Print( + vars, + "$ACCESS$ protocol $ServiceQualifiedName$ClientProtocol: GRPCClient {"); + printer->Print("\n\n"); + printer->Print(" var serviceName: String { get }"); + printer->Print("\n\n"); + printer->Print( + vars, + " var interceptors: " + "$ServiceQualifiedName$ClientInterceptorFactoryProtocol? { get }"); + printer->Print("\n\n"); + vars["GenAccess"] = ""; for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); - vars["Input"] = GenerateMessage(method->get_input_type_name()); - vars["Output"] = GenerateMessage(method->get_output_type_name()); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); vars["MethodName"] = method->name(); vars["isNil"] = ""; - printer->Print("\t"); - auto func = GenerateClientFuncName(method.get()); - printer->Print(vars, func.c_str()); - printer->Print("\n"); + GenerateClientFuncName(method.get(), &*printer, &vars); + printer->Print("\n\n"); } printer->Print("}\n\n"); -} -void GenerateClientClass(const grpc_generator::Service *service, - grpc_generator::Printer *printer, - std::map *dictonary) { - auto vars = *dictonary; + printer->Print(vars, "extension $ServiceQualifiedName$ClientProtocol {"); + printer->Print("\n\n"); printer->Print(vars, - "$ACCESS$ final class $ServiceName$ServiceClient: GRPCClient, " - "$ServiceName$Service {\n"); - printer->Print(vars, "\t$ACCESS$ let connection: ClientConnection\n"); - printer->Print(vars, "\t$ACCESS$ var defaultCallOptions: CallOptions\n"); - printer->Print("\n"); - printer->Print(vars, - "\t$ACCESS$ init(connection: ClientConnection, " - "defaultCallOptions: CallOptions = CallOptions()) {\n"); - printer->Print("\t\tself.connection = connection\n"); - printer->Print("\t\tself.defaultCallOptions = defaultCallOptions\n"); - printer->Print("\t}"); - printer->Print("\n"); - vars["GenAccess"] = "public"; + " $ACCESS$ var serviceName: String { " + "\"$PATH$$ServiceName$\" }\n"); + + vars["GenAccess"] = service->is_internal() ? "internal " : "public "; for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); - vars["Input"] = GenerateMessage(method->get_input_type_name()); - vars["Output"] = GenerateMessage(method->get_output_type_name()); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); vars["MethodName"] = method->name(); vars["isNil"] = " = nil"; - printer->Print("\n\t"); - auto func = GenerateClientFuncName(method.get()); - printer->Print(vars, func.c_str()); + printer->Print("\n"); + GenerateClientFuncName(method.get(), &*printer, &vars); printer->Print(" {\n"); - auto body = GenerateClientFuncBody(method.get()); - printer->Print("\t\t"); - printer->Print(vars, body.c_str()); - printer->Print("\n\t}\n"); + GenerateClientFuncBody(method.get(), &*printer, &vars); + printer->Print(" }\n"); } + printer->Print("}\n\n"); + + printer->Print(vars, + "$ACCESS$ protocol " + "$ServiceQualifiedName$ClientInterceptorFactoryProtocol {\n"); + + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); + vars["MethodName"] = method->name(); + printer->Print( + vars, + " /// - Returns: Interceptors to use when invoking '$MethodName$'.\n"); + printer->Print(vars, + " func make$MethodName$Interceptors() -> " + "[ClientInterceptor<$Input$, $Output$>]\n\n"); + } + printer->Print("}\n\n"); +} + +void GenerateClientClass(grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, + "$ACCESS$ final class $ServiceQualifiedName$ServiceClient: " + "$ServiceQualifiedName$ClientProtocol {\n"); + printer->Print(vars, " $ACCESS$ let channel: GRPCChannel\n"); + printer->Print(vars, " $ACCESS$ var defaultCallOptions: CallOptions\n"); + printer->Print(vars, + " $ACCESS$ var interceptors: " + "$ServiceQualifiedName$ClientInterceptorFactoryProtocol?\n"); + printer->Print("\n"); + printer->Print( + vars, + " $ACCESS$ init(\n" + " channel: GRPCChannel,\n" + " defaultCallOptions: CallOptions = CallOptions(),\n" + " interceptors: " + "$ServiceQualifiedName$ClientInterceptorFactoryProtocol? = nil\n" + " ) {\n"); + printer->Print(" self.channel = channel\n"); + printer->Print(" self.defaultCallOptions = defaultCallOptions\n"); + printer->Print(" self.interceptors = interceptors\n"); + printer->Print(" }"); + printer->Print("\n"); printer->Print("}\n"); } @@ -141,7 +238,7 @@ void GenerateClientClass(const grpc_generator::Service *service, grpc::string GenerateServerFuncName(const grpc_generator::Method *method) { if (method->NoStreaming()) { - return "func $MethodName$(_ request: $Input$" + return "func $MethodName$(request: $Input$" ", context: StatusOnlyCallContext) -> EventLoopFuture<$Output$>"; } @@ -161,50 +258,45 @@ grpc::string GenerateServerFuncName(const grpc_generator::Method *method) { } grpc::string GenerateServerExtensionBody(const grpc_generator::Method *method) { - grpc::string start = "\t\tcase \"$MethodName$\":\n\t\t"; + grpc::string start = " case \"$MethodName$\":\n "; + grpc::string interceptors = + " interceptors: self.interceptors?.make$MethodName$Interceptors() " + "?? [],\n"; if (method->NoStreaming()) { return start + - "return UnaryCallHandler(callHandlerContext: callHandlerContext) { " - "context in" - "\n\t\t\t" - "return { request in" - "\n\t\t\t\t" - "self.$MethodName$(request, context: context)" - "\n\t\t\t}" - "\n\t\t}"; + "return UnaryServerHandler(\n" + " context: context,\n" + " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" + " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + + interceptors + + " userFunction: self.$MethodName$(request:context:))\n"; } - if (method->ClientStreaming()) { + if (method->ServerStreaming()) { return start + - "return ClientStreamingCallHandler(callHandlerContext: " - "callHandlerContext) { context in" - "\n\t\t\t" - "return { request in" - "\n\t\t\t\t" - "self.$MethodName$(request: request, context: context)" - "\n\t\t\t}" - "\n\t\t}"; + "return ServerStreamingServerHandler(\n" + " context: context,\n" + " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" + " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + + interceptors + + " userFunction: self.$MethodName$(request:context:))\n"; } - if (method->ServerStreaming()) { + if (method->ClientStreaming()) { return start + - "return ServerStreamingCallHandler(callHandlerContext: " - "callHandlerContext) { context in" - "\n\t\t\t" - "return { request in" - "\n\t\t\t\t" - "self.$MethodName$(request: request, context: context)" - "\n\t\t\t}" - "\n\t\t}"; + "return ClientStreamingServerHandler(\n" + " context: context,\n" + " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" + " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + + interceptors + + " observerFactory: self.$MethodName$(context:))\n"; } if (method->BidiStreaming()) { return start + - "return BidirectionalStreamingCallHandler(callHandlerContext: " - "callHandlerContext) { context in" - "\n\t\t\t" - "return { request in" - "\n\t\t\t\t" - "self.$MethodName$(request: request, context: context)" - "\n\t\t\t}" - "\n\t\t}"; + "return BidirectionalStreamingServerHandler(\n" + " context: context,\n" + " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" + " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + + interceptors + + " observerFactory: self.$MethodName$(context:))\n"; } return ""; } @@ -213,41 +305,72 @@ void GenerateServerProtocol(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; + printer->Print(vars, + "$ACCESS$ protocol $ServiceQualifiedName$Provider: " + "CallHandlerProvider {\n"); printer->Print( - vars, "$ACCESS$ protocol $ServiceName$Provider: CallHandlerProvider {\n"); + vars, + " var interceptors: " + "$ServiceQualifiedName$ServerInterceptorFactoryProtocol? { get }\n"); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); - vars["Input"] = GenerateMessage(method->get_input_type_name()); - vars["Output"] = GenerateMessage(method->get_output_type_name()); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); vars["MethodName"] = method->name(); - printer->Print("\t"); + printer->Print(" "); auto func = GenerateServerFuncName(method.get()); printer->Print(vars, func.c_str()); printer->Print("\n"); } printer->Print("}\n\n"); - printer->Print(vars, "$ACCESS$ extension $ServiceName$Provider {\n"); + printer->Print(vars, "$ACCESS$ extension $ServiceQualifiedName$Provider {\n"); + printer->Print("\n"); printer->Print(vars, - "\tvar serviceName: String { return " + " var serviceName: Substring { return " "\"$PATH$$ServiceName$\" }\n"); + printer->Print("\n"); printer->Print( - "\tfunc handleMethod(_ methodName: String, callHandlerContext: " - "CallHandlerContext) -> GRPCCallHandler? {\n"); - printer->Print("\t\tswitch methodName {\n"); + " func handle(method name: Substring, context: " + "CallHandlerContext) -> GRPCServerHandlerProtocol? {\n"); + printer->Print(" switch name {\n"); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); - vars["Input"] = GenerateMessage(method->get_input_type_name()); - vars["Output"] = GenerateMessage(method->get_output_type_name()); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); vars["MethodName"] = method->name(); auto body = GenerateServerExtensionBody(method.get()); printer->Print(vars, body.c_str()); printer->Print("\n"); } - printer->Print("\t\tdefault: return nil;\n"); - printer->Print("\t\t}\n"); - printer->Print("\t}\n\n"); + printer->Print(" default: return nil;\n"); + printer->Print(" }\n"); + printer->Print(" }\n\n"); printer->Print("}\n\n"); + + printer->Print(vars, + "$ACCESS$ protocol " + "$ServiceQualifiedName$ServerInterceptorFactoryProtocol {\n"); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), + method->get_input_type_name()); + vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), + method->get_output_type_name()); + vars["MethodName"] = method->name(); + printer->Print( + vars, + " /// - Returns: Interceptors to use when handling '$MethodName$'.\n" + " /// Defaults to calling `self.makeInterceptors()`.\n"); + printer->Print(vars, + " func make$MethodName$Interceptors() -> " + "[ServerInterceptor<$Input$, $Output$>]\n\n"); + } + printer->Print("}"); } grpc::string Generate(grpc_generator::File *file, @@ -256,14 +379,17 @@ grpc::string Generate(grpc_generator::File *file, std::map vars; vars["PATH"] = file->package(); if (!file->package().empty()) { vars["PATH"].append("."); } + vars["ServiceQualifiedName"] = + WrapInNameSpace(service->namespace_parts(), service->name()); vars["ServiceName"] = service->name(); - vars["ACCESS"] = "public"; + vars["ACCESS"] = service->is_internal() ? "internal" : "public"; auto printer = file->CreatePrinter(&output); - printer->Print(vars, - "/// Usage: instantiate $ServiceName$ServiceClient, then call " - "methods of this protocol to make API calls.\n"); + printer->Print( + vars, + "/// Usage: instantiate $ServiceQualifiedName$ServiceClient, then call " + "methods of this protocol to make API calls.\n"); GenerateClientProtocol(service, &*printer, &vars); - GenerateClientClass(service, &*printer, &vars); + GenerateClientClass(&*printer, &vars); printer->Print("\n"); GenerateServerProtocol(service, &*printer, &vars); return output; @@ -277,6 +403,10 @@ grpc::string GenerateHeader() { code += "/// in case of an issue please open github issue, though it would be " "maintained\n"; + code += "\n"; + code += "// swiftlint:disable all\n"; + code += "// swiftformat:disable all\n"; + code += "\n"; code += "import Foundation\n"; code += "import GRPC\n"; code += "import NIO\n"; @@ -288,19 +418,19 @@ grpc::string GenerateHeader() { "{}\n"; code += "public extension GRPCFlatBufPayload {\n"; - code += " init(serializedByteBuffer: inout NIO.ByteBuffer) throws {\n"; + code += " init(serializedByteBuffer: inout NIO.ByteBuffer) throws {\n"; code += - " self.init(byteBuffer: FlatBuffers.ByteBuffer(contiguousBytes: " + " self.init(byteBuffer: FlatBuffers.ByteBuffer(contiguousBytes: " "serializedByteBuffer.readableBytesView, count: " "serializedByteBuffer.readableBytes))\n"; - code += " }\n"; + code += " }\n"; - code += " func serialize(into buffer: inout NIO.ByteBuffer) throws {\n"; + code += " func serialize(into buffer: inout NIO.ByteBuffer) throws {\n"; code += - " let buf = UnsafeRawBufferPointer(start: self.rawPointer, count: " + " let buf = UnsafeRawBufferPointer(start: self.rawPointer, count: " "Int(self.size))\n"; - code += " buffer.writeBytes(buf)\n"; - code += " }\n"; + code += " buffer.writeBytes(buf)\n"; + code += " }\n"; code += "}\n"; code += "extension Message: GRPCFlatBufPayload {}\n"; return code; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.cc b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.cc new file mode 100644 index 00000000..e49fd8d9 --- /dev/null +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.cc @@ -0,0 +1,523 @@ +/* + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * NOTE: The following implementation is a translation for the Swift-grpc + * generator since flatbuffers doesnt allow plugins for now. if an issue arises + * please open an issue in the flatbuffers repository. This file should always + * be maintained according to the Swift-grpc repository + */ + +#include +#include + +#include "flatbuffers/util.h" +#include "src/compiler/schema_interface.h" +#include "src/compiler/ts_generator.h" + +namespace grpc_ts_generator { + +grpc::string ToDasherizedCase(const grpc::string pascal_case) { + std::string dasherized_case; + char p = 0; + for (size_t i = 0; i < pascal_case.length(); i++) { + char const &c = pascal_case[i]; + if (flatbuffers::is_alpha_upper(c)) { + if (i > 0 && p != flatbuffers::kPathSeparator) dasherized_case += "-"; + dasherized_case += flatbuffers::CharToLower(c); + } else { + dasherized_case += c; + } + p = c; + } + return dasherized_case; +} + +grpc::string GenerateNamespace(const std::vector namepsace, + const std::string filename, + const bool include_separator) { + grpc::string path = ""; + if (include_separator) path += "."; + + for (auto it = namepsace.begin(); it < namepsace.end(); it++) { + if (include_separator) path += "/"; + path += include_separator ? ToDasherizedCase(*it) : *it + "_"; + } + + if (include_separator) path += "/"; + path += include_separator ? ToDasherizedCase(filename) : filename; + return path; +} + +// MARK: - Shared code + +void GenerateImports(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary, + const bool grpc_var_import) { + auto vars = *dictonary; + printer->Print( + "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n"); + printer->Print("import * as flatbuffers from 'flatbuffers';\n"); + + std::set generated_imports; + + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + auto output = method->get_output_type_name(); + auto input = method->get_input_type_name(); + auto input_namespace = method->get_input_namespace_parts(); + + vars["OUTPUT"] = output; + vars["INPUT"] = input; + + if (generated_imports.find(output) == generated_imports.end()) { + generated_imports.insert(output); + vars["OUTPUT_DIR"] = + GenerateNamespace(method->get_output_namespace_parts(), output, true); + vars["Output_alias"] = GenerateNamespace( + method->get_output_namespace_parts(), output, false); + printer->Print( + vars, "import { $OUTPUT$ as $Output_alias$ } from '$OUTPUT_DIR$';\n"); + } + if (generated_imports.find(input) == generated_imports.end()) { + generated_imports.insert(input); + vars["INPUT_DIR"] = + GenerateNamespace(method->get_output_namespace_parts(), input, true); + vars["Input_alias"] = + GenerateNamespace(method->get_output_namespace_parts(), input, false); + printer->Print( + vars, "import { $INPUT$ as $Input_alias$ } from '$INPUT_DIR$';\n"); + } + } + printer->Print("\n"); + if (grpc_var_import) + printer->Print("var grpc = require('grpc');\n"); + else + printer->Print("import * as grpc from 'grpc';\n"); + printer->Print("\n"); +} + +// MARK: - Generate Main GRPC Code + +void GetStreamType(grpc_generator::Printer *printer, + const grpc_generator::Method *method, + std::map *dictonary) { + auto vars = *dictonary; + auto client_streaming = method->ClientStreaming() || method->BidiStreaming(); + auto server_streaming = method->ServerStreaming() || method->BidiStreaming(); + vars["ClientStreaming"] = client_streaming ? "true" : "false"; + vars["ServerStreaming"] = server_streaming ? "true" : "false"; + printer->Print(vars, "requestStream: $ClientStreaming$,\n"); + printer->Print(vars, "responseStream: $ServerStreaming$,\n"); +} + +void GenerateSerializeMethod(grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "function serialize_$Type$(buffer_args) {\n"); + printer->Indent(); + printer->Print(vars, "if (!(buffer_args instanceof $Type$)) {\n"); + printer->Indent(); + printer->Print(vars, + "throw new Error('Expected argument of type $VALUE$');\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(vars, "return buffer_args.serialize();\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void GenerateDeserializeMethod( + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "function deserialize_$Type$(buffer) {\n"); + printer->Indent(); + printer->Print(vars, + "return $Type$.getRootAs$VALUE$(new " + "flatbuffers.ByteBuffer(buffer))\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void GenerateMethods(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + + std::set generated_functions; + + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + auto output = method->get_output_type_name(); + auto input = method->get_input_type_name(); + + if (generated_functions.find(output) == generated_functions.end()) { + generated_functions.insert(output); + vars["VALUE"] = output; + vars["Type"] = GenerateNamespace(method->get_output_namespace_parts(), + output, false); + GenerateSerializeMethod(printer, &vars); + GenerateDeserializeMethod(printer, &vars); + } + printer->Print("\n"); + if (generated_functions.find(input) == generated_functions.end()) { + generated_functions.insert(input); + vars["VALUE"] = input; + vars["Type"] = + GenerateNamespace(method->get_input_namespace_parts(), input, false); + GenerateSerializeMethod(printer, &vars); + GenerateDeserializeMethod(printer, &vars); + } + } +} + +void GenerateService(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + vars["NAME"] = service->name() + "Service"; + + printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), + method->get_output_type_name(), false); + vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), + method->get_input_type_name(), false); + printer->Print(vars, "$MethodName$: {\n"); + printer->Indent(); + printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n"); + GetStreamType(printer, &*method, &vars); + printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n"); + printer->Print(vars, "responseType: $OUTPUT$,\n"); + printer->Print(vars, "requestSerialize: serialize_$INPUT$,\n"); + printer->Print(vars, "requestDeserialize: deserialize_$INPUT$,\n"); + printer->Print(vars, "responseSerialize: serialize_$OUTPUT$,\n"); + printer->Print(vars, "responseDeserialize: deserialize_$OUTPUT$,\n"); + printer->Outdent(); + printer->Print("},\n"); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print(vars, + "exports.$ServiceName$Client = " + "grpc.makeGenericClientConstructor($NAME$);"); +} + +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename) { + grpc::string output; + std::map vars; + + vars["PATH"] = file->package(); + + if (!file->package().empty()) { vars["PATH"].append("."); } + + vars["ServiceName"] = service->name(); + vars["FBSFile"] = service->name() + "_fbs"; + vars["Filename"] = filename; + auto printer = file->CreatePrinter(&output); + + GenerateImports(service, &*printer, &vars, true); + GenerateMethods(service, &*printer, &vars); + GenerateService(service, &*printer, &vars); + return output; +} + +// MARK: - Generate Interface + +void FillInterface(grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, + "interface I$ServiceName$Service_I$MethodName$ extends " + "grpc.MethodDefinition<$INPUT$, $OUTPUT$> {\n"); + printer->Indent(); + printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n"); + printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n"); + printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n"); + printer->Print(vars, "requestSerialize: grpc.serialize<$INPUT$>;\n"); + printer->Print(vars, "requestDeserialize: grpc.deserialize<$INPUT$>;\n"); + printer->Print(vars, "responseSerialize: grpc.serialize<$OUTPUT$>;\n"); + printer->Print(vars, "responseDeserialize: grpc.deserialize<$OUTPUT$>;\n"); + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateInterfaces(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + auto client_streaming = + method->ClientStreaming() || method->BidiStreaming(); + auto server_streaming = + method->ServerStreaming() || method->BidiStreaming(); + vars["ClientStreaming"] = client_streaming ? "true" : "false"; + vars["ServerStreaming"] = server_streaming ? "true" : "false"; + vars["MethodName"] = method->name(); + vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), + method->get_output_type_name(), false); + vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), + method->get_input_type_name(), false); + FillInterface(printer, &vars); + printer->Print("\n"); + } +} + +void GenerateExportedInterface( + const grpc_generator::Service *service, grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "export interface I$ServiceName$Server {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["Name"] = method->name(); + vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), + method->get_output_type_name(), false); + vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), + method->get_input_type_name(), false); + if (method->BidiStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleBidiStreamingCall<$INPUT$, " + "$OUTPUT$>;\n"); + continue; + } + if (method->NoStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleUnaryCall<$INPUT$, " + "$OUTPUT$>;\n"); + continue; + } + if (method->ClientStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleClientStreamingCall<$INPUT$, " + "$OUTPUT$>;\n"); + continue; + } + if (method->ServerStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleServerStreamingCall<$INPUT$, " + "$OUTPUT$>;\n"); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateMainInterface(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print( + vars, + "interface I$ServiceName$Service extends " + "grpc.ServiceDefinition {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + printer->Print(vars, + "$MethodName$: I$ServiceName$Service_I$MethodName$;\n"); + } + printer->Outdent(); + printer->Print("}\n"); + GenerateInterfaces(service, printer, &vars); + printer->Print("\n"); + printer->Print(vars, + "export const $ServiceName$Service: I$ServiceName$Service;\n"); + printer->Print("\n"); + GenerateExportedInterface(service, printer, &vars); +} + +grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; } + +grpc::string GenerateOptions() { return "options: Partial"; } + +void GenerateUnaryClientInterface( + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, "; + grpc::string callback = + "callback: (error: grpc.ServiceError | null, response: " + "$OUTPUT$) => void): grpc.ClientUnaryCall;\n"; + auto meta_data = GenerateMetaData() + ", "; + auto options = GenerateOptions() + ", "; + printer->Print(vars, (main + callback).c_str()); + printer->Print(vars, (main + meta_data + callback).c_str()); + printer->Print(vars, (main + meta_data + options + callback).c_str()); +} + +void GenerateClientWriteStreamInterface( + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$("; + grpc::string callback = + "callback: (error: grpc.ServiceError | null, response: " + "$INPUT$) => void): " + "grpc.ClientWritableStream<$OUTPUT$>;\n"; + auto meta_data = GenerateMetaData() + ", "; + auto options = GenerateOptions() + ", "; + printer->Print(vars, (main + callback).c_str()); + printer->Print(vars, (main + meta_data + callback).c_str()); + printer->Print(vars, (main + options + callback).c_str()); + printer->Print(vars, (main + meta_data + options + callback).c_str()); +} + +void GenerateClientReadableStreamInterface( + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, "; + grpc::string end_function = "): grpc.ClientReadableStream<$OUTPUT$>;\n"; + auto meta_data = GenerateMetaData(); + auto options = GenerateOptions(); + printer->Print(vars, (main + meta_data + end_function).c_str()); + printer->Print(vars, (main + options + end_function).c_str()); +} + +void GenerateDepluxStreamInterface( + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$("; + grpc::string end_function = + "): grpc.ClientDuplexStream<$INPUT$, $OUTPUT$>;\n"; + auto meta_data = GenerateMetaData(); + auto options = GenerateOptions(); + printer->Print(vars, (main + end_function).c_str()); + printer->Print(vars, (main + options + end_function).c_str()); + printer->Print(vars, (main + meta_data + + ", options?: Partial" + end_function) + .c_str()); +} + +void GenerateClientInterface(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "export interface I$ServiceName$Client {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), + method->get_output_type_name(), false); + vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), + method->get_input_type_name(), false); + vars["ISPUBLIC"] = ""; + + if (method->NoStreaming()) { + GenerateUnaryClientInterface(printer, &vars); + continue; + } + if (method->BidiStreaming()) { + GenerateDepluxStreamInterface(printer, &vars); + continue; + } + + if (method->ClientStreaming()) { + GenerateClientWriteStreamInterface(printer, &vars); + continue; + } + + if (method->ServerStreaming()) { + GenerateClientReadableStreamInterface(printer, &vars); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateClientClassInterface( + const grpc_generator::Service *service, grpc_generator::Printer *printer, + std::map *dictonary) { + auto vars = *dictonary; + printer->Print(vars, + "export class $ServiceName$Client extends grpc.Client " + "implements I$ServiceName$Client {\n"); + printer->Indent(); + printer->Print( + "constructor(address: string, credentials: grpc.ChannelCredentials, " + "options?: object);"); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), + method->get_output_type_name(), false); + vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), + method->get_input_type_name(), false); + vars["ISPUBLIC"] = "public "; + if (method->NoStreaming()) { + GenerateUnaryClientInterface(printer, &vars); + continue; + } + if (method->BidiStreaming()) { + GenerateDepluxStreamInterface(printer, &vars); + continue; + } + + if (method->ClientStreaming()) { + GenerateClientWriteStreamInterface(printer, &vars); + continue; + } + + if (method->ServerStreaming()) { + GenerateClientReadableStreamInterface(printer, &vars); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +grpc::string GenerateInterface(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename) { + grpc::string output; + + std::set generated_functions; + std::map vars; + + vars["PATH"] = file->package(); + + if (!file->package().empty()) { vars["PATH"].append("."); } + + vars["ServiceName"] = service->name(); + vars["FBSFile"] = service->name() + "_fbs"; + vars["Filename"] = filename; + auto printer = file->CreatePrinter(&output); + + GenerateImports(service, &*printer, &vars, false); + GenerateMainInterface(service, &*printer, &vars); + printer->Print("\n"); + GenerateClientInterface(service, &*printer, &vars); + printer->Print("\n"); + GenerateClientClassInterface(service, &*printer, &vars); + return output; +} +} // namespace grpc_ts_generator diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.h new file mode 100644 index 00000000..a33bb3c5 --- /dev/null +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/grpc/src/compiler/ts_generator.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2020, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "src/compiler/config.h" +#include "src/compiler/schema_interface.h" + +#ifndef GRPC_CUSTOM_STRING +# include +# define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_ts_generator { +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename); + +grpc::string GenerateInterface(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename); +} // namespace grpc_ts_generator + diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/base.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/base.h index 95573806..cc9e22dc 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/base.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/base.h @@ -46,14 +46,17 @@ #include #include +#if defined(__unix__) && !defined(FLATBUFFERS_LOCALE_INDEPENDENT) + #include +#endif + #ifdef _STLPORT_VERSION #define FLATBUFFERS_CPP98_STL #endif -#ifndef FLATBUFFERS_CPP98_STL - #include -#endif -#include "flatbuffers/stl_emulation.h" +#ifdef __ANDROID__ + #include +#endif #if defined(__ICCARM__) #include @@ -139,8 +142,8 @@ #endif #endif // !defined(FLATBUFFERS_LITTLEENDIAN) -#define FLATBUFFERS_VERSION_MAJOR 1 -#define FLATBUFFERS_VERSION_MINOR 12 +#define FLATBUFFERS_VERSION_MAJOR 2 +#define FLATBUFFERS_VERSION_MINOR 0 #define FLATBUFFERS_VERSION_REVISION 0 #define FLATBUFFERS_STRING_EXPAND(X) #X #define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) @@ -154,10 +157,12 @@ namespace flatbuffers { defined(__clang__) #define FLATBUFFERS_FINAL_CLASS final #define FLATBUFFERS_OVERRIDE override + #define FLATBUFFERS_EXPLICIT_CPP11 explicit #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t #else #define FLATBUFFERS_FINAL_CLASS #define FLATBUFFERS_OVERRIDE + #define FLATBUFFERS_EXPLICIT_CPP11 #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE #endif @@ -165,13 +170,16 @@ namespace flatbuffers { (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 200704) #define FLATBUFFERS_CONSTEXPR constexpr + #define FLATBUFFERS_CONSTEXPR_CPP11 constexpr + #define FLATBUFFERS_CONSTEXPR_DEFINED #else #define FLATBUFFERS_CONSTEXPR const + #define FLATBUFFERS_CONSTEXPR_CPP11 #endif #if (defined(__cplusplus) && __cplusplus >= 201402L) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) - #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR + #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11 #else #define FLATBUFFERS_CONSTEXPR_CPP14 #endif @@ -189,9 +197,25 @@ namespace flatbuffers { #if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \ (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \ defined(__clang__) - #define FLATBUFFERS_DELETE_FUNC(func) func = delete; + #define FLATBUFFERS_DELETE_FUNC(func) func = delete #else - #define FLATBUFFERS_DELETE_FUNC(func) private: func; + #define FLATBUFFERS_DELETE_FUNC(func) private: func +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \ + defined(__clang__) + #define FLATBUFFERS_DEFAULT_DECLARATION +#endif + +// Check if we can use template aliases +// Not possible if Microsoft Compiler before 2012 +// Possible is the language feature __cpp_alias_templates is defined well +// Or possible if the C++ std is C+11 or newer +#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \ + || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \ + || (defined(__cplusplus) && __cplusplus >= 201103L) + #define FLATBUFFERS_TEMPLATES_ALIASES #endif #ifndef FLATBUFFERS_HAS_STRING_VIEW @@ -236,10 +260,8 @@ namespace flatbuffers { #ifndef FLATBUFFERS_LOCALE_INDEPENDENT // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}. - // They are part of the POSIX-2008 but not part of the C/C++ standard. - // GCC/Clang have definition (_XOPEN_SOURCE>=700) if POSIX-2008. #if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \ - (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE>=700))) + (defined(_XOPEN_VERSION) && (_XOPEN_VERSION>=700)) && (!defined(__ANDROID_API__) || (defined(__ANDROID_API__) && (__ANDROID_API__>=21)))) #define FLATBUFFERS_LOCALE_INDEPENDENT 1 #else #define FLATBUFFERS_LOCALE_INDEPENDENT 0 @@ -308,7 +330,13 @@ typedef uintmax_t largest_scalar_t; // We support aligning the contents of buffers up to this size. #define FLATBUFFERS_MAX_ALIGNMENT 16 +inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { + return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) && + (align & (align - 1)) == 0; // must be power of 2 +} + #if defined(_MSC_VER) + #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized #pragma warning(push) #pragma warning(disable: 4127) // C4127: conditional expression is constant #endif @@ -374,6 +402,13 @@ T ReadScalar(const void *p) { return EndianScalar(*reinterpret_cast(p)); } +// See https://github.com/google/flatbuffers/issues/5950 + +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. __supress_ubsan__("alignment") @@ -386,6 +421,10 @@ template __supress_ubsan__("alignment") void WriteScalar(void *p, Of *reinterpret_cast(p) = EndianScalar(t.o); } +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic pop +#endif + // Computes how many bytes you'd have to pad to be able to write an // "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in // memory). diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/code_generators.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/code_generators.h index bccd9fe0..d1f7b5a4 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/code_generators.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/code_generators.h @@ -61,7 +61,7 @@ class CodeWriter { } // Appends the given text to the generated code as well as a newline - // character. Any text within {{ and }} delimeters is replaced by values + // character. Any text within {{ and }} delimiters is replaced by values // previously stored in the CodeWriter by calling SetValue above. The newline // will be suppressed if the text ends with the \\ character. void operator+=(std::string text); @@ -76,6 +76,8 @@ class CodeWriter { if (cur_ident_lvl_) cur_ident_lvl_--; } + void SetPadding(const std::string &padding) { pad_ = padding; } + private: std::map value_map_; std::stringstream stream_; @@ -92,7 +94,10 @@ class BaseGenerator { virtual bool generate() = 0; static std::string NamespaceDir(const Parser &parser, const std::string &path, - const Namespace &ns); + const Namespace &ns, + const bool dasherize = false); + + static std::string ToDasherizedCase(const std::string pascal_case); std::string GeneratedFileName(const std::string &path, const std::string &file_name, @@ -114,7 +119,8 @@ class BaseGenerator { BaseGenerator &operator=(const BaseGenerator &); BaseGenerator(const BaseGenerator &); - std::string NamespaceDir(const Namespace &ns) const; + std::string NamespaceDir(const Namespace &ns, + const bool dasherize = false) const; static const char *FlatBuffersGeneratedWarning(); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flatbuffers.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flatbuffers.h index c4dc5bcd..ee34d54e 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flatbuffers.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flatbuffers.h @@ -18,6 +18,11 @@ #define FLATBUFFERS_H_ #include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" + +#ifndef FLATBUFFERS_CPP98_STL +# include +#endif #if defined(FLATBUFFERS_NAN_DEFAULTS) # include @@ -167,8 +172,12 @@ template struct VectorIterator { return (data_ - other.data_) / IndirectHelper::element_stride; } + // Note: return type is incompatible with the standard + // `reference operator*()`. IT operator*() const { return IndirectHelper::Read(data_, 0); } + // Note: return type is incompatible with the standard + // `pointer operator->()`. IT operator->() const { return IndirectHelper::Read(data_, 0); } VectorIterator &operator++() { @@ -222,12 +231,18 @@ struct VectorReverseIterator : public std::reverse_iterator { explicit VectorReverseIterator(Iterator iter) : std::reverse_iterator(iter) {} + // Note: return type is incompatible with the standard + // `reference operator*()`. typename Iterator::value_type operator*() const { - return *(std::reverse_iterator::current); + auto tmp = std::reverse_iterator::current; + return *--tmp; } + // Note: return type is incompatible with the standard + // `pointer operator->()`. typename Iterator::value_type operator->() const { - return *(std::reverse_iterator::current); + auto tmp = std::reverse_iterator::current; + return *--tmp; } }; @@ -252,6 +267,7 @@ template class Vector { typedef typename IndirectHelper::return_type return_type; typedef typename IndirectHelper::mutable_return_type mutable_return_type; + typedef return_type value_type; return_type Get(uoffset_t i) const { FLATBUFFERS_ASSERT(i < size()); @@ -289,14 +305,14 @@ template class Vector { iterator end() { return iterator(Data(), size()); } const_iterator end() const { return const_iterator(Data(), size()); } - reverse_iterator rbegin() { return reverse_iterator(end() - 1); } + reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { - return const_reverse_iterator(end() - 1); + return const_reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin() - 1); } + reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { - return const_reverse_iterator(begin() - 1); + return const_reverse_iterator(begin()); } const_iterator cbegin() const { return begin(); } @@ -430,6 +446,7 @@ template class Array { IndirectHelperType; public: + typedef uint16_t size_type; typedef typename IndirectHelper::return_type return_type; typedef VectorIterator const_iterator; typedef VectorReverseIterator const_reverse_iterator; @@ -456,7 +473,9 @@ template class Array { const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } @@ -487,6 +506,22 @@ template class Array { const T *data() const { return reinterpret_cast(Data()); } T *data() { return reinterpret_cast(Data()); } + // Copy data from a span with endian conversion. + // If this Array and the span overlap, the behavior is undefined. + void CopyFromSpan(flatbuffers::span src) { + const auto p1 = reinterpret_cast(src.data()); + const auto p2 = Data(); + FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) && + !(p2 >= p1 && p2 < (p1 + length))); + (void)p1; + (void)p2; + + CopyFromSpanImpl( + flatbuffers::integral_constant < bool, + !scalar_tag::value || sizeof(T) == 1 || FLATBUFFERS_LITTLEENDIAN > (), + src); + } + protected: void MutateImpl(flatbuffers::integral_constant, uoffset_t i, const T &val) { @@ -499,6 +534,20 @@ template class Array { *(GetMutablePointer(i)) = val; } + void CopyFromSpanImpl(flatbuffers::integral_constant, + flatbuffers::span src) { + // Use std::memcpy() instead of std::copy() to avoid preformance degradation + // due to aliasing if T is char or unsigned char. + // The size is known at compile time, so memcpy would be inlined. + std::memcpy(data(), src.data(), length * sizeof(T)); + } + + // Copy data from flatbuffers::span with endian conversion. + void CopyFromSpanImpl(flatbuffers::integral_constant, + flatbuffers::span src) { + for (size_type k = 0; k < length; k++) { Mutate(k, src[k]); } + } + // This class is only used to access pre-existing data. Don't ever // try to construct these manually. // 'constexpr' allows us to use 'size()' at compile time. @@ -544,6 +593,30 @@ template class Array, length> { uint8_t data_[1]; }; +// Cast a raw T[length] to a raw flatbuffers::Array +// without endian conversion. Use with care. +template +Array &CastToArray(T (&arr)[length]) { + return *reinterpret_cast *>(arr); +} + +template +const Array &CastToArray(const T (&arr)[length]) { + return *reinterpret_cast *>(arr); +} + +template +Array &CastToArrayOfEnum(T (&arr)[length]) { + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + +template +const Array &CastToArrayOfEnum(const T (&arr)[length]) { + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + // Lexicographically compare two strings (possibly containing nulls), and // return true if the first is less than the second. static inline bool StringLessThan(const char *a_data, uoffset_t a_size, @@ -581,6 +654,14 @@ static inline const char *GetCstring(const String *str) { return str ? str->c_str() : ""; } +#ifdef FLATBUFFERS_HAS_STRING_VIEW +// Convenience function to get string_view from a String returning an empty +// string_view on null pointer. +static inline flatbuffers::string_view GetStringView(const String *str) { + return str ? str->string_view() : flatbuffers::string_view(); +} +#endif // FLATBUFFERS_HAS_STRING_VIEW + // Allocator interface. This is flatbuffers-specific and meant only for // `vector_downward` usage. class Allocator { @@ -753,9 +834,9 @@ class DetachedBuffer { #if !defined(FLATBUFFERS_CPP98_STL) // clang-format on // These may change access mode, leave these at end of public section - FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)) + FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)); FLATBUFFERS_DELETE_FUNC( - DetachedBuffer &operator=(const DetachedBuffer &other)) + DetachedBuffer &operator=(const DetachedBuffer &other)); // clang-format off #endif // !defined(FLATBUFFERS_CPP98_STL) // clang-format on @@ -920,7 +1001,7 @@ class vector_downward { Allocator *get_custom_allocator() { return allocator_; } uoffset_t size() const { - return static_cast(reserved_ - (cur_ - buf_)); + return static_cast(reserved_ - static_cast(cur_ - buf_)); } uoffset_t scratch_size() const { @@ -998,8 +1079,8 @@ class vector_downward { private: // You shouldn't really be copying instances of this class. - FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &)) - FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &)) + FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &)); + FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &)); Allocator *allocator_; bool own_allocator_; @@ -1171,6 +1252,14 @@ class FlatBufferBuilder { return buf_.data(); } + /// @brief Get the serialized buffer (after you call `Finish()`) as a span. + /// @return Returns a constructed flatbuffers::span that is a view over the + /// FlatBuffer data inside the buffer. + flatbuffers::span GetBufferSpan() const { + Finished(); + return flatbuffers::span(buf_.data(), buf_.size()); + } + /// @brief Get a pointer to an unfinished buffer. /// @return Returns a `uint8_t` pointer to the unfinished buffer. uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } @@ -1211,7 +1300,7 @@ class FlatBufferBuilder { /// you call Finish()). You can use this information if you need to embed /// a FlatBuffer in some other buffer, such that you can later read it /// without first having to copy it into its own buffer. - size_t GetBufferMinAlignment() { + size_t GetBufferMinAlignment() const { Finished(); return minalign_; } @@ -1295,6 +1384,11 @@ class FlatBufferBuilder { TrackField(field, off); } + template void AddElement(voffset_t field, T e) { + auto off = PushElement(e); + TrackField(field, off); + } + template void AddOffset(voffset_t field, Offset off) { if (off.IsNull()) return; // Don't store. AddElement(field, ReferTo(off.o), static_cast(0)); @@ -1530,6 +1624,16 @@ class FlatBufferBuilder { return off; } +#ifdef FLATBUFFERS_HAS_STRING_VIEW + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const std::string_view to store in the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateSharedString(const flatbuffers::string_view str) { + return CreateSharedString(str.data(), str.size()); + } +#else /// @brief Store a string in the buffer, which null-terminated. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. @@ -1547,6 +1651,7 @@ class FlatBufferBuilder { Offset CreateSharedString(const std::string &str) { return CreateSharedString(str.c_str(), str.length()); } +#endif /// @brief Store a string in the buffer, which can contain any binary data. /// If a string with this exact contents has already been serialized before, @@ -1577,11 +1682,13 @@ class FlatBufferBuilder { // This is useful when storing a nested_flatbuffer in a vector of bytes, // or when storing SIMD floats, etc. void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); PreAlign(len * elemsize, alignment); } // Similar to ForceVectorAlignment but for String fields. void ForceStringAlignment(size_t len, size_t alignment) { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); PreAlign((len + 1) * sizeof(char), alignment); } @@ -1599,6 +1706,7 @@ class FlatBufferBuilder { // causing the wrong overload to be selected, remove it. AssertScalarT(); StartVector(len, sizeof(T)); + if (len == 0) { return Offset>(EndVector(len)); } // clang-format off #if FLATBUFFERS_LITTLEENDIAN PushBytes(reinterpret_cast(v), len * sizeof(T)); @@ -1704,6 +1812,25 @@ class FlatBufferBuilder { return Offset>(EndVector(len)); } + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs( + const S *v, size_t len, T((*const pack_func)(const S &))) { + FLATBUFFERS_ASSERT(pack_func); + std::vector vv(len); + std::transform(v, v + len, vv.begin(), pack_func); + return CreateVectorOfStructs(data(vv), vv.size()); + } + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @tparam S The data type of the native struct array elements. @@ -1716,9 +1843,7 @@ class FlatBufferBuilder { Offset> CreateVectorOfNativeStructs(const S *v, size_t len) { extern T Pack(const S &); - std::vector vv(len); - std::transform(v, v + len, vv.begin(), Pack); - return CreateVectorOfStructs(data(vv), vv.size()); + return CreateVectorOfNativeStructs(v, len, Pack); } // clang-format off @@ -1775,6 +1900,22 @@ class FlatBufferBuilder { return CreateVectorOfStructs(data(v), v.size()); } + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs( + const std::vector &v, T((*const pack_func)(const S &))) { + return CreateVectorOfNativeStructs(data(v), v.size(), pack_func); + } + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer /// `vector`. /// @tparam T The data type of the `std::vector` struct elements. @@ -1795,8 +1936,8 @@ class FlatBufferBuilder { return a.KeyCompareLessThan(&b); } - private: - StructKeyComparator &operator=(const StructKeyComparator &); + FLATBUFFERS_DELETE_FUNC( + StructKeyComparator &operator=(const StructKeyComparator &)); }; /// @endcond @@ -1871,10 +2012,8 @@ class FlatBufferBuilder { vector_downward &buf_; private: - TableKeyComparator &operator=(const TableKeyComparator &other) { - buf_ = other.buf_; - return *this; - } + FLATBUFFERS_DELETE_FUNC( + TableKeyComparator &operator=(const TableKeyComparator &other)); }; /// @endcond @@ -2269,8 +2408,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS { template bool VerifyBufferFromStart(const char *identifier, size_t start) { - if (identifier && (size_ < 2 * sizeof(flatbuffers::uoffset_t) || - !BufferHasIdentifier(buf_ + start, identifier))) { + if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && + BufferHasIdentifier(buf_ + start, identifier)))) { return false; } @@ -2452,12 +2591,26 @@ class Table { return field_offset ? reinterpret_cast

(p) : nullptr; } + template + flatbuffers::Optional GetOptional(voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(static_cast(ReadScalar(p))) + : Optional(); + } + template bool SetField(voffset_t field, T val, T def) { auto field_offset = GetOptionalFieldOffset(field); if (!field_offset) return IsTheSameAs(val, def); WriteScalar(data_ + field_offset, val); return true; } + template bool SetField(voffset_t field, T val) { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) return false; + WriteScalar(data_ + field_offset, val); + return true; + } bool SetPointer(voffset_t field, const uint8_t *val) { auto field_offset = GetOptionalFieldOffset(field); @@ -2525,6 +2678,17 @@ class Table { uint8_t data_[1]; }; +// This specialization allows avoiding warnings like: +// MSVC C4800: type: forcing value to bool 'true' or 'false'. +template<> +inline flatbuffers::Optional Table::GetOptional( + voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(ReadScalar(p) != 0) + : Optional(); +} + template void FlatBufferBuilder::Required(Offset table, voffset_t field) { auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); @@ -2702,10 +2866,16 @@ inline const char * const *ElementaryTypeNames() { // clang-format on // Basic type info cost just 16bits per field! +// We're explicitly defining the signedness since the signedness of integer +// bitfields is otherwise implementation-defined and causes warnings on older +// GCC compilers. struct TypeCode { - uint16_t base_type : 4; // ElementaryType - uint16_t is_vector : 1; - int16_t sequence_ref : 11; // Index into type_refs below, or -1 for none. + // ElementaryType + unsigned short base_type : 4; + // Either vector (in table) or array (in struct) + unsigned short is_repeating : 1; + // Index into type_refs below, or -1 for none. + signed short sequence_ref : 11; }; static_assert(sizeof(TypeCode) == 2, "TypeCode"); @@ -2720,6 +2890,7 @@ struct TypeTable { size_t num_elems; // of type_codes, values, names (but not type_refs). const TypeCode *type_codes; // num_elems count const TypeFunction *type_refs; // less than num_elems entries (see TypeCode). + const int16_t *array_sizes; // less than num_elems entries (see TypeCode). const int64_t *values; // Only set for non-consecutive enum/union or structs. const char *const *names; // Only set if compiled with --reflect-names. }; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flexbuffers.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flexbuffers.h index dceaa3b4..c71928e8 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flexbuffers.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/flexbuffers.h @@ -155,7 +155,7 @@ inline uint64_t ReadUInt64(const uint8_t *data, uint8_t byte_width) { // constant, which here it isn't. Test if memcpy is still faster than // the conditionals in ReadSizedScalar. Can also use inline asm. // clang-format off - #if defined(_MSC_VER) && (defined(_M_X64) || defined _M_IX86) + #if defined(_MSC_VER) && ((defined(_M_X64) && !defined(_M_ARM64EC)) || defined _M_IX86) uint64_t u = 0; __movsb(reinterpret_cast(&u), reinterpret_cast(data), byte_width); @@ -900,6 +900,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { BuilderFlag flags = BUILDER_FLAG_SHARE_KEYS) : buf_(initial_size), finished_(false), + has_duplicate_keys_(false), flags_(flags), force_min_bit_width_(BIT_WIDTH_8), key_pool(KeyOffsetCompare(buf_)), @@ -907,6 +908,11 @@ class Builder FLATBUFFERS_FINAL_CLASS { buf_.clear(); } +#ifdef FLATBUFFERS_DEFAULT_DECLARATION + Builder(Builder &&) = default; + Builder &operator=(Builder &&) = default; +#endif + /// @brief Get the serialized buffer (after you call `Finish()`). /// @return Returns a vector owned by this class. const std::vector &GetBuffer() const { @@ -1124,12 +1130,16 @@ class Builder FLATBUFFERS_FINAL_CLASS { auto bs = reinterpret_cast( flatbuffers::vector_data(buf_) + b.key.u_); auto comp = strcmp(as, bs); - // If this assertion hits, you've added two keys with the same - // value to this map. + // We want to disallow duplicate keys, since this results in a + // map where values cannot be found. + // But we can't assert here (since we don't want to fail on + // random JSON input) or have an error mechanism. + // Instead, we set has_duplicate_keys_ in the builder to + // signal this. // TODO: Have to check for pointer equality, as some sort // implementation apparently call this function with the same // element?? Why? - FLATBUFFERS_ASSERT(comp || &a == &b); + if (!comp && &a != &b) has_duplicate_keys_ = true; return comp < 0; }); // First create a vector out of all keys. @@ -1143,6 +1153,10 @@ class Builder FLATBUFFERS_FINAL_CLASS { return static_cast(vec.u_); } + // Call this after EndMap to see if the map had any duplicate keys. + // Any map with such keys won't be able to retrieve all values. + bool HasDuplicateKeys() const { return has_duplicate_keys_; } + template size_t Vector(F f) { auto start = StartVector(); f(); @@ -1256,7 +1270,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { // auto id = builder.LastValue(); // Remember where we stored it. // .. more code goes here .. // builder.ReuseValue(id); // Refers to same double by offset. - // LastValue works regardless of wether the value has a key or not. + // LastValue works regardless of whether the value has a key or not. // Works on any data type. struct Value; Value LastValue() { return stack_.back(); } @@ -1417,7 +1431,10 @@ class Builder FLATBUFFERS_FINAL_CLASS { Value(uint64_t u, Type t, BitWidth bw) : u_(u), type_(t), min_bit_width_(bw) {} - Value(float f) : f_(f), type_(FBT_FLOAT), min_bit_width_(BIT_WIDTH_32) {} + Value(float f) + : f_(static_cast(f)), + type_(FBT_FLOAT), + min_bit_width_(BIT_WIDTH_32) {} Value(double f) : f_(f), type_(FBT_FLOAT), min_bit_width_(WidthF(f)) {} uint8_t StoredPackedType(BitWidth parent_bit_width_ = BIT_WIDTH_8) const { @@ -1522,7 +1539,8 @@ class Builder FLATBUFFERS_FINAL_CLASS { Type vector_type = FBT_KEY; // Check bit widths and types for all elements. for (size_t i = start; i < stack_.size(); i += step) { - auto elem_width = stack_[i].ElemWidth(buf_.size(), i + prefix_elems); + auto elem_width = + stack_[i].ElemWidth(buf_.size(), i - start + prefix_elems); bit_width = (std::max)(bit_width, elem_width); if (typed) { if (i == start) { @@ -1570,6 +1588,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { std::vector stack_; bool finished_; + bool has_duplicate_keys_; BuilderFlag flags_; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/grpc.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/grpc.h index bd24c501..b7935551 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/grpc.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/grpc.h @@ -20,8 +20,8 @@ // Helper functionality to glue FlatBuffers and GRPC. #include "flatbuffers/flatbuffers.h" -#include "grpc++/support/byte_buffer.h" #include "grpc/byte_buffer_reader.h" +#include "grpcpp/support/byte_buffer.h" namespace flatbuffers { namespace grpc { @@ -287,8 +287,9 @@ template class SerializationTraits> { } // Deserialize by pulling the - static grpc::Status Deserialize(grpc_byte_buffer *buffer, + static grpc::Status Deserialize(ByteBuffer *buf, flatbuffers::grpc::Message *msg) { + grpc_byte_buffer *buffer = *reinterpret_cast(buf); if (!buffer) { return ::grpc::Status(::grpc::StatusCode::INTERNAL, "No payload"); } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/idl.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/idl.h index 12b2b142..29bf6215 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/idl.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/idl.h @@ -35,7 +35,7 @@ // Definition Language) / schema file. // Limits maximum depth of nested objects. -// Prevents stack overflow while parse flatbuffers or json. +// Prevents stack overflow while parse scheme, or json, or flexbuffer. #if !defined(FLATBUFFERS_MAX_PARSING_DEPTH) # define FLATBUFFERS_MAX_PARSING_DEPTH 64 #endif @@ -239,7 +239,7 @@ template class SymbolTable { struct Namespace { Namespace() : from_table(0) {} - // Given a (potentally unqualified) name, return the "fully qualified" name + // Given a (potentially unqualified) name, return the "fully qualified" name // which has a full namespaced descriptor. // With max_components you can request less than the number of components // the current namespace has. @@ -291,11 +291,11 @@ struct Definition { struct FieldDef : public Definition { FieldDef() : deprecated(false), - required(false), key(false), shared(false), native_inline(false), flexbuffer(false), + presence(kDefault), nested_flatbuffer(NULL), padding(0) {} @@ -304,16 +304,48 @@ struct FieldDef : public Definition { bool Deserialize(Parser &parser, const reflection::Field *field); + bool IsScalarOptional() const { + return IsScalar(value.type.base_type) && IsOptional(); + } + bool IsOptional() const { + return presence == kOptional; + } + bool IsRequired() const { + return presence == kRequired; + } + bool IsDefault() const { + return presence == kDefault; + } + Value value; bool deprecated; // Field is allowed to be present in old data, but can't be. // written in new data nor accessed in new code. - bool required; // Field must always be present. bool key; // Field functions as a key for creating sorted vectors. bool shared; // Field will be using string pooling (i.e. CreateSharedString) // as default serialization behavior if field is a string. bool native_inline; // Field will be defined inline (instead of as a pointer) // for native tables if field is a struct. bool flexbuffer; // This field contains FlexBuffer data. + + enum Presence { + // Field must always be present. + kRequired, + // Non-presence should be signalled to and controlled by users. + kOptional, + // Non-presence is hidden from users. + // Implementations may omit writing default values. + kDefault, + }; + Presence static MakeFieldPresence(bool optional, bool required) { + FLATBUFFERS_ASSERT(!(required && optional)); + // clang-format off + return required ? FieldDef::kRequired + : optional ? FieldDef::kOptional + : FieldDef::kDefault; + // clang-format on + } + Presence presence; + StructDef *nested_flatbuffer; // This field contains nested FlatBuffer data. size_t padding; // Bytes to always pad after this field. }; @@ -410,9 +442,7 @@ struct EnumDef : public Definition { size_t size() const { return vals.vec.size(); } - const std::vector &Vals() const { - return vals.vec; - } + const std::vector &Vals() const { return vals.vec; } const EnumVal *Lookup(const std::string &enum_name) const { return vals.Lookup(enum_name); @@ -433,6 +463,10 @@ struct EnumDef : public Definition { SymbolTable vals; }; +inline bool IsString(const Type &type) { + return type.base_type == BASE_TYPE_STRING; +} + inline bool IsStruct(const Type &type) { return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed; } @@ -508,12 +542,10 @@ struct ServiceDef : public Definition { // Container of options that may apply to any of the source/text generators. struct IDLOptions { + bool gen_jvmstatic; // Use flexbuffers instead for binary and text generation bool use_flexbuffers; bool strict_json; - bool skip_js_exports; - bool use_goog_js_export_format; - bool use_ES6_js_export_format; bool output_default_scalars_in_json; int indent_step; bool output_enum_identifiers; @@ -532,6 +564,7 @@ struct IDLOptions { std::string cpp_object_api_pointer_type; std::string cpp_object_api_string_type; bool cpp_object_api_string_flexible_constructor; + bool cpp_direct_copy; bool gen_nullable; bool java_checkerframework; bool gen_generated; @@ -545,11 +578,8 @@ struct IDLOptions { bool binary_schema_comments; bool binary_schema_builtins; bool binary_schema_gen_embed; - bool skip_flatbuffers_import; std::string go_import; std::string go_namespace; - bool reexport_ts_modules; - bool js_ts_short_names; bool protobuf_ascii_alike; bool size_prefixed; std::string root_type; @@ -558,9 +588,11 @@ struct IDLOptions { bool cs_gen_json_serializer; std::vector cpp_includes; std::string cpp_std; + bool cpp_static_reflection; std::string proto_namespace_suffix; std::string filename_suffix; std::string filename_extension; + bool no_warnings; // Possible options for the more general generator below. enum Language { @@ -568,7 +600,6 @@ struct IDLOptions { kCSharp = 1 << 1, kGo = 1 << 2, kCpp = 1 << 3, - kJs = 1 << 4, kPython = 1 << 5, kPhp = 1 << 6, kJson = 1 << 7, @@ -590,6 +621,9 @@ struct IDLOptions { MiniReflect mini_reflect; + // If set, require all fields in a table to be explicitly numbered. + bool require_explicit_ids; + // The corresponding language bit will be set if a language is included // for code generation. unsigned long lang_to_generate; @@ -603,11 +637,9 @@ struct IDLOptions { bool set_empty_vectors_to_null; IDLOptions() - : use_flexbuffers(false), + : gen_jvmstatic(false), + use_flexbuffers(false), strict_json(false), - skip_js_exports(false), - use_goog_js_export_format(false), - use_ES6_js_export_format(false), output_default_scalars_in_json(false), indent_step(2), output_enum_identifiers(true), @@ -625,6 +657,7 @@ struct IDLOptions { gen_compare(false), cpp_object_api_pointer_type("std::unique_ptr"), cpp_object_api_string_flexible_constructor(false), + cpp_direct_copy(true), gen_nullable(false), java_checkerframework(false), gen_generated(false), @@ -636,18 +669,18 @@ struct IDLOptions { binary_schema_comments(false), binary_schema_builtins(false), binary_schema_gen_embed(false), - skip_flatbuffers_import(false), - reexport_ts_modules(true), - js_ts_short_names(false), protobuf_ascii_alike(false), size_prefixed(false), force_defaults(false), java_primitive_has_method(false), cs_gen_json_serializer(false), + cpp_static_reflection(false), filename_suffix("_generated"), filename_extension(), + no_warnings(false), lang(IDLOptions::kJava), mini_reflect(IDLOptions::kNone), + require_explicit_ids(false), lang_to_generate(0), set_empty_strings_to_null(true), set_empty_vectors_to_null(true) {} @@ -747,9 +780,10 @@ class Parser : public ParserState { root_struct_def_(nullptr), opts(options), uses_flexbuffers_(false), + advanced_features_(0), source_(nullptr), - anonymous_counter(0), - recurse_protection_counter(0) { + anonymous_counter_(0), + parse_depth_counter_(0) { if (opts.force_defaults) { builder_.ForceDefaults(true); } // Start out with the empty namespace being current. empty_namespace_ = new Namespace(); @@ -776,6 +810,7 @@ class Parser : public ParserState { known_attributes_["native_inline"] = true; known_attributes_["native_custom_alloc"] = true; known_attributes_["native_type"] = true; + known_attributes_["native_type_pack_name"] = true; known_attributes_["native_default"] = true; known_attributes_["flexbuffer"] = true; known_attributes_["private"] = true; @@ -801,6 +836,8 @@ class Parser : public ParserState { bool Parse(const char *_source, const char **include_paths = nullptr, const char *source_filename = nullptr); + bool ParseJson(const char *json, const char *json_filename = nullptr); + // Set the root type. May override the one set in the schema. bool SetRootType(const char *name); @@ -835,12 +872,20 @@ class Parser : public ParserState { flexbuffers::Builder *builder); StructDef *LookupStruct(const std::string &id) const; + StructDef *LookupStructThruParentNamespaces(const std::string &id) const; std::string UnqualifiedName(const std::string &fullQualifiedName); FLATBUFFERS_CHECKED_ERROR Error(const std::string &msg); + // @brief Verify that any of 'opts.lang_to_generate' supports Optional scalars + // in a schema. + // @param opts Options used to parce a schema and generate code. + static bool SupportsOptionalScalars(const flatbuffers::IDLOptions &opts); + private: + class ParseDepthGuard; + void Message(const std::string &msg); void Warning(const std::string &msg); FLATBUFFERS_CHECKED_ERROR ParseHexNum(int nibbles, uint64_t *val); @@ -859,7 +904,7 @@ class Parser : public ParserState { const std::string &name, const Type &type, FieldDef **dest); FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def); - FLATBUFFERS_CHECKED_ERROR ParseString(Value &val); + FLATBUFFERS_CHECKED_ERROR ParseString(Value &val, bool use_string_pooling); FLATBUFFERS_CHECKED_ERROR ParseComma(); FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field, size_t parent_fieldn, @@ -891,6 +936,7 @@ class Parser : public ParserState { FLATBUFFERS_CHECKED_ERROR TokenError(); FLATBUFFERS_CHECKED_ERROR ParseSingleValue(const std::string *name, Value &e, bool check_now); + FLATBUFFERS_CHECKED_ERROR ParseFunction(const std::string *name, Value &e); FLATBUFFERS_CHECKED_ERROR ParseEnumFromString(const Type &type, std::string *result); StructDef *LookupCreateStruct(const std::string &name, @@ -912,6 +958,8 @@ class Parser : public ParserState { FLATBUFFERS_CHECKED_ERROR ParseProtoCurliesOrIdent(); FLATBUFFERS_CHECKED_ERROR ParseTypeFromProtoType(Type *type); FLATBUFFERS_CHECKED_ERROR SkipAnyJsonValue(); + FLATBUFFERS_CHECKED_ERROR ParseFlexBufferNumericConstant( + flexbuffers::Builder *builder); FLATBUFFERS_CHECKED_ERROR ParseFlexBufferValue(flexbuffers::Builder *builder); FLATBUFFERS_CHECKED_ERROR StartParseFile(const char *source, const char *source_filename); @@ -922,12 +970,17 @@ class Parser : public ParserState { const char **include_paths, const char *source_filename, const char *include_filename); + FLATBUFFERS_CHECKED_ERROR DoParseJson(); FLATBUFFERS_CHECKED_ERROR CheckClash(std::vector &fields, StructDef *struct_def, const char *suffix, BaseType baseType); + FLATBUFFERS_CHECKED_ERROR ParseAlignAttribute( + const std::string &align_constant, size_t min_align, size_t *align); bool SupportsAdvancedUnionFeatures() const; bool SupportsAdvancedArrayFeatures() const; + bool SupportsOptionalScalars() const; + bool SupportsDefaultVectorsAndStrings() const; Namespace *UniqueNamespace(Namespace *ns); FLATBUFFERS_CHECKED_ERROR RecurseError(); @@ -950,7 +1003,7 @@ class Parser : public ParserState { std::string file_identifier_; std::string file_extension_; - std::map included_files_; + std::map included_files_; std::map> files_included_per_file_; std::vector native_included_files_; @@ -959,6 +1012,8 @@ class Parser : public ParserState { IDLOptions opts; bool uses_flexbuffers_; + uint64_t advanced_features_; + private: const char *source_; @@ -966,8 +1021,8 @@ class Parser : public ParserState { std::vector> field_stack_; - int anonymous_counter; - int recurse_protection_counter; + int anonymous_counter_; + int parse_depth_counter_; // stack-overflow guard }; // Utility functions for multiple generators: @@ -992,6 +1047,10 @@ extern bool GenerateText(const Parser &parser, const void *flatbuffer, extern bool GenerateTextFile(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate Json schema to string +// See idl_gen_json_schema.cpp. +extern bool GenerateJsonSchema(const Parser &parser, std::string *json); + // Generate binary files from a given FlatBuffer, and a given Parser // object that has been populated with the corresponding schema. // See code_generators.cpp. @@ -1018,8 +1077,8 @@ extern bool GenerateJava(const Parser &parser, const std::string &path, // Generate JavaScript or TypeScript code from the definitions in the Parser // object. See idl_gen_js. -extern bool GenerateJSTS(const Parser &parser, const std::string &path, - const std::string &file_name); +extern bool GenerateTS(const Parser &parser, const std::string &path, + const std::string &file_name); // Generate Go files from the definitions in the Parser object. // See idl_gen_go.cpp. @@ -1071,10 +1130,10 @@ extern std::string GenerateFBS(const Parser &parser, extern bool GenerateFBS(const Parser &parser, const std::string &path, const std::string &file_name); -// Generate a make rule for the generated JavaScript or TypeScript code. -// See idl_gen_js.cpp. -extern std::string JSTSMakeRule(const Parser &parser, const std::string &path, - const std::string &file_name); +// Generate a make rule for the generated TypeScript code. +// See idl_gen_ts.cpp. +extern std::string TSMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); // Generate a make rule for the generated C++ header. // See idl_gen_cpp.cpp. @@ -1132,6 +1191,8 @@ bool GeneratePythonGRPC(const Parser &parser, const std::string &path, extern bool GenerateSwiftGRPC(const Parser &parser, const std::string &path, const std::string &file_name); +extern bool GenerateTSGRPC(const Parser &parser, const std::string &path, + const std::string &file_name); } // namespace flatbuffers #endif // FLATBUFFERS_IDL_H_ diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/minireflect.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/minireflect.h index 67a79a93..26fd86c9 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/minireflect.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/minireflect.h @@ -38,7 +38,7 @@ struct IterationVisitor { // These mark the scope of a table or struct. virtual void StartSequence() {} virtual void EndSequence() {} - // Called for each field regardless of wether it is present or not. + // Called for each field regardless of whether it is present or not. // If not present, val == nullptr. set_idx is the index of all set fields. virtual void Field(size_t /*field_idx*/, size_t /*set_idx*/, ElementaryType /*type*/, bool /*is_vector*/, @@ -234,10 +234,11 @@ inline void IterateObject(const uint8_t *obj, const TypeTable *type_table, visitor->StartSequence(); const uint8_t *prev_val = nullptr; size_t set_idx = 0; + size_t array_idx = 0; for (size_t i = 0; i < type_table->num_elems; i++) { auto type_code = type_table->type_codes[i]; auto type = static_cast(type_code.base_type); - auto is_vector = type_code.is_vector != 0; + auto is_repeating = type_code.is_repeating != 0; auto ref_idx = type_code.sequence_ref; const TypeTable *ref = nullptr; if (ref_idx >= 0) { ref = type_table->type_refs[ref_idx](); } @@ -249,15 +250,25 @@ inline void IterateObject(const uint8_t *obj, const TypeTable *type_table, } else { val = obj + type_table->values[i]; } - visitor->Field(i, set_idx, type, is_vector, ref, name, val); + visitor->Field(i, set_idx, type, is_repeating, ref, name, val); if (val) { set_idx++; - if (is_vector) { - val += ReadScalar(val); - auto vec = reinterpret_cast *>(val); + if (is_repeating) { + auto elem_ptr = val; + size_t size = 0; + if (type_table->st == ST_TABLE) { + // variable length vector + val += ReadScalar(val); + auto vec = reinterpret_cast *>(val); + elem_ptr = vec->Data(); + size = vec->size(); + } else { + // otherwise fixed size array + size = type_table->array_sizes[array_idx]; + ++array_idx; + } visitor->StartVector(); - auto elem_ptr = vec->Data(); - for (size_t j = 0; j < vec->size(); j++) { + for (size_t j = 0; j < size; j++) { visitor->Element(j, type, ref, elem_ptr); IterateValue(type, elem_ptr, ref, prev_val, static_cast(j), visitor); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/flatc_pch.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/flatc_pch.h new file mode 100644 index 00000000..77132790 --- /dev/null +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/flatc_pch.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_FLATC_PCH_H_ +#define FLATBUFFERS_FLATC_PCH_H_ + +// stl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// flatbuffers +#include "flatbuffers/pch/pch.h" +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/flexbuffers.h" +#include "flatbuffers/idl.h" + +#endif // FLATBUFFERS_FLATC_PCH_H_ diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/pch.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/pch.h new file mode 100644 index 00000000..804e99ed --- /dev/null +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/pch/pch.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_PCH_H_ +#define FLATBUFFERS_PCH_H_ + +// stl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// flatbuffers +#include "flatbuffers/util.h" + +#endif // FLATBUFFERS_PCH_H_ diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection.h index 052e6d92..c6fa411a 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection.h @@ -46,7 +46,32 @@ inline bool IsLong(reflection::BaseType t) { // Size of a basic type, don't use with structs. inline size_t GetTypeSize(reflection::BaseType base_type) { // This needs to correspond to the BaseType enum. - static size_t sizes[] = { 0, 1, 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 4, 4, 4, 4 }; + static size_t sizes[] = { + 0, // None + 1, // UType + 1, // Bool + 1, // Byte + 1, // UByte + 2, // Short + 2, // UShort + 4, // Int + 4, // UInt + 8, // Long + 8, // ULong + 4, // Float + 8, // Double + 4, // String + 4, // Vector + 4, // Obj + 4, // Union + 0, // Array. Only used in structs. 0 was chosen to prevent out-of-bounds + // errors. + + 0 // MaxBaseType. This must be kept the last entry in this array. + }; + static_assert(sizeof(sizes) / sizeof(size_t) == reflection::MaxBaseType + 1, + "Size of sizes[] array does not match the count of BaseType " + "enum values."); return sizes[base_type]; } @@ -362,7 +387,6 @@ template class pointer_inside_vector { reinterpret_cast(flatbuffers::vector_data(vec_)) + offset_); } T *operator->() const { return operator*(); } - void operator=(const pointer_inside_vector &piv); private: size_t offset_; @@ -470,7 +494,8 @@ Offset CopyTable(FlatBufferBuilder &fbb, // buf should point to the start of flatbuffer data. // length specifies the size of the flatbuffer data. bool Verify(const reflection::Schema &schema, const reflection::Object &root, - const uint8_t *buf, size_t length); + const uint8_t *buf, size_t length, uoffset_t max_depth = 64, + uoffset_t max_tables = 1000000); } // namespace flatbuffers diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection_generated.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection_generated.h index e7237ff2..235146e1 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection_generated.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/reflection_generated.h @@ -53,10 +53,11 @@ enum BaseType { Vector = 14, Obj = 15, Union = 16, - Array = 17 + Array = 17, + MaxBaseType = 18 }; -inline const BaseType (&EnumValuesBaseType())[18] { +inline const BaseType (&EnumValuesBaseType())[19] { static const BaseType values[] = { None, UType, @@ -75,13 +76,14 @@ inline const BaseType (&EnumValuesBaseType())[18] { Vector, Obj, Union, - Array + Array, + MaxBaseType }; return values; } inline const char * const *EnumNamesBaseType() { - static const char * const names[19] = { + static const char * const names[20] = { "None", "UType", "Bool", @@ -100,17 +102,56 @@ inline const char * const *EnumNamesBaseType() { "Obj", "Union", "Array", + "MaxBaseType", nullptr }; return names; } inline const char *EnumNameBaseType(BaseType e) { - if (flatbuffers::IsOutRange(e, None, Array)) return ""; + if (flatbuffers::IsOutRange(e, None, MaxBaseType)) return ""; const size_t index = static_cast(e); return EnumNamesBaseType()[index]; } +enum AdvancedFeatures { + AdvancedArrayFeatures = 1ULL, + AdvancedUnionFeatures = 2ULL, + OptionalScalars = 4ULL, + DefaultVectorsAndStrings = 8ULL +}; + +inline const AdvancedFeatures (&EnumValuesAdvancedFeatures())[4] { + static const AdvancedFeatures values[] = { + AdvancedArrayFeatures, + AdvancedUnionFeatures, + OptionalScalars, + DefaultVectorsAndStrings + }; + return values; +} + +inline const char * const *EnumNamesAdvancedFeatures() { + static const char * const names[9] = { + "AdvancedArrayFeatures", + "AdvancedUnionFeatures", + "", + "OptionalScalars", + "", + "", + "", + "DefaultVectorsAndStrings", + nullptr + }; + return names; +} + +inline const char *EnumNameAdvancedFeatures(AdvancedFeatures e) { + if (flatbuffers::IsOutRange(e, AdvancedArrayFeatures, DefaultVectorsAndStrings)) return ""; + const size_t index = static_cast(e) - static_cast(AdvancedArrayFeatures); + return EnumNamesAdvancedFeatures()[index]; +} + struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef TypeBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -161,7 +202,6 @@ struct TypeBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - TypeBuilder &operator=(const TypeBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -225,7 +265,6 @@ struct KeyValueBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - KeyValueBuilder &operator=(const KeyValueBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -325,7 +364,6 @@ struct EnumValBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - EnumValBuilder &operator=(const EnumValBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -448,7 +486,6 @@ struct EnumBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - EnumBuilder &operator=(const EnumBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -512,7 +549,8 @@ struct Field FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_REQUIRED = 18, VT_KEY = 20, VT_ATTRIBUTES = 22, - VT_DOCUMENTATION = 24 + VT_DOCUMENTATION = 24, + VT_OPTIONAL = 26 }; const flatbuffers::String *name() const { return GetPointer(VT_NAME); @@ -553,6 +591,9 @@ struct Field FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *documentation() const { return GetPointer> *>(VT_DOCUMENTATION); } + bool optional() const { + return GetField(VT_OPTIONAL, 0) != 0; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && @@ -572,6 +613,7 @@ struct Field FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && verifier.VerifyVectorOfStrings(documentation()) && + VerifyField(verifier, VT_OPTIONAL) && verifier.EndTable(); } }; @@ -613,11 +655,13 @@ struct FieldBuilder { void add_documentation(flatbuffers::Offset>> documentation) { fbb_.AddOffset(Field::VT_DOCUMENTATION, documentation); } + void add_optional(bool optional) { + fbb_.AddElement(Field::VT_OPTIONAL, static_cast(optional), 0); + } explicit FieldBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - FieldBuilder &operator=(const FieldBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -639,7 +683,8 @@ inline flatbuffers::Offset CreateField( bool required = false, bool key = false, flatbuffers::Offset>> attributes = 0, - flatbuffers::Offset>> documentation = 0) { + flatbuffers::Offset>> documentation = 0, + bool optional = false) { FieldBuilder builder_(_fbb); builder_.add_default_real(default_real); builder_.add_default_integer(default_integer); @@ -649,6 +694,7 @@ inline flatbuffers::Offset CreateField( builder_.add_name(name); builder_.add_offset(offset); builder_.add_id(id); + builder_.add_optional(optional); builder_.add_key(key); builder_.add_required(required); builder_.add_deprecated(deprecated); @@ -667,7 +713,8 @@ inline flatbuffers::Offset CreateFieldDirect( bool required = false, bool key = false, std::vector> *attributes = nullptr, - const std::vector> *documentation = nullptr) { + const std::vector> *documentation = nullptr, + bool optional = false) { auto name__ = name ? _fbb.CreateString(name) : 0; auto attributes__ = attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; auto documentation__ = documentation ? _fbb.CreateVector>(*documentation) : 0; @@ -683,7 +730,8 @@ inline flatbuffers::Offset CreateFieldDirect( required, key, attributes__, - documentation__); + documentation__, + optional); } struct Object FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -773,7 +821,6 @@ struct ObjectBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - ObjectBuilder &operator=(const ObjectBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -898,7 +945,6 @@ struct RPCCallBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RPCCallBuilder &operator=(const RPCCallBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1007,7 +1053,6 @@ struct ServiceBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } - ServiceBuilder &operator=(const ServiceBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1056,7 +1101,8 @@ struct Schema FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_FILE_IDENT = 8, VT_FILE_EXT = 10, VT_ROOT_TABLE = 12, - VT_SERVICES = 14 + VT_SERVICES = 14, + VT_ADVANCED_FEATURES = 16 }; const flatbuffers::Vector> *objects() const { return GetPointer> *>(VT_OBJECTS); @@ -1076,6 +1122,9 @@ struct Schema FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *services() const { return GetPointer> *>(VT_SERVICES); } + reflection::AdvancedFeatures advanced_features() const { + return static_cast(GetField(VT_ADVANCED_FEATURES, 0)); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_OBJECTS) && @@ -1093,6 +1142,7 @@ struct Schema FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyOffset(verifier, VT_SERVICES) && verifier.VerifyVector(services()) && verifier.VerifyVectorOfTables(services()) && + VerifyField(verifier, VT_ADVANCED_FEATURES) && verifier.EndTable(); } }; @@ -1119,11 +1169,13 @@ struct SchemaBuilder { void add_services(flatbuffers::Offset>> services) { fbb_.AddOffset(Schema::VT_SERVICES, services); } + void add_advanced_features(reflection::AdvancedFeatures advanced_features) { + fbb_.AddElement(Schema::VT_ADVANCED_FEATURES, static_cast(advanced_features), 0); + } explicit SchemaBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - SchemaBuilder &operator=(const SchemaBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1140,8 +1192,10 @@ inline flatbuffers::Offset CreateSchema( flatbuffers::Offset file_ident = 0, flatbuffers::Offset file_ext = 0, flatbuffers::Offset root_table = 0, - flatbuffers::Offset>> services = 0) { + flatbuffers::Offset>> services = 0, + reflection::AdvancedFeatures advanced_features = static_cast(0)) { SchemaBuilder builder_(_fbb); + builder_.add_advanced_features(advanced_features); builder_.add_services(services); builder_.add_root_table(root_table); builder_.add_file_ext(file_ext); @@ -1158,7 +1212,8 @@ inline flatbuffers::Offset CreateSchemaDirect( const char *file_ident = nullptr, const char *file_ext = nullptr, flatbuffers::Offset root_table = 0, - std::vector> *services = nullptr) { + std::vector> *services = nullptr, + reflection::AdvancedFeatures advanced_features = static_cast(0)) { auto objects__ = objects ? _fbb.CreateVectorOfSortedTables(objects) : 0; auto enums__ = enums ? _fbb.CreateVectorOfSortedTables(enums) : 0; auto file_ident__ = file_ident ? _fbb.CreateString(file_ident) : 0; @@ -1171,7 +1226,8 @@ inline flatbuffers::Offset CreateSchemaDirect( file_ident__, file_ext__, root_table, - services__); + services__, + advanced_features); } inline const reflection::Schema *GetSchema(const void *buf) { diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/stl_emulation.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/stl_emulation.h index 8bae61bf..70e5dc94 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/stl_emulation.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/stl_emulation.h @@ -18,6 +18,7 @@ #define FLATBUFFERS_STL_EMULATION_H_ // clang-format off +#include "flatbuffers/base.h" #include #include @@ -33,15 +34,34 @@ #include #endif // defined(FLATBUFFERS_CPP98_STL) -// Check if we can use template aliases -// Not possible if Microsoft Compiler before 2012 -// Possible is the language feature __cpp_alias_templates is defined well -// Or possible if the C++ std is C+11 or newer -#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \ - || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \ - || (defined(__cplusplus) && __cplusplus >= 201103L) - #define FLATBUFFERS_TEMPLATES_ALIASES -#endif +// Detect C++17 compatible compiler. +// __cplusplus >= 201703L - a compiler has support of 'static inline' variables. +#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \ + || (defined(__cplusplus) && __cplusplus >= 201703L) \ + || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) + #include + #ifndef FLATBUFFERS_USE_STD_OPTIONAL + #define FLATBUFFERS_USE_STD_OPTIONAL + #endif +#endif // defined(FLATBUFFERS_USE_STD_OPTIONAL) ... + +// The __cpp_lib_span is the predefined feature macro. +#if defined(FLATBUFFERS_USE_STD_SPAN) + #include +#elif defined(__cpp_lib_span) && defined(__has_include) + #if __has_include() + #include + #define FLATBUFFERS_USE_STD_SPAN + #endif +#else + // Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined. + #if !defined(FLATBUFFERS_TEMPLATES_ALIASES) || defined(FLATBUFFERS_CPP98_STL) + #define FLATBUFFERS_SPAN_MINIMAL + #else + // Enable implicit construction of a span from a std::array. + #include + #endif +#endif // defined(FLATBUFFERS_USE_STD_SPAN) // This header provides backwards compatibility for C++98 STLs like stlport. namespace flatbuffers { @@ -144,6 +164,8 @@ inline void vector_emplace_back(std::vector *vector, V &&data) { using conditional = std::conditional; template using integral_constant = std::integral_constant; + template + using bool_constant = integral_constant; #else // Map C++ TR1 templates defined by stlport. template using is_scalar = std::tr1::is_scalar; @@ -167,6 +189,8 @@ inline void vector_emplace_back(std::vector *vector, V &&data) { using conditional = std::tr1::conditional; template using integral_constant = std::tr1::integral_constant; + template + using bool_constant = integral_constant; #endif // !FLATBUFFERS_CPP98_STL #else // MSVC 2010 doesn't support C++11 aliases. @@ -181,6 +205,8 @@ inline void vector_emplace_back(std::vector *vector, V &&data) { struct conditional : public std::conditional {}; template struct integral_constant : public std::integral_constant {}; + template + struct bool_constant : public integral_constant {}; #endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) #ifndef FLATBUFFERS_CPP98_STL @@ -190,7 +216,7 @@ inline void vector_emplace_back(std::vector *vector, V &&data) { // MSVC 2010 doesn't support C++11 aliases. // We're manually "aliasing" the class here as we want to bring unique_ptr // into the flatbuffers namespace. We have unique_ptr in the flatbuffers - // namespace we have a completely independent implemenation (see below) + // namespace we have a completely independent implementation (see below) // for C++98 STL implementations. template class unique_ptr : public std::unique_ptr { public: @@ -302,6 +328,346 @@ inline void vector_emplace_back(std::vector *vector, V &&data) { #endif // !FLATBUFFERS_CPP98_STL +#ifdef FLATBUFFERS_USE_STD_OPTIONAL +template +using Optional = std::optional; +using nullopt_t = std::nullopt_t; +inline constexpr nullopt_t nullopt = std::nullopt; + +#else +// Limited implementation of Optional type for a scalar T. +// This implementation limited by trivial types compatible with +// std::is_arithmetic or std::is_enum type traits. + +// A tag to indicate an empty flatbuffers::optional. +struct nullopt_t { + explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {} +}; + +#if defined(FLATBUFFERS_CONSTEXPR_DEFINED) + namespace internal { + template struct nullopt_holder { + static constexpr nullopt_t instance_ = nullopt_t(0); + }; + template + constexpr nullopt_t nullopt_holder::instance_; + } + static constexpr const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#else + namespace internal { + template struct nullopt_holder { + static const nullopt_t instance_; + }; + template + const nullopt_t nullopt_holder::instance_ = nullopt_t(0); + } + static const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#endif + +template +class Optional FLATBUFFERS_FINAL_CLASS { + // Non-scalar 'T' would extremely complicated Optional. + // Use is_scalar checking because flatbuffers flatbuffers::is_arithmetic + // isn't implemented. + static_assert(flatbuffers::is_scalar::value, "unexpected type T"); + + public: + ~Optional() {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT + : value_(val), has_value_(true) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT + : value_(other.value_), has_value_(other.has_value_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT { + value_ = other.value_; + has_value_ = other.has_value_; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT { + value_ = T(); + has_value_ = false; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT { + value_ = val; + has_value_ = true; + return *this; + } + + void reset() FLATBUFFERS_NOEXCEPT { + *this = nullopt; + } + + void swap(Optional &other) FLATBUFFERS_NOEXCEPT { + std::swap(value_, other.value_); + std::swap(has_value_, other.has_value_); + } + + FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT { + return value_; + } + + const T& value() const { + FLATBUFFERS_ASSERT(has_value()); + return value_; + } + + T value_or(T default_value) const FLATBUFFERS_NOEXCEPT { + return has_value() ? value_ : default_value; + } + + private: + T value_; + bool has_value_; +}; + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& opt, nullopt_t) FLATBUFFERS_NOEXCEPT { + return !opt; +} +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional& opt) FLATBUFFERS_NOEXCEPT { + return !opt; +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) && (*lhs == rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(rhs) && (lhs == *rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) != static_cast(rhs) + ? false + : !static_cast(lhs) ? false : (*lhs == *rhs); +} +#endif // FLATBUFFERS_USE_STD_OPTIONAL + + +// Very limited and naive partial implementation of C++20 std::span. +#if defined(FLATBUFFERS_USE_STD_SPAN) + inline constexpr std::size_t dynamic_extent = std::dynamic_extent; + template + using span = std::span; + +#else // !defined(FLATBUFFERS_USE_STD_SPAN) +FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast(-1); + +// Exclude this code if MSVC2010 or non-STL Android is active. +// The non-STL Android doesn't have `std::is_convertible` required for SFINAE. +#if !defined(FLATBUFFERS_SPAN_MINIMAL) +namespace internal { + // This is SFINAE helper class for checking of a common condition: + // > This overload only participates in overload resolution + // > Check whether a pointer to an array of U can be converted + // > to a pointer to an array of E. + // This helper is used for checking of 'U -> const U'. + template + struct is_span_convertable { + using type = + typename std::conditional::value + && (Extent == dynamic_extent || N == Extent), + int, void>::type; + }; + +} // namespace internal +#endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + +// T - element type; must be a complete type that is not an abstract +// class type. +// Extent - the number of elements in the sequence, or dynamic. +template +class span FLATBUFFERS_FINAL_CLASS { + public: + typedef T element_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + typedef std::size_t size_type; + + static FLATBUFFERS_CONSTEXPR size_type extent = Extent; + + // Returns the number of elements in the span. + FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT { + return count_; + } + + // Returns the size of the sequence in bytes. + FLATBUFFERS_CONSTEXPR_CPP11 + size_type size_bytes() const FLATBUFFERS_NOEXCEPT { + return size() * sizeof(element_type); + } + + // Checks if the span is empty. + FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT { + return size() == 0; + } + + // Returns a pointer to the beginning of the sequence. + FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT { + return data_; + } + + // Returns a reference to the idx-th element of the sequence. + // The behavior is undefined if the idx is greater than or equal to size(). + FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const { + return data()[idx]; + } + + FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT + : data_(other.data_), count_(other.count_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other) + FLATBUFFERS_NOEXCEPT { + data_ = other.data_; + count_ = other.count_; + } + + // Limited implementation of + // `template constexpr std::span(It first, size_type count);`. + // + // Constructs a span that is a view over the range [first, first + count); + // the resulting span has: data() == first and size() == count. + // The behavior is undefined if [first, first + count) is not a valid range, + // or if (extent != flatbuffers::dynamic_extent && count != extent). + FLATBUFFERS_CONSTEXPR_CPP11 + explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT + : data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)), + count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) { + // Make span empty if the count argument is incompatible with span. + } + + // Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11 + // compliant, it doesn't support default template arguments for functions. + #if defined(FLATBUFFERS_SPAN_MINIMAL) + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + #else + // Constructs an empty span whose data() == nullptr and size() == 0. + // This overload only participates in overload resolution if + // extent == 0 || extent == flatbuffers::dynamic_extent. + // A dummy template argument N is need dependency for SFINAE. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + // Constructs a span that is a view over the array arr; the resulting span + // has size() == N and data() == std::data(arr). These overloads only + // participate in overload resolution if + // extent == std::dynamic_extent || N == extent is true and + // std::remove_pointer_t(*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT + : data_(arr), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + //template + //FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + // : data_(arr.data()), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + // Converting constructor from another span s; + // the resulting span has size() == s.size() and data() == s.data(). + // This overload only participates in overload resolution + // if extent == std::dynamic_extent || N == extent is true and U (*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span &s) FLATBUFFERS_NOEXCEPT + : span(s.data(), s.size()) { + } + + #endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + + private: + // This is a naive implementation with 'count_' member even if (Extent != dynamic_extent). + pointer const data_; + const size_type count_; +}; + + #if !defined(FLATBUFFERS_SPAN_MINIMAL) + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); + } +#endif + +#endif // defined(FLATBUFFERS_USE_STD_SPAN) + } // namespace flatbuffers #endif // FLATBUFFERS_STL_EMULATION_H_ diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/util.h b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/util.h index a13fc5da..f30bd98c 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/util.h +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/include/flatbuffers/util.h @@ -20,6 +20,7 @@ #include #include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" #ifndef FLATBUFFERS_PREFER_PRINTF # include @@ -50,6 +51,9 @@ inline bool is_alpha(char c) { return check_ascii_range(c & 0xDF, 'a' & 0xDF, 'z' & 0xDF); } +// Check for uppercase alpha +inline bool is_alpha_upper(char c) { return check_ascii_range(c, 'A', 'Z'); } + // Check (case-insensitive) that `c` is equal to alpha. inline bool is_alpha_char(char c, char alpha) { FLATBUFFERS_ASSERT(is_alpha(alpha)); @@ -72,6 +76,14 @@ inline bool is_xdigit(char c) { // Case-insensitive isalnum inline bool is_alnum(char c) { return is_alpha(c) || is_digit(c); } +inline char CharToUpper(char c) { + return static_cast(::toupper(static_cast(c))); +} + +inline char CharToLower(char c) { + return static_cast(::tolower(static_cast(c))); +} + // @end-locale-independent functions for ASCII character set #ifdef FLATBUFFERS_PREFER_PRINTF @@ -323,6 +335,9 @@ inline bool StringToFloatImpl(T *val, const char *const str) { // - If the converted value falls out of range of corresponding return type, a // range error occurs. In this case value MAX(T)/MIN(T) is returned. template inline bool StringToNumber(const char *s, T *val) { + // Assert on `unsigned long` and `signed long` on LP64. + // If it is necessary, it could be solved with flatbuffers::enable_if. + static_assert(sizeof(T) < sizeof(int64_t), "unexpected type T"); FLATBUFFERS_ASSERT(s && val); int64_t i64; // The errno check isn't needed, will return MAX/MIN on overflow. @@ -446,7 +461,7 @@ std::string StripPath(const std::string &filepath); // Strip the last component of the path + separator. std::string StripFileName(const std::string &filepath); -// Concatenates a path with a filename, regardless of wether the path +// Concatenates a path with a filename, regardless of whether the path // ends in a separator or not. std::string ConCatPathFileName(const std::string &path, const std::string &filename); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/reflection/reflection.fbs b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/reflection/reflection.fbs index 8fed025f..36230b20 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/reflection/reflection.fbs +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/reflection/reflection.fbs @@ -24,12 +24,15 @@ enum BaseType : byte { Vector, Obj, // Used for tables & structs. Union, - Array + Array, + + // Add any new type above this value. + MaxBaseType } table Type { base_type:BaseType; - element:BaseType = None; // Only if base_type == Vector + element:BaseType = None; // Only if base_type == Vector // or base_type == Array. index:int = -1; // If base_type == Object, index into "objects" below. // If base_type == Union, UnionType, or integral derived @@ -71,6 +74,7 @@ table Field { key:bool = false; attributes:[KeyValue]; documentation:[string]; + optional:bool = false; } table Object { // Used for both tables and structs. @@ -98,6 +102,14 @@ table Service { documentation:[string]; } +// New schema language features that are not supported by old code generators. +enum AdvancedFeatures : ulong (bit_flags) { + AdvancedArrayFeatures, + AdvancedUnionFeatures, + OptionalScalars, + DefaultVectorsAndStrings, +} + table Schema { objects:[Object] (required); // Sorted. enums:[Enum] (required); // Sorted. @@ -105,6 +117,7 @@ table Schema { file_ext:string; root_table:Object; services:[Service]; // Sorted. + advanced_features:AdvancedFeatures; } root_type Schema; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/code_generators.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/code_generators.cpp index 46d65f7c..745406ba 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/code_generators.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/code_generators.cpp @@ -84,20 +84,39 @@ const char *BaseGenerator::FlatBuffersGeneratedWarning() { std::string BaseGenerator::NamespaceDir(const Parser &parser, const std::string &path, - const Namespace &ns) { + const Namespace &ns, + const bool dasherize) { EnsureDirExists(path); if (parser.opts.one_file) return path; std::string namespace_dir = path; // Either empty or ends in separator. auto &namespaces = ns.components; for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - namespace_dir += *it + kPathSeparator; + namespace_dir += !dasherize ? *it : ToDasherizedCase(*it); + namespace_dir += kPathSeparator; EnsureDirExists(namespace_dir); } return namespace_dir; } -std::string BaseGenerator::NamespaceDir(const Namespace &ns) const { - return BaseGenerator::NamespaceDir(parser_, path_, ns); +std::string BaseGenerator::NamespaceDir(const Namespace &ns, + const bool dasherize) const { + return BaseGenerator::NamespaceDir(parser_, path_, ns, dasherize); +} + +std::string BaseGenerator::ToDasherizedCase(const std::string pascal_case) { + std::string dasherized_case; + char p = 0; + for (size_t i = 0; i < pascal_case.length(); i++) { + char const &c = pascal_case[i]; + if (is_alpha_upper(c)) { + if (i > 0 && p != kPathSeparator) dasherized_case += "-"; + dasherized_case += CharToLower(c); + } else { + dasherized_case += c; + } + p = c; + } + return dasherized_case; } std::string BaseGenerator::FullNamespace(const char *separator, diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc.cpp index 5cb2a80c..221b8867 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc.cpp @@ -106,6 +106,8 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --gen-nullable Add Clang _Nullable for C++ pointer. or @Nullable for Java\n" " --java-checkerframe work Add @Pure for Java.\n" " --gen-generated Add @Generated annotation for Java\n" + " --gen-jvmstatic Add @JvmStatic annotation for Kotlin methods\n" + " in companion object for interop from Java to Kotlin.\n" " --gen-all Generate not just code for the current schema files,\n" " but for all files it includes as well.\n" " If the language uses a single file for output (by default\n" @@ -124,16 +126,16 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " * 'c++0x' - generate code compatible with old compilers;\n" " * 'c++11' - use C++11 code generator (default);\n" " * 'c++17' - use C++17 features in generated code (experimental).\n" + " --cpp-static-reflection When using C++17, generate extra code to provide compile-time\n" + " (static) reflection of Flatbuffers types. Requires --cpp-std\n" + " to be \"c++17\" or higher.\n" " --object-prefix Customise class prefix for C++ object-based API.\n" " --object-suffix Customise class suffix for C++ object-based API.\n" " Default value is \"T\".\n" - " --no-js-exports Removes Node.js style export lines in JS.\n" - " --goog-js-export Uses goog.exports* for closure compiler exporting in JS.\n" - " --es6-js-export Uses ECMAScript 6 export style lines in JS.\n" - " --go-namespace Generate the overrided namespace in Golang.\n" - " --go-import Generate the overrided import for flatbuffers in Golang\n" + " --go-namespace Generate the overriding namespace in Golang.\n" + " --go-import Generate the overriding import for flatbuffers in Golang\n" " (default is \"github.com/google/flatbuffers/go\").\n" - " --raw-binary Allow binaries without file_indentifier to be read.\n" + " --raw-binary Allow binaries without file_identifier to be read.\n" " This may crash flatc given a mismatched schema.\n" " --size-prefixed Input binaries are size prefixed buffers.\n" " --proto Input is a .proto, translate to .fbs.\n" @@ -155,12 +157,10 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --include-prefix Prefix this path to any generated include statements.\n" " PATH\n" " --keep-prefix Keep original prefix of schema include statement.\n" - " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n" - " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n" - " --short-names Use short function names for JS and TypeScript.\n" " --reflect-types Add minimal type reflection to code generation.\n" " --reflect-names Add minimal type/name reflection.\n" " --root-type T Select or override the default root_type\n" + " --require-explicit-ids When parsing schemas, require explicit ids (id: x).\n" " --force-defaults Emit default values in binary output from JSON\n" " --force-empty When serializing from object API representation,\n" " force strings and vectors to empty rather than null.\n" @@ -168,6 +168,7 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " force vectors to empty rather than null.\n" " --flexbuffers Used with \"binary\" and \"json\" options, it generates\n" " data using schema-less FlexBuffers.\n" + " --no-warnings Inhibit all warning messages.\n" "FILEs may be schemas (must end in .fbs), binary schemas (must end in .bfbs),\n" "or JSON files (conforming to preceding schema). FILEs after the -- must be\n" "binary flatbuffer format files.\n" @@ -236,14 +237,6 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.allow_non_utf8 = true; } else if (arg == "--natural-utf8") { opts.natural_utf8 = true; - } else if (arg == "--no-js-exports") { - opts.skip_js_exports = true; - } else if (arg == "--goog-js-export") { - opts.use_goog_js_export_format = true; - opts.use_ES6_js_export_format = false; - } else if (arg == "--es6-js-export") { - opts.use_goog_js_export_format = false; - opts.use_ES6_js_export_format = true; } else if (arg == "--go-namespace") { if (++argi >= argc) Error("missing golang namespace" + arg, true); opts.go_namespace = argv[argi]; @@ -280,6 +273,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.cpp_object_api_string_type = argv[argi]; } else if (arg == "--cpp-str-flex-ctor") { opts.cpp_object_api_string_flexible_constructor = true; + } else if (arg == "--no-cpp-direct-copy") { + opts.cpp_direct_copy = false; } else if (arg == "--gen-nullable") { opts.gen_nullable = true; } else if (arg == "--java-checkerframework") { @@ -330,16 +325,12 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.binary_schema_builtins = true; } else if (arg == "--bfbs-gen-embed") { opts.binary_schema_gen_embed = true; - } else if (arg == "--no-fb-import") { - opts.skip_flatbuffers_import = true; - } else if (arg == "--no-ts-reexport") { - opts.reexport_ts_modules = false; - } else if (arg == "--short-names") { - opts.js_ts_short_names = true; } else if (arg == "--reflect-types") { opts.mini_reflect = IDLOptions::kTypes; } else if (arg == "--reflect-names") { opts.mini_reflect = IDLOptions::kTypesAndNames; + } else if (arg == "--require-explicit-ids") { + opts.require_explicit_ids = true; } else if (arg == "--root-type") { if (++argi >= argc) Error("missing type following: " + arg, true); opts.root_type = argv[argi]; @@ -362,10 +353,18 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.cs_gen_json_serializer = true; } else if (arg == "--flexbuffers") { opts.use_flexbuffers = true; + } else if (arg == "--gen-jvmstatic") { + opts.gen_jvmstatic = true; + } else if (arg == "--no-warnings") { + opts.no_warnings = true; } else if (arg == "--cpp-std") { if (++argi >= argc) Error("missing C++ standard specification" + arg, true); opts.cpp_std = argv[argi]; + } else if (arg.rfind("--cpp-std=", 0) == 0) { + opts.cpp_std = arg.substr(std::string("--cpp-std=").size()); + } else if (arg == "--cpp-static-reflection") { + opts.cpp_static_reflection = true; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc_main.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc_main.cpp index 0ad26448..b1966660 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc_main.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/flatc_main.cpp @@ -71,17 +71,12 @@ int main(int argc, const char *argv[]) { flatbuffers::GenerateJavaGRPC, flatbuffers::IDLOptions::kJava, "Generate Java classes for tables/structs", flatbuffers::JavaCSharpMakeRule }, - { flatbuffers::GenerateJSTS, "-s", "--js", "JavaScript", true, nullptr, - flatbuffers::IDLOptions::kJs, - "Generate JavaScript code for tables/structs", - flatbuffers::JSTSMakeRule }, { flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr, flatbuffers::IDLOptions::kDart, "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule }, - { flatbuffers::GenerateJSTS, "-T", "--ts", "TypeScript", true, nullptr, - flatbuffers::IDLOptions::kTs, - "Generate TypeScript code for tables/structs", - flatbuffers::JSTSMakeRule }, + { flatbuffers::GenerateTS, "-T", "--ts", "TypeScript", true, + flatbuffers::GenerateTSGRPC, flatbuffers::IDLOptions::kTs, + "Generate TypeScript code for tables/structs", flatbuffers::TSMakeRule }, { flatbuffers::GenerateCSharp, "-n", "--csharp", "C#", true, nullptr, flatbuffers::IDLOptions::kCSharp, "Generate C# classes for tables/structs", diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_cpp.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_cpp.cpp index f78ee268..210a38f7 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_cpp.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_cpp.cpp @@ -26,11 +26,6 @@ namespace flatbuffers { -// Pedantic warning free version of toupper(). -inline char ToUpper(char c) { - return static_cast(::toupper(static_cast(c))); -} - // Make numerical literal with type-suffix. // This function is only needed for C++! Other languages do not need it. static inline std::string NumToStringCpp(std::string val, BaseType type) { @@ -69,7 +64,7 @@ static std::string GenIncludeGuard(const std::string &file_name, // Anything extra to add to the guard? if (!postfix.empty()) { guard += postfix + "_"; } guard += "H_"; - std::transform(guard.begin(), guard.end(), guard.begin(), ToUpper); + std::transform(guard.begin(), guard.end(), guard.begin(), CharToUpper); return guard; } @@ -77,6 +72,12 @@ namespace cpp { enum CppStandard { CPP_STD_X0 = 0, CPP_STD_11, CPP_STD_17 }; +// Define a style of 'struct' constructor if it has 'Array' fields. +enum GenArrayArgMode { + kArrayArgModeNone, // don't generate initialization args + kArrayArgModeSpanStatic, // generate flatbuffers::span +}; + // Extension of IDLOptions for cpp-generator. struct IDLOptionsCpp : public IDLOptions { // All fields start with 'g_' prefix to distinguish from the base IDLOptions. @@ -200,10 +201,12 @@ class CppGenerator : public BaseGenerator { void GenIncludeDependencies() { int num_includes = 0; - for (auto it = parser_.native_included_files_.begin(); - it != parser_.native_included_files_.end(); ++it) { - code_ += "#include \"" + *it + "\""; - num_includes++; + if (opts_.generate_object_based_api) { + for (auto it = parser_.native_included_files_.begin(); + it != parser_.native_included_files_.end(); ++it) { + code_ += "#include \"" + *it + "\""; + num_includes++; + } } for (auto it = parser_.included_files_.begin(); it != parser_.included_files_.end(); ++it) { @@ -547,8 +550,7 @@ class CppGenerator : public BaseGenerator { if (opts_.generate_object_based_api) { // A convenient root unpack function. - auto native_name = - NativeName(WrapInNameSpace(struct_def), &struct_def, opts_); + auto native_name = WrapNativeNameInNameSpace(struct_def, opts_); code_.SetValue("UNPACK_RETURN", GenTypeNativePtr(native_name, nullptr, false)); code_.SetValue("UNPACK_TYPE", @@ -706,6 +708,12 @@ class CppGenerator : public BaseGenerator { : name; } + std::string WrapNativeNameInNameSpace(const StructDef &struct_def, + const IDLOptions &opts) { + return WrapInNameSpace(struct_def.defined_namespace, + NativeName(Name(struct_def), &struct_def, opts)); + } + const std::string &PtrType(const FieldDef *field) { auto attr = field ? field->attributes.Lookup("cpp_ptr_type") : nullptr; return attr ? attr->constant : opts_.cpp_object_api_pointer_type; @@ -749,6 +757,12 @@ class CppGenerator : public BaseGenerator { return ptr_type == "naked" ? "" : ".get()"; } + std::string GenOptionalNull() { return "flatbuffers::nullopt"; } + + std::string GenOptionalDecl(const Type &type) { + return "flatbuffers::Optional<" + GenTypeBasic(type, true) + ">"; + } + std::string GenTypeNative(const Type &type, bool invector, const FieldDef &field) { switch (type.base_type) { @@ -777,8 +791,9 @@ class CppGenerator : public BaseGenerator { return GenTypeNativePtr(type_name, &field, false); } } else { - return GenTypeNativePtr(NativeName(type_name, type.struct_def, opts_), - &field, false); + return GenTypeNativePtr( + WrapNativeNameInNameSpace(*type.struct_def, opts_), &field, + false); } } case BASE_TYPE_UNION: { @@ -786,7 +801,8 @@ class CppGenerator : public BaseGenerator { return type_name + "Union"; } default: { - return GenTypeBasic(type, true); + return field.IsScalarOptional() ? GenOptionalDecl(type) + : GenTypeBasic(type, true); } } } @@ -816,6 +832,38 @@ class CppGenerator : public BaseGenerator { } } + std::string GenTypeSpan(const Type &type, bool immutable, size_t extent) { + // Generate "flatbuffers::span". + FLATBUFFERS_ASSERT(IsSeries(type) && "unexpected type"); + auto element_type = type.VectorType(); + std::string text = "flatbuffers::span<"; + text += immutable ? "const " : ""; + if (IsScalar(element_type.base_type)) { + text += GenTypeBasic(element_type, IsEnum(element_type)); + } else { + switch (element_type.base_type) { + case BASE_TYPE_STRING: { + text += "char"; + break; + } + case BASE_TYPE_STRUCT: { + FLATBUFFERS_ASSERT(type.struct_def); + text += WrapInNameSpace(*type.struct_def); + break; + } + default: + FLATBUFFERS_ASSERT(false && "unexpected element's type"); + break; + } + } + if (extent != flatbuffers::dynamic_extent) { + text += ", "; + text += NumToString(extent); + } + text += "> "; + return text; + } + std::string GenEnumValDecl(const EnumDef &enum_def, const std::string &enum_val) const { return opts_.prefixed_enums ? Name(enum_def) + "_" + enum_val : enum_val; @@ -836,16 +884,16 @@ class CppGenerator : public BaseGenerator { return name.substr(0, name.size() - strlen(UnionTypeFieldSuffix())); } - std::string GetUnionElement(const EnumVal &ev, bool wrap, bool actual_type, - bool native_type = false) { + std::string GetUnionElement(const EnumVal &ev, bool native_type, + const IDLOptions &opts) { if (ev.union_type.base_type == BASE_TYPE_STRUCT) { - auto name = actual_type ? ev.union_type.struct_def->name : Name(ev); - return wrap ? WrapInNameSpace(ev.union_type.struct_def->defined_namespace, - name) - : name; - } else if (ev.union_type.base_type == BASE_TYPE_STRING) { - return actual_type ? (native_type ? "std::string" : "flatbuffers::String") - : Name(ev); + auto name = ev.union_type.struct_def->name; + if (native_type) { + name = NativeName(name, ev.union_type.struct_def, opts); + } + return WrapInNameSpace(ev.union_type.struct_def->defined_namespace, name); + } else if (IsString(ev.union_type)) { + return native_type ? "std::string" : "flatbuffers::String"; } else { FLATBUFFERS_ASSERT(false); return Name(ev); @@ -950,11 +998,13 @@ class CppGenerator : public BaseGenerator { } std::string ts; std::vector type_refs; + std::vector array_sizes; for (auto it = types.begin(); it != types.end(); ++it) { auto &type = *it; if (!ts.empty()) ts += ",\n "; - auto is_vector = type.base_type == BASE_TYPE_VECTOR; - auto bt = is_vector ? type.element : type.base_type; + auto is_vector = IsVector(type); + auto is_array = IsArray(type); + auto bt = is_vector || is_array ? type.element : type.base_type; auto et = IsScalar(bt) || bt == BASE_TYPE_STRING ? bt - BASE_TYPE_UTYPE + ET_UTYPE : ET_SEQUENCE; @@ -976,14 +1026,21 @@ class CppGenerator : public BaseGenerator { type_refs.push_back(ref_name); } } + if (is_array) { array_sizes.push_back(type.fixed_length); } ts += "{ flatbuffers::" + std::string(ElementaryTypeNames()[et]) + ", " + - NumToString(is_vector) + ", " + NumToString(ref_idx) + " }"; + NumToString(is_vector || is_array) + ", " + NumToString(ref_idx) + + " }"; } std::string rs; for (auto it = type_refs.begin(); it != type_refs.end(); ++it) { if (!rs.empty()) rs += ",\n "; rs += *it + "TypeTable"; } + std::string as; + for (auto it = array_sizes.begin(); it != array_sizes.end(); ++it) { + as += NumToString(*it); + as += ", "; + } std::string ns; for (auto it = names.begin(); it != names.end(); ++it) { if (!ns.empty()) ns += ",\n "; @@ -1012,6 +1069,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("TYPES", ts); code_.SetValue("REFS", rs); + code_.SetValue("ARRAYSIZES", as); code_.SetValue("NAMES", ns); code_.SetValue("VALUES", vs); code_ += "inline const flatbuffers::TypeTable *{{NAME}}TypeTable() {"; @@ -1025,6 +1083,9 @@ class CppGenerator : public BaseGenerator { code_ += " {{REFS}}"; code_ += " };"; } + if (!as.empty()) { + code_ += " static const int16_t array_sizes[] = { {{ARRAYSIZES}} };"; + } if (!vs.empty()) { // Problem with uint64_t values greater than 9223372036854775807ULL. code_ += " static const int64_t values[] = { {{VALUES}} };"; @@ -1040,6 +1101,7 @@ class CppGenerator : public BaseGenerator { code_ += std::string(" flatbuffers::{{SEQ_TYPE}}, {{NUM_FIELDS}}, ") + (num_fields ? "type_codes, " : "nullptr, ") + (!type_refs.empty() ? "type_refs, " : "nullptr, ") + + (!as.empty() ? "array_sizes, " : "nullptr, ") + (!vs.empty() ? "values, " : "nullptr, ") + (has_names ? "names" : "nullptr"); code_ += " };"; @@ -1202,7 +1264,7 @@ class CppGenerator : public BaseGenerator { if (it == enum_def.Vals().begin()) { code_ += "template struct {{ENUM_NAME}}Traits {"; } else { - auto name = GetUnionElement(ev, true, true); + auto name = GetUnionElement(ev, false, opts_); code_ += "template<> struct {{ENUM_NAME}}Traits<" + name + "> {"; } @@ -1265,9 +1327,7 @@ class CppGenerator : public BaseGenerator { const auto &ev = **it; if (ev.IsZero()) { continue; } - const auto native_type = - NativeName(GetUnionElement(ev, true, true, true), - ev.union_type.struct_def, opts_); + const auto native_type = GetUnionElement(ev, true, opts_); code_.SetValue("NATIVE_TYPE", native_type); code_.SetValue("NATIVE_NAME", Name(ev)); code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, ev)); @@ -1299,9 +1359,7 @@ class CppGenerator : public BaseGenerator { const auto &ev = **it; code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, ev)); if (ev.IsNonZero()) { - const auto native_type = - NativeName(GetUnionElement(ev, true, true, true), - ev.union_type.struct_def, opts_); + const auto native_type = GetUnionElement(ev, true, opts_); code_.SetValue("NATIVE_TYPE", native_type); code_ += " case {{NATIVE_ID}}: {"; code_ += @@ -1355,7 +1413,7 @@ class CppGenerator : public BaseGenerator { code_.SetValue("LABEL", GetEnumValUse(enum_def, ev)); if (ev.IsNonZero()) { - code_.SetValue("TYPE", GetUnionElement(ev, true, true)); + code_.SetValue("TYPE", GetUnionElement(ev, false, opts_)); code_ += " case {{LABEL}}: {"; auto getptr = " auto ptr = reinterpret_cast(obj);"; @@ -1368,7 +1426,7 @@ class CppGenerator : public BaseGenerator { code_ += getptr; code_ += " return verifier.VerifyTable(ptr);"; } - } else if (ev.union_type.base_type == BASE_TYPE_STRING) { + } else if (IsString(ev.union_type)) { code_ += getptr; code_ += " return verifier.VerifyString(ptr);"; } else { @@ -1410,7 +1468,7 @@ class CppGenerator : public BaseGenerator { if (ev.IsZero()) { continue; } code_.SetValue("LABEL", GetEnumValUse(enum_def, ev)); - code_.SetValue("TYPE", GetUnionElement(ev, true, true)); + code_.SetValue("TYPE", GetUnionElement(ev, false, opts_)); code_ += " case {{LABEL}}: {"; code_ += " auto ptr = reinterpret_cast(obj);"; if (ev.union_type.base_type == BASE_TYPE_STRUCT) { @@ -1420,7 +1478,7 @@ class CppGenerator : public BaseGenerator { } else { code_ += " return ptr->UnPack(resolver);"; } - } else if (ev.union_type.base_type == BASE_TYPE_STRING) { + } else if (IsString(ev.union_type)) { code_ += " return new std::string(ptr->c_str(), ptr->size());"; } else { FLATBUFFERS_ASSERT(false); @@ -1440,19 +1498,18 @@ class CppGenerator : public BaseGenerator { if (ev.IsZero()) { continue; } code_.SetValue("LABEL", GetEnumValUse(enum_def, ev)); - code_.SetValue("TYPE", NativeName(GetUnionElement(ev, true, true, true), - ev.union_type.struct_def, opts_)); - code_.SetValue("NAME", GetUnionElement(ev, false, true)); + code_.SetValue("TYPE", GetUnionElement(ev, true, opts_)); code_ += " case {{LABEL}}: {"; code_ += " auto ptr = reinterpret_cast(value);"; if (ev.union_type.base_type == BASE_TYPE_STRUCT) { if (ev.union_type.struct_def->fixed) { code_ += " return _fbb.CreateStruct(*ptr).Union();"; } else { + code_.SetValue("NAME", ev.union_type.struct_def->name); code_ += " return Create{{NAME}}(_fbb, ptr, _rehasher).Union();"; } - } else if (ev.union_type.base_type == BASE_TYPE_STRING) { + } else if (IsString(ev.union_type)) { code_ += " return _fbb.CreateString(*ptr).Union();"; } else { FLATBUFFERS_ASSERT(false); @@ -1474,11 +1531,11 @@ class CppGenerator : public BaseGenerator { const auto &ev = **it; if (ev.IsZero()) { continue; } code_.SetValue("LABEL", GetEnumValUse(enum_def, ev)); - code_.SetValue("TYPE", NativeName(GetUnionElement(ev, true, true, true), - ev.union_type.struct_def, opts_)); + code_.SetValue("TYPE", GetUnionElement(ev, true, opts_)); code_ += " case {{LABEL}}: {"; bool copyable = true; - if (ev.union_type.base_type == BASE_TYPE_STRUCT) { + if (ev.union_type.base_type == BASE_TYPE_STRUCT && + !ev.union_type.struct_def->fixed) { // Don't generate code to copy if table is not copyable. // TODO(wvo): make tables copyable instead. for (auto fit = ev.union_type.struct_def->fields.vec.begin(); @@ -1519,8 +1576,7 @@ class CppGenerator : public BaseGenerator { const auto &ev = **it; if (ev.IsZero()) { continue; } code_.SetValue("LABEL", GetEnumValUse(enum_def, ev)); - code_.SetValue("TYPE", NativeName(GetUnionElement(ev, true, true, true), - ev.union_type.struct_def, opts_)); + code_.SetValue("TYPE", GetUnionElement(ev, true, opts_)); code_ += " case {{LABEL}}: {"; code_ += " auto ptr = reinterpret_cast<{{TYPE}} *>(value);"; code_ += " delete ptr;"; @@ -1556,7 +1612,7 @@ class CppGenerator : public BaseGenerator { std::string GenFieldOffsetName(const FieldDef &field) { std::string uname = Name(field); - std::transform(uname.begin(), uname.end(), uname.begin(), ToUpper); + std::transform(uname.begin(), uname.end(), uname.begin(), CharToUpper); return "VT_" + uname; } @@ -1579,17 +1635,19 @@ class CppGenerator : public BaseGenerator { } std::string GetDefaultScalarValue(const FieldDef &field, bool is_ctor) { - if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { - auto ev = field.value.type.enum_def->FindByValue(field.value.constant); + const auto &type = field.value.type; + if (field.IsScalarOptional()) { + return GenOptionalNull(); + } else if (type.enum_def && IsScalar(type.base_type)) { + auto ev = type.enum_def->FindByValue(field.value.constant); if (ev) { - return WrapInNameSpace(field.value.type.enum_def->defined_namespace, - GetEnumValUse(*field.value.type.enum_def, *ev)); + return WrapInNameSpace(type.enum_def->defined_namespace, + GetEnumValUse(*type.enum_def, *ev)); } else { return GenUnderlyingCast( - field, true, - NumToStringCpp(field.value.constant, field.value.type.base_type)); + field, true, NumToStringCpp(field.value.constant, type.base_type)); } - } else if (field.value.type.base_type == BASE_TYPE_BOOL) { + } else if (type.base_type == BASE_TYPE_BOOL) { return field.value.constant == "0" ? "false" : "true"; } else if (field.attributes.Lookup("cpp_type")) { if (is_ctor) { @@ -1609,10 +1667,10 @@ class CppGenerator : public BaseGenerator { void GenParam(const FieldDef &field, bool direct, const char *prefix) { code_.SetValue("PRE", prefix); code_.SetValue("PARAM_NAME", Name(field)); - if (direct && field.value.type.base_type == BASE_TYPE_STRING) { + if (direct && IsString(field.value.type)) { code_.SetValue("PARAM_TYPE", "const char *"); code_.SetValue("PARAM_VALUE", "nullptr"); - } else if (direct && field.value.type.base_type == BASE_TYPE_VECTOR) { + } else if (direct && IsVector(field.value.type)) { const auto vtype = field.value.type.VectorType(); std::string type; if (IsStruct(vtype)) { @@ -1627,8 +1685,12 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("PARAM_VALUE", "nullptr"); } else { - code_.SetValue("PARAM_TYPE", GenTypeWire(field.value.type, " ", true)); + const auto &type = field.value.type; code_.SetValue("PARAM_VALUE", GetDefaultScalarValue(field, false)); + if (field.IsScalarOptional()) + code_.SetValue("PARAM_TYPE", GenOptionalDecl(type) + " "); + else + code_.SetValue("PARAM_TYPE", GenTypeWire(type, " ", true)); } code_ += "{{PRE}}{{PARAM_TYPE}}{{PARAM_NAME}} = {{PARAM_VALUE}}\\"; } @@ -1643,22 +1705,43 @@ class CppGenerator : public BaseGenerator { auto cpp_type = field.attributes.Lookup("cpp_type"); auto full_type = (cpp_type - ? (field.value.type.base_type == BASE_TYPE_VECTOR + ? (IsVector(field.value.type) ? "std::vector<" + GenTypeNativePtr(cpp_type->constant, &field, false) + "> " : GenTypeNativePtr(cpp_type->constant, &field, false)) : type + " "); + // Generate default member initializers for >= C++11. + std::string field_di = ""; + if (opts_.g_cpp_std >= cpp::CPP_STD_11) { + field_di = "{}"; + auto native_default = field.attributes.Lookup("native_default"); + // Scalar types get parsed defaults, raw pointers get nullptrs. + if (IsScalar(field.value.type.base_type)) { + field_di = + " = " + (native_default ? std::string(native_default->constant) + : GetDefaultScalarValue(field, true)); + } else if (field.value.type.base_type == BASE_TYPE_STRUCT) { + if (IsStruct(field.value.type) && native_default) { + field_di = " = " + native_default->constant; + } + } + } code_.SetValue("FIELD_TYPE", full_type); code_.SetValue("FIELD_NAME", Name(field)); - code_ += " {{FIELD_TYPE}}{{FIELD_NAME}};"; + code_.SetValue("FIELD_DI", field_di); + code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}{{FIELD_DI}};"; } } // Generate the default constructor for this struct. Properly initialize all // scalar members with default values. void GenDefaultConstructor(const StructDef &struct_def) { + code_.SetValue("NATIVE_NAME", + NativeName(Name(struct_def), &struct_def, opts_)); + // In >= C++11, default member initializers are generated. + if (opts_.g_cpp_std >= cpp::CPP_STD_11) { return; } std::string initializer_list; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -1696,8 +1779,6 @@ class CppGenerator : public BaseGenerator { initializer_list = "\n : " + initializer_list; } - code_.SetValue("NATIVE_NAME", - NativeName(Name(struct_def), &struct_def, opts_)); code_.SetValue("INIT_LIST", initializer_list); code_ += " {{NATIVE_NAME}}(){{INIT_LIST}} {"; @@ -1790,7 +1871,7 @@ class CppGenerator : public BaseGenerator { void GenVerifyCall(const FieldDef &field, const char *prefix) { code_.SetValue("PRE", prefix); code_.SetValue("NAME", Name(field)); - code_.SetValue("REQUIRED", field.required ? "Required" : ""); + code_.SetValue("REQUIRED", field.IsRequired() ? "Required" : ""); code_.SetValue("SIZE", GenTypeSize(field.value.type)); code_.SetValue("OFFSET", GenFieldOffsetName(field)); if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) { @@ -1853,7 +1934,7 @@ class CppGenerator : public BaseGenerator { // Generate CompareWithValue method for a key field. void GenKeyFieldMethods(const FieldDef &field) { FLATBUFFERS_ASSERT(field.key); - const bool is_string = (field.value.type.base_type == BASE_TYPE_STRING); + const bool is_string = (IsString(field.value.type)); code_ += " bool KeyCompareLessThan(const {{STRUCT_NAME}} *o) const {"; if (is_string) { @@ -1885,6 +1966,254 @@ class CppGenerator : public BaseGenerator { } } + void GenTableUnionAsGetters(const FieldDef &field) { + const auto &type = field.value.type; + auto u = type.enum_def; + + if (!type.enum_def->uses_multiple_type_instances) + code_ += + " template " + "const T *{{NULLABLE_EXT}}{{FIELD_NAME}}_as() const;"; + + for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) { + auto &ev = **u_it; + if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } + auto full_struct_name = GetUnionElement(ev, false, opts_); + + // @TODO: Mby make this decisions more universal? How? + code_.SetValue("U_GET_TYPE", + EscapeKeyword(field.name + UnionTypeFieldSuffix())); + code_.SetValue("U_ELEMENT_TYPE", WrapInNameSpace(u->defined_namespace, + GetEnumValUse(*u, ev))); + code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *"); + code_.SetValue("U_FIELD_NAME", Name(field) + "_as_" + Name(ev)); + code_.SetValue("U_NULLABLE", NullableExtension()); + + // `const Type *union_name_asType() const` accessor. + code_ += " {{U_FIELD_TYPE}}{{U_NULLABLE}}{{U_FIELD_NAME}}() const {"; + code_ += + " return {{U_GET_TYPE}}() == {{U_ELEMENT_TYPE}} ? " + "static_cast<{{U_FIELD_TYPE}}>({{FIELD_NAME}}()) " + ": nullptr;"; + code_ += " }"; + } + } + + void GenTableFieldGetter(const FieldDef &field) { + const auto &type = field.value.type; + const auto offset_str = GenFieldOffsetName(field); + + GenComment(field.doc_comment, " "); + // Call a different accessor for pointers, that indirects. + if (false == field.IsScalarOptional()) { + const bool is_scalar = IsScalar(type.base_type); + std::string accessor; + if (is_scalar) + accessor = "GetField<"; + else if (IsStruct(type)) + accessor = "GetStruct<"; + else + accessor = "GetPointer<"; + auto offset_type = GenTypeGet(type, "", "const ", " *", false); + auto call = accessor + offset_type + ">(" + offset_str; + // Default value as second arg for non-pointer types. + if (is_scalar) { call += ", " + GenDefaultConstant(field); } + call += ")"; + + std::string afterptr = " *" + NullableExtension(); + code_.SetValue("FIELD_TYPE", + GenTypeGet(type, " ", "const ", afterptr.c_str(), true)); + code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call)); + code_.SetValue("NULLABLE_EXT", NullableExtension()); + code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {"; + code_ += " return {{FIELD_VALUE}};"; + code_ += " }"; + } else { + auto wire_type = GenTypeBasic(type, false); + auto face_type = GenTypeBasic(type, true); + auto opt_value = "GetOptional<" + wire_type + ", " + face_type + ">(" + + offset_str + ")"; + code_.SetValue("FIELD_TYPE", GenOptionalDecl(type)); + code_ += " {{FIELD_TYPE}} {{FIELD_NAME}}() const {"; + code_ += " return " + opt_value + ";"; + code_ += " }"; + } + + if (type.base_type == BASE_TYPE_UNION) { GenTableUnionAsGetters(field); } + } + + void GenTableFieldType(const FieldDef &field) { + const auto &type = field.value.type; + const auto offset_str = GenFieldOffsetName(field); + if (!field.IsScalarOptional()) { + std::string afterptr = " *" + NullableExtension(); + code_.SetValue("FIELD_TYPE", + GenTypeGet(type, "", "const ", afterptr.c_str(), true)); + code_ += " {{FIELD_TYPE}}\\"; + } else { + code_.SetValue("FIELD_TYPE", GenOptionalDecl(type)); + code_ += " {{FIELD_TYPE}}\\"; + } + } + + void GenStructFieldType(const FieldDef &field) { + const auto is_array = IsArray(field.value.type); + std::string field_type = + GenTypeGet(field.value.type, "", is_array ? "" : "const ", + is_array ? "" : " &", true); + code_.SetValue("FIELD_TYPE", field_type); + code_ += " {{FIELD_TYPE}}\\"; + } + + void GenFieldTypeHelper(const StructDef &struct_def) { + if (struct_def.fields.vec.empty()) { return; } + code_ += " template"; + code_ += " using FieldType = \\"; + code_ += "decltype(std::declval().get_field());"; + } + + void GenIndexBasedFieldGetter(const StructDef &struct_def) { + if (struct_def.fields.vec.empty()) { return; } + code_ += " template"; + code_ += " auto get_field() const {"; + + size_t index = 0; + bool need_else = false; + // Generate one index-based getter for each field. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { + // Deprecated fields won't be accessible. + continue; + } + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("FIELD_INDEX", + std::to_string(static_cast(index++))); + if (need_else) { + code_ += " else \\"; + } else { + code_ += " \\"; + } + need_else = true; + code_ += "if constexpr (Index == {{FIELD_INDEX}}) \\"; + code_ += "return {{FIELD_NAME}}();"; + } + code_ += " else static_assert(Index != Index, \"Invalid Field Index\");"; + code_ += " }"; + } + + // Sample for Vec3: + // + // static constexpr std::array field_names = { + // "x", + // "y", + // "z" + // }; + // + void GenFieldNames(const StructDef &struct_def) { + auto non_deprecated_field_count = std::count_if( + struct_def.fields.vec.begin(), struct_def.fields.vec.end(), + [](const FieldDef *field) { return !field->deprecated; }); + code_ += " static constexpr std::array<\\"; + code_.SetValue( + "FIELD_COUNT", + std::to_string(static_cast(non_deprecated_field_count))); + code_ += "const char *, {{FIELD_COUNT}}> field_names = {\\"; + if (struct_def.fields.vec.empty()) { + code_ += "};"; + return; + } + code_ += ""; + // Generate the field_names elements. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { + // Deprecated fields won't be accessible. + continue; + } + code_.SetValue("FIELD_NAME", Name(field)); + code_ += " \"{{FIELD_NAME}}\"\\"; + if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } + } + code_ += "\n };"; + } + + void GenFieldsNumber(const StructDef &struct_def) { + auto non_deprecated_field_count = std::count_if( + struct_def.fields.vec.begin(), struct_def.fields.vec.end(), + [](const FieldDef *field) { return !field->deprecated; }); + code_.SetValue( + "FIELD_COUNT", + std::to_string(static_cast(non_deprecated_field_count))); + code_ += " static constexpr size_t fields_number = {{FIELD_COUNT}};"; + } + + void GenTraitsStruct(const StructDef &struct_def) { + code_.SetValue( + "FULLY_QUALIFIED_NAME", + struct_def.defined_namespace->GetFullyQualifiedName(Name(struct_def))); + code_ += "struct {{STRUCT_NAME}}::Traits {"; + code_ += " using type = {{STRUCT_NAME}};"; + if (!struct_def.fixed) { + // We have a table and not a struct. + code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; + } + if (opts_.cpp_static_reflection) { + code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + code_ += + " static constexpr auto fully_qualified_name = " + "\"{{FULLY_QUALIFIED_NAME}}\";"; + GenFieldNames(struct_def); + GenFieldTypeHelper(struct_def); + GenFieldsNumber(struct_def); + } + code_ += "};"; + code_ += ""; + } + + void GenTableFieldSetter(const FieldDef &field) { + const auto &type = field.value.type; + const bool is_scalar = IsScalar(type.base_type); + if (is_scalar && IsUnion(type)) + return; // changing of a union's type is forbidden + + auto offset_str = GenFieldOffsetName(field); + if (is_scalar) { + const auto wire_type = GenTypeWire(type, "", false); + code_.SetValue("SET_FN", "SetField<" + wire_type + ">"); + code_.SetValue("OFFSET_NAME", offset_str); + code_.SetValue("FIELD_TYPE", GenTypeBasic(type, true)); + code_.SetValue("FIELD_VALUE", + GenUnderlyingCast(field, false, "_" + Name(field))); + + code_ += + " bool mutate_{{FIELD_NAME}}({{FIELD_TYPE}} " + "_{{FIELD_NAME}}) {"; + if (false == field.IsScalarOptional()) { + code_.SetValue("DEFAULT_VALUE", GenDefaultConstant(field)); + code_ += + " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}}, " + "{{DEFAULT_VALUE}});"; + } else { + code_ += " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}});"; + } + code_ += " }"; + } else { + auto postptr = " *" + NullableExtension(); + auto wire_type = GenTypeGet(type, " ", "", postptr.c_str(), true); + std::string accessor = IsStruct(type) ? "GetStruct<" : "GetPointer<"; + auto underlying = accessor + wire_type + ">(" + offset_str + ")"; + code_.SetValue("FIELD_TYPE", wire_type); + code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, underlying)); + + code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {"; + code_ += " return {{FIELD_VALUE}};"; + code_ += " }"; + } + } + // Generate an accessor struct, builder structs & function for a table. void GenTable(const StructDef &struct_def) { if (opts_.generate_object_based_api) { GenNativeTable(struct_def); } @@ -1944,103 +2273,9 @@ class CppGenerator : public BaseGenerator { continue; } - const bool is_struct = IsStruct(field.value.type); - const bool is_scalar = IsScalar(field.value.type.base_type); code_.SetValue("FIELD_NAME", Name(field)); - - // Call a different accessor for pointers, that indirects. - std::string accessor = ""; - if (is_scalar) { - accessor = "GetField<"; - } else if (is_struct) { - accessor = "GetStruct<"; - } else { - accessor = "GetPointer<"; - } - auto offset_str = GenFieldOffsetName(field); - auto offset_type = - GenTypeGet(field.value.type, "", "const ", " *", false); - - auto call = accessor + offset_type + ">(" + offset_str; - // Default value as second arg for non-pointer types. - if (is_scalar) { call += ", " + GenDefaultConstant(field); } - call += ")"; - - std::string afterptr = " *" + NullableExtension(); - GenComment(field.doc_comment, " "); - code_.SetValue("FIELD_TYPE", GenTypeGet(field.value.type, " ", "const ", - afterptr.c_str(), true)); - code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call)); - code_.SetValue("NULLABLE_EXT", NullableExtension()); - - code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {"; - code_ += " return {{FIELD_VALUE}};"; - code_ += " }"; - - if (field.value.type.base_type == BASE_TYPE_UNION) { - auto u = field.value.type.enum_def; - - if (!field.value.type.enum_def->uses_multiple_type_instances) - code_ += - " template " - "const T *{{NULLABLE_EXT}}{{FIELD_NAME}}_as() const;"; - - for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) { - auto &ev = **u_it; - if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } - auto full_struct_name = GetUnionElement(ev, true, true); - - // @TODO: Mby make this decisions more universal? How? - code_.SetValue("U_GET_TYPE", - EscapeKeyword(field.name + UnionTypeFieldSuffix())); - code_.SetValue( - "U_ELEMENT_TYPE", - WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev))); - code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *"); - code_.SetValue("U_FIELD_NAME", Name(field) + "_as_" + Name(ev)); - code_.SetValue("U_NULLABLE", NullableExtension()); - - // `const Type *union_name_asType() const` accessor. - code_ += " {{U_FIELD_TYPE}}{{U_NULLABLE}}{{U_FIELD_NAME}}() const {"; - code_ += - " return {{U_GET_TYPE}}() == {{U_ELEMENT_TYPE}} ? " - "static_cast<{{U_FIELD_TYPE}}>({{FIELD_NAME}}()) " - ": nullptr;"; - code_ += " }"; - } - } - - if (opts_.mutable_buffer && !(is_scalar && IsUnion(field.value.type))) { - if (is_scalar) { - const auto type = GenTypeWire(field.value.type, "", false); - code_.SetValue("SET_FN", "SetField<" + type + ">"); - code_.SetValue("OFFSET_NAME", offset_str); - code_.SetValue("FIELD_TYPE", GenTypeBasic(field.value.type, true)); - code_.SetValue("FIELD_VALUE", - GenUnderlyingCast(field, false, "_" + Name(field))); - code_.SetValue("DEFAULT_VALUE", GenDefaultConstant(field)); - - code_ += - " bool mutate_{{FIELD_NAME}}({{FIELD_TYPE}} " - "_{{FIELD_NAME}}) {"; - code_ += - " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}}, " - "{{DEFAULT_VALUE}});"; - code_ += " }"; - } else { - auto postptr = " *" + NullableExtension(); - auto type = - GenTypeGet(field.value.type, " ", "", postptr.c_str(), true); - auto underlying = accessor + type + ">(" + offset_str + ")"; - code_.SetValue("FIELD_TYPE", type); - code_.SetValue("FIELD_VALUE", - GenUnderlyingCast(field, true, underlying)); - - code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {"; - code_ += " return {{FIELD_VALUE}};"; - code_ += " }"; - } - } + GenTableFieldGetter(field); + if (opts_.mutable_buffer) { GenTableFieldSetter(field); } auto nested = field.attributes.Lookup("nested_flatbuffer"); if (nested) { @@ -2078,6 +2313,8 @@ class CppGenerator : public BaseGenerator { if (field.key) { GenKeyFieldMethods(field); } } + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } + // Generate a verifier function that can check a buffer from an untrusted // source will never cause reads outside the buffer. code_ += " bool Verify(flatbuffers::Verifier &verifier) const {"; @@ -2119,7 +2356,7 @@ class CppGenerator : public BaseGenerator { auto &ev = **u_it; if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } - auto full_struct_name = GetUnionElement(ev, true, true); + auto full_struct_name = GetUnionElement(ev, false, opts_); code_.SetValue( "U_ELEMENT_TYPE", @@ -2153,7 +2390,7 @@ class CppGenerator : public BaseGenerator { // that doesn't need alignment code. std::string GenVectorForceAlign(const FieldDef &field, const std::string &field_size) { - FLATBUFFERS_ASSERT(field.value.type.base_type == BASE_TYPE_VECTOR); + FLATBUFFERS_ASSERT(IsVector(field.value.type)); // Get the value of the force_align attribute. const auto *force_align = field.attributes.Lookup("force_align"); const int align = force_align ? atoi(force_align->constant.c_str()) : 1; @@ -2181,43 +2418,43 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; - if (!field.deprecated) { - const bool is_scalar = IsScalar(field.value.type.base_type); - const bool is_string = field.value.type.base_type == BASE_TYPE_STRING; - const bool is_vector = field.value.type.base_type == BASE_TYPE_VECTOR; - if (is_string || is_vector) { has_string_or_vector_fields = true; } - - std::string offset = GenFieldOffsetName(field); - std::string name = GenUnderlyingCast(field, false, Name(field)); - std::string value = is_scalar ? GenDefaultConstant(field) : ""; - - // Generate accessor functions of the form: - // void add_name(type name) { - // fbb_.AddElement(offset, name, default); - // } - code_.SetValue("FIELD_NAME", Name(field)); - code_.SetValue("FIELD_TYPE", GenTypeWire(field.value.type, " ", true)); - code_.SetValue("ADD_OFFSET", Name(struct_def) + "::" + offset); - code_.SetValue("ADD_NAME", name); - code_.SetValue("ADD_VALUE", value); - if (is_scalar) { - const auto type = GenTypeWire(field.value.type, "", false); - code_.SetValue("ADD_FN", "AddElement<" + type + ">"); - } else if (IsStruct(field.value.type)) { - code_.SetValue("ADD_FN", "AddStruct"); - } else { - code_.SetValue("ADD_FN", "AddOffset"); - } + if (field.deprecated) continue; + const bool is_scalar = IsScalar(field.value.type.base_type); + const bool is_default_scalar = is_scalar && !field.IsScalarOptional(); + const bool is_string = IsString(field.value.type); + const bool is_vector = IsVector(field.value.type); + if (is_string || is_vector) { has_string_or_vector_fields = true; } + + std::string offset = GenFieldOffsetName(field); + std::string name = GenUnderlyingCast(field, false, Name(field)); + std::string value = is_default_scalar ? GenDefaultConstant(field) : ""; + + // Generate accessor functions of the form: + // void add_name(type name) { + // fbb_.AddElement(offset, name, default); + // } + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("FIELD_TYPE", GenTypeWire(field.value.type, " ", true)); + code_.SetValue("ADD_OFFSET", Name(struct_def) + "::" + offset); + code_.SetValue("ADD_NAME", name); + code_.SetValue("ADD_VALUE", value); + if (is_scalar) { + const auto type = GenTypeWire(field.value.type, "", false); + code_.SetValue("ADD_FN", "AddElement<" + type + ">"); + } else if (IsStruct(field.value.type)) { + code_.SetValue("ADD_FN", "AddStruct"); + } else { + code_.SetValue("ADD_FN", "AddOffset"); + } - code_ += " void add_{{FIELD_NAME}}({{FIELD_TYPE}}{{FIELD_NAME}}) {"; - code_ += " fbb_.{{ADD_FN}}(\\"; - if (is_scalar) { - code_ += "{{ADD_OFFSET}}, {{ADD_NAME}}, {{ADD_VALUE}});"; - } else { - code_ += "{{ADD_OFFSET}}, {{ADD_NAME}});"; - } - code_ += " }"; + code_ += " void add_{{FIELD_NAME}}({{FIELD_TYPE}}{{FIELD_NAME}}) {"; + code_ += " fbb_.{{ADD_FN}}(\\"; + if (is_default_scalar) { + code_ += "{{ADD_OFFSET}}, {{ADD_NAME}}, {{ADD_VALUE}});"; + } else { + code_ += "{{ADD_OFFSET}}, {{ADD_NAME}});"; } + code_ += " }"; } // Builder constructor @@ -2228,11 +2465,6 @@ class CppGenerator : public BaseGenerator { code_ += " start_ = fbb_.StartTable();"; code_ += " }"; - // Assignment operator; - code_ += - " {{STRUCT_NAME}}Builder &operator=" - "(const {{STRUCT_NAME}}Builder &);"; - // Finish() function. code_ += " flatbuffers::Offset<{{STRUCT_NAME}}> Finish() {"; code_ += " const auto end = fbb_.EndTable(start_);"; @@ -2241,7 +2473,7 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; - if (!field.deprecated && field.required) { + if (!field.deprecated && field.IsRequired()) { code_.SetValue("FIELD_NAME", Name(field)); code_.SetValue("OFFSET_NAME", GenFieldOffsetName(field)); code_ += " fbb_.Required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}});"; @@ -2274,7 +2506,13 @@ class CppGenerator : public BaseGenerator { if (!field.deprecated && (!struct_def.sortbysize || size == SizeOf(field.value.type.base_type))) { code_.SetValue("FIELD_NAME", Name(field)); - code_ += " builder_.add_{{FIELD_NAME}}({{FIELD_NAME}});"; + if (field.IsScalarOptional()) { + code_ += + " if({{FIELD_NAME}}) { " + "builder_.add_{{FIELD_NAME}}(*{{FIELD_NAME}}); }"; + } else { + code_ += " builder_.add_{{FIELD_NAME}}({{FIELD_NAME}});"; + } } } } @@ -2284,16 +2522,10 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - code_ += "struct {{STRUCT_NAME}}::Traits {"; - code_ += " using type = {{STRUCT_NAME}};"; - code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; - code_ += "};"; - code_ += ""; - } + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } // Generate a CreateXDirect function with vector types as parameters - if (has_string_or_vector_fields) { + if (opts_.cpp_direct_copy && has_string_or_vector_fields) { code_ += "inline flatbuffers::Offset<{{STRUCT_NAME}}> " "Create{{STRUCT_NAME}}Direct("; @@ -2313,7 +2545,7 @@ class CppGenerator : public BaseGenerator { const auto &field = **it; if (!field.deprecated) { code_.SetValue("FIELD_NAME", Name(field)); - if (field.value.type.base_type == BASE_TYPE_STRING) { + if (IsString(field.value.type)) { if (!field.shared) { code_.SetValue("CREATE_STRING", "CreateString"); } else { @@ -2322,7 +2554,7 @@ class CppGenerator : public BaseGenerator { code_ += " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? " "_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(field.value.type)) { const std::string force_align_code = GenVectorForceAlign(field, Name(field) + "->size()"); if (!force_align_code.empty()) { @@ -2357,8 +2589,7 @@ class CppGenerator : public BaseGenerator { if (!field.deprecated) { code_.SetValue("FIELD_NAME", Name(field)); code_ += ",\n {{FIELD_NAME}}\\"; - if (field.value.type.base_type == BASE_TYPE_STRING || - field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsString(field.value.type) || IsVector(field.value.type)) { code_ += "__\\"; } } @@ -2390,20 +2621,26 @@ class CppGenerator : public BaseGenerator { } } case BASE_TYPE_STRUCT: { - const auto name = WrapInNameSpace(*type.struct_def); if (IsStruct(type)) { - auto native_type = type.struct_def->attributes.Lookup("native_type"); + const auto &struct_attrs = type.struct_def->attributes; + const auto native_type = struct_attrs.Lookup("native_type"); if (native_type) { - return "flatbuffers::UnPack(*" + val + ")"; + std::string unpack_call = "flatbuffers::UnPack"; + const auto pack_name = struct_attrs.Lookup("native_type_pack_name"); + if (pack_name) { unpack_call += pack_name->constant; } + unpack_call += "(*" + val + ")"; + return unpack_call; } else if (invector || afield.native_inline) { return "*" + val; } else { + const auto name = WrapInNameSpace(*type.struct_def); const auto ptype = GenTypeNativePtr(name, &afield, true); return ptype + "(new " + name + "(*" + val + "))"; } } else { const auto ptype = GenTypeNativePtr( - NativeName(name, type.struct_def, opts_), &afield, true); + WrapNativeNameInNameSpace(*type.struct_def, opts_), &afield, + true); return ptype + "(" + val + "->UnPack(_resolver))"; } } @@ -2425,58 +2662,73 @@ class CppGenerator : public BaseGenerator { std::string code; switch (field.value.type.base_type) { case BASE_TYPE_VECTOR: { - auto cpp_type = field.attributes.Lookup("cpp_type"); - std::string indexing; - if (field.value.type.enum_def) { - indexing += "static_cast<" + - WrapInNameSpace(*field.value.type.enum_def) + ">("; - } - indexing += "_e->Get(_i)"; - if (field.value.type.enum_def) { indexing += ")"; } - if (field.value.type.element == BASE_TYPE_BOOL) { indexing += " != 0"; } - - // Generate code that pushes data from _e to _o in the form: - // for (uoffset_t i = 0; i < _e->size(); ++i) { - // _o->field.push_back(_e->Get(_i)); - // } auto name = Name(field); if (field.value.type.element == BASE_TYPE_UTYPE) { name = StripUnionType(Name(field)); } - auto access = - field.value.type.element == BASE_TYPE_UTYPE - ? ".type" - : (field.value.type.element == BASE_TYPE_UNION ? ".value" : ""); code += "{ _o->" + name + ".resize(_e->size()); "; - code += "for (flatbuffers::uoffset_t _i = 0;"; - code += " _i < _e->size(); _i++) { "; - if (cpp_type) { - // Generate code that resolves the cpp pointer type, of the form: - // if (resolver) - // (*resolver)(&_o->field, (hash_value_t)(_e)); - // else - // _o->field = nullptr; - code += "//vector resolver, " + PtrType(&field) + "\n"; - code += "if (_resolver) "; - code += "(*_resolver)"; - code += "(reinterpret_cast(&_o->" + name + "[_i]" + access + - "), "; - code += "static_cast(" + indexing + "));"; - if (PtrType(&field) == "naked") { - code += " else "; - code += "_o->" + name + "[_i]" + access + " = nullptr"; + if (!field.value.type.enum_def && !IsBool(field.value.type.element) && + IsOneByte(field.value.type.element)) { + // For vectors of bytes, std::copy is used to improve performance. + // This doesn't work for: + // - enum types because they have to be explicitly static_cast. + // - vectors of bool, since they are a template specialization. + // - multiple-byte types due to endianness. + code += + "std::copy(_e->begin(), _e->end(), _o->" + name + ".begin()); }"; + } else { + std::string indexing; + if (field.value.type.enum_def) { + indexing += "static_cast<" + + WrapInNameSpace(*field.value.type.enum_def) + ">("; + } + indexing += "_e->Get(_i)"; + if (field.value.type.enum_def) { indexing += ")"; } + if (field.value.type.element == BASE_TYPE_BOOL) { + indexing += " != 0"; + } + // Generate code that pushes data from _e to _o in the form: + // for (uoffset_t i = 0; i < _e->size(); ++i) { + // _o->field.push_back(_e->Get(_i)); + // } + auto access = + field.value.type.element == BASE_TYPE_UTYPE + ? ".type" + : (field.value.type.element == BASE_TYPE_UNION ? ".value" + : ""); + + code += "for (flatbuffers::uoffset_t _i = 0;"; + code += " _i < _e->size(); _i++) { "; + auto cpp_type = field.attributes.Lookup("cpp_type"); + if (cpp_type) { + // Generate code that resolves the cpp pointer type, of the form: + // if (resolver) + // (*resolver)(&_o->field, (hash_value_t)(_e)); + // else + // _o->field = nullptr; + code += "//vector resolver, " + PtrType(&field) + "\n"; + code += "if (_resolver) "; + code += "(*_resolver)"; + code += "(reinterpret_cast(&_o->" + name + "[_i]" + + access + "), "; + code += + "static_cast(" + indexing + "));"; + if (PtrType(&field) == "naked") { + code += " else "; + code += "_o->" + name + "[_i]" + access + " = nullptr"; + } else { + // code += " else "; + // code += "_o->" + name + "[_i]" + access + " = " + + // GenTypeNativePtr(cpp_type->constant, &field, true) + "();"; + code += "/* else do nothing */"; + } } else { - // code += " else "; - // code += "_o->" + name + "[_i]" + access + " = " + - // GenTypeNativePtr(cpp_type->constant, &field, true) + "();"; - code += "/* else do nothing */"; + code += "_o->" + name + "[_i]" + access + " = "; + code += GenUnpackVal(field.value.type.VectorType(), indexing, true, + field); } - } else { - code += "_o->" + name + "[_i]" + access + " = "; - code += GenUnpackVal(field.value.type.VectorType(), indexing, true, - field); + code += "; } }"; } - code += "; } }"; break; } case BASE_TYPE_UTYPE: { @@ -2565,7 +2817,7 @@ class CppGenerator : public BaseGenerator { // in _o->field before attempting to access it. If there isn't, // depending on set_empty_strings_to_null either set it to 0 or an empty // string. - if (!field.required) { + if (!field.IsRequired()) { auto empty_value = opts_.set_empty_strings_to_null ? "0" : "_fbb.CreateSharedString(\"\")"; @@ -2603,15 +2855,24 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_STRUCT: { if (IsStruct(vector_type)) { - auto native_type = - field.value.type.struct_def->attributes.Lookup("native_type"); + const auto &struct_attrs = + field.value.type.struct_def->attributes; + const auto native_type = struct_attrs.Lookup("native_type"); if (native_type) { code += "_fbb.CreateVectorOfNativeStructs<"; - code += WrapInNameSpace(*vector_type.struct_def) + ">"; + code += WrapInNameSpace(*vector_type.struct_def) + ", " + + native_type->constant + ">"; + code += "(" + value; + const auto pack_name = + struct_attrs.Lookup("native_type_pack_name"); + if (pack_name) { + code += ", flatbuffers::Pack" + pack_name->constant; + } + code += ")"; } else { code += "_fbb.CreateVectorOfStructs"; + code += "(" + value + ")"; } - code += "(" + value + ")"; } else { code += "_fbb.CreateVector> "; @@ -2674,7 +2935,7 @@ class CppGenerator : public BaseGenerator { // If set_empty_vectors_to_null option is enabled, for optional fields, // check to see if there actually is any data in _o->field before // attempting to access it. - if (opts_.set_empty_vectors_to_null && !field.required) { + if (opts_.set_empty_vectors_to_null && !field.IsRequired()) { code = value + ".size() ? " + code + " : 0"; } break; @@ -2686,10 +2947,14 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_STRUCT: { if (IsStruct(field.value.type)) { - auto native_type = - field.value.type.struct_def->attributes.Lookup("native_type"); + const auto &struct_attribs = field.value.type.struct_def->attributes; + const auto native_type = struct_attribs.Lookup("native_type"); if (native_type) { - code += "flatbuffers::Pack(" + value + ")"; + code += "flatbuffers::Pack"; + const auto pack_name = + struct_attribs.Lookup("native_type_pack_name"); + if (pack_name) { code += pack_name->constant; } + code += "(" + value + ")"; } else if (field.native_inline) { code += "&" + value; } else { @@ -2717,24 +2982,29 @@ class CppGenerator : public BaseGenerator { code_.SetValue("STRUCT_NAME", Name(struct_def)); code_.SetValue("NATIVE_NAME", NativeName(Name(struct_def), &struct_def, opts_)); - auto native_name = - NativeName(WrapInNameSpace(struct_def), &struct_def, parser_.opts); - code_.SetValue("POINTER_TYPE", - GenTypeNativePtr(native_name, nullptr, false)); if (opts_.generate_object_based_api) { // Generate the X::UnPack() method. code_ += "inline " + TableUnPackSignature(struct_def, false, opts_) + " {"; - code_ += - " {{POINTER_TYPE}} _o = {{POINTER_TYPE}}(new {{NATIVE_NAME}}());"; + if (opts_.g_cpp_std == cpp::CPP_STD_X0) { + auto native_name = WrapNativeNameInNameSpace(struct_def, parser_.opts); + code_.SetValue("POINTER_TYPE", + GenTypeNativePtr(native_name, nullptr, false)); + code_ += + " {{POINTER_TYPE}} _o = {{POINTER_TYPE}}(new {{NATIVE_NAME}}());"; + } else if (opts_.g_cpp_std == cpp::CPP_STD_11) { + code_ += + " auto _o = std::unique_ptr<{{NATIVE_NAME}}>(new " + "{{NATIVE_NAME}}());"; + } else { + code_ += " auto _o = std::make_unique<{{NATIVE_NAME}}>();"; + } code_ += " UnPackTo(_o.get(), _resolver);"; code_ += " return _o.release();"; - code_ += "}"; code_ += ""; - code_ += "inline " + TableUnPackToSignature(struct_def, false, opts_) + " {"; code_ += " (void)_o;"; @@ -2787,7 +3057,7 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) { continue; } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { const std::string force_align_code = GenVectorForceAlign(field, "_o->" + Name(field) + ".size()"); if (!force_align_code.empty()) { code_ += " " + force_align_code; } @@ -2848,15 +3118,162 @@ class CppGenerator : public BaseGenerator { static void PaddingInitializer(int bits, std::string *code_ptr, int *id) { (void)bits; - if (*code_ptr != "") *code_ptr += ",\n "; + if (!code_ptr->empty()) *code_ptr += ",\n "; *code_ptr += "padding" + NumToString((*id)++) + "__(0)"; } static void PaddingNoop(int bits, std::string *code_ptr, int *id) { (void)bits; + if (!code_ptr->empty()) *code_ptr += '\n'; *code_ptr += " (void)padding" + NumToString((*id)++) + "__;"; } + void GenStructDefaultConstructor(const StructDef &struct_def) { + std::string init_list; + std::string body; + bool first_in_init_list = true; + int padding_initializer_id = 0; + int padding_body_id = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto field = *it; + const auto field_name = field->name + "_"; + + if (first_in_init_list) { + first_in_init_list = false; + } else { + init_list += ","; + init_list += "\n "; + } + + init_list += field_name; + if (IsStruct(field->value.type) || IsArray(field->value.type)) { + // this is either default initialization of struct + // or + // implicit initialization of array + // for each object in array it: + // * sets it as zeros for POD types (integral, floating point, etc) + // * calls default constructor for classes/structs + init_list += "()"; + } else { + init_list += "(0)"; + } + if (field->padding) { + GenPadding(*field, &init_list, &padding_initializer_id, + PaddingInitializer); + GenPadding(*field, &body, &padding_body_id, PaddingNoop); + } + } + + if (init_list.empty()) { + code_ += " {{STRUCT_NAME}}()"; + code_ += " {}"; + } else { + code_.SetValue("INIT_LIST", init_list); + code_ += " {{STRUCT_NAME}}()"; + code_ += " : {{INIT_LIST}} {"; + if (!body.empty()) { code_ += body; } + code_ += " }"; + } + } + + void GenStructConstructor(const StructDef &struct_def, + GenArrayArgMode array_mode) { + std::string arg_list; + std::string init_list; + int padding_id = 0; + auto first = struct_def.fields.vec.begin(); + // skip arrays if generate ctor without array assignment + const auto init_arrays = (array_mode != kArrayArgModeNone); + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + const auto &type = field.value.type; + const auto is_array = IsArray(type); + const auto arg_name = "_" + Name(field); + if (!is_array || init_arrays) { + if (it != first && !arg_list.empty()) { arg_list += ", "; } + arg_list += !is_array ? GenTypeGet(type, " ", "const ", " &", true) + : GenTypeSpan(type, true, type.fixed_length); + arg_list += arg_name; + } + // skip an array with initialization from span + if (false == (is_array && init_arrays)) { + if (it != first && !init_list.empty()) { init_list += ",\n "; } + init_list += Name(field) + "_"; + if (IsScalar(type.base_type)) { + auto scalar_type = GenUnderlyingCast(field, false, arg_name); + init_list += "(flatbuffers::EndianScalar(" + scalar_type + "))"; + } else { + FLATBUFFERS_ASSERT((is_array && !init_arrays) || IsStruct(type)); + if (!is_array) + init_list += "(" + arg_name + ")"; + else + init_list += "()"; + } + } + if (field.padding) + GenPadding(field, &init_list, &padding_id, PaddingInitializer); + } + + if (!arg_list.empty()) { + code_.SetValue("ARG_LIST", arg_list); + code_.SetValue("INIT_LIST", init_list); + if (!init_list.empty()) { + code_ += " {{STRUCT_NAME}}({{ARG_LIST}})"; + code_ += " : {{INIT_LIST}} {"; + } else { + code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {"; + } + padding_id = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + const auto &type = field.value.type; + if (IsArray(type) && init_arrays) { + const auto &element_type = type.VectorType(); + const auto is_enum = IsEnum(element_type); + FLATBUFFERS_ASSERT( + (IsScalar(element_type.base_type) || IsStruct(element_type)) && + "invalid declaration"); + const auto face_type = GenTypeGet(type, " ", "", "", is_enum); + std::string get_array = + is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray"; + const auto field_name = Name(field) + "_"; + const auto arg_name = "_" + Name(field); + code_ += " flatbuffers::" + get_array + "(" + field_name + + ").CopyFromSpan(" + arg_name + ");"; + } + if (field.padding) { + std::string padding; + GenPadding(field, &padding, &padding_id, PaddingNoop); + code_ += padding; + } + } + code_ += " }"; + } + } + + void GenArrayAccessor(const Type &type, bool mutable_accessor) { + FLATBUFFERS_ASSERT(IsArray(type)); + const auto is_enum = IsEnum(type.VectorType()); + // The Array is a tricky case, like std::vector. + // It requires a specialization of Array class. + // Generate Array for Array. + const auto face_type = GenTypeGet(type, " ", "", "", is_enum); + std::string ret_type = "flatbuffers::Array<" + face_type + ", " + + NumToString(type.fixed_length) + ">"; + if (mutable_accessor) + code_ += " " + ret_type + " *mutable_{{FIELD_NAME}}() {"; + else + code_ += " const " + ret_type + " *{{FIELD_NAME}}() const {"; + + std::string get_array = + is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray"; + code_ += " return &flatbuffers::" + get_array + "({{FIELD_VALUE}});"; + code_ += " }"; + } + // Generate an accessor struct with constructor for a flatbuffers struct. void GenStruct(const StructDef &struct_def) { // Generate an accessor struct, with private variables of the form: @@ -2897,6 +3314,8 @@ class CppGenerator : public BaseGenerator { code_ += ""; code_ += " public:"; + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { code_ += " struct Traits;"; } + // Make TypeTable accessible via the generated struct. if (opts_.mini_reflect != IDLOptions::kNone) { code_ += @@ -2908,72 +3327,19 @@ class CppGenerator : public BaseGenerator { GenFullyQualifiedNameGetter(struct_def, Name(struct_def)); // Generate a default constructor. - code_ += " {{STRUCT_NAME}}() {"; - code_ += - " memset(static_cast(this), 0, sizeof({{STRUCT_NAME}}));"; - code_ += " }"; + GenStructDefaultConstructor(struct_def); // Generate a constructor that takes all fields as arguments, - // excluding arrays - std::string arg_list; - std::string init_list; - padding_id = 0; - auto first = struct_def.fields.vec.begin(); - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (IsArray(field.value.type)) { - first++; - continue; - } - const auto member_name = Name(field) + "_"; - const auto arg_name = "_" + Name(field); - const auto arg_type = - GenTypeGet(field.value.type, " ", "const ", " &", true); - - if (it != first) { arg_list += ", "; } - arg_list += arg_type; - arg_list += arg_name; - if (!IsArray(field.value.type)) { - if (it != first && init_list != "") { init_list += ",\n "; } - init_list += member_name; - if (IsScalar(field.value.type.base_type)) { - auto type = GenUnderlyingCast(field, false, arg_name); - init_list += "(flatbuffers::EndianScalar(" + type + "))"; - } else { - init_list += "(" + arg_name + ")"; - } - } - if (field.padding) { - GenPadding(field, &init_list, &padding_id, PaddingInitializer); - } - } + // excluding arrays. + GenStructConstructor(struct_def, kArrayArgModeNone); - if (!arg_list.empty()) { - code_.SetValue("ARG_LIST", arg_list); - code_.SetValue("INIT_LIST", init_list); - if (!init_list.empty()) { - code_ += " {{STRUCT_NAME}}({{ARG_LIST}})"; - code_ += " : {{INIT_LIST}} {"; - } else { - code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {"; - } - padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (IsArray(field.value.type)) { - const auto &member = Name(field) + "_"; - code_ += - " std::memset(" + member + ", 0, sizeof(" + member + "));"; - } - if (field.padding) { - std::string padding; - GenPadding(field, &padding, &padding_id, PaddingNoop); - code_ += padding; - } - } - code_ += " }"; + auto arrays_num = std::count_if(struct_def.fields.vec.begin(), + struct_def.fields.vec.end(), + [](const flatbuffers::FieldDef *fd) { + return IsArray(fd->value.type); + }); + if (arrays_num > 0) { + GenStructConstructor(struct_def, kArrayArgModeSpanStatic); } // Generate accessor methods of the form: @@ -2981,11 +3347,12 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; + const auto &type = field.value.type; + const auto is_scalar = IsScalar(type.base_type); + const auto is_array = IsArray(type); - auto field_type = GenTypeGet(field.value.type, " ", - IsArray(field.value.type) ? "" : "const ", - IsArray(field.value.type) ? "" : " &", true); - auto is_scalar = IsScalar(field.value.type.base_type); + const auto field_type = GenTypeGet(type, " ", is_array ? "" : "const ", + is_array ? "" : " &", true); auto member = Name(field) + "_"; auto value = is_scalar ? "flatbuffers::EndianScalar(" + member + ")" : member; @@ -2997,16 +3364,8 @@ class CppGenerator : public BaseGenerator { GenComment(field.doc_comment, " "); // Generate a const accessor function. - if (IsArray(field.value.type)) { - auto underlying = GenTypeGet(field.value.type, "", "", "", false); - code_ += " const flatbuffers::Array<" + field_type + ", " + - NumToString(field.value.type.fixed_length) + "> *" + - "{{FIELD_NAME}}() const {"; - code_ += " return reinterpret_cast *>({{FIELD_VALUE}});"; - code_ += " }"; + if (is_array) { + GenArrayAccessor(type, false); } else { code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {"; code_ += " return {{FIELD_VALUE}};"; @@ -3016,11 +3375,10 @@ class CppGenerator : public BaseGenerator { // Generate a mutable accessor function. if (opts_.mutable_buffer) { auto mut_field_type = - GenTypeGet(field.value.type, " ", "", - IsArray(field.value.type) ? "" : " &", true); + GenTypeGet(type, " ", "", is_array ? "" : " &", true); code_.SetValue("FIELD_TYPE", mut_field_type); if (is_scalar) { - code_.SetValue("ARG", GenTypeBasic(field.value.type, true)); + code_.SetValue("ARG", GenTypeBasic(type, true)); code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, false, "_" + Name(field))); @@ -3029,16 +3387,8 @@ class CppGenerator : public BaseGenerator { " flatbuffers::WriteScalar(&{{FIELD_NAME}}_, " "{{FIELD_VALUE}});"; code_ += " }"; - } else if (IsArray(field.value.type)) { - auto underlying = GenTypeGet(field.value.type, "", "", "", false); - code_ += " flatbuffers::Array<" + mut_field_type + ", " + - NumToString(field.value.type.fixed_length) + "> *" + - "mutable_{{FIELD_NAME}}() {"; - code_ += " return reinterpret_cast *>({{FIELD_VALUE}});"; - code_ += " }"; + } else if (is_array) { + GenArrayAccessor(type, true); } else { code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {"; code_ += " return {{FIELD_VALUE}};"; @@ -3051,12 +3401,19 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("NATIVE_NAME", Name(struct_def)); GenOperatorNewDelete(struct_def); + + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } + code_ += "};"; code_.SetValue("STRUCT_BYTE_SIZE", NumToString(struct_def.bytesize)); code_ += "FLATBUFFERS_STRUCT_END({{STRUCT_NAME}}, {{STRUCT_BYTE_SIZE}});"; if (opts_.gen_compare) GenCompareOperator(struct_def, "()"); code_ += ""; + + // Definition for type traits for this table type. This allows querying var- + // ious compile-time traits of the table. + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } } // Set up the correct namespace. Only open a namespace if the existing one is @@ -3106,8 +3463,8 @@ bool GenerateCPP(const Parser &parser, const std::string &path, cpp::IDLOptionsCpp opts(parser.opts); // The '--cpp_std' argument could be extended (like ASAN): // Example: "flatc --cpp_std c++17:option1:option2". - auto cpp_std = !opts.cpp_std.empty() ? opts.cpp_std : "C++0X"; - std::transform(cpp_std.begin(), cpp_std.end(), cpp_std.begin(), ToUpper); + auto cpp_std = !opts.cpp_std.empty() ? opts.cpp_std : "C++11"; + std::transform(cpp_std.begin(), cpp_std.end(), cpp_std.begin(), CharToUpper); if (cpp_std == "C++0X") { opts.g_cpp_std = cpp::CPP_STD_X0; opts.g_only_fixed_enums = false; @@ -3129,6 +3486,13 @@ bool GenerateCPP(const Parser &parser, const std::string &path, // The opts.scoped_enums has priority. opts.g_only_fixed_enums |= opts.scoped_enums; + if (opts.cpp_static_reflection && opts.g_cpp_std < cpp::CPP_STD_17) { + LogCompilerError( + "--cpp-static-reflection requires using --cpp-std at \"C++17\" or " + "higher."); + return false; + } + cpp::CppGenerator generator(parser, path, file_name, opts); return generator.generate(); } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_csharp.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_csharp.cpp index 7db40fab..681ab6d6 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_csharp.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_csharp.cpp @@ -38,6 +38,11 @@ static CommentConfig comment_config = { namespace csharp { class CSharpGenerator : public BaseGenerator { + struct FieldArrayLength { + std::string name; + int length; + }; + public: CSharpGenerator(const Parser &parser, const std::string &path, const std::string &file_name) @@ -209,6 +214,9 @@ class CSharpGenerator : public BaseGenerator { std::string GenDefaultValue(const FieldDef &field, bool enableLangOverrides) const { + // If it is an optional scalar field, the default is null + if (field.IsScalarOptional()) { return "null"; } + auto &value = field.value; if (enableLangOverrides) { // handles both enum case and vector of enum case @@ -308,7 +316,7 @@ class CSharpGenerator : public BaseGenerator { if (!enum_def.is_union) return false; for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { auto &val = **it; - if (val.union_type.base_type == BASE_TYPE_STRING) { return true; } + if (IsString(val.union_type)) { return true; } } return false; } @@ -391,6 +399,7 @@ class CSharpGenerator : public BaseGenerator { } else { code += ", "; code += GenTypeBasic(type); + if (field.IsScalarOptional()) { code += "?"; } if (array_cnt > 0) { code += "["; for (size_t i = 1; i < array_cnt; i++) code += ","; @@ -481,7 +490,7 @@ class CSharpGenerator : public BaseGenerator { key_getter += "int tableOffset = Table."; key_getter += "__indirect(vectorLocation + 4 * (start + middle)"; key_getter += ", bb);\n "; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { key_getter += "int comp = Table."; key_getter += "CompareStrings("; key_getter += GenOffsetGetter(key_field); @@ -496,7 +505,7 @@ class CSharpGenerator : public BaseGenerator { std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const { std::string key_getter = ""; auto data_buffer = "builder.DataBuffer"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { key_getter += "Table.CompareStrings("; key_getter += GenOffsetGetter(key_field, "o1") + ", "; key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")"; @@ -544,7 +553,7 @@ class CSharpGenerator : public BaseGenerator { // Force compile time error if not using the same version runtime. code += " public static void ValidateVersion() {"; code += " FlatBufferConstants."; - code += "FLATBUFFERS_1_12_0(); "; + code += "FLATBUFFERS_2_0_0(); "; code += "}\n"; // Generate a special accessor for the table that when used as the root @@ -599,17 +608,19 @@ class CSharpGenerator : public BaseGenerator { if (!struct_def.fixed && (field.value.type.base_type == BASE_TYPE_STRUCT || field.value.type.base_type == BASE_TYPE_UNION || - (field.value.type.base_type == BASE_TYPE_VECTOR && + (IsVector(field.value.type) && (field.value.type.element == BASE_TYPE_STRUCT || field.value.type.element == BASE_TYPE_UNION)))) { optional = "?"; conditional_cast = "(" + type_name_dest + optional + ")"; } + if (field.IsScalarOptional()) { optional = "?"; } std::string dest_mask = ""; std::string dest_cast = DestinationCast(field.value.type); std::string src_cast = SourceCast(field.value.type); - std::string method_start = " public " + type_name_dest + optional + " " + - MakeCamel(field.name, true); + std::string field_name_camel = MakeCamel(field.name, true); + std::string method_start = + " public " + type_name_dest + optional + " " + field_name_camel; std::string obj = "(new " + type_name + "())"; // Most field accessors need to retrieve and test the field offset first, @@ -621,10 +632,10 @@ class CSharpGenerator : public BaseGenerator { "); return o != 0 ? "); // Generate the accessors that don't do object reuse. if (field.value.type.base_type == BASE_TYPE_STRUCT) { - } else if (field.value.type.base_type == BASE_TYPE_VECTOR && + } else if (IsVector(field.value.type) && field.value.type.element == BASE_TYPE_STRUCT) { } else if (field.value.type.base_type == BASE_TYPE_UNION || - (field.value.type.base_type == BASE_TYPE_VECTOR && + (IsVector(field.value.type) && field.value.type.VectorType().base_type == BASE_TYPE_UNION)) { method_start += ""; type_name = type_name_dest; @@ -634,16 +645,18 @@ class CSharpGenerator : public BaseGenerator { std::string default_cast = ""; // only create default casts for c# scalars or vectors of scalars if ((IsScalar(field.value.type.base_type) || - (field.value.type.base_type == BASE_TYPE_VECTOR && + (IsVector(field.value.type) && IsScalar(field.value.type.element)))) { // For scalars, default value will be returned by GetDefaultValue(). // If the scalar is an enum, GetDefaultValue() returns an actual c# enum // that doesn't need to be casted. However, default values for enum // elements of vectors are integer literals ("0") and are still casted // for clarity. - if (field.value.type.enum_def == nullptr || - field.value.type.base_type == BASE_TYPE_VECTOR) { - default_cast = "(" + type_name_dest + ")"; + // If the scalar is optional and enum, we still need the cast. + if ((field.value.type.enum_def == nullptr || + IsVector(field.value.type)) || + (IsEnum(field.value.type) && field.IsScalarOptional())) { + default_cast = "(" + type_name_dest + optional + ")"; } } std::string member_suffix = "; "; @@ -751,13 +764,37 @@ class CSharpGenerator : public BaseGenerator { code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING)); code += "(o + __p.bb_pos) : null"; } + // As<> accesors for Unions + // Loop through all the possible union types and generate an As + // accessor that casts to the correct type. + for (auto uit = field.value.type.enum_def->Vals().begin(); + uit != field.value.type.enum_def->Vals().end(); ++uit) { + auto val = *uit; + if (val->union_type.base_type == BASE_TYPE_NONE) { continue; } + auto union_field_type_name = GenTypeGet(val->union_type); + code += member_suffix + "}\n"; + if (val->union_type.base_type == BASE_TYPE_STRUCT && + val->union_type.struct_def->attributes.Lookup("private")) { + code += " internal "; + } else { + code += " public "; + } + code += union_field_type_name + " "; + code += field_name_camel + "As" + val->name + "() { return "; + code += field_name_camel; + if (IsString(val->union_type)) { + code += "AsString()"; + } else { + code += "<" + union_field_type_name + ">().Value"; + } + } break; default: FLATBUFFERS_ASSERT(0); } } code += member_suffix; code += "}\n"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += " public int " + MakeCamel(field.name, true); code += "Length"; code += " { get"; @@ -788,9 +825,9 @@ class CSharpGenerator : public BaseGenerator { } } // Generate a ByteBuffer accessor for strings & vectors of scalars. - if ((field.value.type.base_type == BASE_TYPE_VECTOR && + if ((IsVector(field.value.type) && IsScalar(field.value.type.VectorType().base_type)) || - field.value.type.base_type == BASE_TYPE_STRING) { + IsString(field.value.type)) { code += "#if ENABLE_SPAN_T\n"; code += " public Span<" + GenTypeBasic(field.value.type.VectorType()) + "> Get"; @@ -947,7 +984,8 @@ class CSharpGenerator : public BaseGenerator { } // JVM specifications restrict default constructor params to be < 255. // Longs and doubles take up 2 units, so we set the limit to be < 127. - if (has_no_struct_fields && num_fields && num_fields < 127) { + if ((has_no_struct_fields || opts.generate_object_based_api) && + num_fields && num_fields < 127) { struct_has_create = true; // Generate a table constructor of the form: // public static int createName(FlatBufferBuilder builder, args...) @@ -959,13 +997,23 @@ class CSharpGenerator : public BaseGenerator { auto &field = **it; if (field.deprecated) continue; code += ",\n "; - code += GenTypeBasic(field.value.type); - code += " "; - code += field.name; - if (!IsScalar(field.value.type.base_type)) code += "Offset"; - - code += " = "; - code += GenDefaultValueBasic(field); + if (IsStruct(field.value.type) && opts.generate_object_based_api) { + code += WrapInNameSpace( + field.value.type.struct_def->defined_namespace, + GenTypeName_ObjectAPI(field.value.type.struct_def->name, opts)); + code += " "; + code += field.name; + code += " = null"; + } else { + code += GenTypeBasic(field.value.type); + if (field.IsScalarOptional()) { code += "?"; } + code += " "; + code += field.name; + if (!IsScalar(field.value.type.base_type)) code += "Offset"; + + code += " = "; + code += GenDefaultValueBasic(field); + } } code += ") {\n builder."; code += "StartTable("; @@ -980,8 +1028,16 @@ class CSharpGenerator : public BaseGenerator { size == SizeOf(field.value.type.base_type))) { code += " " + struct_def.name + "."; code += "Add"; - code += MakeCamel(field.name) + "(builder, " + field.name; - if (!IsScalar(field.value.type.base_type)) code += "Offset"; + code += MakeCamel(field.name) + "(builder, "; + if (IsStruct(field.value.type) && + opts.generate_object_based_api) { + code += GenTypePointer(field.value.type) + ".Pack(builder, " + + field.name + ")"; + } else { + code += field.name; + if (!IsScalar(field.value.type.base_type)) code += "Offset"; + } + code += ");\n"; } } @@ -1011,6 +1067,7 @@ class CSharpGenerator : public BaseGenerator { code += GenTypeBasic(field.value.type); auto argname = MakeCamel(field.name, false); if (!IsScalar(field.value.type.base_type)) argname += "Offset"; + if (field.IsScalarOptional()) { code += "?"; } code += " " + argname + ") { builder.Add"; code += GenMethod(field.value.type) + "("; code += NumToString(it - struct_def.fields.vec.begin()) + ", "; @@ -1020,10 +1077,15 @@ class CSharpGenerator : public BaseGenerator { field.value.type.base_type != BASE_TYPE_UNION) { code += ".Value"; } - code += ", "; - code += GenDefaultValue(field, false); + if (!field.IsScalarOptional()) { + // When the scalar is optional, use the builder method that doesn't + // supply a default value. Otherwise, we to continue to use the + // default value method. + code += ", "; + code += GenDefaultValue(field, false); + } code += "); }\n"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { auto vector_type = field.value.type.VectorType(); auto alignment = InlineAlignment(vector_type); auto elem_size = InlineSize(vector_type); @@ -1045,8 +1107,8 @@ class CSharpGenerator : public BaseGenerator { code += "("; code += SourceCastBasic(vector_type); code += "data[i]"; - if ((vector_type.base_type == BASE_TYPE_STRUCT || - vector_type.base_type == BASE_TYPE_STRING)) + if (vector_type.base_type == BASE_TYPE_STRUCT || + IsString(vector_type)) code += ".Value"; code += "); return "; code += "builder.EndVector(); }\n"; @@ -1080,7 +1142,7 @@ class CSharpGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (!field.deprecated && field.required) { + if (!field.deprecated && field.IsRequired()) { code += " builder.Required(o, "; code += NumToString(field.value.offset); code += "); // " + field.name + "\n"; @@ -1124,7 +1186,7 @@ class CSharpGenerator : public BaseGenerator { code += "int vectorLocation, "; code += GenTypeGet(key_field->value.type); code += " key, ByteBuffer bb) {\n"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { code += " byte[] byteKey = "; code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; } @@ -1268,7 +1330,7 @@ class CSharpGenerator : public BaseGenerator { code += " default: return 0;\n"; } else { code += " case " + enum_def.name + "." + ev.name + ": return "; - if (ev.union_type.base_type == BASE_TYPE_STRING) { + if (IsString(ev.union_type)) { code += "builder.CreateString(_o.As" + ev.name + "()).Value;\n"; } else { code += GenTypeGet(ev.union_type) + ".Pack(builder, _o.As" + ev.name + @@ -1402,7 +1464,7 @@ class CSharpGenerator : public BaseGenerator { code += indent + " case " + WrapInNameSpace(enum_def) + "." + ev.name + ":\n"; code += indent + " " + varialbe_name + ".Value = this." + camel_name; - if (ev.union_type.base_type == BASE_TYPE_STRING) { + if (IsString(ev.union_type)) { code += "AsString" + func_suffix + ";\n"; } else { code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix; @@ -1522,6 +1584,15 @@ class CSharpGenerator : public BaseGenerator { GenOffsetType(*field.value.type.struct_def) + ") : " + GenTypeGet(field.value.type) + ".Pack(builder, _o." + camel_name + ");\n"; + } else if (struct_def.fixed && struct_has_create) { + std::vector array_lengths; + FieldArrayLength tmp_array_length = { + field.name, + field.value.type.fixed_length, + }; + array_lengths.push_back(tmp_array_length); + GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr, + array_lengths); } break; } @@ -1602,12 +1673,14 @@ class CSharpGenerator : public BaseGenerator { } case BASE_TYPE_ARRAY: { if (field.value.type.struct_def != nullptr) { - std::vector name_vec; - name_vec.push_back(field.name); - std::vector array_length_vec; - array_length_vec.push_back(field.value.type.fixed_length); - GenArrayPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr, - name_vec, array_length_vec); + std::vector array_lengths; + FieldArrayLength tmp_array_length = { + field.name, + field.value.type.fixed_length, + }; + array_lengths.push_back(tmp_array_length); + GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr, + array_lengths); } else { code += " var _" + field.name + " = _o." + camel_name + ";\n"; } @@ -1638,13 +1711,17 @@ class CSharpGenerator : public BaseGenerator { switch (field.value.type.base_type) { case BASE_TYPE_STRUCT: { if (struct_def.fixed) { - GenStructArgs_ObjectAPI(*field.value.type.struct_def, code_ptr, - " _o." + camel_name + "."); + GenStructPackCall_ObjectAPI(*field.value.type.struct_def, + code_ptr, + " _" + field.name + "_"); } else { code += ",\n"; if (field.value.type.struct_def->fixed) { - code += " " + GenTypeGet(field.value.type) + - ".Pack(builder, _o." + camel_name + ")"; + if (opts.generate_object_based_api) + code += " _o." + camel_name; + else + code += " " + GenTypeGet(field.value.type) + + ".Pack(builder, _o." + camel_name + ")"; } else { code += " _" + field.name; } @@ -1653,8 +1730,9 @@ class CSharpGenerator : public BaseGenerator { } case BASE_TYPE_ARRAY: { if (field.value.type.struct_def != nullptr) { - GenArrayPackCall_ObjectAPI(*field.value.type.struct_def, code_ptr, - " _" + field.name + "_"); + GenStructPackCall_ObjectAPI(*field.value.type.struct_def, + code_ptr, + " _" + field.name + "_"); } else { code += ",\n"; code += " _" + field.name; @@ -1724,28 +1802,9 @@ class CSharpGenerator : public BaseGenerator { code += " }\n"; } - void GenStructArgs_ObjectAPI(const StructDef &struct_def, - std::string *code_ptr, - std::string prefix) const { - auto &code = *code_ptr; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - const auto &field_type = field.value.type; - if (IsStruct(field_type)) { - GenStructArgs_ObjectAPI(*field_type.struct_def, code_ptr, - prefix + "." + MakeCamel(field.name) + "."); - } else { - code += ",\n"; - code += prefix + MakeCamel(field.name); - } - } - } - - void GenArrayPackDecl_ObjectAPI(const StructDef &struct_def, - std::string *code_ptr, - std::vector name_vec, - std::vector array_length_vec) const { + void GenStructPackDecl_ObjectAPI( + const StructDef &struct_def, std::string *code_ptr, + std::vector &array_lengths) const { auto &code = *code_ptr; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -1753,64 +1812,83 @@ class CSharpGenerator : public BaseGenerator { auto is_array = IsArray(field.value.type); const auto &field_type = is_array ? field.value.type.VectorType() : field.value.type; - if (!IsStruct(field_type)) { - auto tmp_name_vec = name_vec; - tmp_name_vec.push_back(field.name); - auto tmp_array_length_vec = array_length_vec; - if (is_array) { - tmp_array_length_vec.push_back(field_type.fixed_length); + FieldArrayLength tmp_array_length = { + field.name, + field_type.fixed_length, + }; + array_lengths.push_back(tmp_array_length); + if (field_type.struct_def != nullptr) { + GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr, + array_lengths); + } else { + std::vector array_only_lengths; + for (size_t i = 0; i < array_lengths.size(); ++i) { + if (array_lengths[i].length > 0) { + array_only_lengths.push_back(array_lengths[i]); + } } std::string name; - for (size_t tmp_name_index = 0; tmp_name_index < tmp_name_vec.size(); - ++tmp_name_index) { - name += "_" + tmp_name_vec[tmp_name_index]; - } - code += " var " + name + " = new " + GenTypeBasic(field_type) + "["; - code += NumToString(tmp_array_length_vec[0]); - for (size_t i = 1; i < tmp_array_length_vec.size(); ++i) { - auto array_length = tmp_array_length_vec[i]; - code += "," + NumToString(array_length); - } - code += "];\n"; - code += " "; - // initialize array - for (size_t i = 0; i < tmp_array_length_vec.size(); ++i) { - auto array_length = tmp_array_length_vec[i]; - auto idx = "idx" + NumToString(i); - code += "for (var " + idx + " = 0; " + idx + " < " + - NumToString(array_length) + "; ++" + idx + ") {"; - } - code += name + "[idx0"; - for (size_t i = 1; i < tmp_array_length_vec.size(); ++i) { - auto idx = "idx" + NumToString(i); - code += "," + idx; - } - code += "] = _o"; - for (size_t i = 0; i < tmp_array_length_vec.size(); ++i) { - auto idx = "idx" + NumToString(i); - code += "." + MakeCamel(tmp_name_vec[i]) + "[" + idx + "]"; + for (size_t i = 0; i < array_lengths.size(); ++i) { + name += "_" + array_lengths[i].name; } - if (!is_array) { code += "." + MakeCamel(field.name); } - code += ";"; - for (size_t i = 0; i < tmp_array_length_vec.size(); ++i) { - code += "}"; + code += " var " + name + " = "; + if (array_only_lengths.size() > 0) { + code += "new " + GenTypeBasic(field_type) + "["; + for (size_t i = 0; i < array_only_lengths.size(); ++i) { + if (i != 0) { code += ","; } + code += NumToString(array_only_lengths[i].length); + } + code += "];\n"; + code += " "; + // initialize array + for (size_t i = 0; i < array_only_lengths.size(); ++i) { + auto idx = "idx" + NumToString(i); + code += "for (var " + idx + " = 0; " + idx + " < " + + NumToString(array_only_lengths[i].length) + "; ++" + idx + + ") {"; + } + for (size_t i = 0; i < array_only_lengths.size(); ++i) { + auto idx = "idx" + NumToString(i); + if (i == 0) { + code += name + "[" + idx; + } else { + code += "," + idx; + } + } + code += "] = _o"; + for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) { + code += "." + MakeCamel(array_lengths[i].name); + if (array_lengths[i].length <= 0) continue; + code += "[idx" + NumToString(j++) + "]"; + } + code += ";"; + for (size_t i = 0; i < array_only_lengths.size(); ++i) { + code += "}"; + } + } else { + code += "_o"; + for (size_t i = 0; i < array_lengths.size(); ++i) { + code += "." + MakeCamel(array_lengths[i].name); + } + code += ";"; } code += "\n"; } + array_lengths.pop_back(); } } - void GenArrayPackCall_ObjectAPI(const StructDef &struct_def, - std::string *code_ptr, - std::string prefix) const { + void GenStructPackCall_ObjectAPI(const StructDef &struct_def, + std::string *code_ptr, + std::string prefix) const { auto &code = *code_ptr; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; const auto &field_type = field.value.type; - if (IsStruct(field_type)) { - GenArrayPackCall_ObjectAPI(*field_type.struct_def, code_ptr, - prefix + field.name + "_"); + if (field_type.struct_def != nullptr) { + GenStructPackCall_ObjectAPI(*field_type.struct_def, code_ptr, + prefix + field.name + "_"); } else { code += ",\n"; code += prefix + field.name; @@ -1882,13 +1960,14 @@ class CSharpGenerator : public BaseGenerator { if (field.value.type.base_type == BASE_TYPE_UTYPE) continue; if (field.value.type.element == BASE_TYPE_UTYPE) continue; auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts); + if (field.IsScalarOptional()) type_name += "?"; auto camel_name = MakeCamel(field.name, true); if (opts.cs_gen_json_serializer) { if (IsUnion(field.value.type)) { auto utype_name = WrapInNameSpace(*field.value.type.enum_def); code += " [Newtonsoft.Json.JsonProperty(\"" + field.name + "_type\")]\n"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += " private " + utype_name + "[] " + camel_name + "Type {\n"; code += " get {\n"; code += " if (this." + camel_name + " == null) return null;\n"; @@ -1927,7 +2006,7 @@ class CSharpGenerator : public BaseGenerator { code += " [Newtonsoft.Json.JsonProperty(\"" + field.name + "\")]\n"; if (IsUnion(field.value.type)) { auto union_name = - (field.value.type.base_type == BASE_TYPE_VECTOR) + (IsVector(field.value.type)) ? GenTypeGet_ObjectAPI(field.value.type.VectorType(), opts) : type_name; code += " [Newtonsoft.Json.JsonConverter(typeof(" + union_name + @@ -1998,8 +2077,8 @@ class CSharpGenerator : public BaseGenerator { code += " }\n"; code += " public byte[] SerializeToBinary() {\n"; code += " var fbb = new FlatBufferBuilder(0x10000);\n"; - code += - " fbb.Finish(" + struct_def.name + ".Pack(fbb, this).Value);\n"; + code += " " + struct_def.name + ".Finish" + struct_def.name + + "Buffer(fbb, " + struct_def.name + ".Pack(fbb, this));\n"; code += " return fbb.DataBuffer.ToSizedArray();\n"; code += " }\n"; } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_dart.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_dart.cpp index c2b1e42d..56c4a825 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_dart.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_dart.cpp @@ -72,10 +72,6 @@ class DartGenerator : public BaseGenerator { code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb + ";\n\n"; - if (parser_.opts.include_dependence_headers) { - GenIncludeDependencies(&code, kv->first); - } - for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end(); ++kv2) { if (kv2->first != kv->first) { @@ -125,16 +121,16 @@ class DartGenerator : public BaseGenerator { auto ret = sstream.str() + ns.components.back(); for (size_t i = 0; i < ret.size(); i++) { - auto lower = tolower(ret[i]); + auto lower = CharToLower(ret[i]); if (lower != ret[i]) { - ret[i] = static_cast(lower); + ret[i] = lower; if (i != 0 && ret[i - 1] != '.') { ret.insert(i, "_"); i++; } } } - // std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + // std::transform(ret.begin(), ret.end(), ret.begin(), CharToLower); return ret; } @@ -271,7 +267,7 @@ class DartGenerator : public BaseGenerator { code += "const " + name + "._(" + enum_def.ToString(ev) + ");\n"; } - code += " static get values => {"; + code += " static const Map values = {"; for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { auto &ev = **it; code += enum_def.ToString(ev) + ": " + ev.name + ","; @@ -334,13 +330,13 @@ class DartGenerator : public BaseGenerator { bool parent_is_vector = false) { if (type.base_type == BASE_TYPE_BOOL) { return "const " + _kFb + ".BoolReader()"; - } else if (type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(type)) { return "const " + _kFb + ".ListReader<" + GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" + GenReaderTypeName(type.VectorType(), current_namespace, def, true) + ")"; - } else if (type.base_type == BASE_TYPE_STRING) { + } else if (IsString(type)) { return "const " + _kFb + ".StringReader()"; } if (IsScalar(type.base_type)) { @@ -473,32 +469,34 @@ class DartGenerator : public BaseGenerator { (*namespace_code)[object_namespace] += code; } - std::string NamespaceAliasFromUnionType(const std::string &in) { - if (in.find('_') == std::string::npos) { return in; } + std::string NamespaceAliasFromUnionType(Namespace *root_namespace, + const Type &type) { + const std::vector qualified_name_parts = + type.struct_def->defined_namespace->components; + if (std::equal(root_namespace->components.begin(), + root_namespace->components.end(), + qualified_name_parts.begin())) { + return type.struct_def->name; + } - std::stringstream ss(in); - std::string item; - std::vector parts; std::string ns; - while (std::getline(ss, item, '_')) { parts.push_back(item); } - - for (auto it = parts.begin(); it != parts.end() - 1; ++it) { + for (auto it = qualified_name_parts.begin(); + it != qualified_name_parts.end(); ++it) { auto &part = *it; for (size_t i = 0; i < part.length(); i++) { - if (i && !isdigit(part[i]) && - part[i] == static_cast(toupper(part[i]))) { + if (i && !isdigit(part[i]) && part[i] == CharToUpper(part[i])) { ns += "_"; - ns += static_cast(tolower(part[i])); + ns += CharToLower(part[i]); } else { - ns += static_cast(tolower(part[i])); + ns += CharToLower(part[i]); } } - if (it != parts.end() - 2) { ns += "_"; } + if (it != qualified_name_parts.end() - 1) { ns += "_"; } } - return ns + "." + parts.back(); + return ns + "." + type.struct_def->name; } void GenImplementationGetters( @@ -527,7 +525,8 @@ class DartGenerator : public BaseGenerator { en_it != enum_def.Vals().end(); ++en_it) { auto &ev = **en_it; - auto enum_name = NamespaceAliasFromUnionType(ev.name); + auto enum_name = NamespaceAliasFromUnionType( + enum_def.defined_namespace, ev.union_type); code += " case " + enum_def.ToString(ev) + ": return " + enum_name + ".reader.vTableGet(_bc, _bcOffset, " + NumToString(field.value.offset) + ", null);\n"; @@ -809,7 +808,7 @@ class DartGenerator : public BaseGenerator { continue; code += " final int " + MakeCamel(field.name, false) + "Offset"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += " = _" + MakeCamel(field.name, false) + "?.isNotEmpty == true\n"; code += " ? fbBuilder.writeList"; @@ -833,7 +832,7 @@ class DartGenerator : public BaseGenerator { code += ")"; } code += "\n : null;\n"; - } else if (field.value.type.base_type == BASE_TYPE_STRING) { + } else if (IsString(field.value.type)) { code += " = fbBuilder.writeString(_" + MakeCamel(field.name, false) + ");\n"; } else { diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_fbs.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_fbs.cpp index e6c8a4a8..35c1a7d4 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_fbs.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_fbs.cpp @@ -136,7 +136,7 @@ std::string GenerateFBS(const Parser &parser, const std::string &file_name) { GenComment(field.doc_comment, &schema, nullptr, " "); schema += " " + field.name + ":" + GenType(field.value.type); if (field.value.constant != "0") schema += " = " + field.value.constant; - if (field.required) schema += " (required)"; + if (field.IsRequired()) schema += " (required)"; schema += ";\n"; } } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_go.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_go.cpp index a1a277ca..867f4023 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_go.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_go.cpp @@ -255,17 +255,30 @@ class GoGenerator : public BaseGenerator { void NewRootTypeFromBuffer(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; - - code += "func GetRootAs"; - code += struct_def.name; - code += "(buf []byte, offset flatbuffers.UOffsetT) "; - code += "*" + struct_def.name + ""; - code += " {\n"; - code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; - code += "\tx := &" + struct_def.name + "{}\n"; - code += "\tx.Init(buf, n+offset)\n"; - code += "\treturn x\n"; - code += "}\n\n"; + std::string size_prefix[] = { "", "SizePrefixed" }; + + for (int i = 0; i < 2; i++) { + code += "func Get" + size_prefix[i] + "RootAs"; + code += struct_def.name; + code += "(buf []byte, offset flatbuffers.UOffsetT) "; + code += "*" + struct_def.name + ""; + code += " {\n"; + if (i == 0) { + code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; + } else { + code += + "\tn := " + "flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])\n"; + } + code += "\tx := &" + struct_def.name + "{}\n"; + if (i == 0) { + code += "\tx.Init(buf, n+offset)\n"; + } else { + code += "\tx.Init(buf, n+offset+flatbuffers.SizeUint32)\n"; + } + code += "\treturn x\n"; + code += "}\n\n"; + } } // Initialize an existing object with other data, to avoid an allocation. @@ -458,7 +471,7 @@ class GoGenerator : public BaseGenerator { "(a + flatbuffers.UOffsetT(j*" + NumToString(InlineSize(vectortype)) + "))"); code += "\n\t}\n"; - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code += "\treturn nil\n"; } else if (vectortype.base_type == BASE_TYPE_BOOL) { code += "\treturn false\n"; @@ -640,7 +653,7 @@ class GoGenerator : public BaseGenerator { default: FLATBUFFERS_ASSERT(0); } } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { GetVectorLen(struct_def, field, code_ptr); if (field.value.type.element == BASE_TYPE_UCHAR) { GetUByteSlice(struct_def, field, code_ptr); @@ -708,7 +721,7 @@ class GoGenerator : public BaseGenerator { } else { MutateScalarFieldOfTable(struct_def, field, code_ptr); } - } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(field.value.type)) { if (IsScalar(field.value.type.element)) { MutateElementOfVectorOfNonStruct(struct_def, field, code_ptr); } @@ -726,7 +739,7 @@ class GoGenerator : public BaseGenerator { auto offset = it - struct_def.fields.vec.begin(); BuildFieldOfTable(struct_def, field, offset, code_ptr); - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { BuildVectorOfTable(struct_def, field, code_ptr); } } @@ -869,10 +882,10 @@ class GoGenerator : public BaseGenerator { std::string offset = MakeCamel(field.name, false) + "Offset"; - if (field.value.type.base_type == BASE_TYPE_STRING) { + if (IsString(field.value.type)) { code += "\t" + offset + " := builder.CreateString(t." + MakeCamel(field.name) + ")\n"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR && + } else if (IsVector(field.value.type) && field.value.type.element == BASE_TYPE_UCHAR && field.value.type.enum_def == nullptr) { code += "\t" + offset + " := flatbuffers.UOffsetT(0)\n"; @@ -880,7 +893,7 @@ class GoGenerator : public BaseGenerator { code += "\t\t" + offset + " = builder.CreateByteString(t." + MakeCamel(field.name) + ")\n"; code += "\t}\n"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(field.value.type)) { code += "\t" + offset + " := flatbuffers.UOffsetT(0)\n"; code += "\tif t." + MakeCamel(field.name) + " != nil {\n"; std::string length = MakeCamel(field.name, false) + "Length"; @@ -984,21 +997,23 @@ class GoGenerator : public BaseGenerator { continue; code += "\tt." + field_name_camel + " = rcv." + field_name_camel + "()\n"; - } else if (field.value.type.base_type == BASE_TYPE_STRING) { + } else if (IsString(field.value.type)) { code += "\tt." + field_name_camel + " = string(rcv." + field_name_camel + "())\n"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR && + } else if (IsVector(field.value.type) && field.value.type.element == BASE_TYPE_UCHAR && field.value.type.enum_def == nullptr) { code += "\tt." + field_name_camel + " = rcv." + field_name_camel + "Bytes()\n"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(field.value.type)) { code += "\t" + length + " := rcv." + field_name_camel + "Length()\n"; code += "\tt." + field_name_camel + " = make(" + NativeType(field.value.type) + ", " + length + ")\n"; code += "\tfor j := 0; j < " + length + "; j++ {\n"; if (field.value.type.element == BASE_TYPE_STRUCT) { - code += "\t\tx := " + field.value.type.struct_def->name + "{}\n"; + code += "\t\tx := " + + WrapInNameSpaceAndTrack(*field.value.type.struct_def) + + "{}\n"; code += "\t\trcv." + field_name_camel + "(&x, j)\n"; } code += "\t\tt." + field_name_camel + "[j] = "; @@ -1225,9 +1240,9 @@ class GoGenerator : public BaseGenerator { } else { return GetEnumTypeName(*type.enum_def); } - } else if (type.base_type == BASE_TYPE_STRING) { + } else if (IsString(type)) { return "string"; - } else if (type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(type)) { return "[]" + NativeType(type.VectorType()); } else if (type.base_type == BASE_TYPE_STRUCT) { return "*" + WrapInNameSpaceAndTrack(type.struct_def->defined_namespace, diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_grpc.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_grpc.cpp index d1a71170..9aea745d 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_grpc.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_grpc.cpp @@ -24,8 +24,8 @@ #include "src/compiler/go_generator.h" #include "src/compiler/java_generator.h" #include "src/compiler/python_generator.h" -#include "src/compiler/python_private_generator.h" #include "src/compiler/swift_generator.h" +#include "src/compiler/ts_generator.h" #if defined(_MSC_VER) # pragma warning(push) @@ -59,12 +59,22 @@ class FlatBufMethod : public grpc_generator::Method { std::string name() const { return method_->name; } + // TODO: This method need to incorporate namespace for C++ side. Other + // language bindings simply don't use this method. std::string GRPCType(const StructDef &sd) const { return "flatbuffers::grpc::Message<" + sd.name + ">"; } + std::vector get_input_namespace_parts() const { + return (*method_->request).defined_namespace->components; + } + std::string get_input_type_name() const { return (*method_->request).name; } + std::vector get_output_namespace_parts() const { + return (*method_->response).defined_namespace->components; + } + std::string get_output_type_name() const { return (*method_->response).name; } bool get_module_and_message_path_input(grpc::string * /*str*/, @@ -111,7 +121,14 @@ class FlatBufService : public grpc_generator::Service { return service_->doc_comment; } + std::vector namespace_parts() const { + return service_->defined_namespace->components; + } + std::string name() const { return service_->name; } + bool is_internal() const { + return service_->Definition::attributes.Lookup("private") ? true : false; + } int method_count() const { return static_cast(service_->calls.vec.size()); @@ -128,7 +145,12 @@ class FlatBufService : public grpc_generator::Service { class FlatBufPrinter : public grpc_generator::Printer { public: - FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {} + FlatBufPrinter(std::string *str, const char indentation_type) + : str_(str), + escape_char_('$'), + indent_(0), + indentation_size_(2), + indentation_type_(indentation_type) {} void Print(const std::map &vars, const char *string_template) { @@ -155,7 +177,7 @@ class FlatBufPrinter : public grpc_generator::Printer { // Add this string, but for each part separated by \n, add indentation. for (;;) { // Current indentation. - str_->insert(str_->end(), indent_ * 2, ' '); + str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_); // See if this contains more than one line. const char *lf = strchr(s, '\n'); if (lf) { @@ -169,6 +191,11 @@ class FlatBufPrinter : public grpc_generator::Printer { } } + void SetIndentationSize(const int size) { + FLATBUFFERS_ASSERT(str_->empty()); + indentation_size_ = size; + } + void Indent() { indent_++; } void Outdent() { @@ -180,6 +207,8 @@ class FlatBufPrinter : public grpc_generator::Printer { std::string *str_; char escape_char_; int indent_; + int indentation_size_; + char indentation_type_; }; class FlatBufFile : public grpc_generator::File { @@ -189,7 +218,8 @@ class FlatBufFile : public grpc_generator::File { kLanguageCpp, kLanguageJava, kLanguagePython, - kLanguageSwift + kLanguageSwift, + kLanguageTS }; FlatBufFile(const Parser &parser, const std::string &file_name, @@ -241,6 +271,9 @@ class FlatBufFile : public grpc_generator::File { case kLanguageSwift: { return ""; } + case kLanguageTS: { + return ""; + } } return ""; } @@ -255,8 +288,9 @@ class FlatBufFile : public grpc_generator::File { } std::unique_ptr CreatePrinter( - std::string *str) const { - return std::unique_ptr(new FlatBufPrinter(str)); + std::string *str, const char indentation_type = ' ') const { + return std::unique_ptr( + new FlatBufPrinter(str, indentation_type)); } private: @@ -377,6 +411,45 @@ bool GenerateJavaGRPC(const Parser &parser, const std::string &path, return JavaGRPCGenerator(parser, path, file_name).generate(); } +class PythonGRPCGenerator : public flatbuffers::BaseGenerator { + private: + CodeWriter code_; + + public: + PythonGRPCGenerator(const Parser &parser, const std::string &filename) + : BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {} + + bool generate() { + code_.Clear(); + code_ += + "# Generated by the gRPC Python protocol compiler plugin. " + "DO NOT EDIT!\n"; + code_ += "import grpc\n"; + + FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython); + + for (int i = 0; i < file.service_count(); i++) { + auto service = file.service(i); + code_ += grpc_python_generator::Generate(&file, service.get()); + } + const auto final_code = code_.ToString(); + const auto filename = GenerateFileName(); + return SaveFile(filename.c_str(), final_code, false); + } + + std::string GenerateFileName() { + std::string namespace_dir; + auto &namespaces = parser_.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + if (it != namespaces.begin()) namespace_dir += kPathSeparator; + namespace_dir += *it; + } + std::string grpc_py_filename = namespace_dir; + if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator; + return grpc_py_filename + file_name_ + "_grpc_fb.py"; + } +}; + bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/, const std::string &file_name) { int nservices = 0; @@ -386,26 +459,7 @@ bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/, } if (!nservices) return true; - grpc_python_generator::GeneratorConfiguration config; - config.grpc_package_root = "grpc"; - config.beta_package_root = "grpc.beta"; - config.import_prefix = ""; - - FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguagePython); - - grpc_python_generator::PrivateGenerator generator(config, &fbfile); - - std::string code = generator.GetGrpcServices(); - std::string namespace_dir; - auto &namespaces = parser.namespaces_.back()->components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (it != namespaces.begin()) namespace_dir += kPathSeparator; - namespace_dir += *it; - } - - std::string grpc_py_filename = - namespace_dir + kPathSeparator + file_name + "_grpc_fb.py"; - return flatbuffers::SaveFile(grpc_py_filename.c_str(), code, false); + return PythonGRPCGenerator(parser, file_name).generate(); } class SwiftGRPCGenerator : public flatbuffers::BaseGenerator { @@ -448,6 +502,54 @@ bool GenerateSwiftGRPC(const Parser &parser, const std::string &path, return SwiftGRPCGenerator(parser, path, file_name).generate(); } +class TSGRPCGenerator : public flatbuffers::BaseGenerator { + private: + CodeWriter code_; + + public: + TSGRPCGenerator(const Parser &parser, const std::string &path, + const std::string &filename) + : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {} + + bool generate() { + code_.Clear(); + FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS); + + for (int i = 0; i < file.service_count(); i++) { + auto service = file.service(i); + code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_); + const auto ts_name = GeneratedFileName(path_, file_name_); + if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false; + + code_.Clear(); + code_ += grpc_ts_generator::GenerateInterface(&file, service.get(), + file_name_); + const auto ts_interface_name = GeneratedFileName(path_, file_name_, true); + if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false)) + return false; + } + return true; + } + + static std::string GeneratedFileName(const std::string &path, + const std::string &file_name, + const bool is_interface = false) { + if (is_interface) return path + file_name + "_grpc.d.ts"; + return path + file_name + "_grpc.js"; + } +}; + +bool GenerateTSGRPC(const Parser &parser, const std::string &path, + const std::string &file_name) { + int nservices = 0; + for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); + ++it) { + if (!(*it)->generated) nservices++; + } + if (!nservices) return true; + return TSGRPCGenerator(parser, path, file_name).generate(); +} + } // namespace flatbuffers #if defined(_MSC_VER) diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_java.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_java.cpp index 9679a1aa..cfd3a55c 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_java.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_java.cpp @@ -60,7 +60,7 @@ class JavaGenerator : public BaseGenerator { one_file_code += enumcode; } else { if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode, - false)) + /* needs_includes= */ false)) return false; } } @@ -76,14 +76,14 @@ class JavaGenerator : public BaseGenerator { one_file_code += declcode; } else { if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode, - true)) + /* needs_includes= */ true)) return false; } } if (parser_.opts.one_file) { return SaveType(file_name_, *parser_.current_namespace_, one_file_code, - true); + /* needs_includes= */ true); } return true; } @@ -112,11 +112,9 @@ class JavaGenerator : public BaseGenerator { if (parser_.opts.java_checkerframework) { code += "\nimport org.checkerframework.dataflow.qual.Pure;\n"; } - code += "\n@SuppressWarnings(\"unused\")\n"; - } - if (parser_.opts.gen_generated) { - code += "\n@javax.annotation.Generated(value=\"flatc\")\n"; + code += "\n"; } + code += classcode; if (!namespace_name.empty()) code += ""; auto filename = NamespaceDir(ns) + defname + ".java"; @@ -226,9 +224,7 @@ class JavaGenerator : public BaseGenerator { // Cast statements for mutator method parameters. // In Java, parameters representing unsigned numbers need to be cast down to // their respective type. For example, a long holding an unsigned int value - // would be cast down to int before being put onto the buffer. In C#, one cast - // directly cast an Enum to its underlying type, which is essential before - // putting it onto the buffer. + // would be cast down to int before being put onto the buffer. std::string SourceCast(const Type &type, bool castFromDest) const { if (IsSeries(type)) { return SourceCast(type.VectorType(), castFromDest); @@ -268,21 +264,26 @@ class JavaGenerator : public BaseGenerator { std::string GenDefaultValue(const FieldDef &field) const { auto &value = field.value; + auto constant = field.IsScalarOptional() ? "0" : value.constant; auto longSuffix = "L"; switch (value.type.base_type) { - case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true"; + case BASE_TYPE_BOOL: return constant == "0" ? "false" : "true"; case BASE_TYPE_ULONG: { // Converts the ulong into its bits signed equivalent - uint64_t defaultValue = StringToUInt(value.constant.c_str()); + uint64_t defaultValue = StringToUInt(constant.c_str()); return NumToString(static_cast(defaultValue)) + longSuffix; } case BASE_TYPE_UINT: - case BASE_TYPE_LONG: return value.constant + longSuffix; + case BASE_TYPE_LONG: return constant + longSuffix; default: - if (IsFloat(value.type.base_type)) + if (IsFloat(value.type.base_type)) { + if (field.IsScalarOptional()) { + return value.type.base_type == BASE_TYPE_DOUBLE ? "0.0" : "0f"; + } return JavaFloatGen.GenFloatConstant(field); - else - return value.constant; + } else { + return constant; + } } } @@ -305,7 +306,6 @@ class JavaGenerator : public BaseGenerator { if (enum_def.attributes.Lookup("private")) { // For Java, we leave the enum unmarked to indicate package-private - // For C# we mark the enum as internal } else { code += "public "; } @@ -316,7 +316,7 @@ class JavaGenerator : public BaseGenerator { auto &ev = **it; GenComment(ev.doc_comment, code_ptr, &comment_config, " "); code += " public static final "; - code += GenTypeBasic(enum_def.underlying_type); + code += GenTypeBasic(DestinationType(enum_def.underlying_type, false)); code += " "; code += ev.name + " = "; code += enum_def.ToString(ev); @@ -324,7 +324,6 @@ class JavaGenerator : public BaseGenerator { } // Generate a generate string table for enum values. - // We do not do that for C# where this functionality is native. // Problem is, if values are very sparse that could generate really big // tables. Ideally in that case we generate a map lookup instead, but for // the moment we simply don't output a table at all. @@ -354,10 +353,7 @@ class JavaGenerator : public BaseGenerator { } // Close the class - code += "}"; - // Java does not need the closing semi-colon on class definitions. - code += ""; - code += "\n\n"; + code += "}\n\n"; } // Returns the function name that is able to read a value of the given type. @@ -526,7 +522,7 @@ class JavaGenerator : public BaseGenerator { key_getter += "int tableOffset = "; key_getter += "__indirect(vectorLocation + 4 * (start + middle)"; key_getter += ", bb);\n "; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { key_getter += "int comp = "; key_getter += "compareStrings("; key_getter += GenOffsetGetter(key_field); @@ -543,7 +539,7 @@ class JavaGenerator : public BaseGenerator { std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const { std::string key_getter = ""; auto data_buffer = "_bb"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { key_getter += " return "; key_getter += ""; key_getter += "compareStrings("; @@ -575,14 +571,17 @@ class JavaGenerator : public BaseGenerator { // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default; // } GenComment(struct_def.doc_comment, code_ptr, &comment_config); + + if (parser_.opts.gen_generated) { + code += "@javax.annotation.Generated(value=\"flatc\")\n"; + } + code += "@SuppressWarnings(\"unused\")\n"; if (struct_def.attributes.Lookup("private")) { // For Java, we leave the struct unmarked to indicate package-private - // For C# we mark the struct as internal } else { code += "public "; } - code += "final "; - code += "class " + struct_def.name; + code += "final class " + struct_def.name; code += " extends "; code += struct_def.fixed ? "Struct" : "Table"; code += " {\n"; @@ -592,7 +591,7 @@ class JavaGenerator : public BaseGenerator { // Force compile time error if not using the same version runtime. code += " public static void ValidateVersion() {"; code += " Constants."; - code += "FLATBUFFERS_1_12_0(); "; + code += "FLATBUFFERS_2_0_0(); "; code += "}\n"; // Generate a special accessor for the table that when used as the root @@ -650,7 +649,7 @@ class JavaGenerator : public BaseGenerator { std::string src_cast = SourceCast(field.value.type); std::string method_start = " public " + - (field.required ? "" : GenNullableAnnotation(field.value.type)) + + (field.IsRequired() ? "" : GenNullableAnnotation(field.value.type)) + GenPureAnnotation(field.value.type) + type_name_dest + optional + " " + MakeCamel(field.name, false); std::string obj = "obj"; @@ -669,7 +668,7 @@ class JavaGenerator : public BaseGenerator { code += MakeCamel(field.name, false); code += "(new "; code += type_name + "()); }\n"; - } else if (field.value.type.base_type == BASE_TYPE_VECTOR && + } else if (IsVector(field.value.type) && field.value.type.element == BASE_TYPE_STRUCT) { // Accessors for vectors of structs also take accessor objects, this // generates a variant without that argument. @@ -678,6 +677,7 @@ class JavaGenerator : public BaseGenerator { code += "(new " + type_name + "(), j); }\n"; } + if (field.IsScalarOptional()) { code += GenOptionalScalarCheck(field); } std::string getter = dest_cast + GenGetter(field.value.type); code += method_start; std::string default_cast = ""; @@ -772,7 +772,7 @@ class JavaGenerator : public BaseGenerator { } code += member_suffix; code += "}\n"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += " public int " + MakeCamel(field.name, false); code += "Length"; code += "()"; @@ -813,7 +813,7 @@ class JavaGenerator : public BaseGenerator { } } // Generate the accessors for vector of structs with vector access object - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { std::string vector_type_name; const auto &element_base_type = field.value.type.VectorType().base_type; if (IsScalar(element_base_type)) { @@ -842,15 +842,15 @@ class JavaGenerator : public BaseGenerator { code += "bb) : null" + member_suffix + "}\n"; } // Generate a ByteBuffer accessor for strings & vectors of scalars. - if ((field.value.type.base_type == BASE_TYPE_VECTOR && + if ((IsVector(field.value.type) && IsScalar(field.value.type.VectorType().base_type)) || - field.value.type.base_type == BASE_TYPE_STRING) { + IsString(field.value.type)) { code += " public ByteBuffer "; code += MakeCamel(field.name, false); code += "AsByteBuffer() { return "; code += "__vector_as_bytebuffer("; code += NumToString(field.value.offset) + ", "; - code += NumToString(field.value.type.base_type == BASE_TYPE_STRING + code += NumToString(IsString(field.value.type) ? 1 : InlineSize(field.value.type.VectorType())); code += "); }\n"; @@ -859,7 +859,7 @@ class JavaGenerator : public BaseGenerator { code += "InByteBuffer(ByteBuffer _bb) { return "; code += "__vector_in_bytebuffer(_bb, "; code += NumToString(field.value.offset) + ", "; - code += NumToString(field.value.type.base_type == BASE_TYPE_STRING + code += NumToString(IsString(field.value.type) ? 1 : InlineSize(field.value.type.VectorType())); code += "); }\n"; @@ -1036,7 +1036,7 @@ class JavaGenerator : public BaseGenerator { code += SourceCastBasic(field.value.type); code += GenDefaultValue(field); code += "); }\n"; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { auto vector_type = field.value.type.VectorType(); auto alignment = InlineAlignment(vector_type); auto elem_size = InlineSize(vector_type); @@ -1095,7 +1095,7 @@ class JavaGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (!field.deprecated && field.required) { + if (!field.deprecated && field.IsRequired()) { code += " builder.required(o, "; code += NumToString(field.value.offset); code += "); // " + field.name + "\n"; @@ -1132,7 +1132,7 @@ class JavaGenerator : public BaseGenerator { code += "int vectorLocation, "; code += GenTypeNameDest(key_field->value.type); code += " key, ByteBuffer bb) {\n"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { code += " byte[] byteKey = "; code += "key.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n"; } @@ -1161,6 +1161,13 @@ class JavaGenerator : public BaseGenerator { code += "\n\n"; } + std::string GenOptionalScalarCheck(FieldDef &field) const { + if (!field.IsScalarOptional()) return ""; + return " public boolean has" + MakeCamel(field.name, true) + + "() { return 0 != __offset(" + NumToString(field.value.offset) + + "); }\n"; + } + void GenVectorAccessObject(StructDef &struct_def, std::string *code_ptr) const { auto &code = *code_ptr; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_js_ts.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_js_ts.cpp deleted file mode 100644 index 030c5526..00000000 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_js_ts.cpp +++ /dev/null @@ -1,1403 +0,0 @@ -/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// independent from idl_parser, since this code is not needed for most clients -#include -#include -#include - -#include "flatbuffers/code_generators.h" -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" -#include "flatbuffers/util.h" - -namespace flatbuffers { - -struct JsTsLanguageParameters { - IDLOptions::Language language; - std::string file_extension; -}; - -struct ReexportDescription { - std::string symbol; - std::string source_namespace; - std::string target_namespace; -}; - -enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 }; - -const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) { - static JsTsLanguageParameters js_language_parameters[] = { - { - IDLOptions::kJs, - ".js", - }, - { - IDLOptions::kTs, - ".ts", - }, - }; - - if (lang == IDLOptions::kJs) { - return js_language_parameters[0]; - } else { - FLATBUFFERS_ASSERT(lang == IDLOptions::kTs); - return js_language_parameters[1]; - } -} - -namespace jsts { -// Iterate through all definitions we haven't generate code for (enums, structs, -// and tables) and output them to a single file. -class JsTsGenerator : public BaseGenerator { - public: - typedef std::unordered_set imported_fileset; - typedef std::unordered_multimap - reexport_map; - - JsTsGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) - : BaseGenerator(parser, path, file_name, "", ".", - parser.opts.lang == IDLOptions::kJs ? "js" : "ts"), - lang_(GetJsLangParams(parser_.opts.lang)) {} - // Iterate through all definitions we haven't generate code for (enums, - // structs, and tables) and output them to a single file. - bool generate() { - imported_fileset imported_files; - reexport_map reexports; - - std::string enum_code, struct_code, import_code, exports_code, code; - generateEnums(&enum_code, &exports_code, reexports); - generateStructs(&struct_code, &exports_code, imported_files); - generateImportDependencies(&import_code, imported_files); - generateReexports(&import_code, reexports, imported_files); - - code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n"; - - // Generate code for all the namespace declarations. - GenNamespaces(&code, &exports_code); - - // Output the main declaration code from above. - code += import_code; - - code += enum_code; - code += struct_code; - - if (lang_.language == IDLOptions::kJs && !exports_code.empty() && - !parser_.opts.skip_js_exports) { - if (parser_.opts.use_ES6_js_export_format) - code += "// Exports for ECMAScript6 Modules\n"; - else - code += "// Exports for Node.js and RequireJS\n"; - code += exports_code; - } - - return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(), - code, false); - } - - private: - JsTsLanguageParameters lang_; - - // Generate code for imports - void generateImportDependencies(std::string *code_ptr, - const imported_fileset &imported_files) { - std::string &code = *code_ptr; - for (auto it = imported_files.begin(); it != imported_files.end(); ++it) { - const auto &file = *it; - const auto basename = - flatbuffers::StripPath(flatbuffers::StripExtension(file)); - if (basename != file_name_) { code += GenPrefixedImport(file, basename); } - } - } - - // Generate reexports, which might not have been explicitly imported using the - // "export import" trick - void generateReexports(std::string *code_ptr, const reexport_map &reexports, - imported_fileset imported_files) { - if (!parser_.opts.reexport_ts_modules || - lang_.language != IDLOptions::kTs) { - return; - } - - std::string &code = *code_ptr; - for (auto it = reexports.begin(); it != reexports.end(); ++it) { - const auto &file = *it; - const auto basename = - flatbuffers::StripPath(flatbuffers::StripExtension(file.first)); - if (basename != file_name_) { - if (imported_files.find(file.first) == imported_files.end()) { - code += GenPrefixedImport(file.first, basename); - imported_files.emplace(file.first); - } - - code += "export namespace " + file.second.target_namespace + " { \n"; - code += "export import " + file.second.symbol + " = "; - code += GenFileNamespacePrefix(file.first) + "." + - file.second.source_namespace + "." + file.second.symbol + - "; }\n"; - } - } - } - - // Generate code for all enums. - void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr, - reexport_map &reexports) { - for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); - ++it) { - auto &enum_def = **it; - GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, false); - GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true); - } - } - - // Generate code for all structs. - void generateStructs(std::string *decl_code_ptr, - std::string *exports_code_ptr, - imported_fileset &imported_files) { - for (auto it = parser_.structs_.vec.begin(); - it != parser_.structs_.vec.end(); ++it) { - auto &struct_def = **it; - GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr, - imported_files); - } - } - void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) { - if (lang_.language == IDLOptions::kTs && - parser_.opts.skip_flatbuffers_import) { - return; - } - - std::set namespaces; - - for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end(); - ++it) { - std::string namespace_so_far; - - // Gather all parent namespaces for this namespace - for (auto component = (*it)->components.begin(); - component != (*it)->components.end(); ++component) { - if (!namespace_so_far.empty()) { namespace_so_far += '.'; } - namespace_so_far += *component; - namespaces.insert(namespace_so_far); - } - } - - // Make sure parent namespaces come before child namespaces - std::vector sorted_namespaces(namespaces.begin(), - namespaces.end()); - std::sort(sorted_namespaces.begin(), sorted_namespaces.end()); - - // Emit namespaces in a form that Closure Compiler can optimize - std::string &code = *code_ptr; - std::string &exports = *exports_ptr; - for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end(); - ++it) { - if (lang_.language == IDLOptions::kTs) { - if (it->find('.') == std::string::npos) { - code += "import { flatbuffers } from \"./flatbuffers\"\n"; - break; - } - } else { - code += "/**\n * @const\n * @namespace\n */\n"; - if (it->find('.') == std::string::npos) { - code += "var "; - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n"; - } else if (parser_.opts.use_ES6_js_export_format) { - exports += "export {" + *it + "};\n"; - } else { - exports += "this." + *it + " = " + *it + ";\n"; - } - } - code += *it + " = " + *it + " || {};\n\n"; - } - } - } - - // Generate a documentation comment, if available. - static void GenDocComment(const std::vector &dc, - std::string *code_ptr, - const std::string &extra_lines, - const char *indent = nullptr) { - if (dc.empty() && extra_lines.empty()) { - // Don't output empty comment blocks with 0 lines of comment content. - return; - } - - std::string &code = *code_ptr; - if (indent) code += indent; - code += "/**\n"; - for (auto it = dc.begin(); it != dc.end(); ++it) { - if (indent) code += indent; - code += " *" + *it + "\n"; - } - if (!extra_lines.empty()) { - if (!dc.empty()) { - if (indent) code += indent; - code += " *\n"; - } - if (indent) code += indent; - std::string::size_type start = 0; - for (;;) { - auto end = extra_lines.find('\n', start); - if (end != std::string::npos) { - code += " * " + extra_lines.substr(start, end - start) + "\n"; - start = end + 1; - } else { - code += " * " + extra_lines.substr(start) + "\n"; - break; - } - } - } - if (indent) code += indent; - code += " */\n"; - } - - static void GenDocComment(std::string *code_ptr, - const std::string &extra_lines) { - GenDocComment(std::vector(), code_ptr, extra_lines); - } - - std::string GenTypeAnnotation(AnnotationType annotation_type, - const std::string &type_name, - const std::string &arg_name, - bool include_newline = true) { - std::string result = ""; - switch (annotation_type) { - case kParam: { - result += "@param"; - break; - } - case kType: { - if (lang_.language != IDLOptions::kTs) { - result += "@type"; - } else { - return ""; - } - break; - } - case kReturns: { - result += "@returns"; - break; - } - } - switch (lang_.language) { - case IDLOptions::kTs: { - result += " " + type_name; - break; - } - default: { - result += " {" + type_name + "}"; - } - } - if (!arg_name.empty()) { result += " " + arg_name; } - if (include_newline) { result += "\n"; } - - return result; - } - - // Generate an enum declaration and an enum string lookup table. - void GenEnum(EnumDef &enum_def, std::string *code_ptr, - std::string *exports_ptr, reexport_map &reexports, - bool reverse) { - if (enum_def.generated) return; - if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME. - std::string &code = *code_ptr; - std::string &exports = *exports_ptr; - GenDocComment(enum_def.doc_comment, code_ptr, - reverse ? "@enum {string}" : "@enum {number}"); - std::string ns = GetNameSpace(enum_def); - std::string enum_def_name = enum_def.name + (reverse ? "Name" : ""); - if (lang_.language == IDLOptions::kTs) { - if (!ns.empty()) { code += "export namespace " + ns + "{\n"; } - code += "export enum " + enum_def.name + "{\n"; - } else { - if (enum_def.defined_namespace->components.empty()) { - code += "var "; - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportSymbol('" + enum_def_name + "', " + - enum_def.name + ");\n"; - } else if (parser_.opts.use_ES6_js_export_format) { - exports += "export {" + enum_def_name + "};\n"; - } else { - exports += "this." + enum_def_name + " = " + enum_def_name + ";\n"; - } - } - code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n"; - } - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { - auto &ev = **it; - if (!ev.doc_comment.empty()) { - if (it != enum_def.Vals().begin()) { code += '\n'; } - GenDocComment(ev.doc_comment, code_ptr, "", " "); - } - - // Generate mapping between EnumName: EnumValue(int) - if (reverse) { - code += " '" + enum_def.ToString(ev) + "'"; - code += lang_.language == IDLOptions::kTs ? "= " : ": "; - code += "'" + ev.name + "'"; - } else { - code += " " + ev.name; - code += lang_.language == IDLOptions::kTs ? "= " : ": "; - code += enum_def.ToString(ev); - } - - code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n"; - - if (ev.union_type.struct_def) { - ReexportDescription desc = { ev.name, - GetNameSpace(*ev.union_type.struct_def), - GetNameSpace(enum_def) }; - reexports.insert( - std::make_pair(ev.union_type.struct_def->file, std::move(desc))); - } - } - - if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; } - code += "};\n\n"; - } - - static std::string GenType(const Type &type) { - switch (type.base_type) { - case BASE_TYPE_BOOL: - case BASE_TYPE_CHAR: return "Int8"; - case BASE_TYPE_UTYPE: - case BASE_TYPE_UCHAR: return "Uint8"; - case BASE_TYPE_SHORT: return "Int16"; - case BASE_TYPE_USHORT: return "Uint16"; - case BASE_TYPE_INT: return "Int32"; - case BASE_TYPE_UINT: return "Uint32"; - case BASE_TYPE_LONG: return "Int64"; - case BASE_TYPE_ULONG: return "Uint64"; - case BASE_TYPE_FLOAT: return "Float32"; - case BASE_TYPE_DOUBLE: return "Float64"; - case BASE_TYPE_STRING: return "String"; - case BASE_TYPE_VECTOR: return GenType(type.VectorType()); - case BASE_TYPE_STRUCT: return type.struct_def->name; - default: return "Table"; - } - } - - std::string GenGetter(const Type &type, const std::string &arguments) { - switch (type.base_type) { - case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments; - case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments; - case BASE_TYPE_UNION: return GenBBAccess() + ".__union" + arguments; - case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments); - default: { - auto getter = - GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments; - if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; } - if (type.enum_def) { - getter = "/** " + - GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "", - false) + - " */ (" + getter + ")"; - } - return getter; - } - } - } - - std::string GenBBAccess() { - return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb"; - } - - std::string GenDefaultValue(const Value &value, const std::string &context) { - if (value.type.enum_def) { - if (auto val = value.type.enum_def->FindByValue(value.constant)) { - if (lang_.language == IDLOptions::kTs) { - return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def), - value.type.enum_def->file) + - "." + val->name; - } else { - return WrapInNameSpace(*value.type.enum_def) + "." + val->name; - } - } else { - return "/** " + - GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def), - "", false) + - "} */ (" + value.constant + ")"; - } - } - - switch (value.type.base_type) { - case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true"; - - case BASE_TYPE_STRING: return "null"; - - case BASE_TYPE_LONG: - case BASE_TYPE_ULONG: { - int64_t constant = StringToInt(value.constant.c_str()); - return context + ".createLong(" + - NumToString(static_cast(constant)) + ", " + - NumToString(static_cast(constant >> 32)) + ")"; - } - - default: return value.constant; - } - } - - std::string GenTypeName(const Type &type, bool input, - bool allowNull = false) { - if (!input) { - if (type.base_type == BASE_TYPE_STRING || - type.base_type == BASE_TYPE_STRUCT) { - std::string name; - if (type.base_type == BASE_TYPE_STRING) { - name = "string|Uint8Array"; - } else { - name = WrapInNameSpace(*type.struct_def); - } - return (allowNull) ? (name + "|null") : (name); - } - } - - switch (type.base_type) { - case BASE_TYPE_BOOL: return "boolean"; - case BASE_TYPE_LONG: - case BASE_TYPE_ULONG: return "flatbuffers.Long"; - default: - if (IsScalar(type.base_type)) { - if (type.enum_def) { return WrapInNameSpace(*type.enum_def); } - return "number"; - } - return "flatbuffers.Offset"; - } - } - - // Returns the method name for use with add/put calls. - static std::string GenWriteMethod(const Type &type) { - // Forward to signed versions since unsigned versions don't exist - switch (type.base_type) { - case BASE_TYPE_UTYPE: - case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR)); - case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT)); - case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT)); - case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG)); - default: break; - } - - return IsScalar(type.base_type) ? MakeCamel(GenType(type)) - : (IsStruct(type) ? "Struct" : "Offset"); - } - - template static std::string MaybeAdd(T value) { - return value != 0 ? " + " + NumToString(value) : ""; - } - - template static std::string MaybeScale(T value) { - return value != 1 ? " * " + NumToString(value) : ""; - } - - static std::string GenFileNamespacePrefix(const std::string &file) { - return "NS" + std::to_string(HashFnv1a(file.c_str())); - } - - std::string GenPrefixedImport(const std::string &full_file_name, - const std::string &base_name) { - // Either keep the include path as it was - // or use only the base_name + kGeneratedFileNamePostfix - std::string path; - if (parser_.opts.keep_include_path) { - auto it = parser_.included_files_.find(full_file_name); - FLATBUFFERS_ASSERT(it != parser_.included_files_.end()); - path = flatbuffers::StripExtension(it->second) + - parser_.opts.filename_suffix; - } else { - path = base_name + parser_.opts.filename_suffix; - } - - // Add the include prefix and make the path always relative - path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path); - path = std::string(".") + kPathSeparator + path; - - return "import * as " + GenFileNamespacePrefix(full_file_name) + - " from \"" + path + "\";\n"; - } - - // Adds a source-dependent prefix, for of import * statements. - std::string GenPrefixedTypeName(const std::string &typeName, - const std::string &file) { - const auto basename = - flatbuffers::StripPath(flatbuffers::StripExtension(file)); - if (basename == file_name_ || parser_.opts.generate_all) { - return typeName; - } - return GenFileNamespacePrefix(file) + "." + typeName; - } - - void GenStructArgs(const StructDef &struct_def, std::string *annotations, - std::string *arguments, const std::string &nameprefix) { - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (IsStruct(field.value.type)) { - // Generate arguments for a struct inside a struct. To ensure names - // don't clash, and to make it obvious these arguments are constructing - // a nested struct, prefix the name with the field name. - GenStructArgs(*field.value.type.struct_def, annotations, arguments, - nameprefix + field.name + "_"); - } else { - *annotations += - GenTypeAnnotation(kParam, GenTypeName(field.value.type, true), - nameprefix + field.name); - if (lang_.language == IDLOptions::kTs) { - *arguments += ", " + nameprefix + field.name + ": " + - GenTypeName(field.value.type, true); - } else { - *arguments += ", " + nameprefix + field.name; - } - } - } - } - - static void GenStructBody(const StructDef &struct_def, std::string *body, - const std::string &nameprefix) { - *body += " builder.prep("; - *body += NumToString(struct_def.minalign) + ", "; - *body += 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) { - *body += " builder.pad(" + NumToString(field.padding) + ");\n"; - } - if (IsStruct(field.value.type)) { - // Generate arguments for a struct inside a struct. To ensure names - // don't clash, and to make it obvious these arguments are constructing - // a nested struct, prefix the name with the field name. - GenStructBody(*field.value.type.struct_def, body, - nameprefix + field.name + "_"); - } else { - *body += " builder.write" + GenWriteMethod(field.value.type) + "("; - if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; } - *body += nameprefix + field.name + ");\n"; - } - } - } - - std::string GenerateNewExpression(const std::string &object_name) { - return "new " + object_name + - (lang_.language == IDLOptions::kTs ? "()" : ""); - } - - void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr, - std::string &code, std::string &object_name, - bool size_prefixed) { - if (!struct_def.fixed) { - GenDocComment(code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") + - GenTypeAnnotation(kParam, object_name + "=", "obj") + - GenTypeAnnotation(kReturns, object_name, "", false)); - std::string sizePrefixed("SizePrefixed"); - if (lang_.language == IDLOptions::kTs) { - code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" + - Verbose(struct_def, "As"); - code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name + - "):" + object_name + " {\n"; - } else { - code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") + - "Root" + Verbose(struct_def, "As"); - code += " = function(bb, obj) {\n"; - } - if (size_prefixed) { - code += - " bb.setPosition(bb.position() + " - "flatbuffers.SIZE_PREFIX_LENGTH);\n"; - } - code += " return (obj || " + GenerateNewExpression(object_name); - code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n"; - code += "};\n\n"; - } - } - - void GenerateFinisher(StructDef &struct_def, std::string *code_ptr, - std::string &code, std::string &object_name, - bool size_prefixed) { - if (parser_.root_struct_def_ == &struct_def) { - std::string sizePrefixed("SizePrefixed"); - GenDocComment( - code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + - GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset", false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static finish" + (size_prefixed ? sizePrefixed : "") + - Verbose(struct_def) + "Buffer"; - code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n"; - } else { - code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") + - Verbose(struct_def) + "Buffer"; - code += " = function(builder, offset) {\n"; - } - - code += " builder.finish(offset"; - if (!parser_.file_identifier_.empty()) { - code += ", '" + parser_.file_identifier_ + "'"; - } - if (size_prefixed) { - if (parser_.file_identifier_.empty()) { code += ", undefined"; } - code += ", true"; - } - code += ");\n"; - code += "};\n\n"; - } - } - - // Generate an accessor struct with constructor for a flatbuffers struct. - void GenStruct(const Parser &parser, StructDef &struct_def, - std::string *code_ptr, std::string *exports_ptr, - imported_fileset &imported_files) { - if (struct_def.generated) return; - std::string &code = *code_ptr; - std::string &exports = *exports_ptr; - - std::string object_name; - std::string object_namespace = GetNameSpace(struct_def); - - // Emit constructor - if (lang_.language == IDLOptions::kTs) { - object_name = struct_def.name; - GenDocComment(struct_def.doc_comment, code_ptr, "@constructor"); - if (!object_namespace.empty()) { - code += "export namespace " + object_namespace + "{\n"; - } - code += "export class " + struct_def.name; - code += " {\n"; - if (lang_.language != IDLOptions::kTs) { - code += " /**\n"; - code += - " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", ""); - code += " */\n"; - } - code += " bb: flatbuffers.ByteBuffer|null = null;\n"; - code += "\n"; - if (lang_.language != IDLOptions::kTs) { - code += " /**\n"; - code += " * " + GenTypeAnnotation(kType, "number", ""); - code += " */\n"; - } - code += " bb_pos:number = 0;\n"; - } else { - bool isStatement = struct_def.defined_namespace->components.empty(); - object_name = WrapInNameSpace(struct_def); - GenDocComment(struct_def.doc_comment, code_ptr, "@constructor"); - if (isStatement) { - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportSymbol('" + struct_def.name + "', " + - struct_def.name + ");\n"; - } else if (parser_.opts.use_ES6_js_export_format) { - exports += "export {" + struct_def.name + "};\n"; - } else { - exports += - "this." + struct_def.name + " = " + struct_def.name + ";\n"; - } - code += "function " + object_name; - } else { - code += object_name + " = function"; - } - code += "() {\n"; - code += " /**\n"; - code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", ""); - code += " */\n"; - code += " this.bb = null;\n"; - code += "\n"; - code += " /**\n"; - code += " * " + GenTypeAnnotation(kType, "number", ""); - code += " */\n"; - code += " this.bb_pos = 0;\n"; - code += isStatement ? "}\n\n" : "};\n\n"; - } - - // Generate the __init method that sets the field in a pre-existing - // accessor object. This is to allow object reuse. - code += "/**\n"; - code += " * " + GenTypeAnnotation(kParam, "number", "i"); - code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb"); - code += " * " + GenTypeAnnotation(kReturns, object_name, ""); - code += " */\n"; - - if (lang_.language == IDLOptions::kTs) { - code += - "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n"; - } else { - code += object_name + ".prototype.__init = function(i, bb) {\n"; - } - - code += " this.bb_pos = i;\n"; - code += " this.bb = bb;\n"; - code += " return this;\n"; - code += "};\n\n"; - - // Generate special accessors for the table that when used as the root of a - // FlatBuffer - GenerateRootAccessor(struct_def, code_ptr, code, object_name, false); - GenerateRootAccessor(struct_def, code_ptr, code, object_name, true); - - // Generate the identifier check method - if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def && - !parser_.file_identifier_.empty()) { - GenDocComment(code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") + - GenTypeAnnotation(kReturns, "boolean", "", false)); - if (lang_.language == IDLOptions::kTs) { - code += - "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean " - "{\n"; - } else { - code += object_name + ".bufferHasIdentifier = function(bb) {\n"; - } - - code += " return bb.__has_identifier('" + parser_.file_identifier_; - code += "');\n};\n\n"; - } - - // Emit field accessors - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) continue; - auto offset_prefix = - " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " + - NumToString(field.value.offset) + ");\n return offset ? "; - - // Emit a scalar field - if (IsScalar(field.value.type.base_type) || - field.value.type.base_type == BASE_TYPE_STRING) { - GenDocComment( - field.doc_comment, code_ptr, - std::string(field.value.type.base_type == BASE_TYPE_STRING - ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=", - "optionalEncoding") - : "") + - GenTypeAnnotation(kReturns, - GenTypeName(field.value.type, false, true), - "", false)); - if (lang_.language == IDLOptions::kTs) { - std::string prefix = MakeCamel(field.name, false) + "("; - if (field.value.type.base_type == BASE_TYPE_STRING) { - code += prefix + "):string|null\n"; - code += prefix + "optionalEncoding:flatbuffers.Encoding" + - "):" + GenTypeName(field.value.type, false, true) + "\n"; - code += prefix + "optionalEncoding?:any"; - } else { - code += prefix; - } - if (field.value.type.enum_def) { - code += - "):" + - GenPrefixedTypeName(GenTypeName(field.value.type, false, true), - field.value.type.enum_def->file) + - " {\n"; - - if (!parser_.opts.generate_all) { - imported_files.insert(field.value.type.enum_def->file); - } - } else { - code += "):" + GenTypeName(field.value.type, false, true) + " {\n"; - } - } else { - code += object_name + ".prototype." + MakeCamel(field.name, false); - code += " = function("; - if (field.value.type.base_type == BASE_TYPE_STRING) { - code += "optionalEncoding"; - } - code += ") {\n"; - } - - if (struct_def.fixed) { - code += - " return " + - GenGetter(field.value.type, - "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") + - ";\n"; - } else { - std::string index = "this.bb_pos + offset"; - if (field.value.type.base_type == BASE_TYPE_STRING) { - index += ", optionalEncoding"; - } - code += offset_prefix + - GenGetter(field.value.type, "(" + index + ")") + " : " + - GenDefaultValue(field.value, GenBBAccess()); - code += ";\n"; - } - } - - // Emit an object field - else { - switch (field.value.type.base_type) { - case BASE_TYPE_STRUCT: { - auto type = WrapInNameSpace(*field.value.type.struct_def); - GenDocComment( - field.doc_comment, code_ptr, - GenTypeAnnotation(kParam, type + "=", "obj") + - GenTypeAnnotation(kReturns, type + "|null", "", false)); - if (lang_.language == IDLOptions::kTs) { - type = - GenPrefixedTypeName(type, field.value.type.struct_def->file); - code += MakeCamel(field.name, false); - code += "(obj?:" + type + "):" + type + "|null {\n"; - } else { - code += - object_name + ".prototype." + MakeCamel(field.name, false); - code += " = function(obj) {\n"; - } - - if (struct_def.fixed) { - code += " return (obj || " + GenerateNewExpression(type); - code += ").__init(this.bb_pos"; - code += - MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n"; - } else { - code += offset_prefix + "(obj || " + GenerateNewExpression(type) + - ").__init("; - code += field.value.type.struct_def->fixed - ? "this.bb_pos + offset" - : GenBBAccess() + ".__indirect(this.bb_pos + offset)"; - code += ", " + GenBBAccess() + ") : null;\n"; - } - - if (lang_.language == IDLOptions::kTs && - !parser_.opts.generate_all) { - imported_files.insert(field.value.type.struct_def->file); - } - - break; - } - - case BASE_TYPE_VECTOR: { - auto vectortype = field.value.type.VectorType(); - auto vectortypename = GenTypeName(vectortype, false); - auto inline_size = InlineSize(vectortype); - auto index = GenBBAccess() + - ".__vector(this.bb_pos + offset) + index" + - MaybeScale(inline_size); - std::string args = GenTypeAnnotation(kParam, "number", "index"); - std::string ret_type; - bool is_union = false; - switch (vectortype.base_type) { - case BASE_TYPE_STRUCT: - args += GenTypeAnnotation(kParam, vectortypename + "=", "obj"); - ret_type = vectortypename; - break; - case BASE_TYPE_STRING: - args += GenTypeAnnotation( - kParam, "flatbuffers.Encoding=", "optionalEncoding"); - ret_type = vectortypename; - break; - case BASE_TYPE_UNION: - args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj"); - ret_type = "?flatbuffers.Table"; - is_union = true; - break; - default: ret_type = vectortypename; - } - GenDocComment( - field.doc_comment, code_ptr, - args + GenTypeAnnotation(kReturns, ret_type, "", false)); - if (lang_.language == IDLOptions::kTs) { - std::string prefix = MakeCamel(field.name, false); - if (is_union) { prefix += ""; } - prefix += "(index: number"; - if (is_union) { - vectortypename = "T"; - code += prefix + ", obj:T"; - } else if (vectortype.base_type == BASE_TYPE_STRUCT) { - vectortypename = GenPrefixedTypeName( - vectortypename, vectortype.struct_def->file); - code += prefix + ", obj?:" + vectortypename; - - if (!parser_.opts.generate_all) { - imported_files.insert(vectortype.struct_def->file); - } - } else if (vectortype.base_type == BASE_TYPE_STRING) { - code += prefix + "):string\n"; - code += prefix + ",optionalEncoding:flatbuffers.Encoding" + - "):" + vectortypename + "\n"; - code += prefix + ",optionalEncoding?:any"; - } else { - code += prefix; - } - code += "):" + vectortypename + "|null {\n"; - } else { - code += - object_name + ".prototype." + MakeCamel(field.name, false); - code += " = function(index"; - if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) { - code += ", obj"; - } else if (vectortype.base_type == BASE_TYPE_STRING) { - code += ", optionalEncoding"; - } - code += ") {\n"; - } - - if (vectortype.base_type == BASE_TYPE_STRUCT) { - code += offset_prefix + "(obj || " + - GenerateNewExpression(vectortypename); - code += ").__init("; - code += vectortype.struct_def->fixed - ? index - : GenBBAccess() + ".__indirect(" + index + ")"; - code += ", " + GenBBAccess() + ")"; - } else { - if (is_union) { - index = "obj, " + index; - } else if (vectortype.base_type == BASE_TYPE_STRING) { - index += ", optionalEncoding"; - } - code += offset_prefix + GenGetter(vectortype, "(" + index + ")"); - } - code += " : "; - if (field.value.type.element == BASE_TYPE_BOOL) { - code += "false"; - } else if (field.value.type.element == BASE_TYPE_LONG || - field.value.type.element == BASE_TYPE_ULONG) { - code += GenBBAccess() + ".createLong(0, 0)"; - } else if (IsScalar(field.value.type.element)) { - if (field.value.type.enum_def) { - code += "/** " + - GenTypeAnnotation( - kType, WrapInNameSpace(*field.value.type.enum_def), - "", false) + - " */ (" + field.value.constant + ")"; - } else { - code += "0"; - } - } else { - code += "null"; - } - code += ";\n"; - break; - } - - case BASE_TYPE_UNION: - GenDocComment( - field.doc_comment, code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") + - GenTypeAnnotation(kReturns, "?flatbuffers.Table", "", - false)); - if (lang_.language == IDLOptions::kTs) { - code += MakeCamel(field.name, false); - code += "(obj:T):T|null {\n"; - } else { - code += - object_name + ".prototype." + MakeCamel(field.name, false); - code += " = function(obj) {\n"; - } - - code += offset_prefix + - GenGetter(field.value.type, "(obj, this.bb_pos + offset)") + - " : null;\n"; - break; - - default: FLATBUFFERS_ASSERT(0); - } - } - code += "};\n\n"; - - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportProperty(" + object_name + ".prototype, '" + - MakeCamel(field.name, false) + "', " + object_name + - ".prototype." + MakeCamel(field.name, false) + ");\n"; - } - - // Adds the mutable scalar value to the output - if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer && - !IsUnion(field.value.type)) { - std::string annotations = GenTypeAnnotation( - kParam, GenTypeName(field.value.type, true), "value"); - GenDocComment( - code_ptr, - annotations + GenTypeAnnotation(kReturns, "boolean", "", false)); - - if (lang_.language == IDLOptions::kTs) { - std::string type; - if (field.value.type.enum_def) { - type = GenPrefixedTypeName(GenTypeName(field.value.type, true), - field.value.type.enum_def->file); - } else { - type = GenTypeName(field.value.type, true); - } - - code += "mutate_" + field.name + "(value:" + type + "):boolean {\n"; - } else { - code += object_name + ".prototype.mutate_" + field.name + - " = function(value) {\n"; - } - - code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " + - NumToString(field.value.offset) + ");\n\n"; - code += " if (offset === 0) {\n"; - code += " return false;\n"; - code += " }\n\n"; - - // special case for bools, which are treated as uint8 - code += " " + GenBBAccess() + ".write" + - MakeCamel(GenType(field.value.type)) + - "(this.bb_pos + offset, "; - if (field.value.type.base_type == BASE_TYPE_BOOL && - lang_.language == IDLOptions::kTs) { - code += "+"; - } - - code += "value);\n"; - code += " return true;\n"; - code += "};\n\n"; - - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportProperty(" + object_name + - ".prototype, 'mutate_" + field.name + "', " + object_name + - ".prototype.mutate_" + field.name + ");\n"; - } - } - - // Emit vector helpers - if (field.value.type.base_type == BASE_TYPE_VECTOR) { - // Emit a length helper - GenDocComment(code_ptr, - GenTypeAnnotation(kReturns, "number", "", false)); - if (lang_.language == IDLOptions::kTs) { - code += MakeCamel(field.name, false); - code += "Length():number {\n" + offset_prefix; - } else { - code += object_name + ".prototype." + MakeCamel(field.name, false); - code += "Length = function() {\n" + offset_prefix; - } - - code += - GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n"; - - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportProperty(" + object_name + ".prototype, '" + - MakeCamel(field.name, false) + "Length', " + object_name + - ".prototype." + MakeCamel(field.name, false) + - "Length);\n"; - } - - // For scalar types, emit a typed array helper - auto vectorType = field.value.type.VectorType(); - if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) { - GenDocComment(code_ptr, GenTypeAnnotation( - kReturns, GenType(vectorType) + "Array", - "", false)); - - if (lang_.language == IDLOptions::kTs) { - code += MakeCamel(field.name, false); - code += "Array():" + GenType(vectorType) + "Array|null {\n" + - offset_prefix; - } else { - code += object_name + ".prototype." + MakeCamel(field.name, false); - code += "Array = function() {\n" + offset_prefix; - } - - code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() + - ".bytes().buffer, " + GenBBAccess() + - ".bytes().byteOffset + " + GenBBAccess() + - ".__vector(this.bb_pos + offset), " + GenBBAccess() + - ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n"; - - if (parser_.opts.use_goog_js_export_format) { - exports += "goog.exportProperty(" + object_name + ".prototype, '" + - MakeCamel(field.name, false) + "Array', " + object_name + - ".prototype." + MakeCamel(field.name, false) + - "Array);\n"; - } - } - } - } - - // Emit a factory constructor - if (struct_def.fixed) { - std::string annotations = - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder"); - std::string arguments; - GenStructArgs(struct_def, &annotations, &arguments, ""); - GenDocComment(code_ptr, annotations + GenTypeAnnotation( - kReturns, "flatbuffers.Offset", - "", false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static create" + Verbose(struct_def) + - "(builder:flatbuffers.Builder"; - code += arguments + "):flatbuffers.Offset {\n"; - } else { - code += object_name + ".create" + Verbose(struct_def); - code += " = function(builder"; - code += arguments + ") {\n"; - } - - GenStructBody(struct_def, &code, ""); - code += " return builder.offset();\n};\n\n"; - } else { - // Generate a method to start building a new object - GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder", - "builder", false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static start" + Verbose(struct_def) + - "(builder:flatbuffers.Builder) {\n"; - } else { - code += object_name + ".start" + Verbose(struct_def); - code += " = function(builder) {\n"; - } - - code += " builder.startObject(" + - NumToString(struct_def.fields.vec.size()) + ");\n"; - code += "};\n\n"; - - // Generate a set of static methods that allow table construction - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) continue; - const auto argname = GetArgName(field); - - // Generate the field insertion method - GenDocComment( - code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + - GenTypeAnnotation(kParam, GenTypeName(field.value.type, true), - argname, false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static add" + MakeCamel(field.name); - code += "(builder:flatbuffers.Builder, " + argname + ":" + - GetArgType(field) + ") {\n"; - } else { - code += object_name + ".add" + MakeCamel(field.name); - code += " = function(builder, " + argname + ") {\n"; - } - - code += " builder.addField" + GenWriteMethod(field.value.type) + "("; - code += NumToString(it - struct_def.fields.vec.begin()) + ", "; - if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; } - code += argname + ", "; - if (!IsScalar(field.value.type.base_type)) { - code += "0"; - } else { - if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; } - code += GenDefaultValue(field.value, "builder"); - } - code += ");\n};\n\n"; - - if (field.value.type.base_type == BASE_TYPE_VECTOR) { - auto vector_type = field.value.type.VectorType(); - auto alignment = InlineAlignment(vector_type); - auto elem_size = InlineSize(vector_type); - - // Generate a method to create a vector from a JavaScript array - if (!IsStruct(vector_type)) { - GenDocComment( - code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + - GenTypeAnnotation( - kParam, - "Array.<" + GenTypeName(vector_type, true) + ">", - "data") + - GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", - false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static create" + MakeCamel(field.name); - std::string type = GenTypeName(vector_type, true) + "[]"; - if (type == "number[]") { type += " | Uint8Array"; } - code += "Vector(builder:flatbuffers.Builder, data:" + type + - "):flatbuffers.Offset {\n"; - } else { - code += object_name + ".create" + MakeCamel(field.name); - code += "Vector = function(builder, data) {\n"; - } - - code += " builder.startVector(" + NumToString(elem_size); - code += ", data.length, " + NumToString(alignment) + ");\n"; - code += " for (var i = data.length - 1; i >= 0; i--) {\n"; - code += " builder.add" + GenWriteMethod(vector_type) + "("; - if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; } - code += "data[i]);\n"; - code += " }\n"; - code += " return builder.endVector();\n"; - code += "};\n\n"; - } - - // Generate a method to start a vector, data to be added manually - // after - GenDocComment( - code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + - GenTypeAnnotation(kParam, "number", "numElems", false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static start" + MakeCamel(field.name); - code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n"; - } else { - code += object_name + ".start" + MakeCamel(field.name); - code += "Vector = function(builder, numElems) {\n"; - } - - code += " builder.startVector(" + NumToString(elem_size); - code += ", numElems, " + NumToString(alignment) + ");\n"; - code += "};\n\n"; - } - } - - // Generate a method to stop building a new object - GenDocComment( - code_ptr, - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + - GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false)); - - if (lang_.language == IDLOptions::kTs) { - code += "static end" + Verbose(struct_def); - code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n"; - } else { - code += object_name + ".end" + Verbose(struct_def); - code += " = function(builder) {\n"; - } - - code += " var offset = builder.endObject();\n"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (!field.deprecated && field.required) { - code += " builder.requiredField(offset, "; - code += NumToString(field.value.offset); - code += "); // " + field.name + "\n"; - } - } - code += " return offset;\n"; - code += "};\n\n"; - - // Generate the methods to complete buffer construction - GenerateFinisher(struct_def, code_ptr, code, object_name, false); - GenerateFinisher(struct_def, code_ptr, code, object_name, true); - - // Generate a convenient CreateX function - if (lang_.language == IDLOptions::kJs) { - std::string paramDoc = - GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder"); - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) continue; - paramDoc += - GenTypeAnnotation(kParam, GetArgType(field), GetArgName(field)); - } - paramDoc += - GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false); - - GenDocComment(code_ptr, paramDoc); - } - - if (lang_.language == IDLOptions::kTs) { - code += "static create" + Verbose(struct_def); - code += "(builder:flatbuffers.Builder"; - } else { - code += object_name + ".create" + Verbose(struct_def); - code += " = function(builder"; - } - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) continue; - - if (lang_.language == IDLOptions::kTs) { - code += ", " + GetArgName(field) + ":" + GetArgType(field); - } else { - code += ", " + GetArgName(field); - } - } - - if (lang_.language == IDLOptions::kTs) { - code += "):flatbuffers.Offset {\n"; - code += " " + struct_def.name + ".start" + Verbose(struct_def) + - "(builder);\n"; - } else { - code += ") {\n"; - code += " " + object_name + ".start" + Verbose(struct_def) + - "(builder);\n"; - } - - std::string methodPrefix = - lang_.language == IDLOptions::kTs ? struct_def.name : object_name; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) continue; - - code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "("; - code += "builder, " + GetArgName(field) + ");\n"; - } - - code += " return " + methodPrefix + ".end" + Verbose(struct_def) + - "(builder);\n"; - code += "}\n"; - if (lang_.language == IDLOptions::kJs) code += "\n"; - } - - if (lang_.language == IDLOptions::kTs) { - if (!object_namespace.empty()) { code += "}\n"; } - code += "}\n"; - } - } - - std::string GetArgType(const FieldDef &field) { - if (field.value.type.enum_def) - return GenPrefixedTypeName(GenTypeName(field.value.type, true), - field.value.type.enum_def->file); - return GenTypeName(field.value.type, true); - } - - static std::string GetArgName(const FieldDef &field) { - auto argname = MakeCamel(field.name, false); - if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; } - - return argname; - } - - std::string Verbose(const StructDef &struct_def, const char *prefix = "") { - return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name; - } -}; -} // namespace jsts - -bool GenerateJSTS(const Parser &parser, const std::string &path, - const std::string &file_name) { - jsts::JsTsGenerator generator(parser, path, file_name); - return generator.generate(); -} - -std::string JSTSMakeRule(const Parser &parser, const std::string &path, - const std::string &file_name) { - FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX); - - std::string filebase = - flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); - jsts::JsTsGenerator generator(parser, path, file_name); - std::string make_rule = - generator.GeneratedFileName(path, filebase, parser.opts) + ": "; - - auto included_files = parser.GetIncludedFilesRecursive(file_name); - for (auto it = included_files.begin(); it != included_files.end(); ++it) { - make_rule += " " + *it; - } - return make_rule; -} - -} // namespace flatbuffers diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_json_schema.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_json_schema.cpp index a1d0704d..d58bb849 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_json_schema.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_json_schema.cpp @@ -24,60 +24,90 @@ namespace flatbuffers { namespace jsons { -std::string GenNativeType(BaseType type) { +template std::string GenFullName(const T *enum_def) { + std::string full_name; + const auto &name_spaces = enum_def->defined_namespace->components; + for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) { + full_name.append(*ns + "_"); + } + full_name.append(enum_def->name); + return full_name; +} + +template std::string GenTypeRef(const T *enum_def) { + return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\""; +} + +std::string GenType(const std::string &name) { + return "\"type\" : \"" + name + "\""; +} + +std::string GenType(BaseType type) { switch (type) { - case BASE_TYPE_BOOL: return "boolean"; + case BASE_TYPE_BOOL: return "\"type\" : \"boolean\""; case BASE_TYPE_CHAR: + return "\"type\" : \"integer\", \"minimum\" : " + + NumToString(std::numeric_limits::min()) + + ", \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_UCHAR: + return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" :" + + NumToString(std::numeric_limits::max()); case BASE_TYPE_SHORT: + return "\"type\" : \"integer\", \"minimum\" : " + + NumToString(std::numeric_limits::min()) + + ", \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_USHORT: + return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_INT: + return "\"type\" : \"integer\", \"minimum\" : " + + NumToString(std::numeric_limits::min()) + + ", \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_UINT: + return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_LONG: + return "\"type\" : \"integer\", \"minimum\" : " + + NumToString(std::numeric_limits::min()) + + ", \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_ULONG: + return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " + + NumToString(std::numeric_limits::max()); case BASE_TYPE_FLOAT: - case BASE_TYPE_DOUBLE: return "number"; - case BASE_TYPE_STRING: return "string"; - case BASE_TYPE_ARRAY: return "array"; + case BASE_TYPE_DOUBLE: return "\"type\" : \"number\""; + case BASE_TYPE_STRING: return "\"type\" : \"string\""; default: return ""; } } -template std::string GenFullName(const T *enum_def) { - std::string full_name; - const auto &name_spaces = enum_def->defined_namespace->components; - for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) { - full_name.append(*ns + "_"); - } - full_name.append(enum_def->name); - return full_name; +std::string GenBaseType(const Type &type) { + if (type.struct_def != nullptr) { return GenTypeRef(type.struct_def); } + if (type.enum_def != nullptr) { return GenTypeRef(type.enum_def); } + return GenType(type.base_type); } -template std::string GenTypeRef(const T *enum_def) { - return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\""; -} +std::string GenArrayType(const Type &type) { + std::string element_type; + if (type.struct_def != nullptr) { + element_type = GenTypeRef(type.struct_def); + } else if (type.enum_def != nullptr) { + element_type = GenTypeRef(type.enum_def); + } else { + element_type = GenType(type.element); + } -std::string GenType(const std::string &name) { - return "\"type\" : \"" + name + "\""; + return "\"type\" : \"array\", \"items\" : {" + element_type + "}"; } std::string GenType(const Type &type) { - if (type.enum_def != nullptr && !type.enum_def->is_union) { - // it is a reference to an enum type - return GenTypeRef(type.enum_def); - } switch (type.base_type) { case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru case BASE_TYPE_VECTOR: { - std::string typeline; - typeline.append("\"type\" : \"array\", \"items\" : { "); - if (type.element == BASE_TYPE_STRUCT) { - typeline.append(GenTypeRef(type.struct_def)); - } else { - typeline.append(GenType(GenNativeType(type.element))); - } - typeline.append(" }"); - return typeline; + return GenArrayType(type); } case BASE_TYPE_STRUCT: { return GenTypeRef(type.struct_def); @@ -86,7 +116,7 @@ std::string GenType(const Type &type) { std::string union_type_string("\"anyOf\": ["); const auto &union_types = type.enum_def->Vals(); for (auto ut = union_types.cbegin(); ut < union_types.cend(); ++ut) { - auto &union_type = *ut; + const auto &union_type = *ut; if (union_type->union_type.base_type == BASE_TYPE_NONE) { continue; } if (union_type->union_type.base_type == BASE_TYPE_STRUCT) { union_type_string.append( @@ -100,13 +130,15 @@ std::string GenType(const Type &type) { return union_type_string; } case BASE_TYPE_UTYPE: return GenTypeRef(type.enum_def); - default: return GenType(GenNativeType(type.base_type)); + default: { + return GenBaseType(type); + } } } class JsonSchemaGenerator : public BaseGenerator { private: - CodeWriter code_; + std::string code_; public: JsonSchemaGenerator(const Parser &parser, const std::string &path, @@ -123,71 +155,93 @@ class JsonSchemaGenerator : public BaseGenerator { return path + file_name + ".schema.json"; } + // If indentation is less than 0, that indicates we don't want any newlines + // either. + std::string NewLine() const { + return parser_.opts.indent_step >= 0 ? "\n" : ""; + } + + std::string Indent(int indent) const { + const auto num_spaces = indent * std::max(parser_.opts.indent_step, 0); + return std::string(num_spaces, ' '); + } + bool generate() { + code_ = ""; if (parser_.root_struct_def_ == nullptr) { return false; } - code_.Clear(); - code_ += "{"; - code_ += " \"$schema\": \"http://json-schema.org/draft-04/schema#\","; - code_ += " \"definitions\": {"; + code_ += "{" + NewLine(); + code_ += Indent(1) + + "\"$schema\": \"https://json-schema.org/draft/2019-09/schema\"," + + NewLine(); + code_ += Indent(1) + "\"definitions\": {" + NewLine(); for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend(); ++e) { - code_ += " \"" + GenFullName(*e) + "\" : {"; - code_ += " " + GenType("string") + ","; - std::string enumdef(" \"enum\": ["); + code_ += Indent(2) + "\"" + GenFullName(*e) + "\" : {" + NewLine(); + code_ += Indent(3) + GenType("string") + "," + NewLine(); + auto enumdef(Indent(3) + "\"enum\": ["); for (auto enum_value = (*e)->Vals().begin(); enum_value != (*e)->Vals().end(); ++enum_value) { enumdef.append("\"" + (*enum_value)->name + "\""); if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); } } enumdef.append("]"); - code_ += enumdef; - code_ += " },"; // close type + code_ += enumdef + NewLine(); + code_ += Indent(2) + "}," + NewLine(); // close type } for (auto s = parser_.structs_.vec.cbegin(); s != parser_.structs_.vec.cend(); ++s) { const auto &structure = *s; - code_ += " \"" + GenFullName(structure) + "\" : {"; - code_ += " " + GenType("object") + ","; + code_ += Indent(2) + "\"" + GenFullName(structure) + "\" : {" + NewLine(); + code_ += Indent(3) + GenType("object") + "," + NewLine(); std::string comment; const auto &comment_lines = structure->doc_comment; for (auto comment_line = comment_lines.cbegin(); comment_line != comment_lines.cend(); ++comment_line) { comment.append(*comment_line); } - if (comment.size() > 0) { + if (!comment.empty()) { std::string description; if (!EscapeString(comment.c_str(), comment.length(), &description, true, true)) { return false; } - code_ += " \"description\" : " + description + ","; + code_ += + Indent(3) + "\"description\" : " + description + "," + NewLine(); } - code_ += " \"properties\" : {"; + code_ += Indent(3) + "\"properties\" : {" + NewLine(); const auto &properties = structure->fields.vec; for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) { const auto &property = *prop; std::string arrayInfo = ""; if (IsArray(property->value.type)) { - arrayInfo = ",\n \"minItems\": " + - NumToString(property->value.type.fixed_length) + - ",\n \"maxItems\": " + + arrayInfo = "," + NewLine() + Indent(8) + "\"minItems\": " + + NumToString(property->value.type.fixed_length) + "," + + NewLine() + Indent(8) + "\"maxItems\": " + NumToString(property->value.type.fixed_length); } - std::string typeLine = - " \"" + property->name + "\" : {\n" + " " + - GenType(property->value.type) + arrayInfo + "\n }"; + std::string deprecated_info = ""; + if (property->deprecated) { + deprecated_info = + "," + NewLine() + Indent(8) + "\"deprecated\" : true,"; + } + std::string typeLine = Indent(4) + "\"" + property->name + "\""; + typeLine += " : {" + NewLine() + Indent(8); + typeLine += GenType(property->value.type); + typeLine += arrayInfo; + typeLine += deprecated_info; + typeLine += NewLine() + Indent(7) + "}"; if (property != properties.back()) { typeLine.append(","); } - code_ += typeLine; + code_ += typeLine + NewLine(); } - code_ += " },"; // close properties + code_ += Indent(3) + "}," + NewLine(); // close properties std::vector requiredProperties; std::copy_if(properties.begin(), properties.end(), back_inserter(requiredProperties), - [](FieldDef const *prop) { return prop->required; }); - if (requiredProperties.size() > 0) { - std::string required_string(" \"required\" : ["); + [](FieldDef const *prop) { return prop->IsRequired(); }); + if (!requiredProperties.empty()) { + auto required_string(Indent(3) + "\"required\" : ["); for (auto req_prop = requiredProperties.cbegin(); req_prop != requiredProperties.cend(); ++req_prop) { required_string.append("\"" + (*req_prop)->name + "\""); @@ -196,31 +250,43 @@ class JsonSchemaGenerator : public BaseGenerator { } } required_string.append("],"); - code_ += required_string; + code_ += required_string + NewLine(); } - code_ += " \"additionalProperties\" : false"; - std::string closeType(" }"); + code_ += Indent(3) + "\"additionalProperties\" : false" + NewLine(); + auto closeType(Indent(2) + "}"); if (*s != parser_.structs_.vec.back()) { closeType.append(","); } - code_ += closeType; // close type + code_ += closeType + NewLine(); // close type } - code_ += " },"; // close definitions + code_ += Indent(1) + "}," + NewLine(); // close definitions // mark root type - code_ += " \"$ref\" : \"#/definitions/" + - GenFullName(parser_.root_struct_def_) + "\""; - - code_ += "}"; // close schema root - const std::string file_path = - GeneratedFileName(path_, file_name_, parser_.opts); - const std::string final_code = code_.ToString(); - return SaveFile(file_path.c_str(), final_code, false); + code_ += Indent(1) + "\"$ref\" : \"#/definitions/" + + GenFullName(parser_.root_struct_def_) + "\"" + NewLine(); + + code_ += "}" + NewLine(); // close schema root + return true; } + + bool save() const { + const auto file_path = GeneratedFileName(path_, file_name_, parser_.opts); + return SaveFile(file_path.c_str(), code_, false); + } + + const std::string getJson() { return code_; } }; } // namespace jsons bool GenerateJsonSchema(const Parser &parser, const std::string &path, const std::string &file_name) { jsons::JsonSchemaGenerator generator(parser, path, file_name); - return generator.generate(); + if (!generator.generate()) { return false; } + return generator.save(); +} + +bool GenerateJsonSchema(const Parser &parser, std::string *json) { + jsons::JsonSchemaGenerator generator(parser, "", ""); + if (!generator.generate()) { return false; } + *json = generator.getJson(); + return true; } } // namespace flatbuffers diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_kotlin.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_kotlin.cpp index bd72f5a4..fb4ce87a 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_kotlin.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_kotlin.cpp @@ -88,7 +88,7 @@ class KotlinGenerator : public BaseGenerator { auto &struct_def = **it; if (!parser_.opts.one_file) cur_name_space_ = struct_def.defined_namespace; - GenStruct(struct_def, structWriter); + GenStruct(struct_def, structWriter, parser_.opts); if (parser_.opts.one_file) { one_file_code += structWriter.ToString(); } else { @@ -159,6 +159,24 @@ class KotlinGenerator : public BaseGenerator { } } + // with the addition of optional scalar types, + // we are adding the nullable '?' operator to return type of a field. + std::string GetterReturnType(const FieldDef &field) const { + auto base_type = field.value.type.base_type; + + auto r_type = GenTypeGet(field.value.type); + if (field.IsScalarOptional() || + // string, structs and unions + (base_type == BASE_TYPE_STRING || base_type == BASE_TYPE_STRUCT || + base_type == BASE_TYPE_UNION) || + // vector of anything not scalar + (base_type == BASE_TYPE_VECTOR && + !IsScalar(field.value.type.VectorType().base_type))) { + r_type += "?"; + } + return r_type; + } + std::string GenTypeGet(const Type &type) const { return IsScalar(type.base_type) ? GenTypeBasic(type.base_type) : GenTypePointer(type); @@ -179,6 +197,18 @@ class KotlinGenerator : public BaseGenerator { // - Floats are upcasted to doubles // - Unsigned are casted to signed std::string GenFBBDefaultValue(const FieldDef &field) const { + if (field.IsScalarOptional()) { + // although default value is null, java API forces us to present a real + // default value for scalars, while adding a field to the buffer. This is + // not a problem because the default can be representing just by not + // calling builder.addMyField() + switch (field.value.type.base_type) { + case BASE_TYPE_DOUBLE: + case BASE_TYPE_FLOAT: return "0.0"; + case BASE_TYPE_BOOL: return "false"; + default: return "0"; + } + } auto out = GenDefaultValue(field, true); // All FlatBufferBuilder default floating point values are doubles if (field.value.type.base_type == BASE_TYPE_FLOAT) { @@ -204,6 +234,8 @@ class KotlinGenerator : public BaseGenerator { bool force_signed = false) const { auto &value = field.value; auto base_type = field.value.type.base_type; + + if (field.IsScalarOptional()) { return "null"; } if (IsFloat(base_type)) { auto val = KotlinFloatGen.GenFloatConstant(field); if (base_type == BASE_TYPE_DOUBLE && val.back() == 'f') { @@ -271,12 +303,15 @@ class KotlinGenerator : public BaseGenerator { } writer += ")"; }); - GenerateFunOneLine(writer, "name", "e: Int", "String", [&]() { - writer += "names[e\\"; - if (enum_def.MinValue()->IsNonZero()) - writer += " - " + enum_def.MinValue()->name + ".toInt()\\"; - writer += "]"; - }); + GenerateFunOneLine( + writer, "name", "e: Int", "String", + [&]() { + writer += "names[e\\"; + if (enum_def.MinValue()->IsNonZero()) + writer += " - " + enum_def.MinValue()->name + ".toInt()\\"; + writer += "]"; + }, + parser_.opts.gen_jvmstatic); } }); writer.DecrementIdentLevel(); @@ -417,7 +452,8 @@ class KotlinGenerator : public BaseGenerator { return key_offset; } - void GenStruct(StructDef &struct_def, CodeWriter &writer) const { + void GenStruct(StructDef &struct_def, CodeWriter &writer, + IDLOptions options) const { if (struct_def.generated) return; GenerateComment(struct_def.doc_comment, writer, &comment_config); @@ -456,15 +492,16 @@ class KotlinGenerator : public BaseGenerator { // Generate verson check method. // Force compile time error if not using the same version // runtime. - GenerateFunOneLine(writer, "validateVersion", "", "", [&]() { - writer += "Constants.FLATBUFFERS_1_12_0()"; - }); + GenerateFunOneLine( + writer, "validateVersion", "", "", + [&]() { writer += "Constants.FLATBUFFERS_2_0_0()"; }, + options.gen_jvmstatic); - GenerateGetRootAsAccessors(Esc(struct_def.name), writer); - GenerateBufferHasIdentifier(struct_def, writer); - GenerateTableCreator(struct_def, writer); + GenerateGetRootAsAccessors(Esc(struct_def.name), writer, options); + GenerateBufferHasIdentifier(struct_def, writer, options); + GenerateTableCreator(struct_def, writer, options); - GenerateStartStructMethod(struct_def, writer); + GenerateStartStructMethod(struct_def, writer, options); // Static Add for fields auto fields = struct_def.fields.vec; @@ -474,29 +511,31 @@ class KotlinGenerator : public BaseGenerator { field_pos++; if (field.deprecated) continue; if (field.key) key_field = &field; - GenerateAddField(NumToString(field_pos), field, writer); + GenerateAddField(NumToString(field_pos), field, writer, options); - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { auto vector_type = field.value.type.VectorType(); if (!IsStruct(vector_type)) { - GenerateCreateVectorField(field, writer); + GenerateCreateVectorField(field, writer, options); } - GenerateStartVectorField(field, writer); + GenerateStartVectorField(field, writer, options); } } - GenerateEndStructMethod(struct_def, writer); + GenerateEndStructMethod(struct_def, writer, options); auto file_identifier = parser_.file_identifier_; if (parser_.root_struct_def_ == &struct_def) { - GenerateFinishStructBuffer(struct_def, file_identifier, writer); - GenerateFinishSizePrefixed(struct_def, file_identifier, writer); + GenerateFinishStructBuffer(struct_def, file_identifier, writer, + options); + GenerateFinishSizePrefixed(struct_def, file_identifier, writer, + options); } if (struct_def.has_key) { - GenerateLookupByKey(key_field, struct_def, writer); + GenerateLookupByKey(key_field, struct_def, writer, options); } } else { - GenerateStaticConstructor(struct_def, writer); + GenerateStaticConstructor(struct_def, writer, options); } }); } @@ -508,7 +547,7 @@ class KotlinGenerator : public BaseGenerator { // TODO: move key_field to reference instead of pointer void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def, - CodeWriter &writer) const { + CodeWriter &writer, const IDLOptions options) const { std::stringstream params; params << "obj: " << Esc(struct_def.name) << "?" << ", "; @@ -532,7 +571,7 @@ class KotlinGenerator : public BaseGenerator { writer += "val tableOffset = __indirect(vector" "Location + 4 * (start + middle), bb)"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { writer += "val comp = compareStrings(\\"; writer += GenOffsetGetter(key_field) + "\\"; writer += ", byteKey, bb)"; @@ -564,53 +603,62 @@ class KotlinGenerator : public BaseGenerator { writer += "return null"; }; GenerateFun(writer, "__lookup_by_key", params.str(), - Esc(struct_def.name) + "?", statements); + Esc(struct_def.name) + "?", statements, options.gen_jvmstatic); } void GenerateFinishSizePrefixed(StructDef &struct_def, const std::string &identifier, - CodeWriter &writer) const { + CodeWriter &writer, + const IDLOptions options) const { auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : ""; auto params = "builder: FlatBufferBuilder, offset: Int"; auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer"; - GenerateFunOneLine(writer, method_name, params, "", [&]() { - writer += "builder.finishSizePrefixed(offset" + id + ")"; - }); + GenerateFunOneLine( + writer, method_name, params, "", + [&]() { writer += "builder.finishSizePrefixed(offset" + id + ")"; }, + options.gen_jvmstatic); } void GenerateFinishStructBuffer(StructDef &struct_def, const std::string &identifier, - CodeWriter &writer) const { + CodeWriter &writer, + const IDLOptions options) const { auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : ""; auto params = "builder: FlatBufferBuilder, offset: Int"; auto method_name = "finish" + Esc(struct_def.name) + "Buffer"; - GenerateFunOneLine(writer, method_name, params, "", - [&]() { writer += "builder.finish(offset" + id + ")"; }); + GenerateFunOneLine( + writer, method_name, params, "", + [&]() { writer += "builder.finish(offset" + id + ")"; }, + options.gen_jvmstatic); } - void GenerateEndStructMethod(StructDef &struct_def, - CodeWriter &writer) const { + void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer, + const IDLOptions options) const { // Generate end{{TableName}}(builder: FlatBufferBuilder) method auto name = "end" + Esc(struct_def.name); auto params = "builder: FlatBufferBuilder"; auto returns = "Int"; auto field_vec = struct_def.fields.vec; - GenerateFun(writer, name, params, returns, [&]() { - writer += "val o = builder.endTable()"; - writer.IncrementIdentLevel(); - for (auto it = field_vec.begin(); it != field_vec.end(); ++it) { - auto &field = **it; - if (field.deprecated || !field.required) { continue; } - writer.SetValue("offset", NumToString(field.value.offset)); - writer += "builder.required(o, {{offset}})"; - } - writer.DecrementIdentLevel(); - writer += "return o"; - }); + GenerateFun( + writer, name, params, returns, + [&]() { + writer += "val o = builder.endTable()"; + writer.IncrementIdentLevel(); + for (auto it = field_vec.begin(); it != field_vec.end(); ++it) { + auto &field = **it; + if (field.deprecated || !field.IsRequired()) { continue; } + writer.SetValue("offset", NumToString(field.value.offset)); + writer += "builder.required(o, {{offset}})"; + } + writer.DecrementIdentLevel(); + writer += "return o"; + }, + options.gen_jvmstatic); } // Generate a method to create a vector from a Kotlin array. - void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer) const { + void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer, + const IDLOptions options) const { auto vector_type = field.value.type.VectorType(); auto method_name = "create" + MakeCamel(Esc(field.name)) + "Vector"; auto params = "builder: FlatBufferBuilder, data: " + @@ -620,18 +668,22 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("root", GenMethod(vector_type)); writer.SetValue("cast", CastToSigned(vector_type)); - GenerateFun(writer, method_name, params, "Int", [&]() { - writer += "builder.startVector({{size}}, data.size, {{align}})"; - writer += "for (i in data.size - 1 downTo 0) {"; - writer.IncrementIdentLevel(); - writer += "builder.add{{root}}(data[i]{{cast}})"; - writer.DecrementIdentLevel(); - writer += "}"; - writer += "return builder.endVector()"; - }); + GenerateFun( + writer, method_name, params, "Int", + [&]() { + writer += "builder.startVector({{size}}, data.size, {{align}})"; + writer += "for (i in data.size - 1 downTo 0) {"; + writer.IncrementIdentLevel(); + writer += "builder.add{{root}}(data[i]{{cast}})"; + writer.DecrementIdentLevel(); + writer += "}"; + writer += "return builder.endVector()"; + }, + options.gen_jvmstatic); } - void GenerateStartVectorField(FieldDef &field, CodeWriter &writer) const { + void GenerateStartVectorField(FieldDef &field, CodeWriter &writer, + const IDLOptions options) const { // Generate a method to start a vector, data to be added manually // after. auto vector_type = field.value.type.VectorType(); @@ -641,28 +693,33 @@ class KotlinGenerator : public BaseGenerator { GenerateFunOneLine( writer, "start" + MakeCamel(Esc(field.name) + "Vector", true), params, - "", [&]() { + "", + [&]() { writer += "builder.startVector({{size}}, numElems, {{align}})"; - }); + }, + options.gen_jvmstatic); } void GenerateAddField(std::string field_pos, FieldDef &field, - CodeWriter &writer) const { + CodeWriter &writer, const IDLOptions options) const { auto field_type = GenTypeBasic(field.value.type.base_type); auto secondArg = MakeCamel(Esc(field.name), false) + ": " + field_type; - GenerateFunOneLine(writer, "add" + MakeCamel(Esc(field.name), true), - "builder: FlatBufferBuilder, " + secondArg, "", [&]() { - auto method = GenMethod(field.value.type); - writer.SetValue("field_name", - MakeCamel(Esc(field.name), false)); - writer.SetValue("method_name", method); - writer.SetValue("pos", field_pos); - writer.SetValue("default", GenFBBDefaultValue(field)); - writer.SetValue("cast", GenFBBValueCast(field)); - - writer += "builder.add{{method_name}}({{pos}}, \\"; - writer += "{{field_name}}{{cast}}, {{default}})"; - }); + + GenerateFunOneLine( + writer, "add" + MakeCamel(Esc(field.name), true), + "builder: FlatBufferBuilder, " + secondArg, "", + [&]() { + auto method = GenMethod(field.value.type); + writer.SetValue("field_name", MakeCamel(Esc(field.name), false)); + writer.SetValue("method_name", method); + writer.SetValue("pos", field_pos); + writer.SetValue("default", GenFBBDefaultValue(field)); + writer.SetValue("cast", GenFBBValueCast(field)); + + writer += "builder.add{{method_name}}({{pos}}, \\"; + writer += "{{field_name}}{{cast}}, {{default}})"; + }, + options.gen_jvmstatic); } static std::string ToSignedType(const Type &type) { @@ -703,17 +760,19 @@ class KotlinGenerator : public BaseGenerator { } // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11) - void GenerateStartStructMethod(StructDef &struct_def, - CodeWriter &code) const { - GenerateFunOneLine(code, "start" + Esc(struct_def.name), - "builder: FlatBufferBuilder", "", [&]() { - code += "builder.startTable(" + - NumToString(struct_def.fields.vec.size()) + - ")"; - }); + void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code, + const IDLOptions options) const { + GenerateFunOneLine( + code, "start" + Esc(struct_def.name), "builder: FlatBufferBuilder", "", + [&]() { + code += "builder.startTable(" + + NumToString(struct_def.fields.vec.size()) + ")"; + }, + options.gen_jvmstatic); } - void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer) const { + void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer, + const IDLOptions options) const { // Generate a method that creates a table in one go. This is only possible // when the table has no struct fields, since those have to be created // inline, and there's no way to do so in Java. @@ -748,49 +807,63 @@ class KotlinGenerator : public BaseGenerator { } else { params << ": "; } - params << GenTypeBasic(field.value.type.base_type); + auto optional = field.IsScalarOptional() ? "?" : ""; + params << GenTypeBasic(field.value.type.base_type) << optional; } - GenerateFun(writer, name, params.str(), "Int", [&]() { - writer.SetValue("vec_size", NumToString(fields_vec.size())); - - writer += "builder.startTable({{vec_size}})"; - - auto sortbysize = struct_def.sortbysize; - auto largest = sortbysize ? sizeof(largest_scalar_t) : 1; - for (size_t size = largest; size; size /= 2) { - for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) { - auto &field = **it; - auto base_type_size = SizeOf(field.value.type.base_type); - if (!field.deprecated && (!sortbysize || size == base_type_size)) { - writer.SetValue("camel_field_name", - MakeCamel(Esc(field.name), true)); - writer.SetValue("field_name", MakeCamel(Esc(field.name), false)); - - writer += "add{{camel_field_name}}(builder, {{field_name}}\\"; - if (!IsScalar(field.value.type.base_type)) { - writer += "Offset\\"; + GenerateFun( + writer, name, params.str(), "Int", + [&]() { + writer.SetValue("vec_size", NumToString(fields_vec.size())); + + writer += "builder.startTable({{vec_size}})"; + + auto sortbysize = struct_def.sortbysize; + auto largest = sortbysize ? sizeof(largest_scalar_t) : 1; + for (size_t size = largest; size; size /= 2) { + for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); + ++it) { + auto &field = **it; + auto base_type_size = SizeOf(field.value.type.base_type); + if (!field.deprecated && + (!sortbysize || size == base_type_size)) { + writer.SetValue("camel_field_name", + MakeCamel(Esc(field.name), true)); + writer.SetValue("field_name", + MakeCamel(Esc(field.name), false)); + + // we wrap on null check for scalar optionals + writer += field.IsScalarOptional() + ? "{{field_name}}?.run { \\" + : "\\"; + + writer += "add{{camel_field_name}}(builder, {{field_name}}\\"; + if (!IsScalar(field.value.type.base_type)) { + writer += "Offset\\"; + } + // we wrap on null check for scalar optionals + writer += field.IsScalarOptional() ? ") }" : ")"; + } } - writer += ")"; } - } - } - writer += "return end{{struct_name}}(builder)"; - }); + writer += "return end{{struct_name}}(builder)"; + }, + options.gen_jvmstatic); } } - void GenerateBufferHasIdentifier(StructDef &struct_def, - CodeWriter &writer) const { + void GenerateBufferHasIdentifier(StructDef &struct_def, CodeWriter &writer, + IDLOptions options) const { auto file_identifier = parser_.file_identifier_; // Check if a buffer has the identifier. if (parser_.root_struct_def_ != &struct_def || !file_identifier.length()) return; auto name = MakeCamel(Esc(struct_def.name), false); - GenerateFunOneLine(writer, name + "BufferHasIdentifier", "_bb: ByteBuffer", - "Boolean", [&]() { - writer += "__has_identifier(_bb, \"" + - file_identifier + "\")"; - }); + GenerateFunOneLine( + writer, name + "BufferHasIdentifier", "_bb: ByteBuffer", "Boolean", + [&]() { + writer += "__has_identifier(_bb, \"" + file_identifier + "\")"; + }, + options.gen_jvmstatic); } void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const { @@ -806,7 +879,7 @@ class KotlinGenerator : public BaseGenerator { auto field_name = MakeCamel(Esc(field.name), false); auto field_type = GenTypeGet(field.value.type); auto field_default_value = GenDefaultValue(field); - auto return_type = GenTypeGet(field.value.type); + auto return_type = GetterReturnType(field); auto bbgetter = ByteBufferGetter(field.value.type, "bb"); auto ucast = CastToUsigned(field); auto offset_val = NumToString(field.value.offset); @@ -823,14 +896,13 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("bbgetter", bbgetter); writer.SetValue("ucast", ucast); - auto opt_ret_type = return_type + "?"; // Generate the accessors that don't do object reuse. if (value_base_type == BASE_TYPE_STRUCT) { // Calls the accessor that takes an accessor object with a // new object. // val pos // get() = pos(Vec3()) - GenerateGetterOneLine(writer, field_name, opt_ret_type, [&]() { + GenerateGetterOneLine(writer, field_name, return_type, [&]() { writer += "{{field_name}}({{field_type}}())"; }); } else if (value_base_type == BASE_TYPE_VECTOR && @@ -838,8 +910,8 @@ class KotlinGenerator : public BaseGenerator { // Accessors for vectors of structs also take accessor objects, // this generates a variant without that argument. // ex: fun weapons(j: Int) = weapons(Weapon(), j) - GenerateFunOneLine(writer, field_name, "j: Int", opt_ret_type, [&]() { - writer += "{{field_name}}({{return_type}}(), j)"; + GenerateFunOneLine(writer, field_name, "j: Int", return_type, [&]() { + writer += "{{field_name}}({{field_type}}(), j)"; }); } @@ -866,7 +938,7 @@ class KotlinGenerator : public BaseGenerator { // fun pos(obj: Vec3) : Vec3? = obj.__assign(bb_pos + 4, bb) // ? adds nullability annotation GenerateFunOneLine( - writer, field_name, "obj: " + field_type, return_type + "?", + writer, field_name, "obj: " + field_type, return_type, [&]() { writer += "obj.__assign(bb_pos + {{offset}}, bb)"; }); } else { // create getter with object reuse @@ -881,8 +953,7 @@ class KotlinGenerator : public BaseGenerator { // } // ? adds nullability annotation GenerateFun( - writer, field_name, "obj: " + field_type, return_type + "?", - [&]() { + writer, field_name, "obj: " + field_type, return_type, [&]() { auto fixed = field.value.type.struct_def->fixed; writer.SetValue("seek", Indirect("o + bb_pos", fixed)); @@ -902,7 +973,7 @@ class KotlinGenerator : public BaseGenerator { // return if (o != 0) __string(o + bb_pos) else null // } // ? adds nullability annotation - GenerateGetter(writer, field_name, return_type + "?", [&]() { + GenerateGetter(writer, field_name, return_type, [&]() { writer += "val o = __offset({{offset}})"; writer += "return if (o != 0) __string(o + bb_pos) else null"; }); @@ -920,15 +991,13 @@ class KotlinGenerator : public BaseGenerator { auto vectortype = field.value.type.VectorType(); std::string params = "j: Int"; - std::string nullable = IsScalar(vectortype.base_type) ? "" : "?"; if (vectortype.base_type == BASE_TYPE_STRUCT || vectortype.base_type == BASE_TYPE_UNION) { params = "obj: " + field_type + ", j: Int"; } - auto ret_type = return_type + nullable; - GenerateFun(writer, field_name, params, ret_type, [&]() { + GenerateFun(writer, field_name, params, return_type, [&]() { auto inline_size = NumToString(InlineSize(vectortype)); auto index = "__vector(o) + j * " + inline_size; auto not_found = NotFoundReturn(field.value.type.element); @@ -942,7 +1011,7 @@ class KotlinGenerator : public BaseGenerator { break; } case BASE_TYPE_UNION: - found = "{{bbgetter}}(obj, {{index}} - bb_pos){{ucast}}"; + found = "{{bbgetter}}(obj, {{index}}){{ucast}}"; break; default: found = "{{bbgetter}}({{index}}){{ucast}}"; } @@ -953,12 +1022,11 @@ class KotlinGenerator : public BaseGenerator { break; } case BASE_TYPE_UNION: - GenerateFun(writer, field_name, "obj: " + field_type, - return_type + "?", [&]() { - writer += OffsetWrapperOneLine( - offset_val, bbgetter + "(obj, o + bb_pos)", - "null"); - }); + GenerateFun( + writer, field_name, "obj: " + field_type, return_type, [&]() { + writer += OffsetWrapperOneLine( + offset_val, bbgetter + "(obj, o + bb_pos)", "null"); + }); break; default: FLATBUFFERS_ASSERT(0); } @@ -1124,7 +1192,7 @@ class KotlinGenerator : public BaseGenerator { GenerateOverrideFun( writer, "keysCompare", "o1: Int, o2: Int, _bb: ByteBuffer", "Int", [&]() { - if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (IsString(key_field->value.type)) { writer.SetValue("offset", NumToString(key_field->value.offset)); writer += " return compareStrings(__offset({{offset}}, o1, " @@ -1218,18 +1286,21 @@ class KotlinGenerator : public BaseGenerator { } static void GenerateGetRootAsAccessors(const std::string &struct_name, - CodeWriter &writer) { + CodeWriter &writer, + IDLOptions options) { // Generate a special accessor for the table that when used as the root // ex: fun getRootAsMonster(_bb: ByteBuffer): Monster {...} writer.SetValue("gr_name", struct_name); writer.SetValue("gr_method", "getRootAs" + struct_name); // create convenience method that doesn't require an existing object + GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic); writer += "fun {{gr_method}}(_bb: ByteBuffer): {{gr_name}} = \\"; writer += "{{gr_method}}(_bb, {{gr_name}}())"; // create method that allows object reuse // ex: fun Monster getRootAsMonster(_bb: ByteBuffer, obj: Monster) {...} + GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic); writer += "fun {{gr_method}}" "(_bb: ByteBuffer, obj: {{gr_name}}): {{gr_name}} {"; @@ -1243,13 +1314,17 @@ class KotlinGenerator : public BaseGenerator { } static void GenerateStaticConstructor(const StructDef &struct_def, - CodeWriter &code) { + CodeWriter &code, + const IDLOptions options) { // create a struct constructor function auto params = StructConstructorParams(struct_def); - GenerateFun(code, "create" + Esc(struct_def.name), params, "Int", [&]() { - GenStructBody(struct_def, code, ""); - code += "return builder.offset()"; - }); + GenerateFun( + code, "create" + Esc(struct_def.name), params, "Int", + [&]() { + GenStructBody(struct_def, code, ""); + code += "return builder.offset()"; + }, + options.gen_jvmstatic); } static std::string StructConstructorParams(const StructDef &struct_def, @@ -1323,7 +1398,8 @@ class KotlinGenerator : public BaseGenerator { static void GenerateFun(CodeWriter &writer, const std::string &name, const std::string ¶ms, const std::string &returnType, - const std::function &body) { + const std::function &body, + bool gen_jvmstatic = false) { // Generates Kotlin function // e.g.: // fun path(j: Int): Vec3 { @@ -1333,6 +1409,7 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("name", name); writer.SetValue("params", params); writer.SetValue("return_type", noreturn ? "" : ": " + returnType); + GenerateJvmStaticAnnotation(writer, gen_jvmstatic); writer += "fun {{name}}({{params}}) {{return_type}} {"; writer.IncrementIdentLevel(); body(); @@ -1343,7 +1420,8 @@ class KotlinGenerator : public BaseGenerator { static void GenerateFunOneLine(CodeWriter &writer, const std::string &name, const std::string ¶ms, const std::string &returnType, - const std::function &body) { + const std::function &body, + bool gen_jvmstatic = false) { // Generates Kotlin function // e.g.: // fun path(j: Int): Vec3 = return path(Vec3(), j) @@ -1351,6 +1429,7 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("params", params); writer.SetValue("return_type_p", returnType.empty() ? "" : " : " + returnType); + GenerateJvmStaticAnnotation(writer, gen_jvmstatic); writer += "fun {{name}}({{params}}){{return_type_p}} = \\"; body(); } @@ -1428,6 +1507,12 @@ class KotlinGenerator : public BaseGenerator { } } + // Prepend @JvmStatic to methods in companion object. + static void GenerateJvmStaticAnnotation(CodeWriter &code, + bool gen_jvmstatic) { + if (gen_jvmstatic) { code += "@JvmStatic"; } + } + // This tracks the current namespace used to determine if a type need to be // prefixed by its namespace const Namespace *cur_name_space_; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lobster.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lobster.cpp index 0c600a74..6fdd6dc2 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lobster.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lobster.cpp @@ -62,7 +62,7 @@ class LobsterGenerator : public BaseGenerator { auto bits = NumToString(SizeOf(type.base_type) * 8); if (IsInteger(type.base_type)) return "int" + bits; if (IsFloat(type.base_type)) return "float" + bits; - if (type.base_type == BASE_TYPE_STRING) return "string"; + if (IsString(type)) return "string"; if (type.base_type == BASE_TYPE_STRUCT) return "table"; return "none"; } @@ -110,11 +110,14 @@ class LobsterGenerator : public BaseGenerator { offsets + ")"; } else { + auto defval = field.IsOptional() ? "0" : field.value.constant; acc = "buf_.flatbuffers_field_" + GenTypeName(field.value.type) + - "(pos_, " + offsets + ", " + field.value.constant + ")"; + "(pos_, " + offsets + ", " + defval + ")"; } if (field.value.type.enum_def) acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")"; + if (field.IsOptional()) + acc += ", buf_.flatbuffers_field_present(pos_, " + offsets + ")"; code += def + "():\n return " + acc + "\n"; return; } @@ -149,7 +152,7 @@ class LobsterGenerator : public BaseGenerator { code += NamespacedName(*field.value.type.struct_def) + " { buf_, " + start + " }\n"; } else { - if (vectortype.base_type == BASE_TYPE_STRING) + if (IsString(vectortype)) code += "buf_.flatbuffers_string"; else code += "buf_.read_" + GenTypeName(vectortype) + "_le"; @@ -173,7 +176,7 @@ class LobsterGenerator : public BaseGenerator { } default: FLATBUFFERS_ASSERT(0); } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += def + "_length():\n return " "buf_.flatbuffers_field_vector_len(pos_, " + @@ -198,7 +201,7 @@ class LobsterGenerator : public BaseGenerator { NormalizedName(field) + ":" + LobsterType(field.value.type) + "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" + NumToString(offset) + ", " + NormalizedName(field); - if (IsScalar(field.value.type.base_type)) + if (IsScalar(field.value.type.base_type) && !field.IsOptional()) code += ", " + field.value.constant; code += ")\n return this\n"; } @@ -207,7 +210,7 @@ class LobsterGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { code += "def " + NormalizedName(struct_def) + "Start" + MakeCamel(NormalizedName(field)) + "Vector(b_:flatbuffers_builder, n_:int):\n b_.StartVector("; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lua.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lua.cpp index 3d9ad9bc..9efc435e 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lua.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_lua.cpp @@ -124,6 +124,10 @@ class LuaGenerator : public BaseGenerator { code += "function " + NormalizedName(struct_def) + ".GetRootAs" + NormalizedName(struct_def) + "(buf, offset)\n"; + code += std::string(Indent) + "if type(buf) == \"string\" then\n"; + code += std::string(Indent) + Indent + + "buf = flatbuffers.binaryArray.New(buf)\n"; + code += std::string(Indent) + "end\n"; code += std::string(Indent) + "local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)\n"; code += std::string(Indent) + "local o = " + NormalizedName(struct_def) + @@ -327,7 +331,7 @@ class LuaGenerator : public BaseGenerator { code += "a + ((j-1) * "; code += NumToString(InlineSize(vectortype)) + "))\n"; code += std::string(Indent) + End; - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code += std::string(Indent) + "return ''\n"; } else { code += std::string(Indent) + "return 0\n"; @@ -335,6 +339,18 @@ class LuaGenerator : public BaseGenerator { code += EndFunc; } + // Access a byte/ubyte vector as a string + void AccessByteVectorAsString(const StructDef &struct_def, + const FieldDef &field, std::string *code_ptr) { + std::string &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += MakeCamel(NormalizedName(field)); + code += "AsString(start, stop)\n"; + code += std::string(Indent) + "return " + SelfData + ":VectorAsString(" + + NumToString(field.value.offset) + ", start, stop)\n"; + code += EndFunc; + } + // Begin the creator function signature. void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -495,6 +511,10 @@ class LuaGenerator : public BaseGenerator { GetMemberOfVectorOfStruct(struct_def, field, code_ptr); } else { GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr); + if (vectortype.base_type == BASE_TYPE_CHAR || + vectortype.base_type == BASE_TYPE_UCHAR) { + AccessByteVectorAsString(struct_def, field, code_ptr); + } } break; } @@ -502,7 +522,7 @@ class LuaGenerator : public BaseGenerator { default: FLATBUFFERS_ASSERT(0); } } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { GetVectorLen(struct_def, field, code_ptr); } } @@ -518,7 +538,7 @@ class LuaGenerator : public BaseGenerator { auto offset = it - struct_def.fields.vec.begin(); BuildFieldOfTable(struct_def, field, offset, code_ptr); - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { BuildVectorOfTable(struct_def, field, code_ptr); } } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_php.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_php.cpp index f346c7ce..602d229e 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_php.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_php.cpp @@ -401,7 +401,7 @@ class PhpGenerator : public BaseGenerator { code += Indent + Indent + "$o = $this->__offset(" + NumToString(field.value.offset) + ");\n"; - if (field.value.type.VectorType().base_type == BASE_TYPE_STRING) { + if (IsString(field.value.type.VectorType())) { code += Indent + Indent; code += "return $o != 0 ? $this->__string($this->__vector($o) + $j * "; code += NumToString(InlineSize(vectortype)) + ") : "; @@ -536,7 +536,7 @@ class PhpGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (!field.deprecated && field.required) { + if (!field.deprecated && field.IsRequired()) { code += Indent + Indent + "$builder->required($o, "; code += NumToString(field.value.offset); code += "); // " + field.name + "\n"; @@ -645,7 +645,7 @@ class PhpGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (!field.deprecated && field.required) { + if (!field.deprecated && field.IsRequired()) { code += Indent + Indent + "$builder->required($o, "; code += NumToString(field.value.offset); code += "); // " + field.name + "\n"; @@ -705,7 +705,7 @@ class PhpGenerator : public BaseGenerator { default: FLATBUFFERS_ASSERT(0); } } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { GetVectorLen(field, code_ptr); if (field.value.type.element == BASE_TYPE_UCHAR) { GetUByte(field, code_ptr); @@ -735,9 +735,7 @@ class PhpGenerator : public BaseGenerator { } else { BuildFieldOfTable(field, offset, code_ptr); } - if (field.value.type.base_type == BASE_TYPE_VECTOR) { - BuildVectorOfTable(field, code_ptr); - } + if (IsVector(field.value.type)) { BuildVectorOfTable(field, code_ptr); } } GetEndOffsetOnTable(struct_def, code_ptr); diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_python.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_python.cpp index dfa8a6b0..b3f394eb 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_python.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_python.cpp @@ -94,7 +94,7 @@ class PythonGenerator : public BaseGenerator { // Converts the name of a definition into lower Camel format. std::string MakeLowerCamel(const Definition &definition) const { auto name = MakeCamel(NormalizedName(definition), false); - name[0] = char(tolower(name[0])); + name[0] = CharToLower(name[0]); return name; } @@ -126,8 +126,7 @@ class PythonGenerator : public BaseGenerator { code += Indent + "@classmethod\n"; code += Indent + "def GetRootAs"; - code += NormalizedName(struct_def); - code += "(cls, buf, offset):"; + code += "(cls, buf, offset=0):"; code += "\n"; code += Indent + Indent; code += "n = flatbuffers.encode.Get"; @@ -136,6 +135,14 @@ class PythonGenerator : public BaseGenerator { code += Indent + Indent + "x.Init(buf, n + offset)\n"; code += Indent + Indent + "return x\n"; code += "\n"; + + // Add an alias with the old name + code += Indent + "@classmethod\n"; + code += Indent + "def GetRootAs"; + code += NormalizedName(struct_def); + code += "(cls, buf, offset=0):\n"; + code += Indent + Indent + "\"\"\"This method is deprecated. Please switch to GetRootAs.\"\"\"\n"; + code += Indent + Indent + "return cls.GetRootAs(buf, offset)\n"; } // Initialize an existing object with other data, to avoid an allocation. @@ -370,7 +377,7 @@ class PythonGenerator : public BaseGenerator { code += "return " + GenGetter(field.value.type); code += "a + flatbuffers.number_types.UOffsetTFlags.py_type(j * "; code += NumToString(InlineSize(vectortype)) + "))\n"; - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code += Indent + Indent + "return \"\"\n"; } else { code += Indent + Indent + "return 0\n"; @@ -400,7 +407,7 @@ class PythonGenerator : public BaseGenerator { code += MakeCamel(GenTypeGet(field.value.type)); code += "Flags, o)\n"; - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code += Indent + Indent + "return \"\"\n"; } else { code += Indent + Indent + "return 0\n"; @@ -408,6 +415,39 @@ class PythonGenerator : public BaseGenerator { code += "\n"; } + // Returns a nested flatbuffer as itself. + void GetVectorAsNestedFlatbuffer(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + auto nested = field.attributes.Lookup("nested_flatbuffer"); + if (!nested) { return; } // There is no nested flatbuffer. + + std::string unqualified_name = nested->constant; + std::string qualified_name = nested->constant; + auto nested_root = parser_.LookupStruct(nested->constant); + if (nested_root == nullptr) { + qualified_name = + parser_.current_namespace_->GetFullyQualifiedName(nested->constant); + nested_root = parser_.LookupStruct(qualified_name); + } + FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser. + (void)nested_root; + + auto &code = *code_ptr; + GenReceiver(struct_def, code_ptr); + code += MakeCamel(NormalizedName(field)) + "NestedRoot(self):"; + + code += OffsetPrefix(field); + + code += Indent + Indent + Indent; + code += "from " + qualified_name + " import " + unqualified_name + "\n"; + code += Indent + Indent + Indent + "return " + unqualified_name; + code += ".GetRootAs"; + code += "(self._tab.Bytes, self._tab.Vector(o))\n"; + code += Indent + Indent + "return 0\n"; + code += "\n"; + } + // Begin the creator function signature. void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) { auto &code = *code_ptr; @@ -513,19 +553,22 @@ class PythonGenerator : public BaseGenerator { // Get the value of a table's starting offset. void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) { auto &code = *code_ptr; - code += "def " + NormalizedName(struct_def) + "Start"; - code += "(builder): "; + code += "def Start(builder): "; code += "builder.StartObject("; code += NumToString(struct_def.fields.vec.size()); code += ")\n"; + + // Add alias with the old name. + code += "def " + NormalizedName(struct_def) + "Start(builder):\n"; + code += Indent + "\"\"\"This method is deprecated. Please switch to Start.\"\"\"\n"; + code += Indent + "return Start(builder)\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) { auto &code = *code_ptr; - code += "def " + NormalizedName(struct_def) + "Add" + - MakeCamel(NormalizedName(field)); + code += "def Add" + MakeCamel(NormalizedName(field)); code += "(builder, "; code += MakeCamel(NormalizedName(field), false); code += "): "; @@ -544,13 +587,27 @@ class PythonGenerator : public BaseGenerator { ? float_const_gen_.GenFloatConstant(field) : field.value.constant; code += ")\n"; + + // Add alias with the old name. + code += "def " + NormalizedName(struct_def) + "Add" + MakeCamel(NormalizedName(field)); + code += "(builder, "; + code += MakeCamel(NormalizedName(field), false); + code += "):\n"; + code += Indent + "\"\"\"This method is deprecated. Please switch to Add"; + code += MakeCamel(NormalizedName(field)) + ".\"\"\"\n"; + code += Indent + "return Add" + MakeCamel(NormalizedName(field)); + code += "(builder, "; + code += MakeCamel(NormalizedName(field), false); + code += ")\n"; + + // Add alias with the old name. } // Set the value of one of the members of a table's vector. void BuildVectorOfTable(const StructDef &struct_def, const FieldDef &field, std::string *code_ptr) { auto &code = *code_ptr; - code += "def " + NormalizedName(struct_def) + "Start"; + code += "def Start"; code += MakeCamel(NormalizedName(field)); code += "Vector(builder, numElems): return builder.StartVector("; auto vector_type = field.value.type.VectorType(); @@ -559,14 +616,71 @@ class PythonGenerator : public BaseGenerator { code += NumToString(elem_size); code += ", numElems, " + NumToString(alignment); code += ")\n"; + + // Add alias with the old name. + code += "def " + NormalizedName(struct_def) + "Start"; + code += MakeCamel(NormalizedName(field)); + code += "Vector(builder, numElems):\n"; + code += Indent + "\"\"\"This method is deprecated. Please switch to Start.\"\"\"\n"; + code += Indent + "return Start"; + code += MakeCamel(NormalizedName(field)); + code += "Vector(builder, numElems)\n"; + } + + // Set the value of one of the members of a table's vector and fills in the + // elements from a bytearray. This is for simplifying the use of nested + // flatbuffers. + void BuildVectorOfTableFromBytes(const FieldDef &field, std::string *code_ptr) { + auto nested = field.attributes.Lookup("nested_flatbuffer"); + if (!nested) { return; } // There is no nested flatbuffer. + + std::string unqualified_name = nested->constant; + std::string qualified_name = nested->constant; + auto nested_root = parser_.LookupStruct(nested->constant); + if (nested_root == nullptr) { + qualified_name = + parser_.current_namespace_->GetFullyQualifiedName(nested->constant); + nested_root = parser_.LookupStruct(qualified_name); + } + FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser. + (void)nested_root; + + auto &code = *code_ptr; + code += "def MakeVectorFromBytes(builder, bytes):\n"; + code += Indent + "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 += ", len(bytes), " + NumToString(alignment); + code += ")\n"; + code += Indent + "builder.head = builder.head - len(bytes)\n"; + code += Indent + "builder.Bytes[builder.head : builder.head + len(bytes)]"; + code += " = bytes\n"; + code += Indent + "return builder.EndVector()\n"; + + // Add alias with the old name. + code += "def Make" + MakeCamel(NormalizedName(field)); + code += "VectorFromBytes(builder, bytes):\n"; + code += Indent + "builder.StartVector("; + code += NumToString(elem_size); + code += ", len(bytes), " + NumToString(alignment); + code += ")\n"; + code += Indent + "builder.head = builder.head - len(bytes)\n"; + code += Indent + "builder.Bytes[builder.head : builder.head + len(bytes)]"; + code += " = bytes\n"; + code += Indent + "return builder.EndVector()\n"; } // Get the offset of the end of a table. void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) { auto &code = *code_ptr; - code += "def " + NormalizedName(struct_def) + "End"; - code += "(builder): "; - code += "return builder.EndObject()\n"; + code += "def End(builder): return builder.EndObject()\n"; + + // Add alias with the old name. + code += "def " + NormalizedName(struct_def) + "End(builder):\n"; + code += Indent + "\"\"\"This method is deprecated. Please switch to End.\"\"\"\n"; + code += Indent + "return End(builder)"; } // Generate the receiver for function signatures. @@ -607,6 +721,7 @@ class PythonGenerator : public BaseGenerator { } else { GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr); GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr); + GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr); } break; } @@ -614,13 +729,22 @@ class PythonGenerator : public BaseGenerator { default: FLATBUFFERS_ASSERT(0); } } - if (field.value.type.base_type == BASE_TYPE_VECTOR || - field.value.type.base_type == BASE_TYPE_ARRAY) { + if (IsVector(field.value.type) || IsArray(field.value.type)) { GetVectorLen(struct_def, field, code_ptr); GetVectorIsNone(struct_def, field, code_ptr); } } + // Generate struct sizeof. + void GenStructSizeOf(const StructDef &struct_def, std::string *code_ptr) { + auto &code = *code_ptr; + code += Indent + "@classmethod\n"; + code += Indent + "def SizeOf(cls):\n"; + code += + Indent + Indent + "return " + NumToString(struct_def.bytesize) + "\n"; + code += "\n"; + } + // Generate table constructors, conditioned on its members' types. void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) { GetStartOfTable(struct_def, code_ptr); @@ -632,8 +756,9 @@ class PythonGenerator : public BaseGenerator { auto offset = it - struct_def.fields.vec.begin(); BuildFieldOfTable(struct_def, field, offset, code_ptr); - if (field.value.type.base_type == BASE_TYPE_VECTOR) { + if (IsVector(field.value.type)) { BuildVectorOfTable(struct_def, field, code_ptr); + BuildVectorOfTableFromBytes(field, code_ptr); } } @@ -678,6 +803,9 @@ class PythonGenerator : public BaseGenerator { // Generate a special function to test file_identifier GenHasFileIdentifier(struct_def, code_ptr); } + } else { + // Generates the SizeOf method for all structs. + GenStructSizeOf(struct_def, code_ptr); } // Generates the Init method that sets the field in a pre-existing // accessor object. This is to allow object reuse. @@ -1000,7 +1128,7 @@ class PythonGenerator : public BaseGenerator { auto field_type_name = TypeName(field); auto one_instance = field_type_name + "_"; - one_instance[0] = char(tolower(one_instance[0])); + one_instance[0] = CharToLower(one_instance[0]); if (parser_.opts.include_dependence_headers) { auto package_reference = GenPackageReference(field.value.type); @@ -1159,16 +1287,15 @@ class PythonGenerator : public BaseGenerator { code_prefix += GenIndents(2) + "if self." + field_instance_name + " is not None:"; if (field.value.type.struct_def->fixed) { - code_prefix += GenIndents(3) + struct_name + "Start" + + code_prefix += GenIndents(3) + "Start" + field_accessor_name + "Vector(builder, len(self." + field_instance_name + "))"; code_prefix += GenIndents(3) + "for i in reversed(range(len(self." + field_instance_name + "))):"; code_prefix += GenIndents(4) + "self." + field_instance_name + "[i].Pack(builder)"; - code_prefix += GenIndents(3) + field_instance_name + - " = builder.EndVector(len(self." + field_instance_name + - "))"; + code_prefix += + GenIndents(3) + field_instance_name + " = builder.EndVector()"; } else { // If the vector is a struct vector, we need to first build accessor for // each struct element. @@ -1178,21 +1305,20 @@ class PythonGenerator : public BaseGenerator { code_prefix += GenIndents(4) + field_instance_name + "list.append(self." + field_instance_name + "[i].Pack(builder))"; - code_prefix += GenIndents(3) + struct_name + "Start" + + code_prefix += GenIndents(3) + "Start" + field_accessor_name + "Vector(builder, len(self." + field_instance_name + "))"; code_prefix += GenIndents(3) + "for i in reversed(range(len(self." + field_instance_name + "))):"; code_prefix += GenIndents(4) + "builder.PrependUOffsetTRelative" + "(" + field_instance_name + "list[i])"; - code_prefix += GenIndents(3) + field_instance_name + - " = builder.EndVector(len(self." + field_instance_name + - "))"; + code_prefix += + GenIndents(3) + field_instance_name + " = builder.EndVector()"; } // Adds the field into the struct. code += GenIndents(2) + "if self." + field_instance_name + " is not None:"; - code += GenIndents(3) + struct_name + "Add" + field_accessor_name + + code += GenIndents(3) + "Add" + field_accessor_name + "(builder, " + field_instance_name + ")"; } @@ -1205,7 +1331,7 @@ class PythonGenerator : public BaseGenerator { auto struct_name = NormalizedName(struct_def); auto vectortype = field.value.type.VectorType(); - code += GenIndents(indents) + struct_name + "Start" + field_accessor_name + + code += GenIndents(indents) + "Start" + field_accessor_name + "Vector(builder, len(self." + field_instance_name + "))"; code += GenIndents(indents) + "for i in reversed(range(len(self." + field_instance_name + "))):"; @@ -1242,7 +1368,7 @@ class PythonGenerator : public BaseGenerator { // Adds the field into the struct. code += GenIndents(2) + "if self." + field_instance_name + " is not None:"; - code += GenIndents(3) + struct_name + "Add" + field_accessor_name + + code += GenIndents(3) + "Add" + field_accessor_name + "(builder, " + field_instance_name + ")"; // Creates the field. @@ -1252,7 +1378,7 @@ class PythonGenerator : public BaseGenerator { // each string element. And this generated code, needs to be // placed ahead of code_prefix. auto vectortype = field.value.type.VectorType(); - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code_prefix += GenIndents(3) + MakeLowerCamel(field) + "list = []"; code_prefix += GenIndents(3) + "for i in range(len(self." + field_instance_name + ")):"; @@ -1261,9 +1387,8 @@ class PythonGenerator : public BaseGenerator { field_instance_name + "[i]))"; GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix_ptr, 3); code_prefix += "(" + MakeLowerCamel(field) + "list[i])"; - code_prefix += GenIndents(3) + field_instance_name + - " = builder.EndVector(len(self." + field_instance_name + - "))"; + code_prefix += + GenIndents(3) + field_instance_name + " = builder.EndVector()"; return; } @@ -1275,9 +1400,8 @@ class PythonGenerator : public BaseGenerator { code_prefix += GenIndents(3) + "else:"; GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix_ptr, 4); code_prefix += "(self." + field_instance_name + "[i])"; - code_prefix += GenIndents(4) + field_instance_name + - " = builder.EndVector(len(self." + field_instance_name + - "))"; + code_prefix += + GenIndents(4) + field_instance_name + " = builder.EndVector()"; } void GenPackForStructField(const StructDef &struct_def, const FieldDef &field, @@ -1307,7 +1431,7 @@ class PythonGenerator : public BaseGenerator { GenIndents(2) + "if self." + field_instance_name + " is not None:"; } - code += GenIndents(3) + struct_name + "Add" + field_accessor_name + + code += GenIndents(3) + "Add" + field_accessor_name + "(builder, " + field_instance_name + ")"; } @@ -1327,7 +1451,7 @@ class PythonGenerator : public BaseGenerator { code_prefix += GenIndents(3) + field_instance_name + " = self." + field_instance_name + ".Pack(builder)"; code += GenIndents(2) + "if self." + field_instance_name + " is not None:"; - code += GenIndents(3) + struct_name + "Add" + field_accessor_name + + code += GenIndents(3) + "Add" + field_accessor_name + "(builder, " + field_instance_name + ")"; } @@ -1339,7 +1463,7 @@ class PythonGenerator : public BaseGenerator { GenReceiverForObjectAPI(struct_def, code_ptr); code_base += "Pack(self, builder):"; - code += GenIndents(2) + struct_name + "Start(builder)"; + code += GenIndents(2) + "Start(builder)"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; @@ -1378,7 +1502,7 @@ class PythonGenerator : public BaseGenerator { ")"; code += GenIndents(2) + "if self." + field_instance_name + " is not None:"; - code += GenIndents(3) + struct_name + "Add" + field_accessor_name + + code += GenIndents(3) + "Add" + field_accessor_name + "(builder, " + field_instance_name + ")"; break; } @@ -1386,14 +1510,13 @@ class PythonGenerator : public BaseGenerator { // Generates code for scalar values. If the value equals to the // default value, builder will automatically ignore it. So we don't // need to check the value ahead. - code += GenIndents(2) + struct_name + "Add" + field_accessor_name + + code += GenIndents(2) + "Add" + field_accessor_name + "(builder, self." + field_instance_name + ")"; break; } } - code += GenIndents(2) + struct_instance_name + " = " + struct_name + - "End(builder)"; + code += GenIndents(2) + struct_instance_name + " = " + "End(builder)"; code += GenIndents(2) + "return " + struct_instance_name; code_base += code_prefix + code; diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_rust.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_rust.cpp index 55b8439b..455780cd 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_rust.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_rust.cpp @@ -24,19 +24,19 @@ namespace flatbuffers { // Convert a camelCaseIdentifier or CamelCaseIdentifier to a -// snake_case_indentifier. +// snake_case_identifier. std::string MakeSnakeCase(const std::string &in) { std::string s; for (size_t i = 0; i < in.length(); i++) { if (i == 0) { - s += static_cast(tolower(in[0])); + s += CharToLower(in[0]); } else if (in[i] == '_') { s += '_'; } else if (!islower(in[i])) { // Prevent duplicate underscores for Upper_Snake_Case strings // and UPPERCASE strings. if (islower(in[i - 1])) { s += '_'; } - s += static_cast(tolower(in[i])); + s += CharToLower(in[i]); } else { s += in[i]; } @@ -47,9 +47,7 @@ std::string MakeSnakeCase(const std::string &in) { // Convert a string to all uppercase. std::string MakeUpper(const std::string &in) { std::string s; - for (size_t i = 0; i < in.length(); i++) { - s += static_cast(toupper(in[i])); - } + for (size_t i = 0; i < in.length(); i++) { s += CharToUpper(in[i]); } return s; } @@ -83,13 +81,17 @@ enum FullType { ftVectorOfTable = 14, ftVectorOfString = 15, ftVectorOfUnionValue = 16, + + ftArrayOfBuiltin = 17, + ftArrayOfEnum = 18, + ftArrayOfStruct = 19, }; // Convert a Type to a FullType (exhaustive). FullType GetFullType(const Type &type) { // N.B. The order of these conditionals matters for some types. - if (type.base_type == BASE_TYPE_STRING) { + if (IsString(type)) { return ftString; } else if (type.base_type == BASE_TYPE_STRUCT) { if (type.struct_def->fixed) { @@ -97,7 +99,7 @@ FullType GetFullType(const Type &type) { } else { return ftTable; } - } else if (type.base_type == BASE_TYPE_VECTOR) { + } else if (IsVector(type)) { switch (GetFullType(type.VectorType())) { case ftInteger: { return ftVectorOfInteger; @@ -129,6 +131,23 @@ FullType GetFullType(const Type &type) { FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported"); } } + } else if (IsArray(type)) { + switch (GetFullType(type.VectorType())) { + case ftInteger: + case ftFloat: + case ftBool: { + return ftArrayOfBuiltin; + } + case ftStruct: { + return ftArrayOfStruct; + } + case ftEnumKey: { + return ftArrayOfEnum; + } + default: { + FLATBUFFERS_ASSERT(false && "Unsupported type for fixed array"); + } + } } else if (type.enum_def != nullptr) { if (type.enum_def->is_union) { if (type.base_type == BASE_TYPE_UNION) { @@ -177,6 +196,20 @@ std::string AddUnwrapIfRequired(std::string s, bool required) { } } +bool IsBitFlagsEnum(const EnumDef &enum_def) { + return enum_def.attributes.Lookup("bit_flags") != nullptr; +} +bool IsBitFlagsEnum(const FieldDef &field) { + EnumDef *ed = field.value.type.enum_def; + return ed && IsBitFlagsEnum(*ed); +} + +// TableArgs make required non-scalars "Option<_>". +// TODO(cneo): Rework how we do defaults and stuff. +bool IsOptionalToBuilder(const FieldDef &field) { + return field.IsOptional() || !IsScalar(field.value.type.base_type); +} + namespace rust { class RustGenerator : public BaseGenerator { @@ -186,6 +219,7 @@ class RustGenerator : public BaseGenerator { : BaseGenerator(parser, path, file_name, "", "::", "rs"), cur_name_space_(nullptr) { const char *keywords[] = { + // clang-format off // list taken from: // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html // @@ -193,19 +227,77 @@ class RustGenerator : public BaseGenerator { // changes to that webpage in the future. // currently-used keywords - "as", "break", "const", "continue", "crate", "else", "enum", "extern", - "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", - "move", "mut", "pub", "ref", "return", "Self", "self", "static", "struct", - "super", "trait", "true", "type", "unsafe", "use", "where", "while", + "as", + "break", + "const", + "continue", + "crate", + "else", + "enum", + "extern", + "false", + "fn", + "for", + "if", + "impl", + "in", + "let", + "loop", + "match", + "mod", + "move", + "mut", + "pub", + "ref", + "return", + "Self", + "self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "while", // future possible keywords - "abstract", "alignof", "become", "box", "do", "final", "macro", - "offsetof", "override", "priv", "proc", "pure", "sizeof", "typeof", - "unsized", "virtual", "yield", + "abstract", + "alignof", + "become", + "box", + "do", + "final", + "macro", + "offsetof", + "override", + "priv", + "proc", + "pure", + "sizeof", + "typeof", + "unsized", + "virtual", + "yield", // other rust terms we should not use - "std", "usize", "isize", "u8", "i8", "u16", "i16", "u32", "i32", "u64", - "i64", "u128", "i128", "f32", "f64", + "std", + "usize", + "isize", + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "u128", + "i128", + "f32", + "f64", // These are terms the code generator can implement on types. // @@ -216,8 +308,18 @@ class RustGenerator : public BaseGenerator { // implementation detail, and how we implement methods could change in // the future. as a result, we proactively block these out as reserved // words. - "follow", "push", "size", "alignment", "to_little_endian", - "from_little_endian", nullptr + "follow", + "push", + "size", + "alignment", + "to_little_endian", + "from_little_endian", + nullptr, + + // used by Enum constants + "ENUM_MAX", + "ENUM_MIN", + "ENUM_VALUES", }; for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw); } @@ -248,8 +350,7 @@ class RustGenerator : public BaseGenerator { for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); ++it) { const auto &enum_def = **it; - if (enum_def.defined_namespace != ns) { continue; } - if (!enum_def.generated) { + if (enum_def.defined_namespace == ns && !enum_def.generated) { SetNameSpace(enum_def.defined_namespace); GenEnum(enum_def); } @@ -259,8 +360,8 @@ class RustGenerator : public BaseGenerator { for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { const auto &struct_def = **it; - if (struct_def.defined_namespace != ns) { continue; } - if (struct_def.fixed && !struct_def.generated) { + if (struct_def.defined_namespace == ns && struct_def.fixed && + !struct_def.generated) { SetNameSpace(struct_def.defined_namespace); GenStruct(struct_def); } @@ -270,10 +371,13 @@ class RustGenerator : public BaseGenerator { for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { const auto &struct_def = **it; - if (struct_def.defined_namespace != ns) { continue; } - if (!struct_def.fixed && !struct_def.generated) { + if (struct_def.defined_namespace == ns && !struct_def.fixed && + !struct_def.generated) { SetNameSpace(struct_def.defined_namespace); GenTable(struct_def); + if (parser_.opts.generate_object_based_api) { + GenTableObject(struct_def); + } } } @@ -335,30 +439,16 @@ class RustGenerator : public BaseGenerator { return false; } - // Determine if a Type needs to be copied (for endian safety) when used in a - // Struct. - bool StructMemberAccessNeedsCopy(const Type &type) const { - switch (GetFullType(type)) { - case ftInteger: // requires endian swap - case ftFloat: // requires endian swap - case ftBool: // no endian-swap, but do the copy for UX consistency - case ftEnumKey: { - return true; - } // requires endian swap - case ftStruct: { - return false; - } // no endian swap - default: { - // logic error: no other types can be struct members. - FLATBUFFERS_ASSERT(false && "invalid struct member type"); - return false; // only to satisfy compiler's return analysis - } - } - } - std::string EscapeKeyword(const std::string &name) const { return keywords_.find(name) == keywords_.end() ? name : name + "_"; } + std::string NamespacedNativeName(const Definition &def) { + return WrapInNameSpace(def.defined_namespace, NativeName(def)); + } + + std::string NativeName(const Definition &def) { + return parser_.opts.object_prefix + Name(def) + parser_.opts.object_suffix; + } std::string Name(const Definition &def) const { return EscapeKeyword(def.name); @@ -498,6 +588,12 @@ class RustGenerator : public BaseGenerator { case ftUnionKey: { return GetTypeBasic(type); } + case ftArrayOfBuiltin: + case ftArrayOfEnum: + case ftArrayOfStruct: { + return "[" + GetTypeGet(type.VectorType()) + "; " + + NumToString(type.fixed_length) + "]"; + } case ftTable: { return WrapInNameSpace(type.struct_def->defined_namespace, type.struct_def->name) + @@ -510,11 +606,28 @@ class RustGenerator : public BaseGenerator { } } - std::string GetEnumValUse(const EnumDef &enum_def, - const EnumVal &enum_val) const { + std::string GetEnumValue(const EnumDef &enum_def, + const EnumVal &enum_val) const { return Name(enum_def) + "::" + Name(enum_val); } + // 1 suffix since old C++ can't figure out the overload. + void ForAllEnumValues1(const EnumDef &enum_def, + std::function cb) { + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + const auto &ev = **it; + code_.SetValue("VARIANT", Name(ev)); + code_.SetValue("VALUE", enum_def.ToString(ev)); + cb(ev); + } + } + void ForAllEnumValues(const EnumDef &enum_def, std::function cb) { + std::function wrapped = [&](const EnumVal &unused) { + (void)unused; + cb(); + }; + ForAllEnumValues1(enum_def, wrapped); + } // Generate an enum declaration, // an enum string lookup table, // an enum match function, @@ -522,158 +635,307 @@ class RustGenerator : public BaseGenerator { void GenEnum(const EnumDef &enum_def) { code_.SetValue("ENUM_NAME", Name(enum_def)); code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type)); - - GenComment(enum_def.doc_comment); - code_ += "#[allow(non_camel_case_types)]"; - code_ += "#[repr({{BASE_TYPE}})]"; - code_ += - "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]"; - code_ += "pub enum " + Name(enum_def) + " {"; - - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { - const auto &ev = **it; - - GenComment(ev.doc_comment, " "); - code_.SetValue("KEY", Name(ev)); - code_.SetValue("VALUE", enum_def.ToString(ev)); - code_ += " {{KEY}} = {{VALUE}},"; - } + code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def))); + code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def)))); const EnumVal *minv = enum_def.MinValue(); const EnumVal *maxv = enum_def.MaxValue(); FLATBUFFERS_ASSERT(minv && maxv); - - code_ += ""; - code_ += "}"; - code_ += ""; - - code_.SetValue("ENUM_NAME", Name(enum_def)); - code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def))); - code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def)))); code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv)); code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv)); - // Generate enum constants, and impls for Follow, EndianScalar, and Push. - code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\"; - code_ += "{{ENUM_MIN_BASE_VALUE}};"; - code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\"; - code_ += "{{ENUM_MAX_BASE_VALUE}};"; - code_ += ""; + if (IsBitFlagsEnum(enum_def)) { + // Defer to the convenient and canonical bitflags crate. We declare it in + // a module to #allow camel case constants in a smaller scope. This + // matches Flatbuffers c-modeled enums where variants are associated + // constants but in camel case. + code_ += "#[allow(non_upper_case_globals)]"; + code_ += "mod bitflags_{{ENUM_NAME_SNAKE}} {"; + code_ += " flatbuffers::bitflags::bitflags! {"; + GenComment(enum_def.doc_comment, " "); + code_ += " #[derive(Default)]"; + code_ += " pub struct {{ENUM_NAME}}: {{BASE_TYPE}} {"; + ForAllEnumValues1(enum_def, [&](const EnumVal &ev) { + this->GenComment(ev.doc_comment, " "); + code_ += " const {{VARIANT}} = {{VALUE}};"; + }); + code_ += " }"; + code_ += " }"; + code_ += "}"; + code_ += "pub use self::bitflags_{{ENUM_NAME_SNAKE}}::{{ENUM_NAME}};"; + code_ += ""; + + code_.SetValue("FROM_BASE", "unsafe { Self::from_bits_unchecked(b) }"); + code_.SetValue("INTO_BASE", "self.bits()"); + } else { + // Normal, c-modelled enums. + // Deprecated associated constants; + const std::string deprecation_warning = + "#[deprecated(since = \"2.0.0\", note = \"Use associated constants" + " instead. This will no longer be generated in 2021.\")]"; + code_ += deprecation_warning; + code_ += + "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" + " = {{ENUM_MIN_BASE_VALUE}};"; + code_ += deprecation_warning; + code_ += + "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" + " = {{ENUM_MAX_BASE_VALUE}};"; + auto num_fields = NumToString(enum_def.size()); + code_ += deprecation_warning; + code_ += "#[allow(non_camel_case_types)]"; + code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}: [{{ENUM_NAME}}; " + + num_fields + "] = ["; + ForAllEnumValues1(enum_def, [&](const EnumVal &ev) { + code_ += " " + GetEnumValue(enum_def, ev) + ","; + }); + code_ += "];"; + code_ += ""; + + GenComment(enum_def.doc_comment); + // Derive Default to be 0. flatc enforces this when the enum + // is put into a struct, though this isn't documented behavior, it is + // needed to derive defaults in struct objects. + code_ += + "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, " + "Default)]"; + code_ += "#[repr(transparent)]"; + code_ += "pub struct {{ENUM_NAME}}(pub {{BASE_TYPE}});"; + code_ += "#[allow(non_upper_case_globals)]"; + code_ += "impl {{ENUM_NAME}} {"; + ForAllEnumValues1(enum_def, [&](const EnumVal &ev) { + this->GenComment(ev.doc_comment, " "); + code_ += " pub const {{VARIANT}}: Self = Self({{VALUE}});"; + }); + code_ += ""; + // Generate Associated constants + code_ += " pub const ENUM_MIN: {{BASE_TYPE}} = {{ENUM_MIN_BASE_VALUE}};"; + code_ += " pub const ENUM_MAX: {{BASE_TYPE}} = {{ENUM_MAX_BASE_VALUE}};"; + code_ += " pub const ENUM_VALUES: &'static [Self] = &["; + ForAllEnumValues(enum_def, [&]() { code_ += " Self::{{VARIANT}},"; }); + code_ += " ];"; + code_ += " /// Returns the variant's name or \"\" if unknown."; + code_ += " pub fn variant_name(self) -> Option<&'static str> {"; + code_ += " match self {"; + ForAllEnumValues(enum_def, [&]() { + code_ += " Self::{{VARIANT}} => Some(\"{{VARIANT}}\"),"; + }); + code_ += " _ => None,"; + code_ += " }"; + code_ += " }"; + code_ += "}"; + + // Generate Debug. Unknown variants are printed like "". + code_ += "impl std::fmt::Debug for {{ENUM_NAME}} {"; + code_ += + " fn fmt(&self, f: &mut std::fmt::Formatter) ->" + " std::fmt::Result {"; + code_ += " if let Some(name) = self.variant_name() {"; + code_ += " f.write_str(name)"; + code_ += " } else {"; + code_ += " f.write_fmt(format_args!(\"\", self.0))"; + code_ += " }"; + code_ += " }"; + code_ += "}"; + + code_.SetValue("FROM_BASE", "Self(b)"); + code_.SetValue("INTO_BASE", "self.0"); + } + + // Generate Follow and Push so we can serialize and stuff. code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {"; code_ += " type Inner = Self;"; code_ += " #[inline]"; code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {"; - code_ += " flatbuffers::read_scalar_at::(buf, loc)"; + code_ += " let b = unsafe {"; + code_ += " flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc)"; + code_ += " };"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += "}"; code_ += ""; + code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {"; + code_ += " type Output = {{ENUM_NAME}};"; + code_ += " #[inline]"; + code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {"; + code_ += + " unsafe { flatbuffers::emplace_scalar::<{{BASE_TYPE}}>" + "(dst, {{INTO_BASE}}); }"; + code_ += " }"; + code_ += "}"; + code_ += ""; code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {"; code_ += " #[inline]"; code_ += " fn to_little_endian(self) -> Self {"; - code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});"; - code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};"; - code_ += " unsafe { *p }"; + code_ += " let b = {{BASE_TYPE}}::to_le({{INTO_BASE}});"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += " #[inline]"; + code_ += " #[allow(clippy::wrong_self_convention)]"; code_ += " fn from_little_endian(self) -> Self {"; - code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});"; - code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};"; - code_ += " unsafe { *p }"; + code_ += " let b = {{BASE_TYPE}}::from_le({{INTO_BASE}});"; + code_ += " {{FROM_BASE}}"; code_ += " }"; code_ += "}"; code_ += ""; - code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {"; - code_ += " type Output = {{ENUM_NAME}};"; - code_ += " #[inline]"; - code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {"; - code_ += - " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>" - "(dst, *self);"; - code_ += " }"; + + // Generate verifier - deferring to the base type. + code_ += "impl<'a> flatbuffers::Verifiable for {{ENUM_NAME}} {"; + code_ += " #[inline]"; + code_ += " fn run_verifier("; + code_ += " v: &mut flatbuffers::Verifier, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " {{BASE_TYPE}}::run_verifier(v, pos)"; + code_ += " }"; code_ += "}"; code_ += ""; + // Enums are basically integers. + code_ += "impl flatbuffers::SimpleToVerifyInSlice for {{ENUM_NAME}} {}"; - // Generate an array of all enumeration values. - auto num_fields = NumToString(enum_def.size()); - code_ += "#[allow(non_camel_case_types)]"; - code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " + - num_fields + "] = ["; - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { - const auto &ev = **it; - auto value = GetEnumValUse(enum_def, ev); - auto suffix = *it != enum_def.Vals().back() ? "," : ""; - code_ += " " + value + suffix; + if (enum_def.is_union) { + // Generate typesafe offset(s) for unions + code_.SetValue("NAME", Name(enum_def)); + code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset"); + code_ += "pub struct {{UNION_OFFSET_NAME}} {}"; + code_ += ""; + if (parser_.opts.generate_object_based_api) { GenUnionObject(enum_def); } } - code_ += "];"; - code_ += ""; + } - // Generate a string table for enum values. - // Problem is, if values are very sparse that could generate really big - // tables. Ideally in that case we generate a map lookup instead, but for - // the moment we simply don't output a table at all. - auto range = enum_def.Distance(); - // Average distance between values above which we consider a table - // "too sparse". Change at will. - static const uint64_t kMaxSparseness = 5; - if (range / static_cast(enum_def.size()) < kMaxSparseness) { - code_ += "#[allow(non_camel_case_types)]"; - code_ += "pub const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " + - NumToString(range + 1) + "] = ["; + // CASPER: dedup Object versions from non object versions. + void ForAllUnionObjectVariantsBesidesNone(const EnumDef &enum_def, + std::function cb) { + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + auto &enum_val = **it; + if (enum_val.union_type.base_type == BASE_TYPE_NONE) continue; + code_.SetValue("VARIANT_NAME", Name(enum_val)); + code_.SetValue("NATIVE_VARIANT", MakeCamel(Name(enum_val))); + code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(enum_val))); + code_.SetValue("U_ELEMENT_TABLE_TYPE", + NamespacedNativeName(*enum_val.union_type.struct_def)); + cb(); + } + } + void GenUnionObject(const EnumDef &enum_def) { + code_.SetValue("ENUM_NAME", Name(enum_def)); + code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def))); + code_.SetValue("NATIVE_NAME", NativeName(enum_def)); + + // Generate native union. + code_ += "#[non_exhaustive]"; + code_ += "#[derive(Debug, Clone, PartialEq)]"; + code_ += "pub enum {{NATIVE_NAME}} {"; + code_ += " NONE,"; + ForAllUnionObjectVariantsBesidesNone(enum_def, [&] { + code_ += " {{NATIVE_VARIANT}}(Box<{{U_ELEMENT_TABLE_TYPE}}>),"; + }); + code_ += "}"; + // Generate Default (NONE). + code_ += "impl Default for {{NATIVE_NAME}} {"; + code_ += " fn default() -> Self {"; + code_ += " Self::NONE"; + code_ += " }"; + code_ += "}"; - auto val = enum_def.Vals().front(); - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); - ++it) { - auto ev = *it; - for (auto k = enum_def.Distance(val, ev); k > 1; --k) { - code_ += " \"\","; - } - val = ev; - auto suffix = *it != enum_def.Vals().back() ? "," : ""; - code_ += " \"" + Name(*ev) + "\"" + suffix; - } - code_ += "];"; - code_ += ""; + // Generate native union methods. + code_ += "impl {{NATIVE_NAME}} {"; + // Get flatbuffers union key. + // CASPER: add docstrings? + code_ += " pub fn {{ENUM_NAME_SNAKE}}_type(&self) -> {{ENUM_NAME}} {"; + code_ += " match self {"; + code_ += " Self::NONE => {{ENUM_NAME}}::NONE,"; + ForAllUnionObjectVariantsBesidesNone(enum_def, [&] { code_ += - "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> " - "&'static str {"; - - code_ += " let index = e as {{BASE_TYPE}}\\"; - if (enum_def.MinValue()->IsNonZero()) { - auto vals = GetEnumValUse(enum_def, *enum_def.MinValue()); - code_ += " - " + vals + " as {{BASE_TYPE}}\\"; - } - code_ += ";"; - - code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]"; - code_ += "}"; - code_ += ""; - } + " Self::{{NATIVE_VARIANT}}(_) => {{ENUM_NAME}}::" + "{{VARIANT_NAME}},"; + }); + code_ += " }"; + code_ += " }"; + // Pack flatbuffers union value + code_ += + " pub fn pack(&self, fbb: &mut flatbuffers::FlatBufferBuilder)" + " -> Option>" + " {"; + code_ += " match self {"; + code_ += " Self::NONE => None,"; + ForAllUnionObjectVariantsBesidesNone(enum_def, [&] { + code_ += + " Self::{{NATIVE_VARIANT}}(v) => " + "Some(v.pack(fbb).as_union_value()),"; + }); + code_ += " }"; + code_ += " }"; - if (enum_def.is_union) { - // Generate tyoesafe offset(s) for unions - code_.SetValue("NAME", Name(enum_def)); - code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset"); - code_ += "pub struct {{UNION_OFFSET_NAME}} {}"; - } + // Generate some accessors; + ForAllUnionObjectVariantsBesidesNone(enum_def, [&] { + // Move accessor. + code_ += + " /// If the union variant matches, return the owned " + "{{U_ELEMENT_TABLE_TYPE}}, setting the union to NONE."; + code_ += + " pub fn take_{{U_ELEMENT_NAME}}(&mut self) -> " + "Option> {"; + code_ += " if let Self::{{NATIVE_VARIANT}}(_) = self {"; + code_ += " let v = std::mem::replace(self, Self::NONE);"; + code_ += " if let Self::{{NATIVE_VARIANT}}(w) = v {"; + code_ += " Some(w)"; + code_ += " } else {"; + code_ += " unreachable!()"; + code_ += " }"; + code_ += " } else {"; + code_ += " None"; + code_ += " }"; + code_ += " }"; + // Immutable reference accessor. + code_ += + " /// If the union variant matches, return a reference to the " + "{{U_ELEMENT_TABLE_TYPE}}."; + code_ += + " pub fn as_{{U_ELEMENT_NAME}}(&self) -> " + "Option<&{{U_ELEMENT_TABLE_TYPE}}> {"; + code_ += + " if let Self::{{NATIVE_VARIANT}}(v) = self " + "{ Some(v.as_ref()) } else { None }"; + code_ += " }"; + // Mutable reference accessor. + code_ += + " /// If the union variant matches, return a mutable reference" + " to the {{U_ELEMENT_TABLE_TYPE}}."; + code_ += + " pub fn as_{{U_ELEMENT_NAME}}_mut(&mut self) -> " + "Option<&mut {{U_ELEMENT_TABLE_TYPE}}> {"; + code_ += + " if let Self::{{NATIVE_VARIANT}}(v) = self " + "{ Some(v.as_mut()) } else { None }"; + code_ += " }"; + }); + code_ += "}"; // End union methods impl. } std::string GetFieldOffsetName(const FieldDef &field) { return "VT_" + MakeUpper(Name(field)); } - std::string GetDefaultConstant(const FieldDef &field) { - return field.value.type.base_type == BASE_TYPE_FLOAT - ? field.value.constant + "" - : field.value.constant; - } - - std::string GetDefaultScalarValue(const FieldDef &field) { - switch (GetFullType(field.value.type)) { - case ftInteger: { - return GetDefaultConstant(field); + enum DefaultContext { kBuilder, kAccessor, kObject }; + std::string GetDefaultValue(const FieldDef &field, + const DefaultContext context) { + if (context == kBuilder) { + // Builders and Args structs model nonscalars "optional" even if they're + // required or have defaults according to the schema. I guess its because + // WIPOffset is not nullable. + if (!IsScalar(field.value.type.base_type) || field.IsOptional()) { + return "None"; } + } else { + // This for defaults in objects. + // Unions have a NONE variant instead of using Rust's None. + if (field.IsOptional() && !IsUnion(field.value.type)) { return "None"; } + } + switch (GetFullType(field.value.type)) { + case ftInteger: case ftFloat: { - return GetDefaultConstant(field); + return field.value.constant; } case ftBool: { return field.value.constant == "0" ? "false" : "true"; @@ -681,17 +943,51 @@ class RustGenerator : public BaseGenerator { case ftUnionKey: case ftEnumKey: { auto ev = field.value.type.enum_def->FindByValue(field.value.constant); - assert(ev); + if (!ev) return "Default::default()"; // Bitflags enum. return WrapInNameSpace(field.value.type.enum_def->defined_namespace, - GetEnumValUse(*field.value.type.enum_def, *ev)); + GetEnumValue(*field.value.type.enum_def, *ev)); } - - // All pointer-ish types have a default value of None, because they are - // wrapped in Option. - default: { - return "None"; + case ftUnionValue: { + return ObjectFieldType(field, true) + "::NONE"; + } + case ftString: { + // Required fields do not have defaults defined by the schema, but we + // need one for Rust's Default trait so we use empty string. The usual + // value of field.value.constant is `0`, which is non-sensical except + // maybe to c++ (nullptr == 0). + // TODO: Escape strings? + const std::string defval = + field.IsRequired() ? "\"\"" : "\"" + field.value.constant + "\""; + if (context == kObject) return defval + ".to_string()"; + if (context == kAccessor) return "&" + defval; + FLATBUFFERS_ASSERT("Unreachable."); + return "INVALID_CODE_GENERATION"; + } + + case ftArrayOfStruct: + case ftArrayOfEnum: + case ftArrayOfBuiltin: + case ftVectorOfBool: + case ftVectorOfFloat: + case ftVectorOfInteger: + case ftVectorOfString: + case ftVectorOfStruct: + case ftVectorOfTable: + case ftVectorOfEnumKey: + case ftVectorOfUnionValue: + case ftStruct: + case ftTable: { + // We only support empty vectors which matches the defaults for + // &[T] and Vec anyway. + // + // For required structs and tables fields, we defer to their object API + // defaults. This works so long as there's nothing recursive happening, + // but `table Infinity { i: Infinity (required); }` does compile. + return "Default::default()"; } } + FLATBUFFERS_ASSERT("Unreachable."); + return "INVALID_CODE_GENERATION"; } // Create the return type for fields in the *BuilderArgs structs that are @@ -708,93 +1004,159 @@ class RustGenerator : public BaseGenerator { std::string TableBuilderArgsDefnType(const FieldDef &field, const std::string &lifetime) { const Type &type = field.value.type; + auto WrapOption = [&](std::string s) { + return IsOptionalToBuilder(field) ? "Option<" + s + ">" : s; + }; + auto WrapVector = [&](std::string ty) { + return WrapOption("flatbuffers::WIPOffset>"); + }; + auto WrapUOffsetsVector = [&](std::string ty) { + return WrapVector("flatbuffers::ForwardsUOffset<" + ty + ">"); + }; switch (GetFullType(type)) { case ftInteger: case ftFloat: case ftBool: { - const auto typname = GetTypeBasic(type); - return typname; + return WrapOption(GetTypeBasic(type)); } case ftStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return "Option<&" + lifetime + " " + typname + ">"; + return WrapOption("&" + lifetime + " " + typname); } case ftTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return "Option>>"; + return WrapOption("flatbuffers::WIPOffset<" + typname + "<" + lifetime + + ">>"); } case ftString: { - return "Option>"; + return WrapOption("flatbuffers::WIPOffset<&" + lifetime + " str>"); } case ftEnumKey: case ftUnionKey: { - const auto typname = WrapInNameSpace(*type.enum_def); - return typname; + return WrapOption(WrapInNameSpace(*type.enum_def)); } case ftUnionValue: { return "Option>"; } case ftVectorOfInteger: + case ftVectorOfBool: case ftVectorOfFloat: { const auto typname = GetTypeBasic(type.VectorType()); - return "Option>>"; - } - case ftVectorOfBool: { - return "Option>>"; + return WrapVector(typname); } case ftVectorOfEnumKey: { const auto typname = WrapInNameSpace(*type.enum_def); - return "Option>>"; + return WrapVector(typname); } case ftVectorOfStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return "Option>>"; + return WrapVector(typname); } case ftVectorOfTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return "Option>>>>"; + return WrapUOffsetsVector(typname + "<" + lifetime + ">"); } case ftVectorOfString: { - return "Option>>>"; + return WrapUOffsetsVector("&" + lifetime + " str"); } case ftVectorOfUnionValue: { - const auto typname = - WrapInNameSpace(*type.enum_def) + "UnionTableOffset"; - return "Option>>>"; + return WrapUOffsetsVector("flatbuffers::Table<" + lifetime + ">"); + } + case ftArrayOfEnum: + case ftArrayOfStruct: + case ftArrayOfBuiltin: { + FLATBUFFERS_ASSERT(false && "arrays are not supported within tables"); + return "ARRAYS_NOT_SUPPORTED_IN_TABLES"; } } return "INVALID_CODE_GENERATION"; // for return analysis } - std::string TableBuilderArgsDefaultValue(const FieldDef &field) { - return GetDefaultScalarValue(field); - } - std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) { - // All branches of switch do the same action! - switch (GetFullType(field.value.type)) { - case ftUnionKey: + std::string ObjectFieldType(const FieldDef &field, bool in_a_table) { + const Type &type = field.value.type; + std::string ty; + switch (GetFullType(type)) { + case ftInteger: + case ftBool: + case ftFloat: { + ty = GetTypeBasic(type); + break; + } + case ftString: { + ty = "String"; + break; + } + case ftStruct: { + ty = NamespacedNativeName(*type.struct_def); + break; + } + case ftTable: { + // Since Tables can contain themselves, Box is required to avoid + // infinite types. + ty = "Box<" + NamespacedNativeName(*type.struct_def) + ">"; + break; + } + case ftUnionKey: { + // There is no native "UnionKey", natively, unions are rust enums with + // newtype-struct-variants. + return "INVALID_CODE_GENERATION"; + } + case ftUnionValue: { + ty = NamespacedNativeName(*type.enum_def); + break; + } case ftEnumKey: { - const std::string basetype = - GetTypeBasic(field.value.type); //<- never used - return GetDefaultScalarValue(field); + ty = WrapInNameSpace(*type.enum_def); + break; } - - default: { - return GetDefaultScalarValue(field); + // Vectors are in tables and are optional + case ftVectorOfEnumKey: { + ty = "Vec<" + WrapInNameSpace(*type.VectorType().enum_def) + ">"; + break; + } + case ftVectorOfInteger: + case ftVectorOfBool: + case ftVectorOfFloat: { + ty = "Vec<" + GetTypeBasic(type.VectorType()) + ">"; + break; + } + case ftVectorOfString: { + ty = "Vec"; + break; + } + case ftVectorOfTable: + case ftVectorOfStruct: { + ty = NamespacedNativeName(*type.VectorType().struct_def); + ty = "Vec<" + ty + ">"; + break; + } + case ftVectorOfUnionValue: { + FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported"); + return "INVALID_CODE_GENERATION"; // OH NO! } + case ftArrayOfEnum: { + ty = "[" + WrapInNameSpace(*type.VectorType().enum_def) + "; " + + NumToString(type.fixed_length) + "]"; + break; + } + case ftArrayOfStruct: { + ty = "[" + NamespacedNativeName(*type.VectorType().struct_def) + "; " + + NumToString(type.fixed_length) + "]"; + break; + } + case ftArrayOfBuiltin: { + ty = "[" + GetTypeBasic(type.VectorType()) + "; " + + NumToString(type.fixed_length) + "]"; + break; + } + } + if (in_a_table && !IsUnion(type) && field.IsOptional()) { + return "Option<" + ty + ">"; + } else { + return ty; } } @@ -815,15 +1177,12 @@ class RustGenerator : public BaseGenerator { ">>>>"; } case ftVectorOfInteger: + case ftVectorOfBool: case ftVectorOfFloat: { const auto typname = GetTypeBasic(type.VectorType()); return "flatbuffers::WIPOffset>"; } - case ftVectorOfBool: { - return "flatbuffers::WIPOffset>"; - } case ftVectorOfString: { return "flatbuffers::WIPOffset>>"; @@ -838,37 +1197,46 @@ class RustGenerator : public BaseGenerator { ", flatbuffers::ForwardsUOffset>>"; } - case ftEnumKey: { + case ftEnumKey: + case ftUnionKey: { const auto typname = WrapInNameSpace(*type.enum_def); return typname; } case ftStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return "&" + lifetime + " " + typname + ""; + return "&" + typname + ""; } case ftTable: { const auto typname = WrapInNameSpace(*type.struct_def); return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>"; } case ftInteger: + case ftBool: case ftFloat: { - const auto typname = GetTypeBasic(type); - return typname; - } - case ftBool: { - return "bool"; + return GetTypeBasic(type); } case ftString: { return "flatbuffers::WIPOffset<&" + lifetime + " str>"; } - case ftUnionKey: { - const auto typname = WrapInNameSpace(*type.enum_def); - return typname; - } case ftUnionValue: { return "flatbuffers::WIPOffset"; } - } + case ftArrayOfBuiltin: { + const auto typname = GetTypeBasic(type.VectorType()); + return "flatbuffers::Array<" + lifetime + ", " + typname + ", " + + NumToString(type.fixed_length) + ">"; + } + case ftArrayOfEnum: { + const auto typname = WrapInNameSpace(*type.enum_def); + return "flatbuffers::Array<" + lifetime + ", " + typname + ", " + + NumToString(type.fixed_length) + ">"; + } + case ftArrayOfStruct: { + const auto typname = WrapInNameSpace(*type.struct_def); + return "flatbuffers::Array<" + lifetime + ", " + typname + ", " + + NumToString(type.fixed_length) + ">"; + } + } return "INVALID_CODE_GENERATION"; // for return analysis } @@ -878,18 +1246,19 @@ class RustGenerator : public BaseGenerator { switch (GetFullType(field.value.type)) { case ftInteger: + case ftBool: case ftFloat: { const auto typname = GetTypeBasic(field.value.type); - return "self.fbb_.push_slot::<" + typname + ">"; + return (field.IsOptional() ? "self.fbb_.push_slot_always::<" + : "self.fbb_.push_slot::<") + + typname + ">"; } - case ftBool: { - return "self.fbb_.push_slot::"; - } - case ftEnumKey: case ftUnionKey: { const auto underlying_typname = GetTypeBasic(type); - return "self.fbb_.push_slot::<" + underlying_typname + ">"; + return (field.IsOptional() ? "self.fbb_.push_slot_always::<" + : "self.fbb_.push_slot::<") + + underlying_typname + ">"; } case ftStruct: { @@ -914,6 +1283,12 @@ class RustGenerator : public BaseGenerator { case ftVectorOfUnionValue: { return "self.fbb_.push_slot_always::>"; } + case ftArrayOfEnum: + case ftArrayOfStruct: + case ftArrayOfBuiltin: { + FLATBUFFERS_ASSERT(false && "arrays are not supported within tables"); + return "ARRAYS_NOT_SUPPORTED_IN_TABLES"; + } } return "INVALID_CODE_GENERATION"; // for return analysis } @@ -921,78 +1296,64 @@ class RustGenerator : public BaseGenerator { std::string GenTableAccessorFuncReturnType(const FieldDef &field, const std::string &lifetime) { const Type &type = field.value.type; + const auto WrapOption = [&](std::string s) { + return field.IsOptional() ? "Option<" + s + ">" : s; + }; switch (GetFullType(field.value.type)) { case ftInteger: - case ftFloat: { - const auto typname = GetTypeBasic(type); - return typname; - } + case ftFloat: case ftBool: { - return "bool"; + return WrapOption(GetTypeBasic(type)); } case ftStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return WrapInOptionIfNotRequired("&" + lifetime + " " + typname, - field.required); + return WrapOption("&" + lifetime + " " + typname); } case ftTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">", - field.required); + return WrapOption(typname + "<" + lifetime + ">"); } case ftEnumKey: case ftUnionKey: { - const auto typname = WrapInNameSpace(*type.enum_def); - return typname; + return WrapOption(WrapInNameSpace(*type.enum_def)); } case ftUnionValue: { - return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">", - field.required); + return WrapOption("flatbuffers::Table<" + lifetime + ">"); } case ftString: { - return WrapInOptionIfNotRequired("&" + lifetime + " str", - field.required); + return WrapOption("&" + lifetime + " str"); } case ftVectorOfInteger: + case ftVectorOfBool: case ftVectorOfFloat: { const auto typname = GetTypeBasic(type.VectorType()); - if (IsOneByte(type.VectorType().base_type)) { - return WrapInOptionIfNotRequired( - "&" + lifetime + " [" + typname + "]", field.required); - } - return WrapInOptionIfNotRequired( - "flatbuffers::Vector<" + lifetime + ", " + typname + ">", - field.required); - } - case ftVectorOfBool: { - return WrapInOptionIfNotRequired("&" + lifetime + " [bool]", - field.required); + const auto vector_type = + IsOneByte(type.VectorType().base_type) + ? "&" + lifetime + " [" + typname + "]" + : "flatbuffers::Vector<" + lifetime + ", " + typname + ">"; + return WrapOption(vector_type); } case ftVectorOfEnumKey: { const auto typname = WrapInNameSpace(*type.enum_def); - return WrapInOptionIfNotRequired( - "flatbuffers::Vector<" + lifetime + ", " + typname + ">", - field.required); + return WrapOption("flatbuffers::Vector<" + lifetime + ", " + typname + + ">"); } case ftVectorOfStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]", - field.required); + return WrapOption("&" + lifetime + " [" + typname + "]"); } case ftVectorOfTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + - ", flatbuffers::ForwardsUOffset<" + - typname + "<" + lifetime + ">>>", - field.required); + return WrapOption("flatbuffers::Vector<" + lifetime + + ", flatbuffers::ForwardsUOffset<" + typname + "<" + + lifetime + ">>>"); } case ftVectorOfString: { - return WrapInOptionIfNotRequired( - "flatbuffers::Vector<" + lifetime + - ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>", - field.required); + return WrapOption("flatbuffers::Vector<" + lifetime + + ", flatbuffers::ForwardsUOffset<&" + lifetime + + " str>>"); } case ftVectorOfUnionValue: { FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported"); @@ -1000,131 +1361,118 @@ class RustGenerator : public BaseGenerator { // Into trait to convert tables to typesafe union values. return "INVALID_CODE_GENERATION"; // for return analysis } + case ftArrayOfEnum: + case ftArrayOfStruct: + case ftArrayOfBuiltin: { + FLATBUFFERS_ASSERT(false && "arrays are not supported within tables"); + return "ARRAYS_NOT_SUPPORTED_IN_TABLES"; + } } return "INVALID_CODE_GENERATION"; // for return analysis } - std::string GenTableAccessorFuncBody(const FieldDef &field, - const std::string &lifetime, - const std::string &offset_prefix) { - const std::string offset_name = - offset_prefix + "::" + GetFieldOffsetName(field); - const Type &type = field.value.type; + std::string FollowType(const Type &type, const std::string &lifetime) { + // IsVector... This can be made iterative? - switch (GetFullType(field.value.type)) { + const auto WrapForwardsUOffset = [](std::string ty) -> std::string { + return "flatbuffers::ForwardsUOffset<" + ty + ">"; + }; + const auto WrapVector = [&](std::string ty) -> std::string { + return "flatbuffers::Vector<" + lifetime + ", " + ty + ">"; + }; + const auto WrapArray = [&](std::string ty, uint16_t length) -> std::string { + return "flatbuffers::Array<" + lifetime + ", " + ty + ", " + + NumToString(length) + ">"; + }; + switch (GetFullType(type)) { case ftInteger: case ftFloat: case ftBool: { - const auto typname = GetTypeBasic(type); - const auto default_value = GetDefaultScalarValue(field); - return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + - default_value + ")).unwrap()"; + return GetTypeBasic(type); } case ftStruct: { - const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::<" + typname + ">(" + offset_name + ", None)", - field.required); + return WrapInNameSpace(*type.struct_def); + } + case ftUnionKey: + case ftEnumKey: { + return WrapInNameSpace(*type.enum_def); } case ftTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset(typname); } case ftUnionValue: { - return AddUnwrapIfRequired( - "self._tab.get::>>(" + offset_name + ", None)", - field.required); - } - case ftUnionKey: - case ftEnumKey: { - const auto underlying_typname = GetTypeBasic(type); //<- never used - const auto typname = WrapInNameSpace(*type.enum_def); - const auto default_value = GetDefaultScalarValue(field); - return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + - default_value + ")).unwrap()"; + return WrapForwardsUOffset("flatbuffers::Table<" + lifetime + ">"); } case ftString: { - return AddUnwrapIfRequired( - "self._tab.get::>(" + - offset_name + ", None)", - field.required); + return WrapForwardsUOffset("&str"); } - case ftVectorOfInteger: + case ftVectorOfBool: case ftVectorOfFloat: { const auto typname = GetTypeBasic(type.VectorType()); - std::string s = - "self._tab.get::>>(" + offset_name + ", None)"; - // single-byte values are safe to slice - if (IsOneByte(type.VectorType().base_type)) { - s += ".map(|v| v.safe_slice())"; - } - return AddUnwrapIfRequired(s, field.required); - } - case ftVectorOfBool: { - return AddUnwrapIfRequired( - "self._tab.get::>>(" + offset_name + - ", None).map(|v| v.safe_slice())", - field.required); + return WrapForwardsUOffset(WrapVector(typname)); } case ftVectorOfEnumKey: { - const auto typname = WrapInNameSpace(*type.enum_def); - return AddUnwrapIfRequired( - "self._tab.get::>>(" + offset_name + ", None)", - field.required); + const auto typname = WrapInNameSpace(*type.VectorType().enum_def); + return WrapForwardsUOffset(WrapVector(typname)); } case ftVectorOfStruct: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::>>(" + offset_name + - ", None).map(|v| v.safe_slice() )", - field.required); + return WrapForwardsUOffset(WrapVector(typname)); } case ftVectorOfTable: { const auto typname = WrapInNameSpace(*type.struct_def); - return AddUnwrapIfRequired( - "self._tab.get::>>>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset(WrapVector(WrapForwardsUOffset(typname))); } case ftVectorOfString: { - return AddUnwrapIfRequired( - "self._tab.get::>>>(" + offset_name + ", None)", - field.required); + return WrapForwardsUOffset( + WrapVector(WrapForwardsUOffset("&" + lifetime + " str"))); } case ftVectorOfUnionValue: { FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported"); return "INVALID_CODE_GENERATION"; // for return analysis } + case ftArrayOfEnum: { + const auto typname = WrapInNameSpace(*type.VectorType().enum_def); + return WrapArray(typname, type.fixed_length); + } + case ftArrayOfStruct: { + const auto typname = WrapInNameSpace(*type.struct_def); + return WrapArray(typname, type.fixed_length); + } + case ftArrayOfBuiltin: { + const auto typname = GetTypeBasic(type.VectorType()); + return WrapArray(typname, type.fixed_length); + } } return "INVALID_CODE_GENERATION"; // for return analysis } - bool TableFieldReturnsOption(const Type &type) { - switch (GetFullType(type)) { - case ftInteger: - case ftFloat: - case ftBool: - case ftEnumKey: - case ftUnionKey: return false; - default: return true; - } + std::string GenTableAccessorFuncBody(const FieldDef &field, + const std::string &lifetime) { + const std::string vt_offset = GetFieldOffsetName(field); + const std::string typname = FollowType(field.value.type, lifetime); + // Default-y fields (scalars so far) are neither optional nor required. + const std::string default_value = + !(field.IsOptional() || field.IsRequired()) + ? "Some(" + GetDefaultValue(field, kAccessor) + ")" + : "None"; + const std::string unwrap = field.IsOptional() ? "" : ".unwrap()"; + + const auto t = GetFullType(field.value.type); + + // TODO(caspern): Shouldn't 1byte VectorOfEnumKey be slice too? + const std::string safe_slice = + (t == ftVectorOfStruct || + ((t == ftVectorOfBool || t == ftVectorOfFloat || + t == ftVectorOfInteger) && + IsOneByte(field.value.type.VectorType().base_type))) + ? ".map(|v| v.safe_slice())" + : ""; + + return "self._tab.get::<" + typname + ">({{STRUCT_NAME}}::" + vt_offset + + ", " + default_value + ")" + safe_slice + unwrap; } // Generates a fully-qualified name getter for use with --gen-name-strings @@ -1137,6 +1485,46 @@ class RustGenerator : public BaseGenerator { code_ += ""; } + void ForAllUnionVariantsBesidesNone( + const EnumDef &def, std::function cb) { + FLATBUFFERS_ASSERT(def.is_union); + + for (auto it = def.Vals().begin(); it != def.Vals().end(); ++it) { + const EnumVal &ev = **it; + // TODO(cneo): Can variants be deprecated, should we skip them? + if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } + code_.SetValue( + "U_ELEMENT_ENUM_TYPE", + WrapInNameSpace(def.defined_namespace, GetEnumValue(def, ev))); + code_.SetValue( + "U_ELEMENT_TABLE_TYPE", + WrapInNameSpace(ev.union_type.struct_def->defined_namespace, + ev.union_type.struct_def->name)); + code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev))); + cb(ev); + } + } + + void ForAllTableFields(const StructDef &struct_def, + std::function cb, + bool reversed = false) { + // TODO(cneo): Remove `reversed` overload. It's only here to minimize the + // diff when refactoring to the `ForAllX` helper functions. + auto go = [&](const FieldDef &field) { + if (field.deprecated) return; + code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field)); + code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("BLDR_DEF_VAL", GetDefaultValue(field, kBuilder)); + cb(field); + }; + const auto &fields = struct_def.fields.vec; + if (reversed) { + for (auto it = fields.rbegin(); it != fields.rend(); ++it) go(**it); + } else { + for (auto it = fields.begin(); it != fields.end(); ++it) go(**it); + } + } // Generate an accessor struct, builder struct, and create function for a // table. void GenTable(const StructDef &struct_def) { @@ -1147,7 +1535,7 @@ class RustGenerator : public BaseGenerator { // Generate an offset type, the base type, the Follow impl, and the // init_from_table impl. code_ += "pub enum {{OFFSET_TYPELABEL}} {}"; - code_ += "#[derive(Copy, Clone, Debug, PartialEq)]"; + code_ += "#[derive(Copy, Clone, PartialEq)]"; code_ += ""; GenComment(struct_def.doc_comment); @@ -1160,9 +1548,7 @@ class RustGenerator : public BaseGenerator { code_ += " type Inner = {{STRUCT_NAME}}<'a>;"; code_ += " #[inline]"; code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {"; - code_ += " Self {"; - code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc },"; - code_ += " }"; + code_ += " Self { _tab: flatbuffers::Table { buf, loc } }"; code_ += " }"; code_ += "}"; code_ += ""; @@ -1176,9 +1562,7 @@ class RustGenerator : public BaseGenerator { code_ += " pub fn init_from_table(table: flatbuffers::Table<'a>) -> " "Self {"; - code_ += " {{STRUCT_NAME}} {"; - code_ += " _tab: table,"; - code_ += " }"; + code_ += " {{STRUCT_NAME}} { _tab: table }"; code_ += " }"; // Generate a convenient create* function that uses the above builder @@ -1198,46 +1582,150 @@ class RustGenerator : public BaseGenerator { code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);"; for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; size; size /= 2) { - for (auto it = struct_def.fields.vec.rbegin(); - it != struct_def.fields.vec.rend(); ++it) { - const auto &field = **it; - // TODO(rw): fully understand this sortbysize usage - if (!field.deprecated && (!struct_def.sortbysize || - size == SizeOf(field.value.type.base_type))) { - code_.SetValue("FIELD_NAME", Name(field)); - if (TableFieldReturnsOption(field.value.type)) { - code_ += - " if let Some(x) = args.{{FIELD_NAME}} " - "{ builder.add_{{FIELD_NAME}}(x); }"; - } else { - code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});"; - } - } - } + ForAllTableFields( + struct_def, + [&](const FieldDef &field) { + if (struct_def.sortbysize && + size != SizeOf(field.value.type.base_type)) + return; + if (IsOptionalToBuilder(field)) { + code_ += + " if let Some(x) = args.{{FIELD_NAME}} " + "{ builder.add_{{FIELD_NAME}}(x); }"; + } else { + code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});"; + } + }, + /*reverse=*/true); } code_ += " builder.finish()"; code_ += " }"; code_ += ""; - - // Generate field id constants. - if (struct_def.fields.vec.size() > 0) { - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) { - // Deprecated fields won't be accessible. - continue; + // Generate Object API Packer function. + if (parser_.opts.generate_object_based_api) { + // TODO(cneo): Replace more for loops with ForAllX stuff. + // TODO(cneo): Manage indentation with IncrementIdentLevel? + code_.SetValue("OBJECT_NAME", NativeName(struct_def)); + code_ += " pub fn unpack(&self) -> {{OBJECT_NAME}} {"; + ForAllObjectTableFields(struct_def, [&](const FieldDef &field) { + const Type &type = field.value.type; + switch (GetFullType(type)) { + case ftInteger: + case ftBool: + case ftFloat: + case ftEnumKey: { + code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}();"; + return; + } + case ftUnionKey: return; + case ftUnionValue: { + const auto &enum_def = *type.enum_def; + code_.SetValue("ENUM_NAME", WrapInNameSpace(enum_def)); + code_.SetValue("NATIVE_ENUM_NAME", NamespacedNativeName(enum_def)); + code_ += + " let {{FIELD_NAME}} = match " + "self.{{FIELD_NAME}}_type() {"; + code_ += + " {{ENUM_NAME}}::NONE =>" + " {{NATIVE_ENUM_NAME}}::NONE,"; + ForAllUnionObjectVariantsBesidesNone(enum_def, [&] { + code_ += + " {{ENUM_NAME}}::{{VARIANT_NAME}} => " + "{{NATIVE_ENUM_NAME}}::{{NATIVE_VARIANT}}(Box::new("; + code_ += + " self.{{FIELD_NAME}}_as_" + "{{U_ELEMENT_NAME}}()"; + code_ += + " .expect(\"Invalid union table, " + "expected `{{ENUM_NAME}}::{{VARIANT_NAME}}`.\")"; + code_ += " .unpack()"; + code_ += " )),"; + }); + // Maybe we shouldn't throw away unknown discriminants? + code_ += " _ => {{NATIVE_ENUM_NAME}}::NONE,"; + code_ += " };"; + return; + } + // The rest of the types need special handling based on if the field + // is optional or not. + case ftString: { + code_.SetValue("EXPR", "x.to_string()"); + break; + } + case ftStruct: { + code_.SetValue("EXPR", "x.unpack()"); + break; + } + case ftTable: { + code_.SetValue("EXPR", "Box::new(x.unpack())"); + break; + } + case ftVectorOfInteger: + case ftVectorOfBool: { + if (IsOneByte(type.VectorType().base_type)) { + // 1 byte stuff is viewed w/ slice instead of flatbuffer::Vector + // and thus needs to be cloned out of the slice. + code_.SetValue("EXPR", "x.to_vec()"); + break; + } + code_.SetValue("EXPR", "x.into_iter().collect()"); + break; + } + case ftVectorOfFloat: + case ftVectorOfEnumKey: { + code_.SetValue("EXPR", "x.into_iter().collect()"); + break; + } + case ftVectorOfString: { + code_.SetValue("EXPR", "x.iter().map(|s| s.to_string()).collect()"); + break; + } + case ftVectorOfStruct: + case ftVectorOfTable: { + code_.SetValue("EXPR", "x.iter().map(|t| t.unpack()).collect()"); + break; + } + case ftVectorOfUnionValue: { + FLATBUFFERS_ASSERT(false && "vectors of unions not yet supported"); + return; + } + case ftArrayOfEnum: + case ftArrayOfStruct: + case ftArrayOfBuiltin: { + FLATBUFFERS_ASSERT(false && + "arrays are not supported within tables"); + return; + } } - - code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field)); - code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); - code_ += - " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = " - "{{OFFSET_VALUE}};"; - } - code_ += ""; + if (field.IsOptional()) { + code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}().map(|x| {"; + code_ += " {{EXPR}}"; + code_ += " });"; + } else { + code_ += " let {{FIELD_NAME}} = {"; + code_ += " let x = self.{{FIELD_NAME}}();"; + code_ += " {{EXPR}}"; + code_ += " };"; + } + }); + code_ += " {{OBJECT_NAME}} {"; + ForAllObjectTableFields(struct_def, [&](const FieldDef &field) { + if (field.value.type.base_type == BASE_TYPE_UTYPE) return; + code_ += " {{FIELD_NAME}},"; + }); + code_ += " }"; + code_ += " }"; } + // Generate field id constants. + ForAllTableFields(struct_def, [&](const FieldDef &unused) { + (void)unused; + code_ += + " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = " + "{{OFFSET_VALUE}};"; + }); + if (struct_def.fields.vec.size() > 0) code_ += ""; + // Generate the accessors. Each has one of two forms: // // If a value can be None: @@ -1249,25 +1737,14 @@ class RustGenerator : public BaseGenerator { // pub fn name(&'a self) -> user_facing_type { // self._tab.get::(offset, defaultval).unwrap() // } - const auto offset_prefix = Name(struct_def); - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) { - // Deprecated fields won't be accessible. - continue; - } - - code_.SetValue("FIELD_NAME", Name(field)); + ForAllTableFields(struct_def, [&](const FieldDef &field) { code_.SetValue("RETURN_TYPE", GenTableAccessorFuncReturnType(field, "'a")); - code_.SetValue("FUNC_BODY", - GenTableAccessorFuncBody(field, "'a", offset_prefix)); - GenComment(field.doc_comment, " "); + this->GenComment(field.doc_comment, " "); code_ += " #[inline]"; code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {"; - code_ += " {{FUNC_BODY}}"; + code_ += " " + GenTableAccessorFuncBody(field, "'a"); code_ += " }"; // Generate a comparison function for this field if it is a key. @@ -1284,99 +1761,132 @@ class RustGenerator : public BaseGenerator { nested_root = parser_.LookupStruct(qualified_name); } FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser. - (void)nested_root; - code_.SetValue("OFFSET_NAME", - offset_prefix + "::" + GetFieldOffsetName(field)); - code_ += - " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> " - " Option<{{STRUCT_NAME}}<'a>> {"; - code_ += " match self.{{FIELD_NAME}}() {"; - code_ += " None => { None }"; - code_ += " Some(data) => {"; - code_ += " use self::flatbuffers::Follow;"; - code_ += - " Some(>>::follow(data, 0))"; - code_ += " },"; - code_ += " }"; + code_.SetValue("NESTED", WrapInNameSpace(*nested_root)); + code_ += " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> \\"; + if (field.IsRequired()) { + code_ += "{{NESTED}}<'a> {"; + code_ += " let data = self.{{FIELD_NAME}}();"; + code_ += " use flatbuffers::Follow;"; + code_ += + " >>" + "::follow(data, 0)"; + } else { + code_ += "Option<{{NESTED}}<'a>> {"; + code_ += " self.{{FIELD_NAME}}().map(|data| {"; + code_ += " use flatbuffers::Follow;"; + code_ += + " >>" + "::follow(data, 0)"; + code_ += " })"; + } code_ += " }"; } - } + }); // Explicit specializations for union accessors - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) { - continue; - } - - auto u = field.value.type.enum_def; - - code_.SetValue("FIELD_NAME", Name(field)); + ForAllTableFields(struct_def, [&](const FieldDef &field) { + if (field.value.type.base_type != BASE_TYPE_UNION) return; code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name); + ForAllUnionVariantsBesidesNone( + *field.value.type.enum_def, [&](const EnumVal &unused) { + (void)unused; + code_ += " #[inline]"; + code_ += " #[allow(non_snake_case)]"; + code_ += + " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> " + "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {"; + // If the user defined schemas name a field that clashes with a + // language reserved word, flatc will try to escape the field name + // by appending an underscore. This works well for most cases, + // except one. When generating union accessors (and referring to + // them internally within the code generated here), an extra + // underscore will be appended to the name, causing build failures. + // + // This only happens when unions have members that overlap with + // language reserved words. + // + // To avoid this problem the type field name is used unescaped here: + code_ += + " if self.{{FIELD_TYPE_FIELD_NAME}}_type() == " + "{{U_ELEMENT_ENUM_TYPE}} {"; + + // The following logic is not tested in the integration test, + // as of April 10, 2020 + if (field.IsRequired()) { + code_ += " let u = self.{{FIELD_NAME}}();"; + code_ += + " Some({{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))"; + } else { + code_ += + " self.{{FIELD_NAME}}().map(" + "{{U_ELEMENT_TABLE_TYPE}}::init_from_table)"; + } + code_ += " } else {"; + code_ += " None"; + code_ += " }"; + code_ += " }"; + code_ += ""; + }); + }); + code_ += "}"; // End of table impl. + code_ += ""; - for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) { - auto &ev = **u_it; - if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } - - auto table_init_type = - WrapInNameSpace(ev.union_type.struct_def->defined_namespace, - ev.union_type.struct_def->name); - - code_.SetValue( - "U_ELEMENT_ENUM_TYPE", - WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev))); - code_.SetValue("U_ELEMENT_TABLE_TYPE", table_init_type); - code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev))); - - code_ += " #[inline]"; - code_ += " #[allow(non_snake_case)]"; - code_ += - " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> " - "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {"; - // If the user defined schemas name a field that clashes with a - // language reserved word, flatc will try to escape the field name by - // appending an underscore. This works well for most cases, except - // one. When generating union accessors (and referring to them - // internally within the code generated here), an extra underscore - // will be appended to the name, causing build failures. - // - // This only happens when unions have members that overlap with - // language reserved words. - // - // To avoid this problem the type field name is used unescaped here: - code_ += - " if self.{{FIELD_TYPE_FIELD_NAME}}_type() == " - "{{U_ELEMENT_ENUM_TYPE}} {"; + // Generate Verifier; + code_ += "impl flatbuffers::Verifiable for {{STRUCT_NAME}}<'_> {"; + code_ += " #[inline]"; + code_ += " fn run_verifier("; + code_ += " v: &mut flatbuffers::Verifier, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " v.visit_table(pos)?\\"; + // Escape newline and insert it onthe next line so we can end the builder + // with a nice semicolon. + ForAllTableFields(struct_def, [&](const FieldDef &field) { + if (GetFullType(field.value.type) == ftUnionKey) return; + + code_.SetValue("IS_REQ", field.IsRequired() ? "true" : "false"); + if (GetFullType(field.value.type) != ftUnionValue) { + // All types besides unions. + code_.SetValue("TY", FollowType(field.value.type, "'_")); code_ += - " self.{{FIELD_NAME}}().map(|u| " - "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))"; - code_ += " } else {"; - code_ += " None"; - code_ += " }"; - code_ += " }"; - code_ += ""; + "\n .visit_field::<{{TY}}>(&\"{{FIELD_NAME}}\", " + "Self::{{OFFSET_NAME}}, {{IS_REQ}})?\\"; + return; } - } - - code_ += "}"; // End of table impl. - code_ += ""; + // Unions. + EnumDef &union_def = *field.value.type.enum_def; + code_.SetValue("UNION_TYPE", WrapInNameSpace(union_def)); + code_ += + "\n .visit_union::<{{UNION_TYPE}}, _>(" + "&\"{{FIELD_NAME}}_type\", Self::{{OFFSET_NAME}}_TYPE, " + "&\"{{FIELD_NAME}}\", Self::{{OFFSET_NAME}}, {{IS_REQ}}, " + "|key, v, pos| {"; + code_ += " match key {"; + ForAllUnionVariantsBesidesNone(union_def, [&](const EnumVal &unused) { + (void)unused; + code_ += + " {{U_ELEMENT_ENUM_TYPE}} => v.verify_union_variant::" + ">(" + "\"{{U_ELEMENT_ENUM_TYPE}}\", pos),"; + }); + code_ += " _ => Ok(()),"; + code_ += " }"; + code_ += " })?\\"; + }); + code_ += "\n .finish();"; + code_ += " Ok(())"; + code_ += " }"; + code_ += "}"; // Generate an args struct: code_.SetValue("MAYBE_LT", TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : ""); code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (!field.deprecated) { - code_.SetValue("PARAM_NAME", Name(field)); - code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a ")); - code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}},"; - } - } + ForAllTableFields(struct_def, [&](const FieldDef &field) { + code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a")); + code_ += " pub {{FIELD_NAME}}: {{PARAM_TYPE}},"; + }); code_ += "}"; // Generate an impl of Default for the *Args type: @@ -1384,16 +1894,10 @@ class RustGenerator : public BaseGenerator { code_ += " #[inline]"; code_ += " fn default() -> Self {"; code_ += " {{STRUCT_NAME}}Args {"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (!field.deprecated) { - code_.SetValue("PARAM_VALUE", TableBuilderArgsDefaultValue(field)); - code_.SetValue("REQ", field.required ? " // required field" : ""); - code_.SetValue("PARAM_NAME", Name(field)); - code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}"; - } - } + ForAllTableFields(struct_def, [&](const FieldDef &field) { + code_ += " {{FIELD_NAME}}: {{BLDR_DEF_VAL}},\\"; + code_ += field.IsRequired() ? " // required field" : ""; + }); code_ += " }"; code_ += " }"; code_ += "}"; @@ -1408,45 +1912,36 @@ class RustGenerator : public BaseGenerator { // Generate builder functions: code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (!field.deprecated) { - const bool is_scalar = IsScalar(field.value.type.base_type); - - std::string offset = GetFieldOffsetName(field); - - // Generate functions to add data, which take one of two forms. - // - // If a value has a default: - // fn add_x(x_: type) { - // fbb_.push_slot::(offset, x_, Some(default)); - // } - // - // If a value does not have a default: - // fn add_x(x_: type) { - // fbb_.push_slot_always::(offset, x_); - // } - code_.SetValue("FIELD_NAME", Name(field)); - code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset); - code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b ")); - code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field)); - code_ += " #[inline]"; + ForAllTableFields(struct_def, [&](const FieldDef &field) { + const bool is_scalar = IsScalar(field.value.type.base_type); + std::string offset = GetFieldOffsetName(field); + // Generate functions to add data, which take one of two forms. + // + // If a value has a default: + // fn add_x(x_: type) { + // fbb_.push_slot::(offset, x_, Some(default)); + // } + // + // If a value does not have a default: + // fn add_x(x_: type) { + // fbb_.push_slot_always::(offset, x_); + // } + code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset); + code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b ")); + code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field)); + code_ += " #[inline]"; + code_ += + " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: " + "{{FIELD_TYPE}}) {"; + if (is_scalar && !field.IsOptional()) { code_ += - " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: " - "{{FIELD_TYPE}}) {"; - if (is_scalar) { - code_.SetValue("FIELD_DEFAULT_VALUE", - TableBuilderAddFuncDefaultValue(field)); - code_ += - " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, " - "{{FIELD_DEFAULT_VALUE}});"; - } else { - code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});"; - } - code_ += " }"; + " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, " + "{{BLDR_DEF_VAL}});"; + } else { + code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});"; } - } + code_ += " }"; + }); // Struct initializer (all fields required); code_ += " #[inline]"; @@ -1468,21 +1963,222 @@ class RustGenerator : public BaseGenerator { "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {"; code_ += " let o = self.fbb_.end_table(self.start_);"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (!field.deprecated && field.required) { - code_.SetValue("FIELD_NAME", MakeSnakeCase(Name(field))); - code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field)); - code_ += - " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}}," - "\"{{FIELD_NAME}}\");"; - } - } + ForAllTableFields(struct_def, [&](const FieldDef &field) { + if (!field.IsRequired()) return; + code_ += + " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}}," + "\"{{FIELD_NAME}}\");"; + }); code_ += " flatbuffers::WIPOffset::new(o.value())"; code_ += " }"; code_ += "}"; code_ += ""; + + code_ += "impl std::fmt::Debug for {{STRUCT_NAME}}<'_> {"; + code_ += + " fn fmt(&self, f: &mut std::fmt::Formatter<'_>" + ") -> std::fmt::Result {"; + code_ += " let mut ds = f.debug_struct(\"{{STRUCT_NAME}}\");"; + ForAllTableFields(struct_def, [&](const FieldDef &field) { + if (GetFullType(field.value.type) == ftUnionValue) { + // Generate a match statement to handle unions properly. + code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, "")); + code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name); + code_.SetValue("UNION_ERR", + "&\"InvalidFlatbuffer: Union discriminant" + " does not match value.\""); + + code_ += " match self.{{FIELD_NAME}}_type() {"; + ForAllUnionVariantsBesidesNone( + *field.value.type.enum_def, [&](const EnumVal &unused) { + (void)unused; + code_ += " {{U_ELEMENT_ENUM_TYPE}} => {"; + code_ += + " if let Some(x) = " + "self.{{FIELD_TYPE_FIELD_NAME}}_as_" + "{{U_ELEMENT_NAME}}() {"; + code_ += " ds.field(\"{{FIELD_NAME}}\", &x)"; + code_ += " } else {"; + code_ += + " ds.field(\"{{FIELD_NAME}}\", {{UNION_ERR}})"; + code_ += " }"; + code_ += " },"; + }); + code_ += " _ => {"; + code_ += " let x: Option<()> = None;"; + code_ += " ds.field(\"{{FIELD_NAME}}\", &x)"; + code_ += " },"; + code_ += " };"; + } else { + // Most fields. + code_ += " ds.field(\"{{FIELD_NAME}}\", &self.{{FIELD_NAME}}());"; + } + }); + code_ += " ds.finish()"; + code_ += " }"; + code_ += "}"; + } + + void GenTableObject(const StructDef &table) { + code_.SetValue("OBJECT_NAME", NativeName(table)); + code_.SetValue("STRUCT_NAME", Name(table)); + + // Generate the native object. + code_ += "#[non_exhaustive]"; + code_ += "#[derive(Debug, Clone, PartialEq)]"; + code_ += "pub struct {{OBJECT_NAME}} {"; + ForAllObjectTableFields(table, [&](const FieldDef &field) { + // Union objects combine both the union discriminant and value, so we + // skip making a field for the discriminant. + if (field.value.type.base_type == BASE_TYPE_UTYPE) return; + code_ += " pub {{FIELD_NAME}}: {{FIELD_OBJECT_TYPE}},"; + }); + code_ += "}"; + + code_ += "impl Default for {{OBJECT_NAME}} {"; + code_ += " fn default() -> Self {"; + code_ += " Self {"; + ForAllObjectTableFields(table, [&](const FieldDef &field) { + if (field.value.type.base_type == BASE_TYPE_UTYPE) return; + std::string default_value = GetDefaultValue(field, kObject); + code_ += " {{FIELD_NAME}}: " + default_value + ","; + }); + code_ += " }"; + code_ += " }"; + code_ += "}"; + + // TODO(cneo): Generate defaults for Native tables. However, since structs + // may be required, they, and therefore enums need defaults. + + // Generate pack function. + code_ += "impl {{OBJECT_NAME}} {"; + code_ += " pub fn pack<'b>("; + code_ += " &self,"; + code_ += " _fbb: &mut flatbuffers::FlatBufferBuilder<'b>"; + code_ += " ) -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'b>> {"; + // First we generate variables for each field and then later assemble them + // using "StructArgs" to more easily manage ownership of the builder. + ForAllObjectTableFields(table, [&](const FieldDef &field) { + const Type &type = field.value.type; + switch (GetFullType(type)) { + case ftInteger: + case ftBool: + case ftFloat: + case ftEnumKey: { + code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}};"; + return; + } + case ftUnionKey: return; // Generate union type with union value. + case ftUnionValue: { + code_.SetValue("SNAKE_CASE_ENUM_NAME", + MakeSnakeCase(Name(*field.value.type.enum_def))); + code_ += + " let {{FIELD_NAME}}_type = " + "self.{{FIELD_NAME}}.{{SNAKE_CASE_ENUM_NAME}}_type();"; + code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}.pack(_fbb);"; + return; + } + // The rest of the types require special casing around optionalness + // due to "required" annotation. + case ftString: { + MapNativeTableField(field, "_fbb.create_string(x)"); + return; + } + case ftStruct: { + // Hold the struct in a variable so we can reference it. + if (field.IsRequired()) { + code_ += + " let {{FIELD_NAME}}_tmp = " + "Some(self.{{FIELD_NAME}}.pack());"; + } else { + code_ += + " let {{FIELD_NAME}}_tmp = self.{{FIELD_NAME}}" + ".as_ref().map(|x| x.pack());"; + } + code_ += " let {{FIELD_NAME}} = {{FIELD_NAME}}_tmp.as_ref();"; + + return; + } + case ftTable: { + MapNativeTableField(field, "x.pack(_fbb)"); + return; + } + case ftVectorOfEnumKey: + case ftVectorOfInteger: + case ftVectorOfBool: + case ftVectorOfFloat: { + MapNativeTableField(field, "_fbb.create_vector(x)"); + return; + } + case ftVectorOfStruct: { + MapNativeTableField( + field, + "let w: Vec<_> = x.iter().map(|t| t.pack()).collect();" + "_fbb.create_vector(&w)"); + return; + } + case ftVectorOfString: { + // TODO(cneo): create_vector* should be more generic to avoid + // allocations. + + MapNativeTableField( + field, + "let w: Vec<_> = x.iter().map(|s| s.as_ref()).collect();" + "_fbb.create_vector_of_strings(&w)"); + return; + } + case ftVectorOfTable: { + MapNativeTableField( + field, + "let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();" + "_fbb.create_vector(&w)"); + return; + } + case ftVectorOfUnionValue: { + FLATBUFFERS_ASSERT(false && "vectors of unions not yet supported"); + return; + } + case ftArrayOfEnum: + case ftArrayOfStruct: + case ftArrayOfBuiltin: { + FLATBUFFERS_ASSERT(false && "arrays are not supported within tables"); + return; + } + } + }); + code_ += " {{STRUCT_NAME}}::create(_fbb, &{{STRUCT_NAME}}Args{"; + ForAllObjectTableFields(table, [&](const FieldDef &field) { + (void)field; // Unused. + code_ += " {{FIELD_NAME}},"; + }); + code_ += " })"; + code_ += " }"; + code_ += "}"; + } + void ForAllObjectTableFields(const StructDef &table, + std::function cb) { + const std::vector &v = table.fields.vec; + for (auto it = v.begin(); it != v.end(); it++) { + const FieldDef &field = **it; + if (field.deprecated) continue; + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("FIELD_OBJECT_TYPE", ObjectFieldType(field, true)); + cb(field); + } + } + void MapNativeTableField(const FieldDef &field, const std::string &expr) { + if (field.IsOptional()) { + code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}.as_ref().map(|x|{"; + code_ += " " + expr; + code_ += " });"; + } else { + // For some reason Args has optional types for required fields. + // TODO(cneo): Fix this... but its a breaking change? + code_ += " let {{FIELD_NAME}} = Some({"; + code_ += " let x = &self.{{FIELD_NAME}};"; + code_ += " " + expr; + code_ += " });"; + } } // Generate functions to compare tables and structs by key. This function @@ -1520,26 +2216,125 @@ class RustGenerator : public BaseGenerator { // The root datatype accessors: code_ += "#[inline]"; + code_ += + "#[deprecated(since=\"2.0.0\", " + "note=\"Deprecated in favor of `root_as...` methods.\")]"; code_ += "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])" " -> {{STRUCT_NAME}}<'a> {"; - code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)"; + code_ += + " unsafe { flatbuffers::root_unchecked::<{{STRUCT_NAME}}" + "<'a>>(buf) }"; code_ += "}"; code_ += ""; code_ += "#[inline]"; + code_ += + "#[deprecated(since=\"2.0.0\", " + "note=\"Deprecated in favor of `root_as...` methods.\")]"; code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {"; code_ += - " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>" - "(buf)"; + " unsafe { flatbuffers::size_prefixed_root_unchecked::<{{STRUCT_NAME}}" + "<'a>>(buf) }"; code_ += "}"; code_ += ""; + // Default verifier root fns. + code_ += "#[inline]"; + code_ += "/// Verifies that a buffer of bytes contains a `{{STRUCT_NAME}}`"; + code_ += "/// and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += + "pub fn root_as_{{STRUCT_NAME_SNAKECASE}}(buf: &[u8]) " + "-> Result<{{STRUCT_NAME}}, flatbuffers::InvalidFlatbuffer> {"; + code_ += " flatbuffers::root::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += "/// Verifies that a buffer of bytes contains a size prefixed"; + code_ += "/// `{{STRUCT_NAME}}` and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += + "pub fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" + "(buf: &[u8]) -> Result<{{STRUCT_NAME}}, " + "flatbuffers::InvalidFlatbuffer> {"; + code_ += " flatbuffers::size_prefixed_root::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + // Verifier with options root fns. + code_ += "#[inline]"; + code_ += "/// Verifies, with the given options, that a buffer of bytes"; + code_ += "/// contains a `{{STRUCT_NAME}}` and returns it."; + code_ += "/// Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += "pub fn root_as_{{STRUCT_NAME_SNAKECASE}}_with_opts<'b, 'o>("; + code_ += " opts: &'o flatbuffers::VerifierOptions,"; + code_ += " buf: &'b [u8],"; + code_ += + ") -> Result<{{STRUCT_NAME}}<'b>, flatbuffers::InvalidFlatbuffer>" + " {"; + code_ += " flatbuffers::root_with_opts::<{{STRUCT_NAME}}<'b>>(opts, buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += "/// Verifies, with the given verifier options, that a buffer of"; + code_ += "/// bytes contains a size prefixed `{{STRUCT_NAME}}` and returns"; + code_ += "/// it. Note that verification is still experimental and may not"; + code_ += "/// catch every error, or be maximally performant. For the"; + code_ += "/// previous, unchecked, behavior use"; + code_ += "/// `root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked`."; + code_ += + "pub fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}_with_opts" + "<'b, 'o>("; + code_ += " opts: &'o flatbuffers::VerifierOptions,"; + code_ += " buf: &'b [u8],"; + code_ += + ") -> Result<{{STRUCT_NAME}}<'b>, flatbuffers::InvalidFlatbuffer>" + " {"; + code_ += + " flatbuffers::size_prefixed_root_with_opts::<{{STRUCT_NAME}}" + "<'b>>(opts, buf)"; + code_ += "}"; + // Unchecked root fns. + code_ += "#[inline]"; + code_ += + "/// Assumes, without verification, that a buffer of bytes " + "contains a {{STRUCT_NAME}} and returns it."; + code_ += "/// # Safety"; + code_ += + "/// Callers must trust the given bytes do indeed contain a valid" + " `{{STRUCT_NAME}}`."; + code_ += + "pub unsafe fn root_as_{{STRUCT_NAME_SNAKECASE}}_unchecked" + "(buf: &[u8]) -> {{STRUCT_NAME}} {"; + code_ += " flatbuffers::root_unchecked::<{{STRUCT_NAME}}>(buf)"; + code_ += "}"; + code_ += "#[inline]"; + code_ += + "/// Assumes, without verification, that a buffer of bytes " + "contains a size prefixed {{STRUCT_NAME}} and returns it."; + code_ += "/// # Safety"; + code_ += + "/// Callers must trust the given bytes do indeed contain a valid" + " size prefixed `{{STRUCT_NAME}}`."; + code_ += + "pub unsafe fn size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" + "_unchecked(buf: &[u8]) -> {{STRUCT_NAME}} {"; + code_ += + " flatbuffers::size_prefixed_root_unchecked::<{{STRUCT_NAME}}>" + "(buf)"; + code_ += "}"; if (parser_.file_identifier_.length()) { // Declare the identifier - code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\"; + // (no lifetime needed as constants have static lifetimes by default) + code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &str\\"; code_ += " = \"" + parser_.file_identifier_ + "\";"; code_ += ""; @@ -1547,22 +2342,22 @@ class RustGenerator : public BaseGenerator { code_ += "#[inline]"; code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\"; code_ += "(buf: &[u8]) -> bool {"; - code_ += " return flatbuffers::buffer_has_identifier(buf, \\"; - code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);"; + code_ += " flatbuffers::buffer_has_identifier(buf, \\"; + code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false)"; code_ += "}"; code_ += ""; code_ += "#[inline]"; code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\"; code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {"; - code_ += " return flatbuffers::buffer_has_identifier(buf, \\"; - code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);"; + code_ += " flatbuffers::buffer_has_identifier(buf, \\"; + code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true)"; code_ += "}"; code_ += ""; } if (parser_.file_extension_.length()) { // Return the extension - code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\"; + code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &str = \\"; code_ += "\"" + parser_.file_extension_ + "\";"; code_ += ""; } @@ -1619,6 +2414,24 @@ class RustGenerator : public BaseGenerator { *code_ptr += "padding" + NumToString((*id)++) + "__: 0,"; } + void ForAllStructFields(const StructDef &struct_def, + std::function cb) { + size_t offset_to_field = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type)); + code_.SetValue("FIELD_OBJECT_TYPE", ObjectFieldType(field, false)); + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("FIELD_OFFSET", NumToString(offset_to_field)); + code_.SetValue( + "REF", + IsStruct(field.value.type) || IsArray(field.value.type) ? "&" : ""); + cb(field); + const size_t size = InlineSize(field.value.type); + offset_to_field += size + field.padding; + } + } // Generate an accessor struct with constructor for a flatbuffers struct. void GenStruct(const StructDef &struct_def) { // Generates manual padding and alignment. @@ -1627,36 +2440,43 @@ class RustGenerator : public BaseGenerator { GenComment(struct_def.doc_comment); code_.SetValue("ALIGN", NumToString(struct_def.minalign)); code_.SetValue("STRUCT_NAME", Name(struct_def)); + code_.SetValue("STRUCT_SIZE", NumToString(struct_def.bytesize)); - code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}"; - code_ += "#[repr(C, align({{ALIGN}}))]"; - + // We represent Flatbuffers-structs in Rust-u8-arrays since the data may be + // of the wrong endianness and alignment 1. + // // PartialEq is useful to derive because we can correctly compare structs // for equality by just comparing their underlying byte data. This doesn't // hold for PartialOrd/Ord. - code_ += "#[derive(Clone, Copy, Debug, PartialEq)]"; - code_ += "pub struct {{STRUCT_NAME}} {"; - - int padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type)); - code_.SetValue("FIELD_NAME", Name(field)); - code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}},"; - - if (field.padding) { - std::string padding; - GenPadding(field, &padding, &padding_id, PaddingDefinition); - code_ += padding; - } - } + code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}"; + code_ += "#[repr(transparent)]"; + code_ += "#[derive(Clone, Copy, PartialEq)]"; + code_ += "pub struct {{STRUCT_NAME}}(pub [u8; {{STRUCT_SIZE}}]);"; + code_ += "impl Default for {{STRUCT_NAME}} { "; + code_ += " fn default() -> Self { "; + code_ += " Self([0; {{STRUCT_SIZE}}])"; + code_ += " }"; + code_ += "}"; - code_ += "} // pub struct {{STRUCT_NAME}}"; + // Debug for structs. + code_ += "impl std::fmt::Debug for {{STRUCT_NAME}} {"; + code_ += + " fn fmt(&self, f: &mut std::fmt::Formatter" + ") -> std::fmt::Result {"; + code_ += " f.debug_struct(\"{{STRUCT_NAME}}\")"; + ForAllStructFields(struct_def, [&](const FieldDef &unused) { + (void)unused; + code_ += " .field(\"{{FIELD_NAME}}\", &self.{{FIELD_NAME}}())"; + }); + code_ += " .finish()"; + code_ += " }"; + code_ += "}"; + code_ += ""; // Generate impls for SafeSliceAccess (because all structs are endian-safe), // Follow for the value type, Follow for the reference type, Push for the // value type, and Push for the reference type. + code_ += "impl flatbuffers::SimpleToVerifyInSlice for {{STRUCT_NAME}} {}"; code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}"; code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {"; code_ += " type Inner = &'a {{STRUCT_NAME}};"; @@ -1698,99 +2518,218 @@ class RustGenerator : public BaseGenerator { code_ += " }"; code_ += "}"; code_ += ""; - code_ += ""; - // Generate a constructor that takes all fields as arguments. - code_ += "impl {{STRUCT_NAME}} {"; - std::string arg_list; - std::string init_list; - padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - const auto member_name = Name(field) + "_"; - const auto reference = - StructMemberAccessNeedsCopy(field.value.type) ? "" : "&'a "; - const auto arg_name = "_" + Name(field); - const auto arg_type = reference + GetTypeGet(field.value.type); - - if (it != struct_def.fields.vec.begin()) { arg_list += ", "; } - arg_list += arg_name + ": "; - arg_list += arg_type; - init_list += " " + member_name; - if (StructMemberAccessNeedsCopy(field.value.type)) { - init_list += ": " + arg_name + ".to_little_endian(),\n"; - } else { - init_list += ": *" + arg_name + ",\n"; - } - } + // Generate verifier: Structs are simple so presence and alignment are + // all that need to be checked. + code_ += "impl<'a> flatbuffers::Verifiable for {{STRUCT_NAME}} {"; + code_ += " #[inline]"; + code_ += " fn run_verifier("; + code_ += " v: &mut flatbuffers::Verifier, pos: usize"; + code_ += " ) -> Result<(), flatbuffers::InvalidFlatbuffer> {"; + code_ += " use self::flatbuffers::Verifiable;"; + code_ += " v.in_buffer::(pos)"; + code_ += " }"; + code_ += "}"; - code_.SetValue("ARG_LIST", arg_list); - code_.SetValue("INIT_LIST", init_list); - code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {"; - code_ += " {{STRUCT_NAME}} {"; - code_ += "{{INIT_LIST}}"; - padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.padding) { - std::string padding; - GenPadding(field, &padding, &padding_id, PaddingInitializer); - code_ += " " + padding; - } - } - code_ += " }"; + // Generate a constructor that takes all fields as arguments. + code_ += "impl<'a> {{STRUCT_NAME}} {"; + code_ += " #[allow(clippy::too_many_arguments)]"; + code_ += " pub fn new("; + ForAllStructFields(struct_def, [&](const FieldDef &unused) { + (void)unused; + code_ += " {{FIELD_NAME}}: {{REF}}{{FIELD_TYPE}},"; + }); + code_ += " ) -> Self {"; + code_ += " let mut s = Self([0; {{STRUCT_SIZE}}]);"; + ForAllStructFields(struct_def, [&](const FieldDef &unused) { + (void)unused; + code_ += " s.set_{{FIELD_NAME}}({{REF}}{{FIELD_NAME}});"; + }); + code_ += " s"; code_ += " }"; + code_ += ""; if (parser_.opts.generate_name_strings) { GenFullyQualifiedNameGetter(struct_def, struct_def.name); } // Generate accessor methods for the struct. - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - - auto field_type = TableBuilderArgsAddFuncType(field, "'a"); - auto member = "self." + Name(field) + "_"; - auto value = StructMemberAccessNeedsCopy(field.value.type) - ? member + ".from_little_endian()" - : member; - - code_.SetValue("FIELD_NAME", Name(field)); - code_.SetValue("FIELD_TYPE", field_type); - code_.SetValue("FIELD_VALUE", value); - code_.SetValue("REF", IsStruct(field.value.type) ? "&" : ""); - - GenComment(field.doc_comment, " "); - code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {"; - code_ += " {{REF}}{{FIELD_VALUE}}"; - code_ += " }"; + ForAllStructFields(struct_def, [&](const FieldDef &field) { + this->GenComment(field.doc_comment, " "); + // Getter. + if (IsStruct(field.value.type)) { + code_ += " pub fn {{FIELD_NAME}}(&self) -> &{{FIELD_TYPE}} {"; + code_ += + " unsafe {" + " &*(self.0[{{FIELD_OFFSET}}..].as_ptr() as *const" + " {{FIELD_TYPE}}) }"; + } else if (IsArray(field.value.type)) { + code_.SetValue("ARRAY_SIZE", + NumToString(field.value.type.fixed_length)); + code_.SetValue("ARRAY_ITEM", GetTypeGet(field.value.type.VectorType())); + code_ += + " pub fn {{FIELD_NAME}}(&'a self) -> " + "flatbuffers::Array<'a, {{ARRAY_ITEM}}, {{ARRAY_SIZE}}> {"; + code_ += " flatbuffers::Array::follow(&self.0, {{FIELD_OFFSET}})"; + } else { + code_ += " pub fn {{FIELD_NAME}}(&self) -> {{FIELD_TYPE}} {"; + code_ += + " let mut mem = core::mem::MaybeUninit::" + "<{{FIELD_TYPE}}>::uninit();"; + code_ += " unsafe {"; + code_ += " core::ptr::copy_nonoverlapping("; + code_ += " self.0[{{FIELD_OFFSET}}..].as_ptr(),"; + code_ += " mem.as_mut_ptr() as *mut u8,"; + code_ += " core::mem::size_of::<{{FIELD_TYPE}}>(),"; + code_ += " );"; + code_ += " mem.assume_init()"; + code_ += " }.from_little_endian()"; + } + code_ += " }\n"; + // Setter. + if (IsStruct(field.value.type)) { + code_.SetValue("FIELD_SIZE", NumToString(InlineSize(field.value.type))); + code_ += " pub fn set_{{FIELD_NAME}}(&mut self, x: &{{FIELD_TYPE}}) {"; + code_ += + " self.0[{{FIELD_OFFSET}}..{{FIELD_OFFSET}}+{{FIELD_SIZE}}]" + ".copy_from_slice(&x.0)"; + } else if (IsArray(field.value.type)) { + if (GetFullType(field.value.type) == ftArrayOfBuiltin) { + code_.SetValue("ARRAY_ITEM", + GetTypeGet(field.value.type.VectorType())); + code_.SetValue( + "ARRAY_ITEM_SIZE", + NumToString(InlineSize(field.value.type.VectorType()))); + code_ += + " pub fn set_{{FIELD_NAME}}(&mut self, items: &{{FIELD_TYPE}}) " + "{"; + code_ += + " flatbuffers::emplace_scalar_array(&mut self.0, " + "{{FIELD_OFFSET}}, items);"; + } else { + code_.SetValue("FIELD_SIZE", + NumToString(InlineSize(field.value.type))); + code_ += + " pub fn set_{{FIELD_NAME}}(&mut self, x: &{{FIELD_TYPE}}) {"; + code_ += " unsafe {"; + code_ += " std::ptr::copy("; + code_ += " x.as_ptr() as *const u8,"; + code_ += " self.0.as_mut_ptr().add({{FIELD_OFFSET}}),"; + code_ += " {{FIELD_SIZE}},"; + code_ += " );"; + code_ += " }"; + } + } else { + code_ += " pub fn set_{{FIELD_NAME}}(&mut self, x: {{FIELD_TYPE}}) {"; + code_ += " let x_le = x.to_little_endian();"; + code_ += " unsafe {"; + code_ += " core::ptr::copy_nonoverlapping("; + code_ += " &x_le as *const {{FIELD_TYPE}} as *const u8,"; + code_ += " self.0[{{FIELD_OFFSET}}..].as_mut_ptr(),"; + code_ += " core::mem::size_of::<{{FIELD_TYPE}}>(),"; + code_ += " );"; + code_ += " }"; + } + code_ += " }\n"; // Generate a comparison function for this field if it is a key. if (field.key) { GenKeyFieldMethods(field); } + }); + + // Generate Object API unpack method. + if (parser_.opts.generate_object_based_api) { + code_.SetValue("NATIVE_STRUCT_NAME", NativeName(struct_def)); + code_ += " pub fn unpack(&self) -> {{NATIVE_STRUCT_NAME}} {"; + code_ += " {{NATIVE_STRUCT_NAME}} {"; + ForAllStructFields(struct_def, [&](const FieldDef &field) { + if (IsArray(field.value.type)) { + if (GetFullType(field.value.type) == ftArrayOfStruct) { + code_ += + " {{FIELD_NAME}}: { let {{FIELD_NAME}} = " + "self.{{FIELD_NAME}}(); flatbuffers::array_init(|i| " + "{{FIELD_NAME}}.get(i).unpack()) },"; + } else { + code_ += " {{FIELD_NAME}}: self.{{FIELD_NAME}}().into(),"; + } + } else { + std::string unpack = IsStruct(field.value.type) ? ".unpack()" : ""; + code_ += " {{FIELD_NAME}}: self.{{FIELD_NAME}}()" + unpack + ","; + } + }); + code_ += " }"; + code_ += " }"; } - code_ += "}"; + + code_ += "}"; // End impl Struct methods. code_ += ""; + + // Generate Struct Object. + if (parser_.opts.generate_object_based_api) { + // Struct declaration + code_ += "#[derive(Debug, Clone, PartialEq, Default)]"; + code_ += "pub struct {{NATIVE_STRUCT_NAME}} {"; + ForAllStructFields(struct_def, [&](const FieldDef &field) { + (void)field; // unused. + code_ += " pub {{FIELD_NAME}}: {{FIELD_OBJECT_TYPE}},"; + }); + code_ += "}"; + // The `pack` method that turns the native struct into its Flatbuffers + // counterpart. + code_ += "impl {{NATIVE_STRUCT_NAME}} {"; + code_ += " pub fn pack(&self) -> {{STRUCT_NAME}} {"; + code_ += " {{STRUCT_NAME}}::new("; + ForAllStructFields(struct_def, [&](const FieldDef &field) { + if (IsStruct(field.value.type)) { + code_ += " &self.{{FIELD_NAME}}.pack(),"; + } else if (IsArray(field.value.type)) { + if (GetFullType(field.value.type) == ftArrayOfStruct) { + code_ += + " &flatbuffers::array_init(|i| " + "self.{{FIELD_NAME}}[i].pack()),"; + } else { + code_ += " &self.{{FIELD_NAME}},"; + } + } else { + code_ += " self.{{FIELD_NAME}},"; + } + }); + code_ += " )"; + code_ += " }"; + code_ += "}"; + code_ += ""; + } } void GenNamespaceImports(const int white_spaces) { + // DO not use global attributes (i.e. #![...]) since it interferes + // with users who include! generated files. + // See: https://github.com/google/flatbuffers/issues/6261 std::string indent = std::string(white_spaces, ' '); code_ += ""; - for (auto it = parser_.included_files_.begin(); - it != parser_.included_files_.end(); ++it) { - if (it->second.empty()) continue; - auto noext = flatbuffers::StripExtension(it->second); - auto basename = flatbuffers::StripPath(noext); + if (!parser_.opts.generate_all) { + for (auto it = parser_.included_files_.begin(); + it != parser_.included_files_.end(); ++it) { + if (it->second.empty()) continue; + auto noext = flatbuffers::StripExtension(it->second); + auto basename = flatbuffers::StripPath(noext); + + if (parser_.opts.include_prefix.empty()) { + code_ += indent + "use crate::" + basename + + parser_.opts.filename_suffix + "::*;"; + } else { + auto prefix = parser_.opts.include_prefix; + prefix.pop_back(); - code_ += indent + "use crate::" + basename + "_generated::*;"; + code_ += indent + "use crate::" + prefix + "::" + basename + + parser_.opts.filename_suffix + "::*;"; + } + } } code_ += indent + "use std::mem;"; code_ += indent + "use std::cmp::Ordering;"; code_ += ""; code_ += indent + "extern crate flatbuffers;"; - code_ += indent + "use self::flatbuffers::EndianScalar;"; + code_ += indent + "use self::flatbuffers::{EndianScalar, Follow};"; } // Set up the correct namespace. This opens a namespace if the current diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_swift.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_swift.cpp index 055d9ddc..3fffd394 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_swift.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_swift.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include "flatbuffers/code_generators.h" @@ -30,28 +31,23 @@ inline std::string GenIndirect(const std::string &reading) { } inline std::string GenArrayMainBody(const std::string &optional) { - return "\tpublic func {{VALUENAME}}(at index: Int32) -> {{VALUETYPE}}" + + return "{{ACCESS_TYPE}} func {{VALUENAME}}(at index: Int32) -> " + "{{VALUETYPE}}" + optional + " { "; } -inline char LowerCase(char c) { - return static_cast(::tolower(static_cast(c))); -} - class SwiftGenerator : public BaseGenerator { private: - const Namespace *cur_name_space_; CodeWriter code_; std::unordered_set keywords_; - std::set namespaces_; int namespace_depth; public: SwiftGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name, "", ".", "swift"), - cur_name_space_(nullptr) { + : BaseGenerator(parser, path, file_name, "", "_", "swift") { namespace_depth = 0; + code_.SetPadding(" "); static const char *const keywords[] = { "associatedtype", "class", @@ -131,6 +127,7 @@ class SwiftGenerator : public BaseGenerator { "unowned", "weak", "willSet", + "Void", nullptr, }; for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw); @@ -139,34 +136,25 @@ class SwiftGenerator : public BaseGenerator { bool generate() { code_.Clear(); code_.SetValue("ACCESS", "_accessor"); - code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n"; + code_.SetValue("TABLEOFFSET", "VTOFFSET"); + code_ += "// " + std::string(FlatBuffersGeneratedWarning()); + code_ += "// swiftlint:disable all"; + code_ += "// swiftformat:disable all\n"; code_ += "import FlatBuffers\n"; // Generate code for all the enum declarations. for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); ++it) { const auto &enum_def = **it; - if (!enum_def.generated) { - SetNameSpace(enum_def.defined_namespace); - GenEnum(enum_def); - } + if (!enum_def.generated) { GenEnum(enum_def); } } for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { const auto &struct_def = **it; if (struct_def.fixed && !struct_def.generated) { - SetNameSpace(struct_def.defined_namespace); GenStructReader(struct_def); - } - } - - for (auto it = parser_.structs_.vec.begin(); - it != parser_.structs_.vec.end(); ++it) { - const auto &struct_def = **it; - if (struct_def.fixed && !struct_def.generated) { - SetNameSpace(struct_def.defined_namespace); - GenStructWriter(struct_def); + GenMutableStructReader(struct_def); } } @@ -174,13 +162,13 @@ class SwiftGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { const auto &struct_def = **it; if (!struct_def.fixed && !struct_def.generated) { - SetNameSpace(struct_def.defined_namespace); GenTable(struct_def); + if (parser_.opts.generate_object_based_api) { + GenObjectAPI(struct_def); + } } } - if (cur_name_space_) SetNameSpace(nullptr); - const auto filename = GeneratedFileName(path_, file_name_, parser_.opts); const auto final_code = code_.ToString(); return SaveFile(filename.c_str(), final_code, false); @@ -191,49 +179,156 @@ class SwiftGenerator : public BaseGenerator { code_ += "\n// MARK: - {{MARKVALUE}}\n"; } - // Generates the create function for swift - void GenStructWriter(const StructDef &struct_def) { - code_.SetValue("STRUCTNAME", Name(struct_def)); - std::string static_type = this->namespace_depth == 0 ? "" : "static "; - code_ += "public " + static_type + "func create{{STRUCTNAME}}(\\"; - std::string func_header = ""; - GenerateStructArgs(struct_def, &func_header, ""); - code_ += func_header.substr(0, func_header.size() - 2) + "\\"; - code_ += ") -> UnsafeMutableRawPointer {"; - code_ += - "\tlet memory = UnsafeMutableRawPointer.allocate(byteCount: " - "{{STRUCTNAME}}.size, alignment: {{STRUCTNAME}}.alignment)"; - code_ += - "\tmemory.initializeMemory(as: UInt8.self, repeating: 0, count: " - "{{STRUCTNAME}}.size)"; - GenerateStructBody(struct_def, ""); - code_ += "\treturn memory"; + // MARK: - Generating structs + + // Generates the reader for swift + void GenStructReader(const StructDef &struct_def) { + auto is_private_access = struct_def.attributes.Lookup("private"); + code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); + GenComment(struct_def.doc_comment); + code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def)); + code_ += "{{ACCESS_TYPE}} struct {{STRUCTNAME}}: NativeStruct\\"; + if (parser_.opts.generate_object_based_api) code_ += ", NativeObject\\"; + code_ += " {"; + code_ += ""; + Indent(); + code_ += ValidateFunc(); + code_ += ""; + int padding_id = 0; + std::string constructor = ""; + std::vector base_constructor; + std::vector main_constructor; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + if (!constructor.empty()) constructor += ", "; + + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + if (IsEnum(field.value.type)) { + code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); + } + code_.SetValue("VALUETYPE", type); + GenComment(field.doc_comment); + std::string valueType = + IsEnum(field.value.type) ? "{{BASEVALUE}}" : "{{VALUETYPE}}"; + code_ += "private var _{{VALUENAME}}: " + valueType; + auto accessing_value = IsEnum(field.value.type) ? ".value" : ""; + auto base_value = + IsStruct(field.value.type) ? (type + "()") : field.value.constant; + + main_constructor.push_back("_" + name + " = " + name + accessing_value); + base_constructor.push_back("_" + name + " = " + base_value); + + if (field.padding) { GenPadding(field, &padding_id); } + constructor += name + ": " + type; + } + code_ += ""; + BuildObjectConstructor(main_constructor, constructor); + BuildObjectConstructor(base_constructor, ""); + + if (parser_.opts.generate_object_based_api) + GenerateObjectAPIStructConstructor(struct_def); + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + code_.SetValue("VALUETYPE", type); + GenComment(field.doc_comment); + if (!IsEnum(field.value.type)) { + code_ += GenReaderMainBody() + "_{{VALUENAME}} }"; + } else if (IsEnum(field.value.type)) { + code_ += + GenReaderMainBody() + "{{VALUETYPE}}(rawValue: _{{VALUENAME}})! }"; + } + } + Outdent(); code_ += "}\n"; } - void GenerateStructBody(const StructDef &struct_def, - const std::string &nameprefix, int offset = 0) { + void GenMutableStructReader(const StructDef &struct_def) { + GenObjectHeader(struct_def); + for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; - auto name = nameprefix + Name(field); - const auto &field_type = field.value.type; - auto type = GenTypeBasic(field_type, false); - if (IsStruct(field.value.type)) { - GenerateStructBody(*field_type.struct_def, (nameprefix + field.name), - static_cast(field.value.offset)); - } else { - auto off = NumToString(offset + field.value.offset); - code_ += "\tmemory.storeBytes(of: " + name + - (field_type.enum_def ? ".rawValue" : "") + - ", toByteOffset: " + off + ", as: " + type + ".self)"; + auto offset = NumToString(field.value.offset); + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + if (IsEnum(field.value.type)) { + code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); } + code_.SetValue("VALUETYPE", type); + code_.SetValue("OFFSET", offset); + if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type)) { + code_ += + GenReaderMainBody() + "return " + GenReader("VALUETYPE") + " }"; + } else if (IsEnum(field.value.type)) { + code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); + code_ += GenReaderMainBody() + "return " + + GenEnumConstructor("{{OFFSET}}") + "?? " + + GenEnumDefaultValue(field) + " }"; + } else if (IsStruct(field.value.type)) { + code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); + code_ += GenReaderMainBody() + "return " + + GenConstructor("{{ACCESS}}.postion + {{OFFSET}}"); + } + if (parser_.opts.mutable_buffer && !IsStruct(field.value.type)) + code_ += GenMutate("{{OFFSET}}", "", IsEnum(field.value.type)); + } + + if (parser_.opts.generate_object_based_api) { + GenerateObjectAPIExtensionHeader(NameWrappedInNameSpace(struct_def)); + code_ += "return builder.create(struct: obj)"; + Outdent(); + code_ += "}"; } + Outdent(); + code_ += "}\n"; + } + + // Generates the create function for swift + void GenStructWriter(const StructDef &struct_def) { + auto is_private_access = struct_def.attributes.Lookup("private"); + code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); + code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def)); + code_.SetValue("SHORT_STRUCTNAME", Name(struct_def)); + code_ += "extension {{STRUCTNAME}} {"; + Indent(); + code_ += "@discardableResult"; + code_ += + "{{ACCESS_TYPE}} static func create{{SHORT_STRUCTNAME}}(builder: inout " + "FlatBufferBuilder, \\"; + std::string func_header = ""; + GenerateStructArgs(struct_def, &func_header, "", ""); + code_ += func_header.substr(0, func_header.size() - 2) + "\\"; + code_ += ") -> Offset {"; + Indent(); + code_ += + "builder.createStructOf(size: {{STRUCTNAME}}.size, alignment: " + "{{STRUCTNAME}}.alignment)"; + code_ += "return builder.endStruct()"; + Outdent(); + code_ += "}\n"; + Outdent(); + code_ += "}\n"; } void GenerateStructArgs(const StructDef &struct_def, std::string *code_ptr, - const std::string &nameprefix) { + const std::string &nameprefix, + const std::string &object_name, + const std::string &obj_api_named = "", + bool is_obj_api = false) { auto &code = *code_ptr; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -241,82 +336,124 @@ class SwiftGenerator : public BaseGenerator { if (field.deprecated) continue; const auto &field_type = field.value.type; if (IsStruct(field.value.type)) { - GenerateStructArgs(*field_type.struct_def, code_ptr, - (nameprefix + field.name)); + GenerateStructArgs( + *field_type.struct_def, code_ptr, (nameprefix + field.name), + (object_name + "." + field.name), obj_api_named, is_obj_api); } else { auto name = Name(field); auto type = GenType(field.value.type); - code += nameprefix + name + ": " + type; + if (!is_obj_api) { + code += nameprefix + name + ": " + type; + if (!IsEnum(field.value.type)) { + code += " = "; + auto is_bool = IsBool(field.value.type.base_type); + auto constant = + is_bool ? ("0" == field.value.constant ? "false" : "true") + : field.value.constant; + code += constant; + } + code += ", "; + continue; + } + code += + nameprefix + name + ": " + obj_api_named + object_name + "." + name; code += ", "; } } } + // MARK: - Table Generator + + // Generates the reader for swift + void GenTable(const StructDef &struct_def) { + auto is_private_access = struct_def.attributes.Lookup("private"); + code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); + + GenObjectHeader(struct_def); + GenTableAccessors(struct_def); + GenTableReader(struct_def); + GenTableWriter(struct_def); + if (parser_.opts.generate_object_based_api) + GenerateObjectAPITableExtension(struct_def); + Outdent(); + code_ += "}\n"; + } + + // Generates the reader for swift + void GenTableAccessors(const StructDef &struct_def) { + // Generate field id constants. + if (struct_def.fields.vec.size() > 0) { + code_ += "private enum {{TABLEOFFSET}}: VOffset {"; + Indent(); + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { continue; } + code_.SetValue("OFFSET_NAME", Name(field)); + code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); + code_ += "case {{OFFSET_NAME}} = {{OFFSET_VALUE}}"; + } + code_ += "var v: Int32 { Int32(self.rawValue) }"; + code_ += "var p: VOffset { self.rawValue }"; + Outdent(); + code_ += "}"; + code_ += ""; + } + } + void GenObjectHeader(const StructDef &struct_def) { GenComment(struct_def.doc_comment); - code_.SetValue("STRUCTNAME", Name(struct_def)); - code_.SetValue("PROTOCOL", - struct_def.fixed ? "Readable" : "FlatBufferObject"); + + code_.SetValue("SHORT_STRUCTNAME", Name(struct_def)); + code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def)); code_.SetValue("OBJECTTYPE", struct_def.fixed ? "Struct" : "Table"); - code_ += "public struct {{STRUCTNAME}}: {{PROTOCOL}} {\n"; + code_.SetValue("MUTABLE", struct_def.fixed ? Mutable() : ""); + code_ += + "{{ACCESS_TYPE}} struct {{STRUCTNAME}}{{MUTABLE}}: FlatBufferObject\\"; + if (!struct_def.fixed && parser_.opts.generate_object_based_api) + code_ += ", ObjectAPIPacker\\"; + code_ += " {\n"; + Indent(); code_ += ValidateFunc(); - code_ += "\tpublic var __buffer: ByteBuffer! { return {{ACCESS}}.bb }"; - code_ += "\n\tprivate var {{ACCESS}}: {{OBJECTTYPE}}"; - if (struct_def.fixed) { - code_.SetValue("BYTESIZE", NumToString(struct_def.bytesize)); - code_.SetValue("MINALIGN", NumToString(struct_def.minalign)); - code_ += "\tpublic static var size = {{BYTESIZE}}"; - code_ += "\tpublic static var alignment = {{MINALIGN}}\t"; - } else { + code_ += + "{{ACCESS_TYPE}} var __buffer: ByteBuffer! { return {{ACCESS}}.bb }"; + code_ += "private var {{ACCESS}}: {{OBJECTTYPE}}\n"; + if (!struct_def.fixed) { if (parser_.file_identifier_.length()) { code_.SetValue("FILENAME", parser_.file_identifier_); code_ += - "\tpublic static func finish(_ fbb: FlatBufferBuilder, end: " - "Offset, prefix: Bool = false) { fbb.finish(offset: end, " + "{{ACCESS_TYPE}} static func finish(_ fbb: inout " + "FlatBufferBuilder, end: " + "Offset, prefix: Bool = false) { fbb.finish(offset: end, " "fileId: " "\"{{FILENAME}}\", addPrefix: prefix) }"; } code_ += - "\tpublic static func getRootAs{{STRUCTNAME}}(bb: ByteBuffer) -> " + "{{ACCESS_TYPE}} static func getRootAs{{SHORT_STRUCTNAME}}(bb: " + "ByteBuffer) -> " "{{STRUCTNAME}} { return {{STRUCTNAME}}(Table(bb: bb, position: " "Int32(bb.read(def: UOffset.self, position: bb.reader)) + " "Int32(bb.reader))) }\n"; - code_ += "\tprivate init(_ t: Table) { {{ACCESS}} = t }"; + code_ += "private init(_ t: Table) { {{ACCESS}} = t }"; } code_ += - "\tpublic init(_ bb: ByteBuffer, o: Int32) { {{ACCESS}} = " + "{{ACCESS_TYPE}} init(_ bb: ByteBuffer, o: Int32) { {{ACCESS}} = " "{{OBJECTTYPE}}(bb: " "bb, position: o) }"; code_ += ""; } - // Generates the reader for swift - void GenTable(const StructDef &struct_def) { - GenObjectHeader(struct_def); - GenTableReader(struct_def); - GenTableWriter(struct_def); - code_ += "}\n"; - } - - void GenTableReader(const StructDef &struct_def) { - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) continue; - GenTableReaderFields(field); - } - } - void GenTableWriter(const StructDef &struct_def) { flatbuffers::FieldDef *key_field = nullptr; std::vector require_fields; - std::string create_func_body; - std::string create_func_header; + std::vector create_func_body; + std::vector create_func_header; auto should_generate_create = struct_def.fields.vec.size() != 0; code_.SetValue("NUMBEROFFIELDS", NumToString(struct_def.fields.vec.size())); code_ += - "\tpublic static func start{{STRUCTNAME}}(_ fbb: FlatBufferBuilder) -> " + "{{ACCESS_TYPE}} static func start{{SHORT_STRUCTNAME}}(_ fbb: inout " + "FlatBufferBuilder) -> " "UOffset { fbb.startTable(with: {{NUMBEROFFIELDS}}) }"; for (auto it = struct_def.fields.vec.begin(); @@ -324,17 +461,16 @@ class SwiftGenerator : public BaseGenerator { auto &field = **it; if (field.deprecated) continue; if (field.key) key_field = &field; - if (field.required) + if (field.IsRequired()) require_fields.push_back(NumToString(field.value.offset)); - GenTableWriterFields( - field, &create_func_body, &create_func_header, - static_cast(it - struct_def.fields.vec.begin())); + GenTableWriterFields(field, &create_func_body, &create_func_header); } code_ += - "\tpublic static func end{{STRUCTNAME}}(_ fbb: FlatBufferBuilder, " + "{{ACCESS_TYPE}} static func end{{SHORT_STRUCTNAME}}(_ fbb: inout " + "FlatBufferBuilder, " "start: " - "UOffset) -> Offset { let end = Offset(offset: " + "UOffset) -> Offset { let end = Offset(offset: " "fbb.endTable(at: start))\\"; if (require_fields.capacity() != 0) { std::string fields = ""; @@ -345,29 +481,42 @@ class SwiftGenerator : public BaseGenerator { } code_ += "; return end }"; - code_ += - "\tpublic static func create{{STRUCTNAME}}(_ fbb: FlatBufferBuilder\\"; - if (should_generate_create) - code_ += ",\n" + - create_func_header.substr(0, create_func_header.size() - 2) + - "\\"; - code_ += ") -> Offset {"; - code_ += "\t\tlet __start = {{STRUCTNAME}}.start{{STRUCTNAME}}(fbb)"; - if (should_generate_create) - code_ += create_func_body.substr(0, create_func_body.size() - 1); - code_ += "\t\treturn {{STRUCTNAME}}.end{{STRUCTNAME}}(fbb, start: __start)"; - code_ += "\t}"; - - std::string spacing = "\t\t"; + if (should_generate_create) { + code_ += "{{ACCESS_TYPE}} static func create{{SHORT_STRUCTNAME}}("; + Indent(); + code_ += "_ fbb: inout FlatBufferBuilder,"; + for (auto it = create_func_header.begin(); it < create_func_header.end(); + ++it) { + code_ += *it + "\\"; + if (it < create_func_header.end() - 1) code_ += ","; + } + code_ += ""; + Outdent(); + code_ += ") -> Offset {"; + Indent(); + code_ += "let __start = {{STRUCTNAME}}.start{{SHORT_STRUCTNAME}}(&fbb)"; + for (auto it = create_func_body.begin(); it < create_func_body.end(); + ++it) { + code_ += *it; + } + code_ += + "return {{STRUCTNAME}}.end{{SHORT_STRUCTNAME}}(&fbb, start: __start)"; + Outdent(); + code_ += "}"; + } + + std::string spacing = ""; if (key_field != nullptr && !struct_def.fixed && struct_def.has_key) { - code_.SetValue("VALUENAME", struct_def.name); + code_.SetValue("VALUENAME", NameWrappedInNameSpace(struct_def)); + code_.SetValue("SHORT_VALUENAME", Name(struct_def)); code_.SetValue("VOFFSET", NumToString(key_field->value.offset)); code_ += - "\tpublic static func " - "sortVectorOf{{VALUENAME}}(offsets:[Offset], " - "_ fbb: FlatBufferBuilder) -> Offset {"; + "{{ACCESS_TYPE}} static func " + "sortVectorOf{{SHORT_VALUENAME}}(offsets:[Offset], " + "_ fbb: inout FlatBufferBuilder) -> Offset {"; + Indent(); code_ += spacing + "var off = offsets"; code_ += spacing + @@ -375,72 +524,123 @@ class SwiftGenerator : public BaseGenerator { "{{VOFFSET}}, fbb: fbb.buffer), Table.offset(Int32($0.o), vOffset: " "{{VOFFSET}}, fbb: fbb.buffer), fbb: fbb.buffer) < 0 } "; code_ += spacing + "return fbb.createVector(ofOffsets: off)"; - code_ += "\t}"; + Outdent(); + code_ += "}"; GenLookup(*key_field); } } - void GenTableWriterFields(const FieldDef &field, std::string *create_body, - std::string *create_header, const int position) { - std::string builder_string = ", _ fbb: FlatBufferBuilder) { fbb.add("; + void GenTableWriterFields(const FieldDef &field, + std::vector *create_body, + std::vector *create_header) { + std::string builder_string = ", _ fbb: inout FlatBufferBuilder) { "; auto &create_func_body = *create_body; auto &create_func_header = *create_header; auto name = Name(field); auto type = GenType(field.value.type); + auto opt_scalar = + field.IsOptional() && IsScalar(field.value.type.base_type); + auto nullable_type = opt_scalar ? type + "?" : type; code_.SetValue("VALUENAME", name); - code_.SetValue("VALUETYPE", type); - code_.SetValue("OFFSET", NumToString(position)); + code_.SetValue("VALUETYPE", nullable_type); + code_.SetValue("OFFSET", name); code_.SetValue("CONSTANT", field.value.constant); std::string check_if_vector = - (field.value.type.base_type == BASE_TYPE_VECTOR || - field.value.type.base_type == BASE_TYPE_ARRAY) - ? "VectorOf(" - : "("; - std::string body = "add" + check_if_vector + name + ": "; - code_ += "\tpublic static func " + body + "\\"; + (IsVector(field.value.type) || IsArray(field.value.type)) ? "VectorOf(" + : "("; + auto body = "add" + check_if_vector + name + ": "; + code_ += "{{ACCESS_TYPE}} static func " + body + "\\"; - create_func_body += "\t\t{{STRUCTNAME}}." + body + name + ", fbb)\n"; + create_func_body.push_back("{{STRUCTNAME}}." + body + name + ", &fbb)"); if (IsScalar(field.value.type.base_type) && !IsBool(field.value.type.base_type)) { - auto default_value = IsEnum(field.value.type) ? GenEnumDefaultValue(field) - : field.value.constant; - auto is_enum = IsEnum(field.value.type) ? ".rawValue" : ""; - code_ += "{{VALUETYPE}}" + builder_string + "element: {{VALUENAME}}" + - is_enum + ", def: {{CONSTANT}}, at: {{OFFSET}}) }"; - create_func_header += - "\t\t" + name + ": " + type + " = " + default_value + ",\n"; + std::string is_enum = IsEnum(field.value.type) ? ".rawValue" : ""; + std::string optional_enum = + IsEnum(field.value.type) ? ("?" + is_enum) : ""; + code_ += + "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{VALUENAME}}\\"; + + code_ += field.IsOptional() ? (optional_enum + "\\") + : (is_enum + ", def: {{CONSTANT}}\\"); + + code_ += ", at: {{TABLEOFFSET}}.{{OFFSET}}.p) }"; + + auto default_value = + IsEnum(field.value.type) + ? (field.IsOptional() ? "nil" : GenEnumDefaultValue(field)) + : field.value.constant; + create_func_header.push_back( + "" + name + ": " + nullable_type + " = " + + (field.IsOptional() ? "nil" : default_value)); return; } if (IsBool(field.value.type.base_type)) { std::string default_value = "0" == field.value.constant ? "false" : "true"; - code_.SetValue("VALUETYPE", "Bool"); + code_.SetValue("CONSTANT", default_value); + code_.SetValue("VALUETYPE", field.IsOptional() ? "Bool?" : "Bool"); code_ += "{{VALUETYPE}}" + builder_string + - "condition: {{VALUENAME}}, def: {{CONSTANT}}, at: {{OFFSET}}) }"; - create_func_header += - "\t\t" + name + ": " + type + " = " + default_value + ",\n"; + "fbb.add(element: {{VALUENAME}},\\"; + code_ += field.IsOptional() ? "\\" : " def: {{CONSTANT}},"; + code_ += " at: {{TABLEOFFSET}}.{{OFFSET}}.p) }"; + create_func_header.push_back( + name + ": " + nullable_type + " = " + + (field.IsOptional() ? "nil" : default_value)); + return; + } + + if (IsStruct(field.value.type)) { + auto create_struct = + "guard let {{VALUENAME}} = {{VALUENAME}} else { return };" + " fbb.create(struct: {{VALUENAME}}, position: " + "{{TABLEOFFSET}}.{{OFFSET}}.p) }"; + code_ += type + "?" + builder_string + create_struct; + /// Optional hard coded since structs are always optional + create_func_header.push_back(name + ": " + type + "? = nil"); return; } - auto offset_type = field.value.type.base_type == BASE_TYPE_STRING - ? "Offset" - : "Offset"; auto camel_case_name = - (field.value.type.base_type == BASE_TYPE_VECTOR || - field.value.type.base_type == BASE_TYPE_ARRAY - ? "vectorOf" - : "offsetOf") + - MakeCamel(name, true); - create_func_header += "\t\t" + camel_case_name + " " + name + ": " + - offset_type + " = Offset(),\n"; + MakeCamel(name, false) + + (IsVector(field.value.type) || IsArray(field.value.type) + ? "VectorOffset" + : "Offset"); + create_func_header.push_back(camel_case_name + " " + name + ": " + + "Offset = Offset()"); auto reader_type = IsStruct(field.value.type) && field.value.type.struct_def->fixed - ? "structOffset: {{OFFSET}}) }" - : "offset: {{VALUENAME}}, at: {{OFFSET}}) }"; - code_ += offset_type + builder_string + reader_type; + ? "structOffset: {{TABLEOFFSET}}.{{OFFSET}}.p) }" + : "offset: {{VALUENAME}}, at: {{TABLEOFFSET}}.{{OFFSET}}.p) }"; + code_ += "Offset" + builder_string + "fbb.add(" + reader_type; + + auto vectortype = field.value.type.VectorType(); + + if ((vectortype.base_type == BASE_TYPE_STRUCT && + field.value.type.struct_def->fixed) && + (IsVector(field.value.type) || IsArray(field.value.type))) { + auto field_name = NameWrappedInNameSpace(*vectortype.struct_def); + code_ += "public static func startVectorOf" + MakeCamel(name, true) + + "(_ size: Int, in builder: inout " + "FlatBufferBuilder) {"; + Indent(); + code_ += "builder.startVector(size * MemoryLayout<" + field_name + + ">.size, elementSize: MemoryLayout<" + field_name + + ">.alignment)"; + Outdent(); + code_ += "}"; + } + } + + void GenTableReader(const StructDef &struct_def) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + GenTableReaderFields(field); + } } void GenTableReaderFields(const FieldDef &field) { @@ -449,33 +649,38 @@ class SwiftGenerator : public BaseGenerator { auto type = GenType(field.value.type); code_.SetValue("VALUENAME", name); code_.SetValue("VALUETYPE", type); - code_.SetValue("OFFSET", offset); + code_.SetValue("OFFSET", name); code_.SetValue("CONSTANT", field.value.constant); - std::string const_string = "return o == 0 ? {{CONSTANT}} : "; - GenComment(field.doc_comment, "\t"); + std::string def_Val = field.IsDefault() ? "{{CONSTANT}}" : "nil"; + std::string optional = field.IsOptional() ? "?" : ""; + auto const_string = "return o == 0 ? " + def_Val + " : "; + GenComment(field.doc_comment); if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type) && !IsBool(field.value.type.base_type)) { - code_ += GenReaderMainBody() + GenOffset() + const_string + + code_ += GenReaderMainBody(optional) + GenOffset() + const_string + GenReader("VALUETYPE", "o") + " }"; if (parser_.opts.mutable_buffer) code_ += GenMutate("o", GenOffset()); return; } if (IsBool(field.value.type.base_type)) { + std::string default_value = + "0" == field.value.constant ? "false" : "true"; + code_.SetValue("CONSTANT", default_value); code_.SetValue("VALUETYPE", "Bool"); - code_ += GenReaderMainBody() + "\\"; + code_ += GenReaderMainBody(optional) + "\\"; code_.SetValue("VALUETYPE", "Byte"); - code_ += GenOffset() + - "return o == 0 ? false : 0 != " + GenReader("VALUETYPE", "o") + - " }"; + code_ += GenOffset() + "return o == 0 ? {{CONSTANT}} : 0 != " + + GenReader("VALUETYPE", "o") + " }"; if (parser_.opts.mutable_buffer) code_ += GenMutate("o", GenOffset()); return; } if (IsEnum(field.value.type)) { - auto default_value = GenEnumDefaultValue(field); + auto default_value = + field.IsOptional() ? "nil" : GenEnumDefaultValue(field); code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); - code_ += GenReaderMainBody() + "\\"; + code_ += GenReaderMainBody(optional) + "\\"; code_ += GenOffset() + "return o == 0 ? " + default_value + " : " + GenEnumConstructor("o") + "?? " + default_value + " }"; if (parser_.opts.mutable_buffer && !IsUnion(field.value.type)) @@ -483,10 +688,18 @@ class SwiftGenerator : public BaseGenerator { return; } + std::string is_required = field.IsRequired() ? "!" : "?"; + auto required_reader = field.IsRequired() ? "return " : const_string; + if (IsStruct(field.value.type) && field.value.type.struct_def->fixed) { code_.SetValue("VALUETYPE", GenType(field.value.type)); code_.SetValue("CONSTANT", "nil"); - code_ += GenReaderMainBody("?") + GenOffset() + const_string + + code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader + + "{{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, at: o) }"; + code_.SetValue("VALUENAME", "mutable" + MakeCamel(name)); + code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); + code_.SetValue("CONSTANT", "nil"); + code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader + GenConstructor("o + {{ACCESS}}.postion"); return; } @@ -494,62 +707,65 @@ class SwiftGenerator : public BaseGenerator { case BASE_TYPE_STRUCT: code_.SetValue("VALUETYPE", GenType(field.value.type)); code_.SetValue("CONSTANT", "nil"); - code_ += GenReaderMainBody("?") + GenOffset() + const_string + + code_ += GenReaderMainBody(is_required) + GenOffset() + + required_reader + GenConstructor(GenIndirect("o + {{ACCESS}}.postion")); break; - case BASE_TYPE_STRING: + case BASE_TYPE_STRING: { + auto default_string = "\"" + field.value.constant + "\""; code_.SetValue("VALUETYPE", GenType(field.value.type)); - code_.SetValue("CONSTANT", "nil"); - code_ += GenReaderMainBody("?") + GenOffset() + const_string + - "{{ACCESS}}.string(at: o) }"; - code_ += - "\tpublic var {{VALUENAME}}SegmentArray: [UInt8]? { return " - "{{ACCESS}}.getVector(at: {{OFFSET}}) }"; + code_.SetValue("CONSTANT", field.IsDefault() ? default_string : "nil"); + code_ += GenReaderMainBody(is_required) + GenOffset() + + required_reader + "{{ACCESS}}.string(at: o) }"; + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}SegmentArray: [UInt8]" + + is_required + + " { return " + "{{ACCESS}}.getVector(at: {{TABLEOFFSET}}.{{OFFSET}}.v) }"; break; - + } case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru - case BASE_TYPE_VECTOR: - GenTableReaderVectorFields(field, const_string); - break; + case BASE_TYPE_VECTOR: GenTableReaderVectorFields(field); break; case BASE_TYPE_UNION: code_.SetValue("CONSTANT", "nil"); code_ += - "\tpublic func {{VALUENAME}}(type: " - "T.Type) -> T? { " + - GenOffset() + const_string + "{{ACCESS}}.union(o) }"; + "{{ACCESS_TYPE}} func {{VALUENAME}}(type: " + "T.Type) -> T" + + is_required + " { " + GenOffset() + required_reader + + "{{ACCESS}}.union(o) }"; break; default: FLATBUFFERS_ASSERT(0); } } - void GenTableReaderVectorFields(const FieldDef &field, - const std::string &const_string) { + void GenTableReaderVectorFields(const FieldDef &field) { + std::string const_string = "return o == 0 ? {{CONSTANT}} : "; auto vectortype = field.value.type.VectorType(); code_.SetValue("SIZE", NumToString(InlineSize(vectortype))); - code_ += "\tpublic var {{VALUENAME}}Count: Int32 { " + GenOffset() + - const_string + "{{ACCESS}}.vector(count: o) }"; - code_.SetValue("CONSTANT", IsScalar(vectortype.base_type) == true - ? field.value.constant - : "nil"); + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}Count: Int32 { " + GenOffset() + + "return o == 0 ? 0 : {{ACCESS}}.vector(count: o) }"; + code_.SetValue("CONSTANT", + IsScalar(vectortype.base_type) == true ? "0" : "nil"); auto nullable = IsScalar(vectortype.base_type) == true ? "" : "?"; nullable = IsEnum(vectortype) == true ? "?" : nullable; + if (vectortype.base_type != BASE_TYPE_UNION) { code_ += GenArrayMainBody(nullable) + GenOffset() + "\\"; } else { code_ += - "\tpublic func {{VALUENAME}}(at index: " + "{{ACCESS_TYPE}} func {{VALUENAME}}(at " + "index: " "Int32, type: T.Type) -> T? { " + GenOffset() + "\\"; } if (IsBool(vectortype.base_type)) { code_.SetValue("CONSTANT", field.value.offset == 0 ? "false" : "true"); - code_.SetValue("VALUETYPE", "Byte"); + code_.SetValue("VALUETYPE", "Bool"); } - if (!IsEnum(vectortype)) - code_ += - const_string + (IsBool(vectortype.base_type) ? "0 != " : "") + "\\"; + + if (!IsEnum(vectortype)) code_ += const_string + "\\"; if (IsScalar(vectortype.base_type) && !IsEnum(vectortype) && !IsBool(field.value.type.base_type)) { @@ -557,18 +773,26 @@ class SwiftGenerator : public BaseGenerator { "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: " "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; code_ += - "\tpublic var {{VALUENAME}}: [{{VALUETYPE}}] { return " - "{{ACCESS}}.getVector(at: {{OFFSET}}) ?? [] }"; + "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}] { return " + "{{ACCESS}}.getVector(at: {{TABLEOFFSET}}.{{OFFSET}}.v) ?? [] }"; if (parser_.opts.mutable_buffer) code_ += GenMutateArray(); return; } + if (vectortype.base_type == BASE_TYPE_STRUCT && field.value.type.struct_def->fixed) { - code_ += GenConstructor("{{ACCESS}}.vector(at: o) + index * {{SIZE}}"); + code_ += + "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: " + "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; + code_.SetValue("VALUENAME", "mutable" + MakeCamel(Name(field))); + code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); + code_ += GenArrayMainBody(nullable) + GenOffset() + const_string + + GenConstructor("{{ACCESS}}.vector(at: o) + index * {{SIZE}}"); + return; } - if (vectortype.base_type == BASE_TYPE_STRING) { + if (IsString(vectortype)) { code_ += "{{ACCESS}}.directString(at: {{ACCESS}}.vector(at: o) + " "index * {{SIZE}}) }"; @@ -610,139 +834,604 @@ class SwiftGenerator : public BaseGenerator { void GenByKeyFunctions(const FieldDef &key_field) { code_.SetValue("TYPE", GenType(key_field.value.type)); code_ += - "\tpublic func {{VALUENAME}}By(key: {{TYPE}}) -> {{VALUETYPE}}? { \\"; + "{{ACCESS_TYPE}} func {{VALUENAME}}By(key: {{TYPE}}) -> {{VALUETYPE}}? " + "{ \\"; code_ += GenOffset() + "return o == 0 ? nil : {{VALUETYPE}}.lookupByKey(vector: " "{{ACCESS}}.vector(at: o), key: key, fbb: {{ACCESS}}.bb) }"; } - // Generates the reader for swift - void GenStructReader(const StructDef &struct_def) { - GenObjectHeader(struct_def); + void GenEnum(const EnumDef &enum_def) { + if (enum_def.generated) return; + auto is_private_access = enum_def.attributes.Lookup("private"); + code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); + code_.SetValue("ENUM_NAME", NameWrappedInNameSpace(enum_def)); + code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false)); + GenComment(enum_def.doc_comment); + code_ += "{{ACCESS_TYPE}} enum {{ENUM_NAME}}: {{BASE_TYPE}}, Enum {"; + Indent(); + code_ += "{{ACCESS_TYPE}} typealias T = {{BASE_TYPE}}"; + code_ += + "{{ACCESS_TYPE}} static var byteSize: Int { return " + "MemoryLayout<{{BASE_TYPE}}>.size " + "}"; + code_ += + "{{ACCESS_TYPE}} var value: {{BASE_TYPE}} { return self.rawValue }"; + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + const auto &ev = **it; + auto name = Name(ev); + code_.SetValue("KEY", name); + code_.SetValue("VALUE", enum_def.ToString(ev)); + GenComment(ev.doc_comment); + code_ += "case {{KEY}} = {{VALUE}}"; + } + code_ += "\n"; + AddMinOrMaxEnumValue(Name(*enum_def.MaxValue()), "max"); + AddMinOrMaxEnumValue(Name(*enum_def.MinValue()), "min"); + Outdent(); + code_ += "}\n"; + if (parser_.opts.generate_object_based_api && enum_def.is_union) { + code_ += "{{ACCESS_TYPE}} struct {{ENUM_NAME}}Union {"; + Indent(); + code_ += "{{ACCESS_TYPE}} var type: {{ENUM_NAME}}"; + code_ += "{{ACCESS_TYPE}} var value: NativeObject?"; + code_ += + "{{ACCESS_TYPE}} init(_ v: NativeObject?, type: {{ENUM_NAME}}) {"; + Indent(); + code_ += "self.type = type"; + code_ += "self.value = v"; + Outdent(); + code_ += "}"; + code_ += + "{{ACCESS_TYPE}} func pack(builder: inout FlatBufferBuilder) -> " + "Offset {"; + Indent(); + BuildUnionEnumSwitchCaseWritter(enum_def); + Outdent(); + code_ += "}"; + Outdent(); + code_ += "}"; + } + } + + // MARK: - Object API + + void GenerateObjectAPIExtensionHeader(std::string name) { + code_ += "\n"; + code_ += "{{ACCESS_TYPE}} mutating func unpack() -> " + name + " {"; + Indent(); + code_ += "return " + name + "(&self)"; + Outdent(); + code_ += "}"; + code_ += + "{{ACCESS_TYPE}} static func pack(_ builder: inout FlatBufferBuilder, " + "obj: " + "inout " + + name + "?) -> Offset {"; + Indent(); + code_ += "guard var obj = obj else { return Offset() }"; + code_ += "return pack(&builder, obj: &obj)"; + Outdent(); + code_ += "}"; + code_ += ""; + code_ += + "{{ACCESS_TYPE}} static func pack(_ builder: inout FlatBufferBuilder, " + "obj: " + "inout " + + name + ") -> Offset {"; + Indent(); + } + + void GenerateObjectAPIStructConstructor(const StructDef &struct_def) { + code_ += + "{{ACCESS_TYPE}} init(_ _t: inout {{STRUCTNAME}}" + Mutable() + ") {"; + Indent(); for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; - auto offset = NumToString(field.value.offset); + auto name = Name(field); auto type = GenType(field.value.type); code_.SetValue("VALUENAME", name); - code_.SetValue("VALUETYPE", type); - code_.SetValue("OFFSET", offset); - GenComment(field.doc_comment, "\t"); - if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type)) { - code_ += - GenReaderMainBody() + "return " + GenReader("VALUETYPE") + " }"; - if (parser_.opts.mutable_buffer) code_ += GenMutate("{{OFFSET}}", ""); - } else if (IsEnum(field.value.type)) { - code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); - code_ += GenReaderMainBody() + "return " + - GenEnumConstructor("{{OFFSET}}") + "?? " + - GenEnumDefaultValue(field) + " }"; - } else if (IsStruct(field.value.type)) { - code_.SetValue("VALUETYPE", GenType(field.value.type)); - code_ += GenReaderMainBody() + "return " + - GenConstructor("{{ACCESS}}.postion + {{OFFSET}}"); + if (IsStruct(field.value.type)) { + code_ += "var _v{{VALUENAME}} = _t.{{VALUENAME}}"; + code_ += "_{{VALUENAME}} = _v{{VALUENAME}}.unpack()"; + continue; } + std::string is_enum = IsEnum(field.value.type) ? ".value" : ""; + code_ += "_{{VALUENAME}} = _t.{{VALUENAME}}" + is_enum; } - + Outdent(); code_ += "}\n"; } - void GenEnum(const EnumDef &enum_def) { - if (enum_def.generated) return; - code_.SetValue("ENUM_NAME", Name(enum_def)); - code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false)); - GenComment(enum_def.doc_comment); - code_ += "public enum {{ENUM_NAME}}: {{BASE_TYPE}}, Enum { "; - code_ += "\tpublic typealias T = {{BASE_TYPE}}"; + void GenObjectAPI(const StructDef &struct_def) { + code_ += "{{ACCESS_TYPE}} class " + ObjectAPIName("{{STRUCTNAME}}") + + ": NativeObject {\n"; + std::vector buffer_constructor; + std::vector base_constructor; + Indent(); + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + BuildObjectAPIConstructorBody(field, struct_def.fixed, buffer_constructor, + base_constructor); + } + code_ += ""; + BuildObjectConstructor(buffer_constructor, + "_ _t: inout " + NameWrappedInNameSpace(struct_def)); + BuildObjectConstructor(base_constructor); + if (!struct_def.fixed) + code_ += + "{{ACCESS_TYPE}} func serialize() -> ByteBuffer { return " + "serialize(type: " + "{{STRUCTNAME}}.self) }\n"; + Outdent(); + code_ += "}"; + } + + void GenerateObjectAPITableExtension(const StructDef &struct_def) { + GenerateObjectAPIExtensionHeader(ObjectAPIName("{{STRUCTNAME}}")); + std::vector unpack_body; + std::string builder = ", &builder)"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + auto name = Name(field); + auto type = GenType(field.value.type); + std::string check_if_vector = + (IsVector(field.value.type) || IsArray(field.value.type)) + ? "VectorOf(" + : "("; + std::string body = "add" + check_if_vector + name + ": "; + switch (field.value.type.base_type) { + case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); + case BASE_TYPE_VECTOR: { + GenerateVectorObjectAPITableExtension(field, name, type); + unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name + + builder); + break; + } + case BASE_TYPE_UNION: { + code_ += "let __" + name + " = obj." + name + + "?.pack(builder: &builder) ?? Offset()"; + unpack_body.push_back("if let o = obj." + name + "?.type {"); + unpack_body.push_back(" {{STRUCTNAME}}.add(" + name + "Type: o" + + builder); + unpack_body.push_back(" {{STRUCTNAME}}." + body + "__" + name + + builder); + unpack_body.push_back("}\n"); + break; + } + case BASE_TYPE_STRUCT: { + if (field.value.type.struct_def && + field.value.type.struct_def->fixed) { + // This is a Struct (IsStruct), not a table. We create + // a native swift object in this case. + std::string code; + GenerateStructArgs(*field.value.type.struct_def, &code, "", "", + "$0", true); + code = code.substr(0, code.size() - 2); + unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." + name + + builder); + } else { + code_ += "let __" + name + " = " + type + + ".pack(&builder, obj: &obj." + name + ")"; + unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name + + builder); + } + break; + } + case BASE_TYPE_STRING: { + unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name + + builder); + if (field.IsRequired()) { + code_ += + "let __" + name + " = builder.create(string: obj." + name + ")"; + } else { + BuildingOptionalObjects(name, "builder.create(string: s)"); + } + break; + } + case BASE_TYPE_UTYPE: break; + default: + unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." + name + + builder); + } + } + code_ += "let __root = {{STRUCTNAME}}.start{{SHORT_STRUCTNAME}}(&builder)"; + for (auto it = unpack_body.begin(); it < unpack_body.end(); it++) + code_ += *it; code_ += - "\tpublic static var byteSize: Int { return " - "MemoryLayout<{{BASE_TYPE}}>.size " - "}"; - code_ += "\tpublic var value: {{BASE_TYPE}} { return self.rawValue }"; + "return {{STRUCTNAME}}.end{{SHORT_STRUCTNAME}}(&builder, start: " + "__root)"; + Outdent(); + code_ += "}"; + } - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { - const auto &ev = **it; - auto name = Name(ev); - std::transform(name.begin(), name.end(), name.begin(), LowerCase); - code_.SetValue("KEY", name); - code_.SetValue("VALUE", enum_def.ToString(ev)); - GenComment(ev.doc_comment, "\t"); - code_ += "\tcase {{KEY}} = {{VALUE}}"; + void GenerateVectorObjectAPITableExtension(const FieldDef &field, + const std::string &name, + const std::string &type) { + auto vectortype = field.value.type.VectorType(); + switch (vectortype.base_type) { + case BASE_TYPE_UNION: { + code_ += "var __" + name + "__: [Offset] = []"; + code_ += "for i in obj." + name + " {"; + Indent(); + code_ += "guard let off = i?.pack(builder: &builder) else { continue }"; + code_ += "__" + name + "__.append(off)"; + Outdent(); + code_ += "}"; + code_ += "let __" + name + " = builder.createVector(ofOffsets: __" + + name + "__)"; + code_ += "let __" + name + "Type = builder.createVector(obj." + name + + ".compactMap { $0?.type })"; + break; + } + case BASE_TYPE_UTYPE: break; + case BASE_TYPE_STRUCT: { + if (field.value.type.struct_def && + !field.value.type.struct_def->fixed) { + code_ += "var __" + name + "__: [Offset] = []"; + code_ += "for var i in obj." + name + " {"; + Indent(); + code_ += + "__" + name + "__.append(" + type + ".pack(&builder, obj: &i))"; + Outdent(); + code_ += "}"; + code_ += "let __" + name + " = builder.createVector(ofOffsets: __" + + name + "__)"; + } else { + code_ += "{{STRUCTNAME}}.startVectorOf" + MakeCamel(name, true) + + "(obj." + name + ".count, in: &builder)"; + std::string code; + GenerateStructArgs(*field.value.type.struct_def, &code, "", "", "_o", + true); + code = code.substr(0, code.size() - 2); + code_ += "for i in obj." + name + " {"; + Indent(); + code_ += "guard let _o = i else { continue }"; + code_ += "builder.create(struct: _o)"; + Outdent(); + code_ += "}"; + code_ += "let __" + name + " = builder.endVector(len: obj." + name + + ".count)"; + } + break; + } + case BASE_TYPE_STRING: { + code_ += "let __" + name + " = builder.createVector(ofStrings: obj." + + name + ".compactMap({ $0 }) )"; + break; + } + default: { + code_ += "let __" + name + " = builder.createVector(obj." + name + ")"; + break; + } } - code_ += "\n"; - AddMinOrMaxEnumValue(enum_def.MaxValue()->name, "max"); - AddMinOrMaxEnumValue(enum_def.MinValue()->name, "min"); + } + + void BuildingOptionalObjects(const std::string &name, + const std::string &body_front) { + code_ += "let __" + name + ": Offset"; + code_ += "if let s = obj." + name + " {"; + Indent(); + code_ += "__" + name + " = " + body_front; + Outdent(); + code_ += "} else {"; + Indent(); + code_ += "__" + name + " = Offset()"; + Outdent(); + code_ += "}"; + code_ += ""; + } + + void BuildObjectConstructor(const std::vector &body, + const std::string &header = "") { + code_.SetValue("HEADER", header); + code_ += "{{ACCESS_TYPE}} init({{HEADER}}) {"; + Indent(); + for (auto it = body.begin(); it < body.end(); ++it) code_ += *it; + Outdent(); code_ += "}\n"; } + void BuildObjectAPIConstructorBody( + const FieldDef &field, bool is_fixed, + std::vector &buffer_constructor, + std::vector &base_constructor) { + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + code_.SetValue("VALUETYPE", type); + std::string is_required = field.IsRequired() ? "" : "?"; + + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + type = GenType(field.value.type, true); + code_.SetValue("VALUETYPE", type); + auto optional = + (field.value.type.struct_def && field.value.type.struct_def->fixed); + std::string question_mark = + (field.IsRequired() || (optional && is_fixed) ? "" : "?"); + + code_ += + "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + question_mark; + base_constructor.push_back("" + name + " = " + type + "()"); + + if (field.value.type.struct_def->fixed) { + buffer_constructor.push_back("" + name + " = _t." + name); + } else { + buffer_constructor.push_back("var __" + name + " = _t." + name); + buffer_constructor.push_back( + "" + name + " = __" + name + + (field.IsRequired() ? "!" : question_mark) + ".unpack()"); + } + break; + } + case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); + case BASE_TYPE_VECTOR: { + BuildObjectAPIConstructorBodyVectors(field, name, buffer_constructor, + base_constructor, " "); + break; + } + case BASE_TYPE_STRING: { + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: String" + is_required; + buffer_constructor.push_back(name + " = _t." + name); + + if (field.IsRequired()) { + std::string default_value = + field.IsDefault() ? field.value.constant : ""; + base_constructor.push_back(name + " = \"" + default_value + "\""); + break; + } + if (field.IsDefault() && !field.IsRequired()) { + std::string value = field.IsDefault() ? field.value.constant : "nil"; + base_constructor.push_back(name + " = \"" + value + "\""); + } + break; + } + case BASE_TYPE_UTYPE: break; + case BASE_TYPE_UNION: { + BuildUnionEnumSwitchCase(*field.value.type.enum_def, name, + buffer_constructor); + break; + } + default: { + buffer_constructor.push_back(name + " = _t." + name); + std::string nullable = field.IsOptional() ? "?" : ""; + if (IsScalar(field.value.type.base_type) && + !IsBool(field.value.type.base_type) && !IsEnum(field.value.type)) { + code_ += + "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + nullable; + if (!field.IsOptional()) + base_constructor.push_back(name + " = " + field.value.constant); + break; + } + + if (IsEnum(field.value.type)) { + auto default_value = IsEnum(field.value.type) + ? GenEnumDefaultValue(field) + : field.value.constant; + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}"; + base_constructor.push_back(name + " = " + default_value); + break; + } + + if (IsBool(field.value.type.base_type)) { + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: Bool" + nullable; + std::string default_value = + "0" == field.value.constant ? "false" : "true"; + if (!field.IsOptional()) + base_constructor.push_back(name + " = " + default_value); + } + } + } + } + + void BuildObjectAPIConstructorBodyVectors( + const FieldDef &field, const std::string &name, + std::vector &buffer_constructor, + std::vector &base_constructor, + const std::string &indentation) { + auto vectortype = field.value.type.VectorType(); + + if (vectortype.base_type != BASE_TYPE_UTYPE) { + buffer_constructor.push_back(name + " = []"); + buffer_constructor.push_back("for index in 0..<_t." + name + "Count {"); + base_constructor.push_back(name + " = []"); + } + + switch (vectortype.base_type) { + case BASE_TYPE_STRUCT: { + code_.SetValue("VALUETYPE", GenType(vectortype, true)); + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}?]"; + if (!vectortype.struct_def->fixed) { + buffer_constructor.push_back(indentation + "var __v_ = _t." + name + + "(at: index)"); + buffer_constructor.push_back(indentation + name + + ".append(__v_?.unpack())"); + } else { + buffer_constructor.push_back(indentation + name + ".append(_t." + + name + "(at: index))"); + } + break; + } + case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); + case BASE_TYPE_VECTOR: { + break; + } + case BASE_TYPE_UNION: { + BuildUnionEnumSwitchCase(*field.value.type.enum_def, name, + buffer_constructor, indentation, true); + break; + } + case BASE_TYPE_UTYPE: break; + default: { + code_.SetValue( + "VALUETYPE", + (IsString(vectortype) ? "String?" : GenType(vectortype))); + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}]"; + + if (IsEnum(vectortype) && vectortype.base_type != BASE_TYPE_UNION) { + auto default_value = IsEnum(field.value.type) + ? GenEnumDefaultValue(field) + : field.value.constant; + buffer_constructor.push_back(indentation + name + ".append(_t." + + name + "(at: index)!)"); + break; + } + buffer_constructor.push_back(indentation + name + ".append(_t." + name + + "(at: index))"); + break; + } + } + if (vectortype.base_type != BASE_TYPE_UTYPE) + buffer_constructor.push_back("}"); + } + + void BuildUnionEnumSwitchCaseWritter(const EnumDef &ev) { + auto field_name = Name(ev); + code_.SetValue("VALUETYPE", field_name); + code_ += "switch type {"; + for (auto it = ev.Vals().begin(); it < ev.Vals().end(); ++it) { + auto field = **it; + auto ev_name = Name(field); + auto type = GenType(field.union_type); + auto is_struct = IsStruct(field.union_type) ? type + Mutable() : type; + if (field.union_type.base_type == BASE_TYPE_NONE) { continue; } + code_ += "case ." + ev_name + ":"; + Indent(); + code_ += "var __obj = value as? " + GenType(field.union_type, true); + code_ += "return " + is_struct + ".pack(&builder, obj: &__obj)"; + Outdent(); + } + code_ += "default: return Offset()"; + code_ += "}"; + } + + void BuildUnionEnumSwitchCase(const EnumDef &ev, const std::string &name, + std::vector &buffer_constructor, + const std::string &indentation = "", + const bool is_vector = false) { + auto field_name = NameWrappedInNameSpace(ev); + code_.SetValue("VALUETYPE", field_name); + code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: \\"; + code_ += is_vector ? "[{{VALUETYPE}}Union?]" : "{{VALUETYPE}}Union?"; + + auto vector_reader = is_vector ? "(at: index" : ""; + buffer_constructor.push_back(indentation + "switch _t." + name + "Type" + + vector_reader + (is_vector ? ")" : "") + " {"); + + for (auto it = ev.Vals().begin(); it < ev.Vals().end(); ++it) { + auto field = **it; + auto ev_name = Name(field); + if (field.union_type.base_type == BASE_TYPE_NONE) { continue; } + auto type = IsStruct(field.union_type) + ? GenType(field.union_type) + Mutable() + : GenType(field.union_type); + buffer_constructor.push_back(indentation + "case ." + ev_name + ":"); + buffer_constructor.push_back( + indentation + " var _v = _t." + name + (is_vector ? "" : "(") + + vector_reader + (is_vector ? ", " : "") + "type: " + type + ".self)"); + auto constructor = + field_name + "Union(_v?.unpack(), type: ." + ev_name + ")"; + buffer_constructor.push_back( + indentation + " " + name + + (is_vector ? ".append(" + constructor + ")" : " = " + constructor)); + } + buffer_constructor.push_back(indentation + "default: break"); + buffer_constructor.push_back(indentation + "}"); + } + void AddMinOrMaxEnumValue(const std::string &str, const std::string &type) { auto current_value = str; - std::transform(current_value.begin(), current_value.end(), - current_value.begin(), LowerCase); code_.SetValue(type, current_value); - code_ += "\tpublic static var " + type + ": {{ENUM_NAME}} { return .{{" + - type + "}} }"; + code_ += "{{ACCESS_TYPE}} static var " + type + + ": {{ENUM_NAME}} { return .{{" + type + "}} }"; } void GenLookup(const FieldDef &key_field) { code_.SetValue("OFFSET", NumToString(key_field.value.offset)); - auto offset_reader = + std::string offset_reader = "Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: {{OFFSET}}, " "fbb: fbb)"; - std::string spacing = "\t\t"; - std::string double_spacing = spacing + "\t"; code_.SetValue("TYPE", GenType(key_field.value.type)); code_ += - "\tfileprivate static func lookupByKey(vector: Int32, key: {{TYPE}}, " + "fileprivate static func lookupByKey(vector: Int32, key: {{TYPE}}, " "fbb: " "ByteBuffer) -> {{VALUENAME}}? {"; - - if (key_field.value.type.base_type == BASE_TYPE_STRING) - code_ += spacing + "let key = key.utf8.map { $0 }"; - code_ += spacing + - "var span = fbb.read(def: Int32.self, position: Int(vector - 4))"; - code_ += spacing + "var start: Int32 = 0"; - code_ += spacing + "while span != 0 {"; - code_ += double_spacing + "var middle = span / 2"; + Indent(); + if (IsString(key_field.value.type)) + code_ += "let key = key.utf8.map { $0 }"; + code_ += "var span = fbb.read(def: Int32.self, position: Int(vector - 4))"; + code_ += "var start: Int32 = 0"; + code_ += "while span != 0 {"; + Indent(); + code_ += "var middle = span / 2"; code_ += - double_spacing + "let tableOffset = Table.indirect(vector + 4 * (start + middle), fbb)"; - if (key_field.value.type.base_type == BASE_TYPE_STRING) { - code_ += double_spacing + "let comp = Table.compare(" + offset_reader + - ", key, fbb: fbb)"; + if (IsString(key_field.value.type)) { + code_ += "let comp = Table.compare(" + offset_reader + ", key, fbb: fbb)"; } else { - code_ += double_spacing + - "let comp = fbb.read(def: {{TYPE}}.self, position: Int(" + + code_ += "let comp = fbb.read(def: {{TYPE}}.self, position: Int(" + offset_reader + "))"; } - code_ += double_spacing + "if comp > 0 {"; - code_ += double_spacing + "\tspan = middle"; - code_ += double_spacing + "} else if comp < 0 {"; - code_ += double_spacing + "\tmiddle += 1"; - code_ += double_spacing + "\tstart += middle"; - code_ += double_spacing + "\tspan -= middle"; - code_ += double_spacing + "} else {"; - code_ += double_spacing + "\treturn {{VALUENAME}}(fbb, o: tableOffset)"; - code_ += double_spacing + "}"; - code_ += spacing + "}"; - code_ += spacing + "return nil"; - code_ += "\t}"; + code_ += "if comp > 0 {"; + Indent(); + code_ += "span = middle"; + Outdent(); + code_ += "} else if comp < 0 {"; + Indent(); + code_ += "middle += 1"; + code_ += "start += middle"; + code_ += "span -= middle"; + Outdent(); + code_ += "} else {"; + Indent(); + code_ += "return {{VALUENAME}}(fbb, o: tableOffset)"; + Outdent(); + code_ += "}"; + Outdent(); + code_ += "}"; + code_ += "return nil"; + Outdent(); + code_ += "}"; } - void GenComment(const std::vector &dc, const char *prefix = "") { - std::string text; - ::flatbuffers::GenComment(dc, &text, nullptr, prefix); - code_ += text + "\\"; + inline void GenPadding(const FieldDef &field, int *id) { + if (field.padding) { + for (int i = 0; i < 4; i++) { + if (static_cast(field.padding) & (1 << i)) { + auto bits = (1 << i) * 8; + code_ += "private let padding" + NumToString((*id)++) + "__: UInt" + + NumToString(bits) + " = 0"; + } + } + FLATBUFFERS_ASSERT(!(field.padding & ~0xF)); + } } - std::string GenOffset() { return "let o = {{ACCESS}}.offset({{OFFSET}}); "; } + void GenComment(const std::vector &dc) { + if (dc.begin() == dc.end()) { + // Don't output empty comment blocks with 0 lines of comment content. + return; + } + for (auto it = dc.begin(); it != dc.end(); ++it) { code_ += "/// " + *it; } + } + + std::string GenOffset() { + return "let o = {{ACCESS}}.offset({{TABLEOFFSET}}.{{OFFSET}}.v); "; + } std::string GenReaderMainBody(const std::string &optional = "") { - return "\tpublic var {{VALUENAME}}: {{VALUETYPE}}" + optional + " { "; + return "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + optional + + " { "; } std::string GenReader(const std::string &type, @@ -756,13 +1445,15 @@ class SwiftGenerator : public BaseGenerator { std::string GenMutate(const std::string &offset, const std::string &get_offset, bool isRaw = false) { - return "\tpublic func mutate({{VALUENAME}}: {{VALUETYPE}}) -> Bool {" + + return "@discardableResult {{ACCESS_TYPE}} func mutate({{VALUENAME}}: " + "{{VALUETYPE}}) -> Bool {" + get_offset + " return {{ACCESS}}.mutate({{VALUENAME}}" + (isRaw ? ".rawValue" : "") + ", index: " + offset + ") }"; } std::string GenMutateArray() { - return "\tpublic func mutate({{VALUENAME}}: {{VALUETYPE}}, at index: " + return "{{ACCESS_TYPE}} func mutate({{VALUENAME}}: {{VALUETYPE}}, at " + "index: " "Int32) -> Bool { " + GenOffset() + "return {{ACCESS}}.directMutate({{VALUENAME}}, index: " @@ -773,15 +1464,16 @@ class SwiftGenerator : public BaseGenerator { auto &value = field.value; FLATBUFFERS_ASSERT(value.type.enum_def); auto &enum_def = *value.type.enum_def; - auto enum_val = enum_def.FindByValue(value.constant); + // Vector of enum defaults are always "[]" which never works. + const std::string constant = IsVector(value.type) ? "0" : value.constant; + auto enum_val = enum_def.FindByValue(constant); std::string name; if (enum_val) { - name = enum_val->name; + name = Name(*enum_val); } else { const auto &ev = **enum_def.Vals().begin(); - name = ev.name; + name = Name(ev); } - std::transform(name.begin(), name.end(), name.begin(), LowerCase); return "." + name; } @@ -790,23 +1482,32 @@ class SwiftGenerator : public BaseGenerator { } std::string ValidateFunc() { - return "\tstatic func validateVersion() { FlatBuffersVersion_1_12_0() }"; + return "static func validateVersion() { FlatBuffersVersion_2_0_0() }"; } - std::string GenType(const Type &type) const { + std::string GenType(const Type &type, + const bool should_consider_suffix = false) const { return IsScalar(type.base_type) ? GenTypeBasic(type) : (IsArray(type) ? GenType(type.VectorType()) - : GenTypePointer(type)); + : GenTypePointer(type, should_consider_suffix)); } - std::string GenTypePointer(const Type &type) const { + std::string GenTypePointer(const Type &type, + const bool should_consider_suffix) const { switch (type.base_type) { case BASE_TYPE_STRING: return "String"; case BASE_TYPE_VECTOR: return GenType(type.VectorType()); - case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def); + case BASE_TYPE_STRUCT: { + auto &struct_ = *type.struct_def; + if (should_consider_suffix && !struct_.fixed) { + return WrapInNameSpace(struct_.defined_namespace, + ObjectAPIName(Name(struct_))); + } + return WrapInNameSpace(struct_.defined_namespace, Name(struct_)); + } case BASE_TYPE_UNION: - default: return "FlatBufferObject"; + default: return "FlatbuffersInitializable"; } } @@ -814,6 +1515,22 @@ class SwiftGenerator : public BaseGenerator { return GenTypeBasic(type, true); } + std::string ObjectAPIName(const std::string &name) const { + return parser_.opts.object_prefix + name + parser_.opts.object_suffix; + } + + void Indent() { code_.IncrementIdentLevel(); } + + void Outdent() { code_.DecrementIdentLevel(); } + + std::string NameWrappedInNameSpace(const EnumDef &enum_def) const { + return WrapInNameSpace(enum_def.defined_namespace, Name(enum_def)); + } + + std::string NameWrappedInNameSpace(const StructDef &struct_def) const { + return WrapInNameSpace(struct_def.defined_namespace, Name(struct_def)); + } + std::string GenTypeBasic(const Type &type, bool can_override) const { // clang-format off static const char * const swift_type[] = { @@ -825,9 +1542,7 @@ class SwiftGenerator : public BaseGenerator { }; // clang-format on if (can_override) { - if (type.enum_def) - return WrapInNameSpace(type.enum_def->defined_namespace, - Name(*type.enum_def)); + if (type.enum_def) return NameWrappedInNameSpace(*type.enum_def); if (type.base_type == BASE_TYPE_BOOL) return "Bool"; } return swift_type[static_cast(type.base_type)]; @@ -837,60 +1552,18 @@ class SwiftGenerator : public BaseGenerator { return keywords_.find(name) == keywords_.end() ? name : name + "_"; } - std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); } - - std::string Name(const Definition &def) const { - return EscapeKeyword(MakeCamel(def.name, false)); - } + std::string Mutable() const { return "_Mutable"; } - // MARK: - Copied from the cpp implementation, needs revisiting - void SetNameSpace(const Namespace *ns) { - if (cur_name_space_ == ns) { return; } - // Compute the size of the longest common namespace prefix. - // If cur_name_space is A::B::C::D and ns is A::B::E::F::G, - // the common prefix is A::B:: and we have old_size = 4, new_size = 5 - // and common_prefix_size = 2 - size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0; - size_t new_size = ns ? ns->components.size() : 0; - - size_t common_prefix_size = 0; - while (common_prefix_size < old_size && common_prefix_size < new_size && - ns->components[common_prefix_size] == - cur_name_space_->components[common_prefix_size]) { - common_prefix_size++; + std::string Name(const EnumVal &ev) const { + auto name = ev.name; + if (isupper(name.front())) { + std::transform(name.begin(), name.end(), name.begin(), CharToLower); } + return EscapeKeyword(MakeCamel(name, false)); + } - // Close cur_name_space in reverse order to reach the common prefix. - // In the previous example, D then C are closed. - for (size_t j = old_size; j > common_prefix_size; --j) { - if (namespace_depth != 0) { - code_ += "}"; - namespace_depth -= 1; - } - mark(cur_name_space_->components[j - 1]); - } - if (old_size != common_prefix_size) { code_ += ""; } - - // open namespace parts to reach the ns namespace - // in the previous example, E, then F, then G are opened - bool is_extension = false; - for (auto j = common_prefix_size; j < new_size; ++j) { - std::string name = ns->components[j]; - if (namespaces_.find(name) == namespaces_.end()) { - code_ += "public enum " + name + " {"; - namespace_depth += 1; - namespaces_.insert(name); - } else { - code_ += "}"; - is_extension = true; - } - } - if (is_extension) { - code_.SetValue("EXTENSION", FullNamespace(".", *ns)); - code_ += "extension {{EXTENSION}} {"; - } - if (new_size != common_prefix_size) { code_ += ""; } - cur_name_space_ = ns; + std::string Name(const Definition &def) const { + return EscapeKeyword(MakeCamel(def.name, false)); } }; } // namespace swift diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_text.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_text.cpp index e4d21824..903c41ec 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_text.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_text.cpp @@ -292,7 +292,7 @@ struct JsonPrinter { it != struct_def.fields.vec.end(); ++it) { FieldDef &fd = **it; auto is_present = struct_def.fixed || table->CheckField(fd.value.offset); - auto output_anyway = opts.output_default_scalars_in_json && + auto output_anyway = (opts.output_default_scalars_in_json || fd.key) && IsScalar(fd.value.type.base_type) && !fd.deprecated; if (is_present || output_anyway) { if (fieldout++) { AddComma(); } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_ts.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_ts.cpp new file mode 100644 index 00000000..53e088fe --- /dev/null +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_gen_ts.cpp @@ -0,0 +1,1583 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// independent from idl_parser, since this code is not needed for most clients +#include +#include +#include +#include + +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { + +struct ImportDefinition { + std::string name; + std::string statement; + const Definition *dependent; + const Definition *dependency; +}; + +enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 }; + +namespace ts { +// Iterate through all definitions we haven't generate code for (enums, structs, +// and tables) and output them to a single file. +class TsGenerator : public BaseGenerator { + public: + typedef std::map import_set; + + TsGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "", ".", "ts") {} + bool generate() { + generateEnums(); + generateStructs(); + return true; + } + + // Save out the generated code for a single class while adding + // declaration boilerplate. + bool SaveType(const Definition &definition, const std::string &classcode, + import_set &imports, import_set &bare_imports) const { + if (!classcode.length()) return true; + + std::string code = + "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n"; + + for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) + code += it->second.statement + "\n"; + if (!bare_imports.empty()) code += "\n"; + + for (auto it = imports.begin(); it != imports.end(); it++) + if (it->second.dependency != &definition) // do not import itself + code += it->second.statement + "\n"; + if (!imports.empty()) code += "\n\n"; + + code += classcode; + auto filename = NamespaceDir(*definition.defined_namespace, true) + + ToDasherizedCase(definition.name) + ".ts"; + return SaveFile(filename.c_str(), code, false); + } + + private: + // Generate code for all enums. + void generateEnums() { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + import_set bare_imports; + import_set imports; + std::string enumcode; + auto &enum_def = **it; + GenEnum(enum_def, &enumcode, imports, false); + GenEnum(enum_def, &enumcode, imports, true); + SaveType(enum_def, enumcode, imports, bare_imports); + } + } + + // Generate code for all structs. + void generateStructs() { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + import_set bare_imports; + import_set imports; + AddImport(bare_imports, "* as flatbuffers", "flatbuffers"); + auto &struct_def = **it; + std::string declcode; + GenStruct(parser_, struct_def, &declcode, imports); + SaveType(struct_def, declcode, imports, bare_imports); + } + } + + // Generate a documentation comment, if available. + static void GenDocComment(const std::vector &dc, + std::string *code_ptr, + const char *indent = nullptr) { + if (dc.empty()) { + // Don't output empty comment blocks with 0 lines of comment content. + return; + } + + std::string &code = *code_ptr; + if (indent) code += indent; + code += "/**\n"; + for (auto it = dc.begin(); it != dc.end(); ++it) { + if (indent) code += indent; + code += " *" + *it + "\n"; + } + if (indent) code += indent; + code += " */\n"; + } + + static void GenDocComment(std::string *code_ptr) { + GenDocComment(std::vector(), code_ptr); + } + + // Generate an enum declaration and an enum string lookup table. + void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports, + bool reverse) { + if (enum_def.generated) return; + if (reverse) return; // FIXME. + std::string &code = *code_ptr; + GenDocComment(enum_def.doc_comment, code_ptr); + std::string ns = GetNameSpace(enum_def); + std::string enum_def_name = enum_def.name + (reverse ? "Name" : ""); + code += "export enum " + enum_def.name + "{\n"; + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + auto &ev = **it; + if (!ev.doc_comment.empty()) { + if (it != enum_def.Vals().begin()) { code += '\n'; } + GenDocComment(ev.doc_comment, code_ptr, " "); + } + + // Generate mapping between EnumName: EnumValue(int) + if (reverse) { + code += " '" + enum_def.ToString(ev) + "'"; + code += " = "; + code += "'" + ev.name + "'"; + } else { + code += " " + ev.name; + code += " = "; + code += enum_def.ToString(ev); + } + + code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n"; + } + code += "}"; + + if (enum_def.is_union) { + code += GenUnionConvFunc(enum_def.underlying_type, imports); + } + + code += "\n\n"; + } + + static std::string GenType(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_BOOL: + case BASE_TYPE_CHAR: return "Int8"; + case BASE_TYPE_UTYPE: + case BASE_TYPE_UCHAR: return "Uint8"; + case BASE_TYPE_SHORT: return "Int16"; + case BASE_TYPE_USHORT: return "Uint16"; + case BASE_TYPE_INT: return "Int32"; + case BASE_TYPE_UINT: return "Uint32"; + case BASE_TYPE_LONG: return "Int64"; + case BASE_TYPE_ULONG: return "Uint64"; + case BASE_TYPE_FLOAT: return "Float32"; + case BASE_TYPE_DOUBLE: return "Float64"; + case BASE_TYPE_STRING: return "String"; + case BASE_TYPE_VECTOR: return GenType(type.VectorType()); + case BASE_TYPE_STRUCT: return type.struct_def->name; + default: return "flatbuffers.Table"; + } + } + + std::string GenGetter(const Type &type, const std::string &arguments) { + switch (type.base_type) { + case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments; + case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments; + case BASE_TYPE_UNION: + if (!UnionHasStringType(*type.enum_def)) { + return GenBBAccess() + ".__union" + arguments; + } + return GenBBAccess() + ".__union_with_string" + arguments; + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments); + default: { + auto getter = + GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments; + if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; } + return getter; + } + } + } + + std::string GenBBAccess() const { return "this.bb!"; } + + std::string GenDefaultValue(const FieldDef &field, const std::string &context, + import_set &imports) { + if (field.IsScalarOptional()) { return "null"; } + + const auto &value = field.value; + if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION && + value.type.base_type != BASE_TYPE_VECTOR) { + if (auto val = value.type.enum_def->FindByValue(value.constant)) { + return AddImport(imports, *value.type.enum_def, *value.type.enum_def) + + "." + val->name; + } else { + return value.constant; + } + } + + switch (value.type.base_type) { + case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true"; + + case BASE_TYPE_STRING: + case BASE_TYPE_UNION: + case BASE_TYPE_STRUCT: { + return "null"; + } + + case BASE_TYPE_VECTOR: return "[]"; + + case BASE_TYPE_LONG: + case BASE_TYPE_ULONG: { + int64_t constant = StringToInt(value.constant.c_str()); + std::string createLong = context + ".createLong"; + return createLong + "(" + NumToString(static_cast(constant)) + + ", " + NumToString(static_cast(constant >> 32)) + ")"; + } + + default: return value.constant; + } + } + + std::string GenTypeName(import_set &imports, const Definition &owner, + const Type &type, bool input, + bool allowNull = false) { + if (!input) { + if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) { + std::string name; + if (IsString(type)) { + name = "string|Uint8Array"; + } else { + name = AddImport(imports, owner, *type.struct_def); + } + return allowNull ? (name + "|null") : name; + } + } + + switch (type.base_type) { + case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean"; + case BASE_TYPE_LONG: + case BASE_TYPE_ULONG: + return allowNull ? "flatbuffers.Long|null" : "flatbuffers.Long"; + default: + if (IsScalar(type.base_type)) { + if (type.enum_def) { + const auto enum_name = AddImport(imports, owner, *type.enum_def); + return allowNull ? (enum_name + "|null") : enum_name; + } + return allowNull ? "number|null" : "number"; + } + return "flatbuffers.Offset"; + } + } + + // Returns the method name for use with add/put calls. + static std::string GenWriteMethod(const Type &type) { + // Forward to signed versions since unsigned versions don't exist + switch (type.base_type) { + case BASE_TYPE_UTYPE: + case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR)); + case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT)); + case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT)); + case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG)); + default: break; + } + + return IsScalar(type.base_type) ? MakeCamel(GenType(type)) + : (IsStruct(type) ? "Struct" : "Offset"); + } + + template static std::string MaybeAdd(T value) { + return value != 0 ? " + " + NumToString(value) : ""; + } + + template static std::string MaybeScale(T value) { + return value != 1 ? " * " + NumToString(value) : ""; + } + + void GenStructArgs(import_set &imports, const StructDef &struct_def, + std::string *arguments, const std::string &nameprefix) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (IsStruct(field.value.type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the field name. + GenStructArgs(imports, *field.value.type.struct_def, arguments, + nameprefix + field.name + "_"); + } else { + *arguments += + ", " + nameprefix + field.name + ": " + + GenTypeName(imports, field, field.value.type, true, field.IsOptional()); + } + } + } + + static void GenStructBody(const StructDef &struct_def, std::string *body, + const std::string &nameprefix) { + *body += " builder.prep("; + *body += NumToString(struct_def.minalign) + ", "; + *body += 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) { + *body += " builder.pad(" + NumToString(field.padding) + ");\n"; + } + if (IsStruct(field.value.type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the field name. + GenStructBody(*field.value.type.struct_def, body, + nameprefix + field.name + "_"); + } else { + *body += " builder.write" + GenWriteMethod(field.value.type) + "("; + if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; } + *body += nameprefix + field.name + ");\n"; + } + } + } + + std::string GenerateNewExpression(const std::string &object_name) { + return "new " + object_name + "()"; + } + + void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr, + std::string &code, const std::string &object_name, + bool size_prefixed) { + if (!struct_def.fixed) { + GenDocComment(code_ptr); + std::string sizePrefixed("SizePrefixed"); + code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" + + GetPrefixedName(struct_def, "As"); + code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name + + "):" + object_name + " {\n"; + if (size_prefixed) { + code += + " bb.setPosition(bb.position() + " + "flatbuffers.SIZE_PREFIX_LENGTH);\n"; + } + code += " return (obj || " + GenerateNewExpression(object_name); + code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n"; + code += "}\n\n"; + } + } + + void GenerateFinisher(StructDef &struct_def, std::string *code_ptr, + std::string &code, bool size_prefixed) { + if (parser_.root_struct_def_ == &struct_def) { + std::string sizePrefixed("SizePrefixed"); + GenDocComment(code_ptr); + + code += "static finish" + (size_prefixed ? sizePrefixed : "") + + GetPrefixedName(struct_def) + "Buffer"; + code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n"; + code += " builder.finish(offset"; + if (!parser_.file_identifier_.empty()) { + code += ", '" + parser_.file_identifier_ + "'"; + } + if (size_prefixed) { + if (parser_.file_identifier_.empty()) { code += ", undefined"; } + code += ", true"; + } + code += ");\n"; + code += "}\n\n"; + } + } + + static std::string GetObjApiClassName(const StructDef &sd, + const IDLOptions &opts) { + return GetObjApiClassName(sd.name, opts); + } + + static std::string GetObjApiClassName(const std::string &name, + const IDLOptions &opts) { + return opts.object_prefix + name + opts.object_suffix; + } + + bool UnionHasStringType(const EnumDef &union_enum) { + return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(), + [](const EnumVal *ev) { + return !ev->IsZero() && IsString(ev->union_type); + }); + } + + std::string GenUnionGenericTypeTS(const EnumDef &union_enum) { + // TODO: make it work without any + // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" : + // ""); + return std::string("any") + + (UnionHasStringType(union_enum) ? "|string" : ""); + } + + std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) { + std::string ret; + std::set type_list; + + for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end(); + ++it) { + const auto &ev = **it; + if (ev.IsZero()) { continue; } + + std::string type = ""; + if (IsString(ev.union_type)) { + type = "string"; // no need to wrap string type in namespace + } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { + type = AddImport(imports, union_enum, *ev.union_type.struct_def); + } else { + FLATBUFFERS_ASSERT(false); + } + type_list.insert(type); + } + + for (auto it = type_list.begin(); it != type_list.end(); ++it) { + ret += *it + ((std::next(it) == type_list.end()) ? "" : "|"); + } + + return ret; + } + + std::string AddImport(import_set &imports, const Definition &dependent, + const StructDef &dependency) { + std::string ns; + const auto &depc_comps = dependency.defined_namespace->components; + for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) ns += *it; + std::string unique_name = ns + dependency.name; + std::string import_name = dependency.name; + std::string long_import_name; + if (imports.find(unique_name) != imports.end()) + return imports.find(unique_name)->second.name; + for (auto it = imports.begin(); it != imports.end(); it++) { + if (it->second.name == import_name) { + long_import_name = ns + import_name; + break; + } + } + std::string import_statement; + import_statement += "import { "; + if (long_import_name.empty()) { + import_statement += import_name; + if (parser_.opts.generate_object_based_api) + import_statement += ", " + import_name + "T"; + } else { + import_statement += dependency.name + " as " + long_import_name; + if (parser_.opts.generate_object_based_api) + import_statement += + ", " + dependency.name + "T as " + long_import_name + "T"; + } + import_statement += " } from '"; + std::string file_name; + const auto &dep_comps = dependent.defined_namespace->components; + for (size_t i = 0; i < dep_comps.size(); i++) + file_name += i == 0 ? ".." : (kPathSeparator + std::string("..")); + if (dep_comps.size() == 0) file_name += "."; + for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) + file_name += kPathSeparator + ToDasherizedCase(*it); + file_name += kPathSeparator + ToDasherizedCase(dependency.name); + import_statement += file_name + "';"; + ImportDefinition import; + import.name = long_import_name.empty() ? import_name : long_import_name; + import.statement = import_statement; + import.dependency = &dependency; + import.dependent = &dependent; + imports.insert(std::make_pair(unique_name, import)); + return import.name; + } + + // TODO: largely (but not identical) duplicated code from above couln't find a + // good way to refactor + std::string AddImport(import_set &imports, const Definition &dependent, + const EnumDef &dependency) { + std::string ns; + const auto &depc_comps = dependency.defined_namespace->components; + for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) ns += *it; + std::string unique_name = ns + dependency.name; + std::string import_name = dependency.name; + std::string long_import_name; + if (imports.find(unique_name) != imports.end()) + return imports.find(unique_name)->second.name; + for (auto it = imports.begin(); it != imports.end(); it++) { + if (it->second.name == import_name) { + long_import_name = ns + import_name; + break; + } + } + std::string import_statement; + import_statement += "import { "; + if (long_import_name.empty()) + import_statement += import_name; + else + import_statement += dependency.name + " as " + long_import_name; + if (dependency.is_union) { + import_statement += ", unionTo" + import_name; + import_statement += ", unionListTo" + import_name; + } + import_statement += " } from '"; + std::string file_name; + const auto &dep_comps = dependent.defined_namespace->components; + for (size_t i = 0; i < dep_comps.size(); i++) + file_name += i == 0 ? ".." : (kPathSeparator + std::string("..")); + if (dep_comps.size() == 0) file_name += "."; + for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) + file_name += kPathSeparator + ToDasherizedCase(*it); + file_name += kPathSeparator + ToDasherizedCase(dependency.name); + import_statement += file_name + "';"; + ImportDefinition import; + import.name = long_import_name.empty() ? import_name : long_import_name; + import.statement = import_statement; + import.dependency = &dependency; + import.dependent = &dependent; + imports.insert(std::make_pair(unique_name, import)); + return import.name; + } + + void AddImport(import_set &imports, std::string import_name, + std::string fileName) { + ImportDefinition import; + import.name = import_name; + import.statement = "import " + import_name + " from '" + fileName + "';"; + imports.insert(std::make_pair(import_name, import)); + } + + // Generate a TS union type based on a union's enum + std::string GenObjApiUnionTypeTS(import_set &imports, const IDLOptions &opts, + const EnumDef &union_enum) { + std::string ret = ""; + std::set type_list; + + for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end(); + ++it) { + const auto &ev = **it; + if (ev.IsZero()) { continue; } + + std::string type = ""; + if (IsString(ev.union_type)) { + type = "string"; // no need to wrap string type in namespace + } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { + type = GetObjApiClassName( + AddImport(imports, union_enum, *ev.union_type.struct_def), opts); + } else { + FLATBUFFERS_ASSERT(false); + } + type_list.insert(type); + } + + size_t totalPrinted = 0; + for (auto it = type_list.begin(); it != type_list.end(); ++it) { + ++totalPrinted; + ret += *it + ((totalPrinted == type_list.size()) ? "" : "|"); + } + + return ret; + } + + std::string GenUnionConvFuncName(const EnumDef &enum_def) { + return "unionTo" + enum_def.name; + } + + std::string GenUnionListConvFuncName(const EnumDef &enum_def) { + return "unionListTo" + enum_def.name; + } + + std::string GenUnionConvFunc(const Type &union_type, import_set &imports) { + if (union_type.enum_def) { + const auto &enum_def = *union_type.enum_def; + + const auto valid_union_type = GenUnionTypeTS(enum_def, imports); + const auto valid_union_type_with_null = valid_union_type + "|null"; + + auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) + + "(\n type: " + enum_def.name + + ",\n accessor: (obj:" + valid_union_type + ") => " + + valid_union_type_with_null + + "\n): " + valid_union_type_with_null + " {\n"; + + const auto enum_type = AddImport(imports, enum_def, enum_def); + + const auto union_enum_loop = [&](const std::string &accessor_str) { + ret += " switch(" + enum_type + "[type]) {\n"; + ret += " case 'NONE': return null; \n"; + + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); + ++it) { + const auto &ev = **it; + if (ev.IsZero()) { continue; } + + ret += " case '" + ev.name + "': "; + + if (IsString(ev.union_type)) { + ret += "return " + accessor_str + "'') as string;"; + } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { + const auto type = + AddImport(imports, enum_def, *ev.union_type.struct_def); + ret += "return " + accessor_str + "new " + type + "())! as " + + type + ";"; + } else { + FLATBUFFERS_ASSERT(false); + } + ret += "\n"; + } + + ret += " default: return null;\n"; + ret += " }\n"; + }; + + union_enum_loop("accessor("); + ret += "}"; + + ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) + + "(\n type: " + enum_def.name + + ", \n accessor: (index: number, obj:" + valid_union_type + + ") => " + valid_union_type_with_null + + ", \n index: number\n): " + valid_union_type_with_null + " {\n"; + union_enum_loop("accessor(index, "); + ret += "}"; + + return ret; + } + FLATBUFFERS_ASSERT(0); + return ""; + } + + // Used for generating a short function that returns the correct class + // based on union enum type. Assume the context is inside the non object api + // type + std::string GenUnionValTS(import_set &imports, const std::string &field_name, + const Type &union_type, + const bool is_array = false) { + if (union_type.enum_def) { + const auto &enum_def = *union_type.enum_def; + const auto enum_type = AddImport(imports, enum_def, enum_def); + const std::string union_accessor = "this." + field_name; + + const auto union_has_string = UnionHasStringType(enum_def); + const auto field_binded_method = "this." + field_name + ".bind(this)"; + + std::string ret; + + if (!is_array) { + const auto conversion_function = GenUnionConvFuncName(enum_def); + const auto target_enum = "this." + field_name + "Type()"; + + ret = "(() => {\n"; + ret += " let temp = " + conversion_function + "(" + target_enum + + ", " + field_binded_method + ");\n"; + ret += " if(temp === null) { return null; }\n"; + ret += union_has_string + ? " if(typeof temp === 'string') { return temp; }\n" + : ""; + ret += " return temp.unpack()\n"; + ret += " })()"; + } else { + const auto conversion_function = GenUnionListConvFuncName(enum_def); + const auto target_enum_accesor = "this." + field_name + "Type"; + const auto target_enum_length = target_enum_accesor + "Length()"; + + ret = "(() => {\n"; + ret += " let ret = [];\n"; + ret += " for(let targetEnumIndex = 0; targetEnumIndex < " + + target_enum_length + + "; " + "++targetEnumIndex) {\n"; + ret += " let targetEnum = " + target_enum_accesor + + "(targetEnumIndex);\n"; + ret += " if(targetEnum === null || " + enum_type + + "[targetEnum!] === 'NONE') { " + "continue; }\n\n"; + ret += " let temp = " + conversion_function + "(targetEnum, " + + field_binded_method + ", targetEnumIndex);\n"; + ret += " if(temp === null) { continue; }\n"; + ret += union_has_string ? " if(typeof temp === 'string') { " + "ret.push(temp); continue; }\n" + : ""; + ret += " ret.push(temp.unpack());\n"; + ret += " }\n"; + ret += " return ret;\n"; + ret += " })()"; + } + + return ret; + } + + FLATBUFFERS_ASSERT(0); + return ""; + } + + static std::string GenNullCheckConditional( + const std::string &nullCheckVar, const std::string &trueVal, + const std::string &falseVal = "null") { + return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal + + ")"; + } + + std::string GenStructMemberValueTS(const StructDef &struct_def, + const std::string &prefix, + const std::string &delimiter, + const bool nullCheck = true) { + std::string ret; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + + const auto curr_member_accessor = + prefix + "." + MakeCamel(field.name, false); + if (IsStruct(field.value.type)) { + ret += GenStructMemberValueTS(*field.value.type.struct_def, + curr_member_accessor, delimiter); + } else { + if (nullCheck) { + ret += + "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)"; + } else { + ret += curr_member_accessor; + } + } + + if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; } + } + + return ret; + } + + void GenObjApi(const Parser &parser, StructDef &struct_def, + std::string &obj_api_unpack_func, std::string &obj_api_class, + import_set &imports) { + const auto class_name = GetObjApiClassName(struct_def, parser.opts); + + std::string unpack_func = "\nunpack(): " + class_name + + " {\n return new " + class_name + "(" + + (struct_def.fields.vec.empty() ? "" : "\n"); + std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" + + +(struct_def.fields.vec.empty() ? "" : "\n"); + + std::string constructor_func = "constructor("; + constructor_func += (struct_def.fields.vec.empty() ? "" : "\n"); + + const auto has_create = + struct_def.fixed || CanCreateFactoryMethod(struct_def); + + std::string pack_func_prototype = + "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n"; + + std::string pack_func_offset_decl; + std::string pack_func_create_call; + + const auto struct_name = AddImport(imports, struct_def, struct_def); + + if (has_create) { + pack_func_create_call = " return " + struct_name + ".create" + + GetPrefixedName(struct_def) + "(builder" + + (struct_def.fields.vec.empty() ? "" : ",\n "); + } else { + pack_func_create_call = " " + struct_name + ".start" + + GetPrefixedName(struct_def) + "(builder);\n"; + } + + if (struct_def.fixed) { + // when packing struct, nested struct's members instead of the struct's + // offset are used + pack_func_create_call += + GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n "; + } + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + const auto field_name = MakeCamel(field.name, false); + const std::string field_binded_method = + "this." + field_name + ".bind(this)"; + + std::string field_val; + std::string field_type; + // a string that declares a variable containing the + // offset for things that can't be generated inline + // empty otw + std::string field_offset_decl; + // a string that contains values for things that can be created inline or + // the variable name from field_offset_decl + std::string field_offset_val; + const auto field_default_val = + GenDefaultValue(field, "flatbuffers", imports); + + // Emit a scalar field + const auto is_string = IsString(field.value.type); + if (IsScalar(field.value.type.base_type) || is_string) { + const auto has_null_default = is_string || HasNullDefault(field); + + field_type += GenTypeName(imports, field, field.value.type, false, + has_null_default); + field_val = "this." + field_name + "()"; + + if (field.value.type.base_type != BASE_TYPE_STRING) { + field_offset_val = "this." + field_name; + } else { + field_offset_decl = GenNullCheckConditional( + "this." + field_name, + "builder.createString(this." + field_name + "!)", "0"); + } + } + + // Emit an object field + else { + auto is_vector = false; + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + const auto &sd = *field.value.type.struct_def; + field_type += GetObjApiClassName(sd, parser.opts); + + const std::string field_accessor = "this." + field_name + "()"; + field_val = GenNullCheckConditional(field_accessor, + field_accessor + "!.unpack()"); + auto packing = GenNullCheckConditional( + "this." + field_name, "this." + field_name + "!.pack(builder)", + "0"); + + if (sd.fixed) { + field_offset_val = std::move(packing); + } else { + field_offset_decl = std::move(packing); + } + + break; + } + + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + auto vectortypename = + GenTypeName(imports, struct_def, vectortype, false); + is_vector = true; + + field_type = "("; + + switch (vectortype.base_type) { + case BASE_TYPE_STRUCT: { + const auto &sd = *field.value.type.struct_def; + field_type += GetObjApiClassName(sd, parser.opts); + field_type += ")[]"; + + field_val = GenBBAccess() + ".createObjList(" + + field_binded_method + ", this." + field_name + + "Length())"; + + if (sd.fixed) { + field_offset_decl = + "builder.createStructOffsetList(this." + field_name + + ", " + AddImport(imports, struct_def, struct_def) + + ".start" + MakeCamel(field_name) + "Vector)"; + } else { + field_offset_decl = + AddImport(imports, struct_def, struct_def) + ".create" + + MakeCamel(field_name) + + "Vector(builder, builder.createObjectOffsetList(" + + "this." + field_name + "))"; + } + + break; + } + + case BASE_TYPE_STRING: { + field_type += "string)[]"; + field_val = GenBBAccess() + ".createScalarList(" + + field_binded_method + ", this." + field_name + + "Length())"; + field_offset_decl = + AddImport(imports, struct_def, struct_def) + ".create" + + MakeCamel(field_name) + + "Vector(builder, builder.createObjectOffsetList(" + + "this." + field_name + "))"; + break; + } + + case BASE_TYPE_UNION: { + field_type += GenObjApiUnionTypeTS(imports, parser.opts, + *(vectortype.enum_def)); + field_type += ")[]"; + field_val = + GenUnionValTS(imports, field_name, vectortype, true); + + field_offset_decl = + AddImport(imports, struct_def, struct_def) + ".create" + + MakeCamel(field_name) + + "Vector(builder, builder.createObjectOffsetList(" + + "this." + field_name + "))"; + + break; + } + default: { + if (vectortype.enum_def) { + field_type += GenTypeName(imports, struct_def, vectortype, + false, HasNullDefault(field)); + } else { + field_type += vectortypename; + } + field_type += ")[]"; + field_val = GenBBAccess() + ".createScalarList(" + + field_binded_method + ", this." + field_name + + "Length())"; + + field_offset_decl = AddImport(imports, struct_def, struct_def) + + ".create" + MakeCamel(field_name) + + "Vector(builder, this." + field_name + ")"; + + break; + } + } + + break; + } + + case BASE_TYPE_UNION: { + field_type += GenObjApiUnionTypeTS(imports, parser.opts, + *(field.value.type.enum_def)); + + field_val = GenUnionValTS(imports, field_name, field.value.type); + field_offset_decl = + "builder.createObjectOffset(this." + field_name + ")"; + break; + } + + default: FLATBUFFERS_ASSERT(0); break; + } + + // length 0 vector is simply empty instead of null + field_type += is_vector ? "" : "|null"; + } + + if (!field_offset_decl.empty()) { + field_offset_decl = + " const " + field_name + " = " + field_offset_decl + ";"; + } + if (field_offset_val.empty()) { field_offset_val = field_name; } + + unpack_func += " " + field_val; + unpack_to_func += " _o." + field_name + " = " + field_val + ";"; + + constructor_func += " public " + field_name + ": " + field_type + " = " + + field_default_val; + + if (!struct_def.fixed) { + if (!field_offset_decl.empty()) { + pack_func_offset_decl += field_offset_decl + "\n"; + } + + if (has_create) { + pack_func_create_call += field_offset_val; + } else { + pack_func_create_call += " " + struct_name + ".add" + + MakeCamel(field.name) + "(builder, " + + field_offset_val + ");\n"; + } + } + + if (std::next(it) != struct_def.fields.vec.end()) { + constructor_func += ",\n"; + + if (!struct_def.fixed && has_create) { + pack_func_create_call += ",\n "; + } + + unpack_func += ",\n"; + unpack_to_func += "\n"; + } else { + constructor_func += "\n"; + if (!struct_def.fixed) { + pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n"); + pack_func_create_call += "\n "; + } + + unpack_func += "\n "; + unpack_to_func += "\n"; + } + } + + constructor_func += "){}\n\n"; + + if (has_create) { + pack_func_create_call += ");"; + } else { + pack_func_create_call += "return " + struct_name + ".end" + + GetPrefixedName(struct_def) + "(builder);"; + } + + obj_api_class = "\nexport class " + + GetObjApiClassName(struct_def, parser.opts) + " {\n"; + + obj_api_class += constructor_func; + obj_api_class += pack_func_prototype + pack_func_offset_decl + + pack_func_create_call + "\n}"; + + obj_api_class += "\n}\n"; + + unpack_func += ");\n}"; + unpack_to_func += "}\n"; + + obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func; + } + + static bool CanCreateFactoryMethod(const StructDef &struct_def) { + // to preserve backwards compatibility, we allow the first field to be a + // struct + return struct_def.fields.vec.size() < 2 || + std::all_of(std::begin(struct_def.fields.vec) + 1, + std::end(struct_def.fields.vec), + [](const FieldDef *f) -> bool { + FLATBUFFERS_ASSERT(f != nullptr); + return f->value.type.base_type != BASE_TYPE_STRUCT; + }); + } + + // Generate an accessor struct with constructor for a flatbuffers struct. + void GenStruct(const Parser &parser, StructDef &struct_def, + std::string *code_ptr, import_set &imports) { + if (struct_def.generated) return; + std::string &code = *code_ptr; + + std::string object_name; + std::string object_namespace = GetNameSpace(struct_def); + + // Emit constructor + object_name = struct_def.name; + GenDocComment(struct_def.doc_comment, code_ptr); + code += "export class " + struct_def.name; + code += " {\n"; + code += " bb: flatbuffers.ByteBuffer|null = null;\n"; + code += " bb_pos = 0;\n"; + + // Generate the __init method that sets the field in a pre-existing + // accessor object. This is to allow object reuse. + code += + "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n"; + code += " this.bb_pos = i;\n"; + code += " this.bb = bb;\n"; + code += " return this;\n"; + code += "}\n\n"; + + // Generate special accessors for the table that when used as the root of a + // FlatBuffer + GenerateRootAccessor(struct_def, code_ptr, code, object_name, false); + GenerateRootAccessor(struct_def, code_ptr, code, object_name, true); + + // Generate the identifier check method + if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def && + !parser_.file_identifier_.empty()) { + GenDocComment(code_ptr); + code += + "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean " + "{\n"; + code += " return bb.__has_identifier('" + parser_.file_identifier_; + code += "');\n}\n\n"; + } + + // Emit field accessors + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + auto offset_prefix = + " const offset = " + GenBBAccess() + ".__offset(this.bb_pos, " + + NumToString(field.value.offset) + ");\n return offset ? "; + + // Emit a scalar field + const auto is_string = IsString(field.value.type); + if (IsScalar(field.value.type.base_type) || is_string) { + const auto has_null_default = is_string || HasNullDefault(field); + + GenDocComment(field.doc_comment, code_ptr); + std::string prefix = MakeCamel(field.name, false) + "("; + if (is_string) { + code += prefix + "):string|null\n"; + code += + prefix + "optionalEncoding:flatbuffers.Encoding" + "):" + + GenTypeName(imports, struct_def, field.value.type, false, true) + + "\n"; + code += prefix + "optionalEncoding?:any"; + } else { + code += prefix; + } + if (field.value.type.enum_def) { + code += "):" + + GenTypeName(imports, struct_def, field.value.type, false, + field.IsOptional()) + + " {\n"; + } else { + code += "):" + + GenTypeName(imports, struct_def, field.value.type, false, + has_null_default) + + " {\n"; + } + + if (struct_def.fixed) { + code += + " return " + + GenGetter(field.value.type, + "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") + + ";\n"; + } else { + std::string index = "this.bb_pos + offset"; + if (is_string) { index += ", optionalEncoding"; } + code += offset_prefix + + GenGetter(field.value.type, "(" + index + ")") + " : " + + GenDefaultValue(field, GenBBAccess(), imports); + code += ";\n"; + } + } + + // Emit an object field + else { + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + const auto type = + AddImport(imports, struct_def, *field.value.type.struct_def); + GenDocComment(field.doc_comment, code_ptr); + code += MakeCamel(field.name, false); + code += "(obj?:" + type + "):" + type + "|null {\n"; + + if (struct_def.fixed) { + code += " return (obj || " + GenerateNewExpression(type); + code += ").__init(this.bb_pos"; + code += + MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n"; + } else { + code += offset_prefix + "(obj || " + GenerateNewExpression(type) + + ").__init("; + code += field.value.type.struct_def->fixed + ? "this.bb_pos + offset" + : GenBBAccess() + ".__indirect(this.bb_pos + offset)"; + code += ", " + GenBBAccess() + ") : null;\n"; + } + + break; + } + + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + auto vectortypename = + GenTypeName(imports, struct_def, vectortype, false); + auto inline_size = InlineSize(vectortype); + auto index = GenBBAccess() + + ".__vector(this.bb_pos + offset) + index" + + MaybeScale(inline_size); + std::string ret_type; + bool is_union = false; + switch (vectortype.base_type) { + case BASE_TYPE_STRUCT: ret_type = vectortypename; break; + case BASE_TYPE_STRING: ret_type = vectortypename; break; + case BASE_TYPE_UNION: + ret_type = "?flatbuffers.Table"; + is_union = true; + break; + default: ret_type = vectortypename; + } + GenDocComment(field.doc_comment, code_ptr); + std::string prefix = MakeCamel(field.name, false); + // TODO: make it work without any + // if (is_union) { prefix += ""; } + if (is_union) { prefix += ""; } + prefix += "(index: number"; + if (is_union) { + const auto union_type = + GenUnionGenericTypeTS(*(field.value.type.enum_def)); + + vectortypename = union_type; + code += prefix + ", obj:" + union_type; + } else if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += prefix + ", obj?:" + vectortypename; + } else if (IsString(vectortype)) { + code += prefix + "):string\n"; + code += prefix + ",optionalEncoding:flatbuffers.Encoding" + + "):" + vectortypename + "\n"; + code += prefix + ",optionalEncoding?:any"; + } else { + code += prefix; + } + code += "):" + vectortypename + "|null {\n"; + + if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += offset_prefix + "(obj || " + + GenerateNewExpression(vectortypename); + code += ").__init("; + code += vectortype.struct_def->fixed + ? index + : GenBBAccess() + ".__indirect(" + index + ")"; + code += ", " + GenBBAccess() + ")"; + } else { + if (is_union) { + index = "obj, " + index; + } else if (IsString(vectortype)) { + index += ", optionalEncoding"; + } + code += offset_prefix + GenGetter(vectortype, "(" + index + ")"); + } + code += " : "; + if (field.value.type.element == BASE_TYPE_BOOL) { + code += "false"; + } else if (field.value.type.element == BASE_TYPE_LONG || + field.value.type.element == BASE_TYPE_ULONG) { + code += GenBBAccess() + ".createLong(0, 0)"; + } else if (IsScalar(field.value.type.element)) { + if (field.value.type.enum_def) { + code += field.value.constant; + } else { + code += "0"; + } + } else { + code += "null"; + } + code += ";\n"; + break; + } + + case BASE_TYPE_UNION: { + GenDocComment(field.doc_comment, code_ptr); + code += MakeCamel(field.name, false); + + const auto &union_enum = *(field.value.type.enum_def); + const auto union_type = GenUnionGenericTypeTS(union_enum); + code += "(obj:" + union_type + + "):" + union_type + + "|null " + "{\n"; + + code += offset_prefix + + GenGetter(field.value.type, "(obj, this.bb_pos + offset)") + + " : null;\n"; + break; + } + default: FLATBUFFERS_ASSERT(0); + } + } + code += "}\n\n"; + + // Adds the mutable scalar value to the output + if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer && + !IsUnion(field.value.type)) { + std::string type = + GenTypeName(imports, struct_def, field.value.type, true); + + code += "mutate_" + field.name + "(value:" + type + "):boolean {\n"; + + if (struct_def.fixed) { + code += " " + GenBBAccess() + ".write" + + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + " + + NumToString(field.value.offset) + ", "; + } else { + code += " const offset = " + GenBBAccess() + + ".__offset(this.bb_pos, " + NumToString(field.value.offset) + + ");\n\n"; + code += " if (offset === 0) {\n"; + code += " return false;\n"; + code += " }\n\n"; + + // special case for bools, which are treated as uint8 + code += " " + GenBBAccess() + ".write" + + MakeCamel(GenType(field.value.type)) + + "(this.bb_pos + offset, "; + if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; } + } + + code += "value);\n"; + code += " return true;\n"; + code += "}\n\n"; + } + + // Emit vector helpers + if (IsVector(field.value.type)) { + // Emit a length helper + GenDocComment(code_ptr); + code += MakeCamel(field.name, false); + code += "Length():number {\n" + offset_prefix; + + code += + GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n"; + + // For scalar types, emit a typed array helper + auto vectorType = field.value.type.VectorType(); + if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) { + GenDocComment(code_ptr); + + code += MakeCamel(field.name, false); + code += "Array():" + GenType(vectorType) + "Array|null {\n" + + offset_prefix; + + code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() + + ".bytes().buffer, " + GenBBAccess() + + ".bytes().byteOffset + " + GenBBAccess() + + ".__vector(this.bb_pos + offset), " + GenBBAccess() + + ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n"; + } + } + } + + // Emit the fully qualified name + if (parser_.opts.generate_name_strings) { + GenDocComment(code_ptr); + code += "static getFullyQualifiedName():string {\n"; + code += " return '" + WrapInNameSpace(struct_def) + "';\n"; + code += "}\n\n"; + } + + // Emit the size of the struct. + if (struct_def.fixed) { + GenDocComment(code_ptr); + code += "static sizeOf():number {\n"; + code += " return " + NumToString(struct_def.bytesize) + ";\n"; + code += "}\n\n"; + } + + // Emit a factory constructor + if (struct_def.fixed) { + std::string arguments; + GenStructArgs(imports, struct_def, &arguments, ""); + GenDocComment(code_ptr); + + code += "static create" + GetPrefixedName(struct_def) + + "(builder:flatbuffers.Builder"; + code += arguments + "):flatbuffers.Offset {\n"; + + GenStructBody(struct_def, &code, ""); + code += " return builder.offset();\n}\n\n"; + } else { + // Generate a method to start building a new object + GenDocComment(code_ptr); + + code += "static start" + GetPrefixedName(struct_def) + + "(builder:flatbuffers.Builder) {\n"; + + code += " builder.startObject(" + + NumToString(struct_def.fields.vec.size()) + ");\n"; + code += "}\n\n"; + + // Generate a set of static methods that allow table construction + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + const auto argname = GetArgName(field); + + // Generate the field insertion method + GenDocComment(code_ptr); + code += "static add" + MakeCamel(field.name); + code += "(builder:flatbuffers.Builder, " + argname + ":" + + GetArgType(imports, struct_def, field, false) + ") {\n"; + code += " builder.addField" + GenWriteMethod(field.value.type) + "("; + code += NumToString(it - struct_def.fields.vec.begin()) + ", "; + if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; } + code += argname + ", "; + if (!IsScalar(field.value.type.base_type)) { + code += "0"; + } else if (HasNullDefault(field)) { + if (IsLong(field.value.type.base_type)) { + code += "builder.createLong(0, 0)"; + } else { + code += "0"; + } + } else { + if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; } + code += GenDefaultValue(field, "builder", imports); + } + code += ");\n}\n\n"; + + if (IsVector(field.value.type)) { + auto vector_type = field.value.type.VectorType(); + auto alignment = InlineAlignment(vector_type); + auto elem_size = InlineSize(vector_type); + + // Generate a method to create a vector from a JavaScript array + if (!IsStruct(vector_type)) { + GenDocComment(code_ptr); + + const std::string sig_begin = + "static create" + MakeCamel(field.name) + + "Vector(builder:flatbuffers.Builder, data:"; + const std::string sig_end = "):flatbuffers.Offset"; + std::string type = + GenTypeName(imports, struct_def, vector_type, true) + "[]"; + if (type == "number[]") { + const auto &array_type = GenType(vector_type); + // the old type should be deprecated in the future + std::string type_old = "number[]|Uint8Array"; + std::string type_new = "number[]|" + array_type + "Array"; + if (type_old == type_new) { + type = type_new; + } else { + // add function overloads + code += sig_begin + type_new + sig_end + ";\n"; + code += + "/**\n * @deprecated This Uint8Array overload will " + "be removed in the future.\n */\n"; + code += sig_begin + type_old + sig_end + ";\n"; + type = type_new + "|Uint8Array"; + } + } + code += sig_begin + type + sig_end + " {\n"; + code += " builder.startVector(" + NumToString(elem_size); + code += ", data.length, " + NumToString(alignment) + ");\n"; + code += " for (let i = data.length - 1; i >= 0; i--) {\n"; + code += " builder.add" + GenWriteMethod(vector_type) + "("; + if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; } + code += "data[i]!);\n"; + code += " }\n"; + code += " return builder.endVector();\n"; + code += "}\n\n"; + } + + // Generate a method to start a vector, data to be added manually + // after + GenDocComment(code_ptr); + + code += "static start" + MakeCamel(field.name); + code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n"; + code += " builder.startVector(" + NumToString(elem_size); + code += ", numElems, " + NumToString(alignment) + ");\n"; + code += "}\n\n"; + } + } + + // Generate a method to stop building a new object + GenDocComment(code_ptr); + + code += "static end" + GetPrefixedName(struct_def); + code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n"; + + code += " const offset = builder.endObject();\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated && field.IsRequired()) { + code += " builder.requiredField(offset, "; + code += NumToString(field.value.offset); + code += ") // " + field.name + "\n"; + } + } + code += " return offset;\n"; + code += "}\n\n"; + + // Generate the methods to complete buffer construction + GenerateFinisher(struct_def, code_ptr, code, false); + GenerateFinisher(struct_def, code_ptr, code, true); + + // Generate a convenient CreateX function + if (CanCreateFactoryMethod(struct_def)) { + code += "static create" + GetPrefixedName(struct_def); + code += "(builder:flatbuffers.Builder"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) continue; + code += ", " + GetArgName(field) + ":" + + GetArgType(imports, struct_def, field, true); + } + + code += "):flatbuffers.Offset {\n"; + code += " " + struct_def.name + ".start" + + GetPrefixedName(struct_def) + "(builder);\n"; + + std::string methodPrefix = struct_def.name; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) continue; + + const auto arg_name = GetArgName(field); + + if (field.IsScalarOptional()) { + code += " if (" + arg_name + " !== null)\n "; + } + + code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "("; + code += "builder, " + arg_name + ");\n"; + } + + code += " return " + methodPrefix + ".end" + + GetPrefixedName(struct_def) + "(builder);\n"; + code += "}\n"; + } + } + + if (!struct_def.fixed && parser_.services_.vec.size() != 0) { + auto name = GetPrefixedName(struct_def, ""); + code += "\n"; + code += "serialize():Uint8Array {\n"; + code += " return this.bb!.bytes();\n"; + code += "}\n"; + + code += "\n"; + code += "static deserialize(buffer: Uint8Array):" + name + " {\n"; + code += " return " + AddImport(imports, struct_def, struct_def) + + ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n"; + code += "}\n"; + } + + if (parser_.opts.generate_object_based_api) { + std::string obj_api_class; + std::string obj_api_unpack_func; + GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class, + imports); + + code += obj_api_unpack_func + "}\n" + obj_api_class; + } else { + code += "}\n"; + } + } + + static bool HasNullDefault(const FieldDef &field) { + return field.IsOptional() && field.value.constant == "null"; + } + + std::string GetArgType(import_set &imports, const Definition &owner, + const FieldDef &field, bool allowNull) { + return GenTypeName(imports, owner, field.value.type, true, + allowNull && field.IsOptional()); + } + + static std::string GetArgName(const FieldDef &field) { + auto argname = MakeCamel(field.name, false); + if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; } + + return argname; + } + + std::string GetPrefixedName(const StructDef &struct_def, + const char *prefix = "") { + return prefix + struct_def.name; + } +}; // namespace ts +} // namespace ts + +bool GenerateTS(const Parser &parser, const std::string &path, + const std::string &file_name) { + ts::TsGenerator generator(parser, path, file_name); + return generator.generate(); +} + +std::string TSMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name) { + FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX); + + std::string filebase = + flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); + ts::TsGenerator generator(parser, path, file_name); + std::string make_rule = + generator.GeneratedFileName(path, filebase, parser.opts) + ": "; + + auto included_files = parser.GetIncludedFilesRecursive(file_name); + for (auto it = included_files.begin(); it != included_files.end(); ++it) { + make_rule += " " + *it; + } + return make_rule; +} + +} // namespace flatbuffers diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_parser.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_parser.cpp index 28ff3ec0..5bf0635e 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_parser.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/idl_parser.cpp @@ -82,15 +82,25 @@ static bool ValidateUTF8(const std::string &str) { return true; } -// Convert an underscore_based_indentifier in to camelCase. +static bool IsLowerSnakeCase(const std::string &str) { + for (size_t i = 0; i < str.length(); i++) { + char c = str[i]; + if (!check_ascii_range(c, 'a', 'z') && !is_digit(c) && c != '_') { + return false; + } + } + return true; +} + +// Convert an underscore_based_identifier in to camelCase. // Also uppercases the first character if first is true. std::string MakeCamel(const std::string &in, bool first) { std::string s; for (size_t i = 0; i < in.length(); i++) { if (!i && first) - s += static_cast(toupper(in[0])); + s += CharToUpper(in[0]); else if (in[i] == '_' && i + 1 < in.length()) - s += static_cast(toupper(in[++i])); + s += CharToUpper(in[++i]); else s += in[i]; } @@ -102,7 +112,7 @@ std::string MakeScreamingCamel(const std::string &in) { std::string s; for (size_t i = 0; i < in.length(); i++) { if (in[i] != '_') - s += static_cast(toupper(in[i])); + s += CharToUpper(in[i]); else s += in[i]; } @@ -132,7 +142,9 @@ void Parser::Message(const std::string &msg) { error_ += ": " + msg; } -void Parser::Warning(const std::string &msg) { Message("warning: " + msg); } +void Parser::Warning(const std::string &msg) { + if (!opts.no_warnings) Message("warning: " + msg); +} CheckedError Parser::Error(const std::string &msg) { Message("error: " + msg); @@ -142,18 +154,34 @@ CheckedError Parser::Error(const std::string &msg) { inline CheckedError NoError() { return CheckedError(false); } CheckedError Parser::RecurseError() { - return Error("maximum parsing recursion of " + - NumToString(FLATBUFFERS_MAX_PARSING_DEPTH) + " reached"); + return Error("maximum parsing depth " + NumToString(parse_depth_counter_) + + " reached"); } -template CheckedError Parser::Recurse(F f) { - if (recurse_protection_counter >= (FLATBUFFERS_MAX_PARSING_DEPTH)) - return RecurseError(); - recurse_protection_counter++; - auto ce = f(); - recurse_protection_counter--; - return ce; -} +class Parser::ParseDepthGuard { + public: + explicit ParseDepthGuard(Parser *parser_not_null) + : parser_(*parser_not_null), caller_depth_(parser_.parse_depth_counter_) { + FLATBUFFERS_ASSERT(caller_depth_ <= (FLATBUFFERS_MAX_PARSING_DEPTH) && + "Check() must be called to prevent stack overflow"); + parser_.parse_depth_counter_ += 1; + } + + ~ParseDepthGuard() { parser_.parse_depth_counter_ -= 1; } + + CheckedError Check() { + return caller_depth_ >= (FLATBUFFERS_MAX_PARSING_DEPTH) + ? parser_.RecurseError() + : CheckedError(false); + } + + FLATBUFFERS_DELETE_FUNC(ParseDepthGuard(const ParseDepthGuard &)); + FLATBUFFERS_DELETE_FUNC(ParseDepthGuard &operator=(const ParseDepthGuard &)); + + private: + Parser &parser_; + const int caller_depth_; +}; template std::string TypeToIntervalString() { return "[" + NumToString((flatbuffers::numeric_limits::lowest)()) + "; " + @@ -162,8 +190,20 @@ template std::string TypeToIntervalString() { // atot: template version of atoi/atof: convert a string to an instance of T. template -inline CheckedError atot(const char *s, Parser &parser, T *val) { - auto done = StringToNumber(s, val); +bool atot_scalar(const char *s, T *val, bool_constant) { + return StringToNumber(s, val); +} + +template +bool atot_scalar(const char *s, T *val, bool_constant) { + // Normalize NaN parsed from fbs or json to unsigned NaN. + if (false == StringToNumber(s, val)) return false; + *val = (*val != *val) ? std::fabs(*val) : *val; + return true; +} + +template CheckedError atot(const char *s, Parser &parser, T *val) { + auto done = atot_scalar(s, val, bool_constant::value>()); if (done) return NoError(); if (0 == *val) return parser.Error("invalid number: \"" + std::string(s) + "\""); @@ -185,9 +225,10 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name, if (components.empty() || !max_components) { return name; } std::string stream_str; for (size_t i = 0; i < std::min(components.size(), max_components); i++) { - if (i) { stream_str += '.'; } - stream_str += std::string(components[i]); + stream_str += components[i]; + stream_str += '.'; } + if (!stream_str.empty()) stream_str.pop_back(); if (name.length()) { stream_str += '.'; stream_str += name; @@ -195,6 +236,29 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name, return stream_str; } +template +T *LookupTableByName(const SymbolTable &table, const std::string &name, + const Namespace ¤t_namespace, size_t skip_top) { + const auto &components = current_namespace.components; + if (table.dict.empty()) return nullptr; + if (components.size() < skip_top) return nullptr; + const auto N = components.size() - skip_top; + std::string full_name; + for (size_t i = 0; i < N; i++) { + full_name += components[i]; + full_name += '.'; + } + for (size_t i = N; i > 0; i--) { + full_name += name; + auto obj = table.Lookup(full_name); + if (obj) return obj; + auto len = full_name.size() - components[i - 1].size() - 1 - name.size(); + full_name.resize(len); + } + FLATBUFFERS_ASSERT(full_name.empty()); + return table.Lookup(name); // lookup in global namespace +} + // Declare tokens we'll use. Single character tokens are represented by their // ascii character code (e.g. '{'), others above 256. // clang-format off @@ -434,15 +498,19 @@ CheckedError Parser::Next() { } FLATBUFFERS_FALLTHROUGH(); // else fall thru default: - const auto has_sign = (c == '+') || (c == '-'); - // '-'/'+' and following identifier - can be a predefined constant like: - // NAN, INF, PI, etc. - if (IsIdentifierStart(c) || (has_sign && IsIdentifierStart(*cursor_))) { + if (IsIdentifierStart(c)) { // Collect all chars of an identifier: const char *start = cursor_ - 1; while (IsIdentifierStart(*cursor_) || is_digit(*cursor_)) cursor_++; attribute_.append(start, cursor_); - token_ = has_sign ? kTokenStringConstant : kTokenIdentifier; + token_ = kTokenIdentifier; + return NoError(); + } + + const auto has_sign = (c == '+') || (c == '-'); + if (has_sign && IsIdentifierStart(*cursor_)) { + // '-'/'+' and following identifier - it could be a predefined + // constant. Return the sign in token_, see ParseSingleValue. return NoError(); } @@ -534,13 +602,7 @@ CheckedError Parser::ParseNamespacing(std::string *id, std::string *last) { EnumDef *Parser::LookupEnum(const std::string &id) { // Search thru parent namespaces. - for (int components = static_cast(current_namespace_->components.size()); - components >= 0; components--) { - auto ed = enums_.Lookup( - current_namespace_->GetFullyQualifiedName(id, components)); - if (ed) return ed; - } - return nullptr; + return LookupTableByName(enums_, id, *current_namespace_, 0); } StructDef *Parser::LookupStruct(const std::string &id) const { @@ -549,6 +611,13 @@ StructDef *Parser::LookupStruct(const std::string &id) const { return sd; } +StructDef *Parser::LookupStructThruParentNamespaces( + const std::string &id) const { + auto sd = LookupTableByName(structs_, id, *current_namespace_, 1); + if (sd) sd->refcount++; + return sd; +} + CheckedError Parser::ParseTypeIdent(Type &type) { std::string id = attribute_; EXPECT(kTokenIdentifier); @@ -607,9 +676,11 @@ CheckedError Parser::ParseType(Type &type) { ECHECK(ParseTypeIdent(type)); } } else if (token_ == '[') { + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); NEXT(); Type subtype; - ECHECK(Recurse([&]() { return ParseType(subtype); })); + ECHECK(ParseType(subtype)); if (IsSeries(subtype)) { // We could support this, but it will complicate things, and it's // easier to work around with a struct around the inner vector. @@ -671,23 +742,36 @@ CheckedError Parser::ParseField(StructDef &struct_def) { if (LookupCreateStruct(name, false, false)) return Error("field name can not be the same as table/struct name"); +// if (!IsLowerSnakeCase(name)) { +// Warning("field names should be lowercase snake_case, got: " + name); +// } + std::vector dc = doc_comment_; EXPECT(kTokenIdentifier); EXPECT(':'); Type type; ECHECK(ParseType(type)); - if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type) && - !IsArray(type)) - return Error("structs_ may contain only scalar or struct fields"); + if (struct_def.fixed) { + auto valid = IsScalar(type.base_type) || IsStruct(type); + if (!valid && IsArray(type)) { + const auto &elem_type = type.VectorType(); + valid |= IsScalar(elem_type.base_type) || IsStruct(elem_type); + } + if (!valid) + return Error("structs may contain only scalar or struct fields"); + } if (!struct_def.fixed && IsArray(type)) return Error("fixed-length array in table must be wrapped in struct"); - if (IsArray(type) && !SupportsAdvancedArrayFeatures()) { - return Error( - "Arrays are not yet supported in all " - "the specified programming languages."); + if (IsArray(type)) { + advanced_features_ |= reflection::AdvancedArrayFeatures; + if (!SupportsAdvancedArrayFeatures()) { + return Error( + "Arrays are not yet supported in all " + "the specified programming languages."); + } } FieldDef *typefield = nullptr; @@ -696,12 +780,12 @@ CheckedError Parser::ParseField(StructDef &struct_def) { // with a special suffix. ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), type.enum_def->underlying_type, &typefield)); - } else if (type.base_type == BASE_TYPE_VECTOR && - type.element == BASE_TYPE_UNION) { + } else if (IsVector(type) && type.element == BASE_TYPE_UNION) { + advanced_features_ |= reflection::AdvancedUnionFeatures; // Only cpp, js and ts supports the union vector feature so far. if (!SupportsAdvancedUnionFeatures()) { return Error( - "Vectors of unions are not yet supported in all " + "Vectors of unions are not yet supported in at least one of " "the specified programming languages."); } // For vector of union fields, add a second auto-generated vector field to @@ -718,11 +802,26 @@ CheckedError Parser::ParseField(StructDef &struct_def) { if (token_ == '=') { NEXT(); ECHECK(ParseSingleValue(&field->name, field->value, true)); - if (!IsScalar(type.base_type) || - (struct_def.fixed && field->value.constant != "0")) + if (IsStruct(type) || (struct_def.fixed && field->value.constant != "0")) return Error( - "default values currently only supported for scalars in tables"); + "default values are not supported for struct fields, table fields, " + "or in structs."); + if (IsString(type) || IsVector(type)) { + advanced_features_ |= reflection::DefaultVectorsAndStrings; + if (field->value.constant != "0" && field->value.constant != "null" && + !SupportsDefaultVectorsAndStrings()) { + return Error( + "Default values for strings and vectors are not supported in one " + "of the specified programming languages"); + } + } + + if (IsVector(type) && field->value.constant != "0" && + field->value.constant != "[]") { + return Error("The only supported default for vectors is `[]`."); + } } + // Append .0 if the value has not it (skip hex and scientific floats). // This suffix needed for generated C++ code. if (IsFloat(type.base_type)) { @@ -738,35 +837,13 @@ CheckedError Parser::ParseField(StructDef &struct_def) { field->value.constant += ".0"; } } - if (type.enum_def) { - // The type.base_type can only be scalar, union, array or vector. - // Table, struct or string can't have enum_def. - // Default value of union and vector in NONE, NULL translated to "0". - FLATBUFFERS_ASSERT(IsInteger(type.base_type) || - (type.base_type == BASE_TYPE_UNION) || - (type.base_type == BASE_TYPE_VECTOR) || - (type.base_type == BASE_TYPE_ARRAY)); - if (type.base_type == BASE_TYPE_VECTOR) { - // Vector can't use initialization list. - FLATBUFFERS_ASSERT(field->value.constant == "0"); - } else { - // All unions should have the NONE ("0") enum value. - auto in_enum = type.enum_def->attributes.Lookup("bit_flags") || - type.enum_def->FindByValue(field->value.constant); - if (false == in_enum) - return Error("default value of " + field->value.constant + - " for field " + name + " is not part of enum " + - type.enum_def->name); - } - } field->doc_comment = dc; ECHECK(ParseMetaData(&field->attributes)); field->deprecated = field->attributes.Lookup("deprecated") != nullptr; auto hash_name = field->attributes.Lookup("hash"); if (hash_name) { - switch ((type.base_type == BASE_TYPE_VECTOR) ? type.element - : type.base_type) { + switch ((IsVector(type)) ? type.element : type.base_type) { case BASE_TYPE_SHORT: case BASE_TYPE_USHORT: { if (FindHashFunction16(hash_name->constant.c_str()) == nullptr) @@ -794,6 +871,87 @@ CheckedError Parser::ParseField(StructDef &struct_def) { "hashing."); } } + + // For historical convenience reasons, string keys are assumed required. + // Scalars are kDefault unless otherwise specified. + // Nonscalars are kOptional unless required; + field->key = field->attributes.Lookup("key") != nullptr; + const bool required = field->attributes.Lookup("required") != nullptr || + (IsString(type) && field->key); + const bool default_str_or_vec = + ((IsString(type) || IsVector(type)) && field->value.constant != "0"); + const bool optional = IsScalar(type.base_type) + ? (field->value.constant == "null") + : !(required || default_str_or_vec); + if (required && optional) { + return Error("Fields cannot be both optional and required."); + } + field->presence = FieldDef::MakeFieldPresence(optional, required); + + if (required && (struct_def.fixed || IsScalar(type.base_type))) { + return Error("only non-scalar fields in tables may be 'required'"); + } + if (field->key) { + if (struct_def.has_key) return Error("only one field may be set as 'key'"); + struct_def.has_key = true; + if (!IsScalar(type.base_type) && !IsString(type)) { + return Error("'key' field must be string or scalar type"); + } + } + + if (field->IsScalarOptional()) { + advanced_features_ |= reflection::OptionalScalars; + if (type.enum_def && type.enum_def->Lookup("null")) { + FLATBUFFERS_ASSERT(IsInteger(type.base_type)); + return Error( + "the default 'null' is reserved for declaring optional scalar " + "fields, it conflicts with declaration of enum '" + + type.enum_def->name + "'."); + } + if (field->attributes.Lookup("key")) { + return Error( + "only a non-optional scalar field can be used as a 'key' field"); + } + if (!SupportsOptionalScalars()) { + return Error( + "Optional scalars are not yet supported in at least one the of " + "the specified programming languages."); + } + } + + if (type.enum_def) { + // Verify the enum's type and default value. + const std::string &constant = field->value.constant; + if (type.base_type == BASE_TYPE_UNION) { + if (constant != "0") { return Error("Union defaults must be NONE"); } + } else if (IsVector(type)) { + if (constant != "0" && constant != "[]") { + return Error("Vector defaults may only be `[]`."); + } + } else if (IsArray(type)) { + if (constant != "0") { + return Error("Array defaults are not supported yet."); + } + } else { + if (!IsInteger(type.base_type)) { + return Error("Enums must have integer base types"); + } + // Optional and bitflags enums may have default constants that are not + // their specified variants. + if (!field->IsOptional() && + type.enum_def->attributes.Lookup("bit_flags") == nullptr) { + if (type.enum_def->FindByValue(constant) == nullptr) { + return Error("default value of `" + constant + "` for " + "field `" + + name + "` is not part of enum `" + type.enum_def->name + + "`."); + } + } + } + } + + if (field->deprecated && struct_def.fixed) + return Error("can't deprecate fields in a struct"); + auto cpp_type = field->attributes.Lookup("cpp_type"); if (cpp_type) { if (!hash_name) @@ -807,21 +965,7 @@ CheckedError Parser::ParseField(StructDef &struct_def) { field->attributes.Add("cpp_ptr_type", val); } } - if (field->deprecated && struct_def.fixed) - return Error("can't deprecate fields in a struct"); - field->required = field->attributes.Lookup("required") != nullptr; - if (field->required && (struct_def.fixed || IsScalar(type.base_type))) - return Error("only non-scalar fields in tables may be 'required'"); - field->key = field->attributes.Lookup("key") != nullptr; - if (field->key) { - if (struct_def.has_key) return Error("only one field may be set as 'key'"); - struct_def.has_key = true; - if (!IsScalar(type.base_type)) { - field->required = true; - if (type.base_type != BASE_TYPE_STRING) - return Error("'key' field must be string or scalar type"); - } - } + field->shared = field->attributes.Lookup("shared") != nullptr; if (field->shared && field->value.type.base_type != BASE_TYPE_STRING) return Error("shared can only be defined on strings"); @@ -860,28 +1004,46 @@ CheckedError Parser::ParseField(StructDef &struct_def) { if (typefield) { if (!IsScalar(typefield->value.type.base_type)) { // this is a union vector field - typefield->required = field->required; + typefield->presence = field->presence; } // If this field is a union, and it has a manually assigned id, // the automatically added type field should have an id as well (of N - 1). auto attr = field->attributes.Lookup("id"); if (attr) { - auto id = atoi(attr->constant.c_str()); - auto val = new Value(); - val->type = attr->type; - val->constant = NumToString(id - 1); - typefield->attributes.Add("id", val); + const auto &id_str = attr->constant; + voffset_t id = 0; + const auto done = !atot(id_str.c_str(), *this, &id).Check(); + if (done && id > 0) { + auto val = new Value(); + val->type = attr->type; + val->constant = NumToString(id - 1); + typefield->attributes.Add("id", val); + } else { + return Error( + "a union type effectively adds two fields with non-negative ids, " + "its id must be that of the second field (the first field is " + "the type field and not explicitly declared in the schema);\n" + "field: " + + field->name + ", id: " + id_str); + } } + // if this field is a union that is deprecated, + // the automatically added type field should be deprecated as well + if (field->deprecated) { typefield->deprecated = true; } } EXPECT(';'); return NoError(); } -CheckedError Parser::ParseString(Value &val) { +CheckedError Parser::ParseString(Value &val, bool use_string_pooling) { auto s = attribute_; EXPECT(kTokenStringConstant); - val.constant = NumToString(builder_.CreateString(s).o); + if (use_string_pooling) { + val.constant = NumToString(builder_.CreateSharedString(s).o); + } else { + val.constant = NumToString(builder_.CreateString(s).o); + } return NoError(); } @@ -905,8 +1067,7 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, auto &type = elem->second->value.type; if (type.enum_def == val.type.enum_def) { if (inside_vector) { - if (type.base_type == BASE_TYPE_VECTOR && - type.element == BASE_TYPE_UTYPE) { + if (IsVector(type) && type.element == BASE_TYPE_UTYPE) { // Vector of union type field. uoffset_t offset; ECHECK(atot(elem->first.constant.c_str(), *this, &offset)); @@ -946,6 +1107,8 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, } if (next_name == type_name) { EXPECT(':'); + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); Value type_val = type_field->value; ECHECK(ParseAnyValue(type_val, type_field, 0, nullptr, 0)); constant = type_val.constant; @@ -973,8 +1136,8 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, builder_.ClearOffsets(); val.constant = NumToString(builder_.GetSize()); } - } else if (enum_val->union_type.base_type == BASE_TYPE_STRING) { - ECHECK(ParseString(val)); + } else if (IsString(enum_val->union_type)) { + ECHECK(ParseString(val, field->shared)); } else { FLATBUFFERS_ASSERT(false); } @@ -984,7 +1147,7 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr)); break; case BASE_TYPE_STRING: { - ECHECK(ParseString(val)); + ECHECK(ParseString(val, field->shared)); break; } case BASE_TYPE_VECTOR: { @@ -1072,6 +1235,9 @@ CheckedError Parser::ParseTableDelimiters(size_t &fieldn, CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, uoffset_t *ovalue) { + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); + size_t fieldn_outer = 0; auto err = ParseTableDelimiters( fieldn_outer, &struct_def, @@ -1107,9 +1273,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, ECHECK( ParseNestedFlatbuffer(val, field, fieldn, struct_def_inner)); } else { - ECHECK(Recurse([&]() { - return ParseAnyValue(val, field, fieldn, struct_def_inner, 0); - })); + ECHECK(ParseAnyValue(val, field, fieldn, struct_def_inner, 0)); } // Hardcoded insertion-sort with error-check. // If fields are specified in order, then this loop exits @@ -1135,7 +1299,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, for (auto field_it = struct_def.fields.vec.begin(); field_it != struct_def.fields.vec.end(); ++field_it) { auto required_field = *field_it; - if (!required_field->required) { continue; } + if (!required_field->IsRequired()) { continue; } bool found = false; for (auto pf_it = field_stack_.end() - fieldn_outer; pf_it != field_stack_.end(); ++pf_it) { @@ -1243,22 +1407,71 @@ CheckedError Parser::ParseVectorDelimiters(uoffset_t &count, F body) { return NoError(); } -static bool CompareType(const uint8_t *a, const uint8_t *b, BaseType ftype) { - switch (ftype) { -#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ - case BASE_TYPE_##ENUM: return ReadScalar(a) < ReadScalar(b); +static bool CompareSerializedScalars(const uint8_t *a, const uint8_t *b, + const FieldDef &key) { + switch (key.value.type.base_type) { +#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ + case BASE_TYPE_##ENUM: { \ + CTYPE def = static_cast(0); \ + if (!a || !b) { StringToNumber(key.value.constant.c_str(), &def); } \ + const auto av = a ? ReadScalar(a) : def; \ + const auto bv = b ? ReadScalar(b) : def; \ + return av < bv; \ + } FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) #undef FLATBUFFERS_TD - case BASE_TYPE_STRING: - // Indirect offset pointer to string pointer. - a += ReadScalar(a); - b += ReadScalar(b); - return *reinterpret_cast(a) < - *reinterpret_cast(b); - default: return false; + default: { + FLATBUFFERS_ASSERT(false && "scalar type expected"); + return false; + } } } +static bool CompareTablesByScalarKey(const Offset *_a, + const Offset
*_b, + const FieldDef &key) { + const voffset_t offset = key.value.offset; + // Indirect offset pointer to table pointer. + auto a = reinterpret_cast(_a) + ReadScalar(_a); + auto b = reinterpret_cast(_b) + ReadScalar(_b); + // Fetch field address from table. + a = reinterpret_cast(a)->GetAddressOf(offset); + b = reinterpret_cast(b)->GetAddressOf(offset); + return CompareSerializedScalars(a, b, key); +} + +static bool CompareTablesByStringKey(const Offset
*_a, + const Offset
*_b, + const FieldDef &key) { + const voffset_t offset = key.value.offset; + // Indirect offset pointer to table pointer. + auto a = reinterpret_cast(_a) + ReadScalar(_a); + auto b = reinterpret_cast(_b) + ReadScalar(_b); + // Fetch field address from table. + a = reinterpret_cast(a)->GetAddressOf(offset); + b = reinterpret_cast(b)->GetAddressOf(offset); + if (a && b) { + // Indirect offset pointer to string pointer. + a += ReadScalar(a); + b += ReadScalar(b); + return *reinterpret_cast(a) < + *reinterpret_cast(b); + } else { + return a ? true : false; + } +} + +static void SwapSerializedTables(Offset
*a, Offset
*b) { + // These are serialized offsets, so are relative where they are + // stored in memory, so compute the distance between these pointers: + ptrdiff_t diff = (b - a) * sizeof(Offset
); + FLATBUFFERS_ASSERT(diff >= 0); // Guaranteed by SimpleQsort. + auto udiff = static_cast(diff); + a->o = EndianScalar(ReadScalar(a) - udiff); + b->o = EndianScalar(ReadScalar(b) + udiff); + std::swap(*a, *b); +} + // See below for why we need our own sort :( template void SimpleQsort(T *begin, T *end, size_t width, F comparator, S swapper) { @@ -1270,7 +1483,7 @@ void SimpleQsort(T *begin, T *end, size_t width, F comparator, S swapper) { r -= width; swapper(l, r); } else { - l++; + l += width; } } l -= width; @@ -1279,27 +1492,43 @@ void SimpleQsort(T *begin, T *end, size_t width, F comparator, S swapper) { SimpleQsort(r, end, width, comparator, swapper); } +CheckedError Parser::ParseAlignAttribute(const std::string &align_constant, + size_t min_align, size_t *align) { + // Use uint8_t to avoid problems with size_t==`unsigned long` on LP64. + uint8_t align_value; + if (StringToNumber(align_constant.c_str(), &align_value) && + VerifyAlignmentRequirements(static_cast(align_value), + min_align)) { + *align = align_value; + return NoError(); + } + return Error("unexpected force_align value '" + align_constant + + "', alignment must be a power of two integer ranging from the " + "type\'s natural alignment " + + NumToString(min_align) + " to " + + NumToString(FLATBUFFERS_MAX_ALIGNMENT)); +} + CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue, FieldDef *field, size_t fieldn) { uoffset_t count = 0; auto err = ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError { Value val; val.type = type; - ECHECK(Recurse([&]() { - return ParseAnyValue(val, field, fieldn, nullptr, count, true); - })); + ECHECK(ParseAnyValue(val, field, fieldn, nullptr, count, true)); field_stack_.push_back(std::make_pair(val, nullptr)); return NoError(); }); ECHECK(err); - const auto *force_align = field->attributes.Lookup("force_align"); - const size_t align = - force_align ? static_cast(atoi(force_align->constant.c_str())) - : 1; const size_t len = count * InlineSize(type) / InlineAlignment(type); const size_t elemsize = InlineAlignment(type); - if (align > 1) { builder_.ForceVectorAlignment(len, elemsize, align); } + const auto force_align = field->attributes.Lookup("force_align"); + if (force_align) { + size_t align; + ECHECK(ParseAlignAttribute(force_align->constant, 1, &align)); + if (align > 1) { builder_.ForceVectorAlignment(len, elemsize, align); } + } builder_.StartVector(len, elemsize); for (uoffset_t i = 0; i < count; i++) { @@ -1345,23 +1574,21 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue, // globals, making parsing thread-unsafe. // So for now, we use SimpleQsort above. // TODO: replace with something better, preferably not recursive. - static voffset_t offset = key->value.offset; - static BaseType ftype = key->value.type.base_type; if (type.struct_def->fixed) { + const voffset_t offset = key->value.offset; + const size_t struct_size = type.struct_def->bytesize; auto v = reinterpret_cast(builder_.GetCurrentBufferPointer()); SimpleQsort( v->Data(), v->Data() + v->size() * type.struct_def->bytesize, type.struct_def->bytesize, - [](const uint8_t *a, const uint8_t *b) -> bool { - return CompareType(a + offset, b + offset, ftype); + [offset, key](const uint8_t *a, const uint8_t *b) -> bool { + return CompareSerializedScalars(a + offset, b + offset, *key); }, - [&](uint8_t *a, uint8_t *b) { + [struct_size](uint8_t *a, uint8_t *b) { // FIXME: faster? - for (size_t i = 0; i < type.struct_def->bytesize; i++) { - std::swap(a[i], b[i]); - } + for (size_t i = 0; i < struct_size; i++) { std::swap(a[i], b[i]); } }); } else { auto v = reinterpret_cast> *>( @@ -1369,29 +1596,21 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue, // Here also can't use std::sort. We do have an iterator type for it, // but it is non-standard as it will dereference the offsets, and thus // can't be used to swap elements. - SimpleQsort>( - v->data(), v->data() + v->size(), 1, - [](const Offset
*_a, const Offset
*_b) -> bool { - // Indirect offset pointer to table pointer. - auto a = reinterpret_cast(_a) + - ReadScalar(_a); - auto b = reinterpret_cast(_b) + - ReadScalar(_b); - // Fetch field address from table. - a = reinterpret_cast(a)->GetAddressOf(offset); - b = reinterpret_cast(b)->GetAddressOf(offset); - return CompareType(a, b, ftype); - }, - [&](Offset
*a, Offset
*b) { - // These are serialized offsets, so are relative where they are - // stored in memory, so compute the distance between these pointers: - ptrdiff_t diff = (b - a) * sizeof(Offset
); - FLATBUFFERS_ASSERT(diff >= 0); // Guaranteed by SimpleQsort. - auto udiff = static_cast(diff); - a->o = EndianScalar(ReadScalar(a) - udiff); - b->o = EndianScalar(ReadScalar(b) + udiff); - std::swap(*a, *b); - }); + if (key->value.type.base_type == BASE_TYPE_STRING) { + SimpleQsort>( + v->data(), v->data() + v->size(), 1, + [key](const Offset
*_a, const Offset
*_b) -> bool { + return CompareTablesByStringKey(_a, _b, *key); + }, + SwapSerializedTables); + } else { + SimpleQsort>( + v->data(), v->data() + v->size(), 1, + [key](const Offset
*_a, const Offset
*_b) -> bool { + return CompareTablesByScalarKey(_a, _b, *key); + }, + SwapSerializedTables); + } } } return NoError(); @@ -1461,7 +1680,7 @@ CheckedError Parser::ParseNestedFlatbuffer(Value &val, FieldDef *field, nested_parser.enums_ = enums_; nested_parser.opts = opts; nested_parser.uses_flexbuffers_ = uses_flexbuffers_; - + nested_parser.parse_depth_counter_ = parse_depth_counter_; // Parse JSON substring into new flatbuffer builder using nested_parser bool ok = nested_parser.Parse(substring.c_str(), nullptr, nullptr); @@ -1496,7 +1715,7 @@ CheckedError Parser::ParseMetaData(SymbolTable *attributes) { name); NEXT(); auto e = new Value(); - attributes->Add(name, e); + if (attributes->Add(name, e)) Warning("attribute already found: " + name); if (Is(':')) { NEXT(); ECHECK(ParseSingleValue(&name, *e, true)); @@ -1511,45 +1730,6 @@ CheckedError Parser::ParseMetaData(SymbolTable *attributes) { return NoError(); } -CheckedError Parser::TryTypedValue(const std::string *name, int dtoken, - bool check, Value &e, BaseType req, - bool *destmatch) { - bool match = dtoken == token_; - if (match) { - FLATBUFFERS_ASSERT(*destmatch == false); - *destmatch = true; - e.constant = attribute_; - // Check token match - if (!check) { - if (e.type.base_type == BASE_TYPE_NONE) { - e.type.base_type = req; - } else { - return Error( - std::string("type mismatch: expecting: ") + - kTypeNames[e.type.base_type] + ", found: " + kTypeNames[req] + - ", name: " + (name ? *name : "") + ", value: " + e.constant); - } - } - // The exponent suffix of hexadecimal float-point number is mandatory. - // A hex-integer constant is forbidden as an initializer of float number. - if ((kTokenFloatConstant != dtoken) && IsFloat(e.type.base_type)) { - const auto &s = e.constant; - const auto k = s.find_first_of("0123456789."); - if ((std::string::npos != k) && (s.length() > (k + 1)) && - (s[k] == '0' && is_alpha_char(s[k + 1], 'X')) && - (std::string::npos == s.find_first_of("pP", k + 2))) { - return Error( - "invalid number, the exponent suffix of hexadecimal " - "floating-point literals is mandatory: \"" + - s + "\""); - } - } - - NEXT(); - } - return NoError(); -} - CheckedError Parser::ParseEnumFromString(const Type &type, std::string *result) { const auto base_type = @@ -1638,7 +1818,8 @@ template inline void SingleValueRepack(Value &e, T val) { if (IsInteger(e.type.base_type)) { e.constant = NumToString(val); } } #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) -// Normilaze defaults NaN to unsigned quiet-NaN(0). +// Normalize defaults NaN to unsigned quiet-NaN(0) if value was parsed from +// hex-float literal. static inline void SingleValueRepack(Value &e, float val) { if (val != val) e.constant = "nan"; } @@ -1647,67 +1828,119 @@ static inline void SingleValueRepack(Value &e, double val) { } #endif -CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, - bool check_now) { - // First see if this could be a conversion function: - if (token_ == kTokenIdentifier && *cursor_ == '(') { - // todo: Extract processing of conversion functions to ParseFunction. - const auto functionname = attribute_; - if (!IsFloat(e.type.base_type)) { - return Error(functionname + ": type of argument mismatch, expecting: " + - kTypeNames[BASE_TYPE_DOUBLE] + - ", found: " + kTypeNames[e.type.base_type] + +CheckedError Parser::ParseFunction(const std::string *name, Value &e) { + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); + + // Copy name, attribute will be changed on NEXT(). + const auto functionname = attribute_; + if (!IsFloat(e.type.base_type)) { + return Error(functionname + ": type of argument mismatch, expecting: " + + kTypeNames[BASE_TYPE_DOUBLE] + + ", found: " + kTypeNames[e.type.base_type] + + ", name: " + (name ? *name : "") + ", value: " + e.constant); + } + NEXT(); + EXPECT('('); + ECHECK(ParseSingleValue(name, e, false)); + EXPECT(')'); + // calculate with double precision + double x, y = 0.0; + ECHECK(atot(e.constant.c_str(), *this, &x)); + // clang-format off + auto func_match = false; + #define FLATBUFFERS_FN_DOUBLE(name, op) \ + if (!func_match && functionname == name) { y = op; func_match = true; } + FLATBUFFERS_FN_DOUBLE("deg", x / kPi * 180); + FLATBUFFERS_FN_DOUBLE("rad", x * kPi / 180); + FLATBUFFERS_FN_DOUBLE("sin", sin(x)); + FLATBUFFERS_FN_DOUBLE("cos", cos(x)); + FLATBUFFERS_FN_DOUBLE("tan", tan(x)); + FLATBUFFERS_FN_DOUBLE("asin", asin(x)); + FLATBUFFERS_FN_DOUBLE("acos", acos(x)); + FLATBUFFERS_FN_DOUBLE("atan", atan(x)); + // TODO(wvo): add more useful conversion functions here. + #undef FLATBUFFERS_FN_DOUBLE + // clang-format on + if (true != func_match) { + return Error(std::string("Unknown conversion function: ") + functionname + + ", field name: " + (name ? *name : "") + + ", value: " + e.constant); + } + e.constant = NumToString(y); + return NoError(); +} + +CheckedError Parser::TryTypedValue(const std::string *name, int dtoken, + bool check, Value &e, BaseType req, + bool *destmatch) { + FLATBUFFERS_ASSERT(*destmatch == false && dtoken == token_); + *destmatch = true; + e.constant = attribute_; + // Check token match + if (!check) { + if (e.type.base_type == BASE_TYPE_NONE) { + e.type.base_type = req; + } else { + return Error(std::string("type mismatch: expecting: ") + + kTypeNames[e.type.base_type] + + ", found: " + kTypeNames[req] + ", name: " + (name ? *name : "") + ", value: " + e.constant); } - NEXT(); - EXPECT('('); - ECHECK(Recurse([&]() { return ParseSingleValue(name, e, false); })); - EXPECT(')'); - // calculate with double precision - double x, y = 0.0; - ECHECK(atot(e.constant.c_str(), *this, &x)); - auto func_match = false; - // clang-format off - #define FLATBUFFERS_FN_DOUBLE(name, op) \ - if (!func_match && functionname == name) { y = op; func_match = true; } - FLATBUFFERS_FN_DOUBLE("deg", x / kPi * 180); - FLATBUFFERS_FN_DOUBLE("rad", x * kPi / 180); - FLATBUFFERS_FN_DOUBLE("sin", sin(x)); - FLATBUFFERS_FN_DOUBLE("cos", cos(x)); - FLATBUFFERS_FN_DOUBLE("tan", tan(x)); - FLATBUFFERS_FN_DOUBLE("asin", asin(x)); - FLATBUFFERS_FN_DOUBLE("acos", acos(x)); - FLATBUFFERS_FN_DOUBLE("atan", atan(x)); - // TODO(wvo): add more useful conversion functions here. - #undef FLATBUFFERS_FN_DOUBLE - // clang-format on - if (true != func_match) { - return Error(std::string("Unknown conversion function: ") + functionname + - ", field name: " + (name ? *name : "") + - ", value: " + e.constant); + } + // The exponent suffix of hexadecimal float-point number is mandatory. + // A hex-integer constant is forbidden as an initializer of float number. + if ((kTokenFloatConstant != dtoken) && IsFloat(e.type.base_type)) { + const auto &s = e.constant; + const auto k = s.find_first_of("0123456789."); + if ((std::string::npos != k) && (s.length() > (k + 1)) && + (s[k] == '0' && is_alpha_char(s[k + 1], 'X')) && + (std::string::npos == s.find_first_of("pP", k + 2))) { + return Error( + "invalid number, the exponent suffix of hexadecimal " + "floating-point literals is mandatory: \"" + + s + "\""); } - e.constant = NumToString(y); - return NoError(); + } + NEXT(); + return NoError(); +} + +CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, + bool check_now) { + if (token_ == '+' || token_ == '-') { + const char sign = static_cast(token_); + // Get an indentifier: NAN, INF, or function name like cos/sin/deg. + NEXT(); + if (token_ != kTokenIdentifier) return Error("constant name expected"); + attribute_.insert(0, 1, sign); } - auto match = false; const auto in_type = e.type.base_type; + const auto is_tok_ident = (token_ == kTokenIdentifier); + const auto is_tok_string = (token_ == kTokenStringConstant); + + // First see if this could be a conversion function. + if (is_tok_ident && *cursor_ == '(') { return ParseFunction(name, e); } + // clang-format off + auto match = false; + #define IF_ECHECK_(force, dtoken, check, req) \ - if (!match && ((check) || IsConstTrue(force))) \ - ECHECK(TryTypedValue(name, dtoken, check, e, req, &match)) + if (!match && ((dtoken) == token_) && ((check) || IsConstTrue(force))) \ + ECHECK(TryTypedValue(name, dtoken, check, e, req, &match)) #define TRY_ECHECK(dtoken, check, req) IF_ECHECK_(false, dtoken, check, req) #define FORCE_ECHECK(dtoken, check, req) IF_ECHECK_(true, dtoken, check, req) // clang-format on - if (token_ == kTokenStringConstant || token_ == kTokenIdentifier) { + if (is_tok_ident || is_tok_string) { const auto kTokenStringOrIdent = token_; // The string type is a most probable type, check it first. TRY_ECHECK(kTokenStringConstant, in_type == BASE_TYPE_STRING, BASE_TYPE_STRING); // avoid escaped and non-ascii in the string - if (!match && (token_ == kTokenStringConstant) && IsScalar(in_type) && + if (!match && is_tok_string && IsScalar(in_type) && !attr_is_trivial_ascii_string_) { return Error( std::string("type mismatch or invalid value, an initializer of " @@ -1725,6 +1958,12 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, TRY_ECHECK(kTokenStringOrIdent, IsBool(in_type), BASE_TYPE_BOOL); } } + // Check for optional scalars. + if (!match && IsScalar(in_type) && attribute_ == "null") { + e.constant = "null"; + NEXT(); + match = true; + } // Check if this could be a string/identifier enum value. // Enum can have only true integer base type. if (!match && IsInteger(in_type) && !IsBool(in_type) && @@ -1734,12 +1973,19 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, match = true; } // Parse a float/integer number from the string. - if (!match) check_now = true; // Re-pack if parsed from string literal. - if (!match && (token_ == kTokenStringConstant) && IsScalar(in_type)) { - // remove trailing whitespaces from attribute_ - auto last = attribute_.find_last_not_of(' '); - if (std::string::npos != last) // has non-whitespace - attribute_.resize(last + 1); + // A "scalar-in-string" value needs extra checks. + if (!match && is_tok_string && IsScalar(in_type)) { + // Strip trailing whitespaces from attribute_. + auto last_non_ws = attribute_.find_last_not_of(' '); + if (std::string::npos != last_non_ws) attribute_.resize(last_non_ws + 1); + if (IsFloat(e.type.base_type)) { + // The functions strtod() and strtof() accept both 'nan' and + // 'nan(number)' literals. While 'nan(number)' is rejected by the parser + // as an unsupported function if is_tok_ident is true. + if (attribute_.find_last_of(')') != std::string::npos) { + return Error("invalid number: " + attribute_); + } + } } // Float numbers or nan, inf, pi, etc. TRY_ECHECK(kTokenStringOrIdent, IsFloat(in_type), BASE_TYPE_FLOAT); @@ -1755,6 +2001,15 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, // Integer token can init any scalar (integer of float). FORCE_ECHECK(kTokenIntegerConstant, IsScalar(in_type), BASE_TYPE_INT); } + // Match empty vectors for default-empty-vectors. + if (!match && IsVector(e.type) && token_ == '[') { + NEXT(); + if (token_ != ']') { return Error("Expected `]` in vector default"); } + NEXT(); + match = true; + e.constant = "[]"; + } + #undef FORCE_ECHECK #undef TRY_ECHECK #undef IF_ECHECK_ @@ -1770,7 +2025,8 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e, // This flag forces to check default scalar values or metadata of field. // For JSON parser the flag should be false. // If it is set for JSON each value will be checked twice (see ParseTable). - if (check_now && IsScalar(match_type)) { + // Special case 'null' since atot can't handle that. + if (check_now && IsScalar(match_type) && e.constant != "null") { // clang-format off switch (match_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ @@ -1811,13 +2067,8 @@ StructDef *Parser::LookupCreateStruct(const std::string &name, } return struct_def; } - if (!definition) { - // Search thru parent namespaces. - for (size_t components = current_namespace_->components.size(); - components && !struct_def; components--) { - struct_def = LookupStruct( - current_namespace_->GetFullyQualifiedName(name, components - 1)); - } + if (!definition && !struct_def) { + struct_def = LookupStructThruParentNamespaces(name); } if (!struct_def && create_if_new) { struct_def = new StructDef(); @@ -1971,21 +2222,16 @@ struct EnumValBuilder { FLATBUFFERS_CHECKED_ERROR AssignEnumeratorValue(const std::string &value) { user_value = true; auto fit = false; - auto ascending = false; if (enum_def.IsUInt64()) { uint64_t u64; fit = StringToNumber(value.c_str(), &u64); - ascending = u64 > temp->GetAsUInt64(); temp->value = static_cast(u64); // well-defined since C++20. } else { int64_t i64; fit = StringToNumber(value.c_str(), &i64); - ascending = i64 > temp->GetAsInt64(); temp->value = i64; } if (!fit) return parser.Error("enum value does not fit, \"" + value + "\""); - if (!ascending && strict_ascending && !enum_def.vals.vec.empty()) - return parser.Error("enum values must be specified in ascending order"); return NoError(); } @@ -2021,11 +2267,10 @@ struct EnumValBuilder { return parser.Error("fatal: invalid enum underlying type"); } - EnumValBuilder(Parser &_parser, EnumDef &_enum_def, bool strict_order = true) + EnumValBuilder(Parser &_parser, EnumDef &_enum_def) : parser(_parser), enum_def(_enum_def), temp(nullptr), - strict_ascending(strict_order), user_value(false) {} ~EnumValBuilder() { delete temp; } @@ -2033,7 +2278,6 @@ struct EnumValBuilder { Parser &parser; EnumDef &enum_def; EnumVal *temp; - const bool strict_ascending; bool user_value; }; @@ -2070,9 +2314,7 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest) { // todo: Convert to the Error in the future? Warning("underlying type of bit_flags enum must be unsigned"); } - // Protobuf allows them to be specified in any order, so sort afterwards. - const auto strict_ascending = (false == opts.proto_mode); - EnumValBuilder evb(*this, *enum_def, strict_ascending); + EnumValBuilder evb(*this, *enum_def); EXPECT('{'); // A lot of code generatos expect that an enum is not-empty. if ((is_union || Is('}')) && !opts.proto_mode) { @@ -2116,9 +2358,6 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest) { NEXT(); ECHECK(evb.AssignEnumeratorValue(attribute_)); EXPECT(kTokenIntegerConstant); - } else if (false == strict_ascending) { - // The opts.proto_mode flag is active. - return Error("Protobuf mode doesn't allow implicit enum values."); } ECHECK(evb.AcceptEnumerator()); @@ -2154,8 +2393,18 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest) { } } - if (false == strict_ascending) - enum_def->SortByValue(); // Must be sorted to use MinValue/MaxValue. + enum_def->SortByValue(); // Must be sorted to use MinValue/MaxValue. + + // Ensure enum value uniqueness. + auto prev_it = enum_def->Vals().begin(); + for (auto it = prev_it + 1; it != enum_def->Vals().end(); ++it) { + auto prev_ev = *prev_it; + auto ev = *it; + if (prev_ev->GetAsUInt64() == ev->GetAsUInt64()) + return Error("all enum values must be unique: " + prev_ev->name + + " and " + ev->name + " are both " + + NumToString(ev->GetAsInt64())); + } if (dest) *dest = enum_def; types_.Add(current_namespace_->GetFullyQualifiedName(enum_def->name), @@ -2197,20 +2446,38 @@ CheckedError Parser::CheckClash(std::vector &fields, return NoError(); } +bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) { + static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = + IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster | + IDLOptions::kKotlin | IDLOptions::kCpp | IDLOptions::kJava | + IDLOptions::kCSharp | IDLOptions::kTs | IDLOptions::kBinary; + unsigned long langs = opts.lang_to_generate; + return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs); +} +bool Parser::SupportsOptionalScalars() const { + // Check in general if a language isn't specified. + return opts.lang_to_generate == 0 || SupportsOptionalScalars(opts); +} + +bool Parser::SupportsDefaultVectorsAndStrings() const { + static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = + IDLOptions::kRust | IDLOptions::kSwift; + return !(opts.lang_to_generate & ~supported_langs); +} + bool Parser::SupportsAdvancedUnionFeatures() const { return opts.lang_to_generate != 0 && (opts.lang_to_generate & - ~(IDLOptions::kCpp | IDLOptions::kJs | IDLOptions::kTs | - IDLOptions::kPhp | IDLOptions::kJava | IDLOptions::kCSharp | - IDLOptions::kKotlin | IDLOptions::kBinary | IDLOptions::kSwift)) == - 0; + ~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kPhp | + IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kKotlin | + IDLOptions::kBinary | IDLOptions::kSwift)) == 0; } bool Parser::SupportsAdvancedArrayFeatures() const { return (opts.lang_to_generate & ~(IDLOptions::kCpp | IDLOptions::kPython | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kJsonSchema | IDLOptions::kJson | - IDLOptions::kBinary)) == 0; + IDLOptions::kBinary | IDLOptions::kRust)) == 0; } Namespace *Parser::UniqueNamespace(Namespace *ns) { @@ -2261,17 +2528,12 @@ CheckedError Parser::ParseDecl() { struct_def->attributes.Lookup("original_order") == nullptr && !fixed; EXPECT('{'); while (token_ != '}') ECHECK(ParseField(*struct_def)); - auto force_align = struct_def->attributes.Lookup("force_align"); if (fixed) { + const auto force_align = struct_def->attributes.Lookup("force_align"); if (force_align) { - auto align = static_cast(atoi(force_align->constant.c_str())); - if (force_align->type.base_type != BASE_TYPE_INT || - align < struct_def->minalign || align > FLATBUFFERS_MAX_ALIGNMENT || - align & (align - 1)) - return Error( - "force_align must be a power of two integer ranging from the" - "struct\'s natural alignment to " + - NumToString(FLATBUFFERS_MAX_ALIGNMENT)); + size_t align; + ECHECK(ParseAlignAttribute(force_align->constant, struct_def->minalign, + &align)); struct_def->minalign = align; } if (!struct_def->bytesize) return Error("size 0 structs not allowed"); @@ -2285,20 +2547,41 @@ CheckedError Parser::ParseDecl() { if ((*it)->attributes.Lookup("id")) num_id_fields++; } // If any fields have ids.. - if (num_id_fields) { + if (num_id_fields || opts.require_explicit_ids) { // Then all fields must have them. - if (num_id_fields != fields.size()) - return Error( - "either all fields or no fields must have an 'id' attribute"); + if (num_id_fields != fields.size()) { + if (opts.require_explicit_ids) { + return Error( + "all fields must have an 'id' attribute when " + "--require-explicit-ids is used"); + } else { + return Error( + "either all fields or no fields must have an 'id' attribute"); + } + } // Simply sort by id, then the fields are the same as if no ids had // been specified. std::sort(fields.begin(), fields.end(), compareFieldDefs); // Verify we have a contiguous set, and reassign vtable offsets. - for (int i = 0; i < static_cast(fields.size()); i++) { - if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str())) + FLATBUFFERS_ASSERT(fields.size() <= + flatbuffers::numeric_limits::max()); + for (voffset_t i = 0; i < static_cast(fields.size()); i++) { + auto &field = *fields[i]; + const auto &id_str = field.attributes.Lookup("id")->constant; + // Metadata values have a dynamic type, they can be `float`, 'int', or + // 'string`. + // The FieldIndexToOffset(i) expects the voffset_t so `id` is limited by + // this type. + voffset_t id = 0; + const auto done = !atot(id_str.c_str(), *this, &id).Check(); + if (!done) + return Error("field id\'s must be non-negative number, field: " + + field.name + ", id: " + id_str); + if (i != id) return Error("field id\'s must be consecutive from 0, id " + - NumToString(i) + " missing or set twice"); - fields[i]->value.offset = FieldIndexToOffset(static_cast(i)); + NumToString(i) + " missing or set twice, field: " + + field.name + ", id: " + id_str); + field.value.offset = FieldIndexToOffset(i); } } } @@ -2537,7 +2820,7 @@ CheckedError Parser::ParseProtoFields(StructDef *struct_def, bool isextend, ECHECK(StartEnum(name, true, &oneof_union)); type = Type(BASE_TYPE_UNION, nullptr, oneof_union); } else { - auto name = "Anonymous" + NumToString(anonymous_counter++); + auto name = "Anonymous" + NumToString(anonymous_counter_++); ECHECK(StartStruct(name, &anonymous_struct)); type = Type(BASE_TYPE_STRUCT, anonymous_struct); } @@ -2573,7 +2856,9 @@ CheckedError Parser::ParseProtoFields(StructDef *struct_def, bool isextend, } if (!field) ECHECK(AddField(*struct_def, name, type, &field)); field->doc_comment = field_comment; - if (!IsScalar(type.base_type)) field->required = required; + if (!IsScalar(type.base_type) && required) { + field->presence = FieldDef::kRequired; + } // See if there's a default specified. if (Is('[')) { NEXT(); @@ -2584,10 +2869,13 @@ CheckedError Parser::ParseProtoFields(StructDef *struct_def, bool isextend, auto val = attribute_; ECHECK(ParseProtoCurliesOrIdent()); if (key == "default") { - // Temp: skip non-numeric defaults (enums). + // Temp: skip non-numeric and non-boolean defaults (enums). auto numeric = strpbrk(val.c_str(), "0123456789-+."); - if (IsScalar(type.base_type) && numeric == val.c_str()) + if (IsScalar(type.base_type) && numeric == val.c_str()) { + field->value.constant = val; + } else if (val == "true") { field->value.constant = val; + } // "false" is default, no need to handle explicitly. } else if (key == "deprecated") { field->deprecated = val == "true"; } @@ -2707,22 +2995,24 @@ CheckedError Parser::ParseTypeFromProtoType(Type *type) { } CheckedError Parser::SkipAnyJsonValue() { + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); + switch (token_) { case '{': { size_t fieldn_outer = 0; - return ParseTableDelimiters( - fieldn_outer, nullptr, - [&](const std::string &, size_t &fieldn, - const StructDef *) -> CheckedError { - ECHECK(Recurse([&]() { return SkipAnyJsonValue(); })); - fieldn++; - return NoError(); - }); + return ParseTableDelimiters(fieldn_outer, nullptr, + [&](const std::string &, size_t &fieldn, + const StructDef *) -> CheckedError { + ECHECK(SkipAnyJsonValue()); + fieldn++; + return NoError(); + }); } case '[': { uoffset_t count = 0; return ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError { - return Recurse([&]() { return SkipAnyJsonValue(); }); + return SkipAnyJsonValue(); }); } case kTokenStringConstant: @@ -2737,7 +3027,19 @@ CheckedError Parser::SkipAnyJsonValue() { return NoError(); } +CheckedError Parser::ParseFlexBufferNumericConstant( + flexbuffers::Builder *builder) { + double d; + if (!StringToNumber(attribute_.c_str(), &d)) + return Error("unexpected floating-point constant: " + attribute_); + builder->Double(d); + return NoError(); +} + CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) { + ParseDepthGuard depth_guard(this); + ECHECK(depth_guard.Check()); + switch (token_) { case '{': { auto start = builder->StartMap(); @@ -2753,6 +3055,8 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) { }); ECHECK(err); builder->EndMap(start); + if (builder->HasDuplicateKeys()) + return Error("FlexBuffers map has duplicate keys"); break; } case '[': { @@ -2779,6 +3083,18 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) { EXPECT(kTokenFloatConstant); break; } + case '-': + case '+': { + // `[-+]?(nan|inf|infinity)`, see ParseSingleValue(). + const auto sign = static_cast(token_); + NEXT(); + if (token_ != kTokenIdentifier) + return Error("floating-point constant expected"); + attribute_.insert(0, 1, sign); + ECHECK(ParseFlexBufferNumericConstant(builder)); + NEXT(); + break; + } default: if (IsIdent("true")) { builder->Bool(true); @@ -2789,6 +3105,9 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) { } else if (IsIdent("null")) { builder->Null(); NEXT(); + } else if (IsIdent("inf") || IsIdent("infinity") || IsIdent("nan")) { + ECHECK(ParseFlexBufferNumericConstant(builder)); + NEXT(); } else return TokenError(); } @@ -2797,15 +3116,19 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) { bool Parser::ParseFlexBuffer(const char *source, const char *source_filename, flexbuffers::Builder *builder) { + const auto initial_depth = parse_depth_counter_; + (void)initial_depth; auto ok = !StartParseFile(source, source_filename).Check() && !ParseFlexBufferValue(builder).Check(); if (ok) builder->Finish(); + FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); return ok; } bool Parser::Parse(const char *source, const char **include_paths, const char *source_filename) { - FLATBUFFERS_ASSERT(0 == recurse_protection_counter); + const auto initial_depth = parse_depth_counter_; + (void)initial_depth; bool r; if (opts.use_flexbuffers) { @@ -2813,10 +3136,20 @@ bool Parser::Parse(const char *source, const char **include_paths, } else { r = !ParseRoot(source, include_paths, source_filename).Check(); } - FLATBUFFERS_ASSERT(0 == recurse_protection_counter); + FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); return r; } +bool Parser::ParseJson(const char *json, const char *json_filename) { + const auto initial_depth = parse_depth_counter_; + (void)initial_depth; + builder_.Clear(); + const auto done = + !StartParseFile(json, json_filename).Check() && !DoParseJson().Check(); + FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); + return done; +} + CheckedError Parser::StartParseFile(const char *source, const char *source_filename) { file_being_parsed_ = source_filename ? source_filename : ""; @@ -2861,7 +3194,7 @@ CheckedError Parser::ParseRoot(const char *source, const char **include_paths, if (field.value.type.struct_def == &struct_def) { field.value.type.struct_def = nullptr; field.value.type.enum_def = enum_def; - auto &bt = field.value.type.base_type == BASE_TYPE_VECTOR + auto &bt = IsVector(field.value.type) ? field.value.type.element : field.value.type.base_type; FLATBUFFERS_ASSERT(bt == BASE_TYPE_STRUCT); @@ -2899,24 +3232,46 @@ CheckedError Parser::ParseRoot(const char *source, const char **include_paths, for (auto val_it = enum_def.Vals().begin(); val_it != enum_def.Vals().end(); ++val_it) { auto &val = **val_it; - if (!SupportsAdvancedUnionFeatures() && val.union_type.struct_def && - val.union_type.struct_def->fixed) + if (!SupportsAdvancedUnionFeatures() && + (IsStruct(val.union_type) || IsString(val.union_type))) return Error( "only tables can be union elements in the generated language: " + val.name); } } } + // Parse JSON object only if the scheme has been parsed. + if (token_ == '{') { ECHECK(DoParseJson()); } + EXPECT(kTokenEof); return NoError(); } +// Generate a unique hash for a file based on its name and contents (if any). +static uint64_t HashFile(const char *source_filename, const char *source) { + uint64_t hash = 0; + + if (source_filename) + hash = HashFnv1a(StripPath(source_filename).c_str()); + + if (source && *source) hash ^= HashFnv1a(source); + + return hash; +} + CheckedError Parser::DoParse(const char *source, const char **include_paths, const char *source_filename, const char *include_filename) { + uint64_t source_hash = 0; if (source_filename) { - if (included_files_.find(source_filename) == included_files_.end()) { - included_files_[source_filename] = - include_filename ? include_filename : ""; + // If the file is in-memory, don't include its contents in the hash as we + // won't be able to load them later. + if (FileExists(source_filename)) + source_hash = HashFile(source_filename, source); + else + source_hash = HashFile(source_filename, nullptr); + + if (included_files_.find(source_hash) == included_files_.end()) { + included_files_[source_hash] = include_filename ? include_filename : ""; files_included_per_file_[source_filename] = std::set(); } else { return NoError(); @@ -2949,22 +3304,32 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, if (opts.proto_mode && attribute_ == "public") NEXT(); auto name = flatbuffers::PosixPath(attribute_.c_str()); EXPECT(kTokenStringConstant); - // Look for the file in include_paths. + // Look for the file relative to the directory of the current file. std::string filepath; - for (auto paths = include_paths; paths && *paths; paths++) { - filepath = flatbuffers::ConCatPathFileName(*paths, name); - if (FileExists(filepath.c_str())) break; + if (source_filename) { + auto source_file_directory = + flatbuffers::StripFileName(source_filename); + filepath = flatbuffers::ConCatPathFileName(source_file_directory, name); + } + if (filepath.empty() || !FileExists(filepath.c_str())) { + // Look for the file in include_paths. + for (auto paths = include_paths; paths && *paths; paths++) { + filepath = flatbuffers::ConCatPathFileName(*paths, name); + if (FileExists(filepath.c_str())) break; + } } if (filepath.empty()) return Error("unable to locate include file: " + name); if (source_filename) files_included_per_file_[source_filename].insert(filepath); - if (included_files_.find(filepath) == included_files_.end()) { + + std::string contents; + bool file_loaded = LoadFile(filepath.c_str(), true, &contents); + if (included_files_.find(HashFile(filepath.c_str(), contents.c_str())) == + included_files_.end()) { // We found an include file that we have not parsed yet. - // Load it and parse it. - std::string contents; - if (!LoadFile(filepath.c_str(), true, &contents)) - return Error("unable to load include file: " + name); + // Parse it. + if (!file_loaded) return Error("unable to load include file: " + name); ECHECK(DoParse(contents.c_str(), include_paths, filepath.c_str(), name.c_str())); // We generally do not want to output code for any included files: @@ -2981,7 +3346,7 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, // entered into included_files_. // This is recursive, but only go as deep as the number of include // statements. - if (source_filename) { included_files_.erase(source_filename); } + included_files_.erase(source_hash); return DoParse(source, include_paths, source_filename, include_filename); } @@ -2997,25 +3362,7 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, } else if (IsIdent("namespace")) { ECHECK(ParseNamespace()); } else if (token_ == '{') { - if (!root_struct_def_) - return Error("no root type set to parse json with"); - if (builder_.GetSize()) { - return Error("cannot have more than one json object in a file"); - } - uoffset_t toff; - ECHECK(ParseTable(*root_struct_def_, nullptr, &toff)); - if (opts.size_prefixed) { - builder_.FinishSizePrefixed( - Offset
(toff), - file_identifier_.length() ? file_identifier_.c_str() : nullptr); - } else { - builder_.Finish(Offset
(toff), file_identifier_.length() - ? file_identifier_.c_str() - : nullptr); - } - // Check that JSON file doesn't contain more objects or IDL directives. - // Comments after JSON are allowed. - EXPECT(kTokenEof); + return NoError(); } else if (IsIdent("enum")) { ECHECK(ParseEnum(false, nullptr)); } else if (IsIdent("union")) { @@ -3066,6 +3413,32 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, return NoError(); } +CheckedError Parser::DoParseJson() { + if (token_ != '{') { + EXPECT('{'); + } else { + if (!root_struct_def_) return Error("no root type set to parse json with"); + if (builder_.GetSize()) { + return Error("cannot have more than one json object in a file"); + } + uoffset_t toff; + ECHECK(ParseTable(*root_struct_def_, nullptr, &toff)); + if (opts.size_prefixed) { + builder_.FinishSizePrefixed( + Offset
(toff), + file_identifier_.length() ? file_identifier_.c_str() : nullptr); + } else { + builder_.Finish(Offset
(toff), file_identifier_.length() + ? file_identifier_.c_str() + : nullptr); + } + } + // Check that JSON file doesn't contain more objects or IDL directives. + // Comments after JSON are allowed. + EXPECT(kTokenEof); + return NoError(); +} + std::set Parser::GetIncludedFilesRecursive( const std::string &file_name) const { std::set included_files; @@ -3135,7 +3508,8 @@ void Parser::Serialize() { auto serv__ = builder_.CreateVectorOfSortedTables(&service_offsets); auto schema_offset = reflection::CreateSchema( builder_, objs__, enum__, fiid__, fext__, - (root_struct_def_ ? root_struct_def_->serialized_location : 0), serv__); + (root_struct_def_ ? root_struct_def_->serialized_location : 0), serv__, + static_cast(advanced_features_)); if (opts.size_prefixed) { builder_.FinishSizePrefixed(schema_offset, reflection::SchemaIdentifier()); } else { @@ -3238,8 +3612,8 @@ Offset FieldDef::Serialize(FlatBufferBuilder *builder, // Is uint64>max(int64) tested? IsInteger(value.type.base_type) ? StringToInt(value.constant.c_str()) : 0, // result may be platform-dependent if underlying is float (not double) - IsFloat(value.type.base_type) ? d : 0.0, deprecated, required, key, - attr__, docs__); + IsFloat(value.type.base_type) ? d : 0.0, deprecated, IsRequired(), key, + attr__, docs__, IsOptional()); // TODO: value.constant is almost always "0", we could save quite a bit of // space by sharing it. Same for common values of value.type. } @@ -3253,13 +3627,8 @@ bool FieldDef::Deserialize(Parser &parser, const reflection::Field *field) { value.constant = NumToString(field->default_integer()); } else if (IsFloat(value.type.base_type)) { value.constant = FloatToString(field->default_real(), 16); - size_t last_zero = value.constant.find_last_not_of('0'); - if (last_zero != std::string::npos && last_zero != 0) { - value.constant.erase(last_zero, std::string::npos); - } } - deprecated = field->deprecated(); - required = field->required(); + presence = FieldDef::MakeFieldPresence(field->optional(), field->required()); key = field->key(); if (!DeserializeAttributes(parser, field->attributes())) return false; // TODO: this should probably be handled by a separate attribute @@ -3276,6 +3645,7 @@ bool FieldDef::Deserialize(Parser &parser, const reflection::Field *field) { nested_flatbuffer = parser.LookupStruct(nested_qualified_name); if (!nested_flatbuffer) return false; } + shared = attributes.Lookup("shared") != nullptr; DeserializeDoc(doc_comment, field->documentation()); return true; } @@ -3556,7 +3926,7 @@ bool Parser::Deserialize(const reflection::Schema *schema) { } } } - + advanced_features_ = schema->advanced_features(); return true; } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/reflection.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/reflection.cpp index 77ea0dcf..2dedcb4f 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/reflection.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/reflection.cpp @@ -23,7 +23,7 @@ namespace flatbuffers { int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) { - // clang-format off +// clang-format off #define FLATBUFFERS_GET(T) static_cast(ReadScalar(data)) switch (type) { case reflection::UType: @@ -121,7 +121,7 @@ std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, } void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) { - // clang-format off +// clang-format off #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast(val)) switch (type) { case reflection::UType: @@ -195,7 +195,7 @@ class ResizeContext { if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0); else - buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_); + buf_.erase(buf_.begin() + start + delta_, buf_.begin() + start); } // Check if the range between first (lower address) and second straddles @@ -296,8 +296,6 @@ class ResizeContext { } } - void operator=(const ResizeContext &rc); - private: const reflection::Schema &schema_; uint8_t *startptr_; @@ -398,16 +396,17 @@ Offset CopyTable(FlatBufferBuilder &fbb, case reflection::Obj: { auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); if (!subobjectdef.is_struct()) { - offset = - CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef)) - .o; + offset = CopyTable(fbb, schema, subobjectdef, + *GetFieldT(table, fielddef), use_string_pooling) + .o; } break; } case reflection::Union: { auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table); - offset = - CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef)).o; + offset = CopyTable(fbb, schema, subobjectdef, + *GetFieldT(table, fielddef), use_string_pooling) + .o; break; } case reflection::Vector: { @@ -434,8 +433,8 @@ Offset CopyTable(FlatBufferBuilder &fbb, if (!elemobjectdef->is_struct()) { std::vector> elements(vec->size()); for (uoffset_t i = 0; i < vec->size(); i++) { - elements[i] = - CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i)); + elements[i] = CopyTable(fbb, schema, *elemobjectdef, + *vec->Get(i), use_string_pooling); } offset = fbb.CreateVector(elements).o; break; @@ -705,8 +704,9 @@ bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema, } bool Verify(const reflection::Schema &schema, const reflection::Object &root, - const uint8_t *buf, size_t length) { - Verifier v(buf, length); + const uint8_t *buf, size_t length, uoffset_t max_depth /*= 64*/, + uoffset_t max_tables /*= 1000000*/) { + Verifier v(buf, length, max_depth, max_tables); return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true); } diff --git a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/util.cpp b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/util.cpp index 08b77918..3670a019 100644 --- a/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/util.cpp +++ b/third_party/flatbuffers-c-bridge/third_party/flatbuffers/src/util.cpp @@ -16,7 +16,13 @@ // clang-format off // Dont't remove `format off`, it prevent reordering of win-includes. -#define _POSIX_C_SOURCE 200112L // For stat from stat/stat.h and fseeko() (POSIX extensions). + +#if defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) || \ + defined(__QNXNTO__) +# define _POSIX_C_SOURCE 200809L +# define _XOPEN_SOURCE 700L +#endif + #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN @@ -31,9 +37,6 @@ # include # include # undef interface // This is also important because of reasons -#else -# define _XOPEN_SOURCE 600 // For PATH_MAX from limits.h (SUSv2 extension) -# include #endif // clang-format on @@ -42,6 +45,7 @@ #include #include +#include #include namespace flatbuffers { @@ -196,8 +200,14 @@ std::string AbsolutePath(const std::string &filepath) { char abs_path[MAX_PATH]; return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr) #else - char abs_path[PATH_MAX]; - return realpath(filepath.c_str(), abs_path) + char *abs_path_temp = realpath(filepath.c_str(), nullptr); + bool success = abs_path_temp != nullptr; + std::string abs_path; + if(success) { + abs_path = abs_path_temp; + free(abs_path_temp); + } + return success #endif ? abs_path : filepath;