diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 260061f13..2e74d7072 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -551,6 +551,7 @@ struct IDLOptions { bool output_enum_identifiers; bool prefixed_enums; bool scoped_enums; + bool swift_implementation_only; bool include_dependence_headers; bool mutable_buffer; bool one_file; @@ -658,6 +659,7 @@ struct IDLOptions { output_enum_identifiers(true), prefixed_enums(true), scoped_enums(false), + swift_implementation_only(false), include_dependence_headers(true), mutable_buffer(false), one_file(false), diff --git a/scripts/generate_code.py b/scripts/generate_code.py index 6d3524f7e..b8941f31d 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py @@ -63,6 +63,7 @@ assert flatc_path.exists(), "Cannot find the flatc compiler " + str(flatc_path) # Specify the other paths that will be referenced tests_path = Path(root_path, "tests") +swift_code_gen = Path(root_path, "tests/FlatBuffers.Test.Swift/CodeGenerationTests") samples_path = Path(root_path, "samples") reflection_path = Path(root_path, "reflection") @@ -138,6 +139,12 @@ RUST_SERIALIZE_OPTS = BASE_OPTS + [ TS_OPTS = ["--ts", "--gen-name-strings"] LOBSTER_OPTS = ["--lobster"] SWIFT_OPTS = ["--swift", "--gen-json-emit", "--bfbs-filenames", str(tests_path)] +SWIFT_OPTS_CODE_GEN = [ + "--swift", + "--gen-json-emit", + "--bfbs-filenames", + swift_code_gen +] JAVA_OPTS = ["--java"] KOTLIN_OPTS = ["--kotlin"] PHP_OPTS = ["--php"] @@ -380,6 +387,18 @@ flatc( prefix=swift_prefix, ) +flatc( + SWIFT_OPTS_CODE_GEN + BASE_OPTS + ["--grpc", "--swift-implementation-only"], + schema="test_import.fbs", + cwd=swift_code_gen +) + +flatc( + SWIFT_OPTS_CODE_GEN + NO_INCL_OPTS + ["--grpc"], + schema="test_no_include.fbs", + cwd=swift_code_gen +) + # --filename-suffix and --filename-ext tests flatc( CPP_OPTS + NO_INCL_OPTS + ["--filename-suffix", "_suffix", "--filename-ext", "hpp"], diff --git a/src/flatc.cpp b/src/flatc.cpp index 1f0e6e114..990e77948 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -86,6 +86,8 @@ const static FlatCOption options[] = { { "", "scoped-enums", "", "Use C++11 style scoped and strongly typed enums. Also implies " "--no-prefix." }, + { "", "swift-implementation-only", "", + "Adds a @_implementationOnly to swift imports" }, { "", "gen-inclues", "", "(deprecated), this is the default behavior. If the original behavior is " "required (no include statements) use --no-includes." }, @@ -490,6 +492,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.java_checkerframework = true; } else if (arg == "--gen-generated") { opts.gen_generated = true; + } else if (arg == "--swift-implementation-only") { + opts.swift_implementation_only = true; } else if (arg == "--gen-json-emit") { opts.gen_json_coders = true; } else if (arg == "--object-prefix") { diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp index b38481a86..0fbbc7c29 100644 --- a/src/idl_gen_swift.cpp +++ b/src/idl_gen_swift.cpp @@ -167,8 +167,12 @@ class SwiftGenerator : public BaseGenerator { code_ += "// " + std::string(FlatBuffersGeneratedWarning()); code_ += "// swiftlint:disable all"; code_ += "// swiftformat:disable all\n"; - if (parser_.opts.include_dependence_headers || parser_.opts.generate_all) + if (parser_.opts.include_dependence_headers || parser_.opts.generate_all) { + if (parser_.opts.swift_implementation_only) + code_ += "@_implementationOnly \\"; + code_ += "import FlatBuffers\n"; + } // Generate code for all the enum declarations. for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); @@ -212,6 +216,7 @@ class SwiftGenerator : public BaseGenerator { // Generates the reader for swift void GenStructReader(const StructDef &struct_def) { const bool is_private_access = + parser_.opts.swift_implementation_only || struct_def.attributes.Lookup("private") != nullptr; code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); GenComment(struct_def.doc_comment); @@ -286,7 +291,8 @@ class SwiftGenerator : public BaseGenerator { } code_ += ""; code_ += - "public static func verify(_ verifier: inout Verifier, at position: " + "{{ACCESS_TYPE}} static func verify(_ verifier: inout Verifier, at " + "position: " "Int, of type: T.Type) throws where T: Verifiable {"; Indent(); code_ += @@ -371,6 +377,7 @@ class SwiftGenerator : public BaseGenerator { // Generates the create function for swift void GenStructWriter(const StructDef &struct_def) { const bool is_private_access = + parser_.opts.swift_implementation_only || struct_def.attributes.Lookup("private") != nullptr; code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def)); @@ -440,6 +447,7 @@ class SwiftGenerator : public BaseGenerator { // Generates the reader for swift void GenTable(const StructDef &struct_def) { const bool is_private_access = + parser_.opts.swift_implementation_only || struct_def.attributes.Lookup("private") != nullptr; code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); GenObjectHeader(struct_def); @@ -699,7 +707,7 @@ class SwiftGenerator : public BaseGenerator { field.value.type.struct_def->fixed) && (IsVector(field.value.type) || IsArray(field.value.type))) { const auto field_name = namer_.NamespacedType(*vectortype.struct_def); - code_ += "public static func " + + code_ += "{{ACCESS_TYPE}} static func " + namer_.Method("start_vector_of", field_var) + "(_ size: Int, in builder: inout " "FlatBufferBuilder) {"; @@ -1054,7 +1062,7 @@ class SwiftGenerator : public BaseGenerator { code_ += ""; if (struct_def.fields.vec.empty() == false) GenerateCodingKeys(struct_def); - code_ += "public func encode(to encoder: Encoder) throws {"; + code_ += "{{ACCESS_TYPE}} func encode(to encoder: Encoder) throws {"; Indent(); if (struct_def.fields.vec.empty() == false) GenerateEncoderBody(struct_def); Outdent(); @@ -1066,7 +1074,8 @@ class SwiftGenerator : public BaseGenerator { void GenerateVerifier(const StructDef &struct_def) { code_ += - "public static func verify(_ verifier: inout Verifier, at position: " + "{{ACCESS_TYPE}} static func verify(_ verifier: inout Verifier, at " + "position: " "Int, of type: T.Type) throws where T: Verifiable {"; Indent(); code_ += "var _v = try verifier.visitTable(at: position)"; diff --git a/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import.fbs b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import.fbs new file mode 100644 index 000000000..0a43956c4 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import.fbs @@ -0,0 +1,3 @@ +table Message { + internal_message: string; +} \ No newline at end of file diff --git a/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import_generated.swift b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import_generated.swift new file mode 100644 index 000000000..ec11dcefc --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_import_generated.swift @@ -0,0 +1,91 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// swiftlint:disable all +// swiftformat:disable all + +@_implementationOnly import FlatBuffers + +internal struct Message: FlatBufferObject, Verifiable, ObjectAPIPacker { + + static func validateVersion() { FlatBuffersVersion_2_0_0() } + internal var __buffer: ByteBuffer! { return _accessor.bb } + private var _accessor: Table + + internal static func getRootAsMessage(bb: ByteBuffer) -> Message { return Message(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + internal init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + private enum VTOFFSET: VOffset { + case internalMessage = 4 + var v: Int32 { Int32(self.rawValue) } + var p: VOffset { self.rawValue } + } + + internal var internalMessage: String? { let o = _accessor.offset(VTOFFSET.internalMessage.v); return o == 0 ? nil : _accessor.string(at: o) } + internal var internalMessageSegmentArray: [UInt8]? { return _accessor.getVector(at: VTOFFSET.internalMessage.v) } + internal static func startMessage(_ fbb: inout FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + internal static func add(internalMessage: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: internalMessage, at: VTOFFSET.internalMessage.p) } + internal static func endMessage(_ fbb: inout FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } + internal static func createMessage( + _ fbb: inout FlatBufferBuilder, + internalMessageOffset internalMessage: Offset = Offset() + ) -> Offset { + let __start = Message.startMessage(&fbb) + Message.add(internalMessage: internalMessage, &fbb) + return Message.endMessage(&fbb, start: __start) + } + + + internal mutating func unpack() -> MessageT { + return MessageT(&self) + } + internal static func pack(_ builder: inout FlatBufferBuilder, obj: inout MessageT?) -> Offset { + guard var obj = obj else { return Offset() } + return pack(&builder, obj: &obj) + } + + internal static func pack(_ builder: inout FlatBufferBuilder, obj: inout MessageT) -> Offset { + let __internalMessage: Offset + if let s = obj.internalMessage { + __internalMessage = builder.create(string: s) + } else { + __internalMessage = Offset() + } + + let __root = Message.startMessage(&builder) + Message.add(internalMessage: __internalMessage, &builder) + return Message.endMessage(&builder, start: __root) + } + + internal static func verify(_ verifier: inout Verifier, at position: Int, of type: T.Type) throws where T: Verifiable { + var _v = try verifier.visitTable(at: position) + try _v.visit(field: VTOFFSET.internalMessage.p, fieldName: "internalMessage", required: false, type: ForwardOffset.self) + _v.finish() + } +} + +extension Message: Encodable { + + enum CodingKeys: String, CodingKey { + case internalMessage = "internal_message" + } + internal func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(internalMessage, forKey: .internalMessage) + } +} + +internal class MessageT: NativeObject { + + internal var internalMessage: String? + + internal init(_ _t: inout Message) { + internalMessage = _t.internalMessage + } + + internal init() { + } + + internal func serialize() -> ByteBuffer { return serialize(type: Message.self) } + +} diff --git a/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include.fbs b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include.fbs new file mode 100644 index 000000000..0a43956c4 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include.fbs @@ -0,0 +1,3 @@ +table Message { + internal_message: string; +} \ No newline at end of file diff --git a/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include_generated.swift b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include_generated.swift new file mode 100644 index 000000000..282a75328 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/CodeGenerationTests/test_no_include_generated.swift @@ -0,0 +1,89 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// swiftlint:disable all +// swiftformat:disable all + +public struct Message: FlatBufferObject, Verifiable, ObjectAPIPacker { + + static func validateVersion() { FlatBuffersVersion_2_0_0() } + public var __buffer: ByteBuffer! { return _accessor.bb } + private var _accessor: Table + + public static func getRootAsMessage(bb: ByteBuffer) -> Message { return Message(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + private enum VTOFFSET: VOffset { + case internalMessage = 4 + var v: Int32 { Int32(self.rawValue) } + var p: VOffset { self.rawValue } + } + + public var internalMessage: String? { let o = _accessor.offset(VTOFFSET.internalMessage.v); return o == 0 ? nil : _accessor.string(at: o) } + public var internalMessageSegmentArray: [UInt8]? { return _accessor.getVector(at: VTOFFSET.internalMessage.v) } + public static func startMessage(_ fbb: inout FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + public static func add(internalMessage: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: internalMessage, at: VTOFFSET.internalMessage.p) } + public static func endMessage(_ fbb: inout FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } + public static func createMessage( + _ fbb: inout FlatBufferBuilder, + internalMessageOffset internalMessage: Offset = Offset() + ) -> Offset { + let __start = Message.startMessage(&fbb) + Message.add(internalMessage: internalMessage, &fbb) + return Message.endMessage(&fbb, start: __start) + } + + + public mutating func unpack() -> MessageT { + return MessageT(&self) + } + public static func pack(_ builder: inout FlatBufferBuilder, obj: inout MessageT?) -> Offset { + guard var obj = obj else { return Offset() } + return pack(&builder, obj: &obj) + } + + public static func pack(_ builder: inout FlatBufferBuilder, obj: inout MessageT) -> Offset { + let __internalMessage: Offset + if let s = obj.internalMessage { + __internalMessage = builder.create(string: s) + } else { + __internalMessage = Offset() + } + + let __root = Message.startMessage(&builder) + Message.add(internalMessage: __internalMessage, &builder) + return Message.endMessage(&builder, start: __root) + } + + public static func verify(_ verifier: inout Verifier, at position: Int, of type: T.Type) throws where T: Verifiable { + var _v = try verifier.visitTable(at: position) + try _v.visit(field: VTOFFSET.internalMessage.p, fieldName: "internalMessage", required: false, type: ForwardOffset.self) + _v.finish() + } +} + +extension Message: Encodable { + + enum CodingKeys: String, CodingKey { + case internalMessage = "internal_message" + } + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(internalMessage, forKey: .internalMessage) + } +} + +public class MessageT: NativeObject { + + public var internalMessage: String? + + public init(_ _t: inout Message) { + internalMessage = _t.internalMessage + } + + public init() { + } + + public func serialize() -> ByteBuffer { return serialize(type: Message.self) } + +} diff --git a/tests/FlatBuffers.Test.Swift/SwiftTest.sh b/tests/FlatBuffers.Test.Swift/SwiftTest.sh index 99ab1f1be..2d153c12d 100755 --- a/tests/FlatBuffers.Test.Swift/SwiftTest.sh +++ b/tests/FlatBuffers.Test.Swift/SwiftTest.sh @@ -1,7 +1,7 @@ swift_dir=`pwd` cd .. test_dir=`pwd` -alias fbc='${test_dir}/../flatc' +alias fbc='${test_dir}/../Debug/flatc' shopt -s expand_aliases cd ${swift_dir}/Tests/FlatBuffers.Test.SwiftTests @@ -11,6 +11,12 @@ fbc --swift --gen-json-emit --gen-object-api ${test_dir}/more_defaults.fbs fbc --swift --gen-json-emit --gen-mutable --gen-object-api ${test_dir}/MutatingBool.fbs cd ${swift_dir} +# Goes into the code generation tests +cd CodeGenerationTests +fbc --swift --gen-mutable --grpc --gen-json-emit --gen-object-api --swift-implementation-only test_import.fbs +fbc --swift --gen-mutable --grpc --gen-json-emit --gen-object-api --no-includes test_no_include.fbs +cd .. + cd ${swift_dir}/Sources/SwiftFlatBuffers # create better fuzzing test file fbc --swift --gen-json-emit fuzzer.fbs diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift index 5eb489950..e7c40e3e9 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift @@ -830,7 +830,7 @@ internal struct MyGame_Example_TestSimpleTableWithEnum: FlatBufferObject, Verifi return MyGame_Example_TestSimpleTableWithEnum.endTestSimpleTableWithEnum(&builder, start: __root) } - public static func verify(_ verifier: inout Verifier, at position: Int, of type: T.Type) throws where T: Verifiable { + internal static func verify(_ verifier: inout Verifier, at position: Int, of type: T.Type) throws where T: Verifiable { var _v = try verifier.visitTable(at: position) try _v.visit(field: VTOFFSET.color.p, fieldName: "color", required: false, type: MyGame_Example_Color.self) _v.finish() @@ -842,7 +842,7 @@ extension MyGame_Example_TestSimpleTableWithEnum: Encodable { enum CodingKeys: String, CodingKey { case color = "color" } - public func encode(to encoder: Encoder) throws { + internal func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) if color != .green { try container.encodeIfPresent(color, forKey: .color)