/* * 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. */ #include "idl_gen_swift.h" #include #include #include "flatbuffers/code_generators.h" #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" #include "flatbuffers/util.h" #include "idl_namer.h" namespace flatbuffers { namespace swift { namespace { static Namer::Config SwiftDefaultConfig() { return {/*types=*/Case::kKeep, /*constants=*/Case::kLowerCamel, /*methods=*/Case::kLowerCamel, /*functions=*/Case::kLowerCamel, /*fields=*/Case::kLowerCamel, /*variables=*/Case::kLowerCamel, /*variants=*/Case::kLowerCamel, /*enum_variant_seperator=*/".", /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase, /*namespaces=*/Case::kKeep, /*namespace_seperator=*/"_", /*object_prefix=*/"", /*object_suffix=*/"T", /*keyword_prefix=*/"", /*keyword_suffix=*/"_", /*keywords_casing=*/Namer::Config::KeywordsCasing::CaseSensitive, /*filenames=*/Case::kKeep, /*directories=*/Case::kKeep, /*output_path=*/"", /*filename_suffix=*/"_generated", /*filename_extension=*/".swift"}; } static std::set SwiftKeywords() { return { "associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init", "inout", "internal", "let", "open", "operator", "private", "protocol", "public", "rethrows", "static", "struct", "subscript", "typealias", "var", "break", "case", "continue", "default", "defer", "do", "else", "fallthrough", "for", "guard", "if", "in", "repeat", "return", "switch", "where", "while", "Any", "catch", "false", "is", "nil", "super", "self", "Self", "throw", "throws", "true", "try", "associativity", "convenience", "dynamic", "didSet", "final", "get", "infix", "indirect", "lazy", "left", "mutating", "none", "nonmutating", "optional", "override", "postfix", "precedence", "prefix", "Protocol", "required", "right", "set", "Type", "unowned", "weak", "willSet", "Void", }; } static std::string GenIndirect(const std::string& reading) { return "{{ACCESS}}.indirect(" + reading + ")"; } } // namespace class SwiftGenerator : public BaseGenerator { private: CodeWriter code_; std::unordered_set keywords_; int namespace_depth; public: SwiftGenerator(const Parser& parser, const std::string& path, const std::string& file_name) : BaseGenerator(parser, path, file_name, "", "_", "swift"), namer_(WithFlagOptions(SwiftDefaultConfig(), parser.opts, path), SwiftKeywords()) { namespace_depth = 0; code_.SetPadding(" "); } bool generate() { code_.Clear(); code_.SetValue("ACCESS", "_accessor"); code_.SetValue("TABLEOFFSET", "VT"); code_ += "// " + std::string(FlatBuffersGeneratedWarning()); code_ += "// swiftlint:disable all"; code_ += "// swiftformat:disable all\n"; if (parser_.opts.include_dependence_headers || parser_.opts.generate_all) { code_.SetValue("IMPLEMENTONLY", parser_.opts.swift_implementation_only ? "@_implementationOnly " : ""); code_ += "#if canImport(Common)"; code_ += "{{IMPLEMENTONLY}}import Common"; code_ += "#endif"; code_ += ""; code_ += "{{IMPLEMENTONLY}}import FlatBuffers\n"; } if (parser_.advanced_features_ == reflection::AdvancedArrayFeatures) { code_ += "#if compiler(>=6.2)"; } GenerateCode(); if (parser_.advanced_features_ == reflection::AdvancedArrayFeatures) { code_ += "#endif"; } const auto filename = GeneratedFileName(path_, file_name_, parser_.opts); const auto final_code = code_.ToString(); return parser_.opts.file_saver->SaveFile(filename.c_str(), final_code, false); } void GenerateCode() { // 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) { 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) { GenStructReader(struct_def); GenMutableStructReader(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) { GenTable(struct_def); if (parser_.opts.generate_object_based_api) { GenObjectAPI(struct_def); } } } } void mark(const std::string& str) { code_.SetValue("MARKVALUE", str); code_ += "\n// MARK: - {{MARKVALUE}}\n"; } // MARK: - Generating structs // 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); code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def)); GenOSVersionChecks(); code_ += "{{ACCESS_TYPE}} struct {{STRUCTNAME}}: NativeStruct, " "FlatbuffersVectorInitializable, Verifiable, " "FlatbuffersInitializable\\"; 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) { const auto& field = **it; if (field.deprecated) continue; if (!constructor.empty()) constructor += ", "; const auto field_var = namer_.Variable(field); code_.SetValue("FIELDVAR", field_var); const auto type = GenType(field.value.type); if (IsEnum(field.value.type)) { code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); } code_.SetValue("VALUETYPE", type); GenComment(field.doc_comment); if (IsArray(field.value.type)) { std::string valueType = IsEnum(field.value.type.VectorType()) ? "{{BASEVALUE}}" : "{{VALUETYPE}}"; const auto fixed_length = NumToString(field.value.type.VectorType().fixed_length); code_.SetValue("FIXEDLENGTH", fixed_length); std::string vector_base_type; if (IsStruct(field.value.type.VectorType())) { vector_base_type = type + "()"; } else if (IsBool(field.value.type.VectorType().base_type)) { vector_base_type = "false"; } else if (IsFloat(field.value.type.VectorType().base_type)) { vector_base_type = "0.0"; } else { vector_base_type = SwiftConstant(field); } code_ += "private var _{{FIELDVAR}}: InlineArray<{{FIXEDLENGTH}}, " + valueType + ">"; main_constructor.push_back("_" + field_var + " = " + field_var); base_constructor.push_back( "_" + field_var + " = InlineArray(repeating: " + vector_base_type + ")"); if (field.padding) { GenPadding(field, &padding_id); } constructor += field_var + ": " + "InlineArray<" + fixed_length + ", "; if (IsEnum(field.value.type.VectorType())) { constructor += GenTypeBasic(field.value.type.VectorType(), false) + ">"; } else { constructor += type + ">"; } } else { const auto accessing_value = IsEnum(field.value.type) ? ".value" : ""; const auto base_value = IsStruct(field.value.type) ? (type + "()") : SwiftConstant(field); std::string valueType = IsEnum(field.value.type) ? "{{BASEVALUE}}" : "{{VALUETYPE}}"; code_ += "private var _{{FIELDVAR}}: " + valueType; main_constructor.push_back("_" + field_var + " = " + field_var + accessing_value); base_constructor.push_back("_" + field_var + " = " + base_value); if (field.padding) { GenPadding(field, &padding_id); } constructor += field_var + ": " + type; } } code_ += ""; code_ += "{{ACCESS_TYPE}} init(_ bb: ByteBuffer, o: Int32) {"; Indent(); code_ += "self = bb.read(def: Self.self, position: Int(o))"; Outdent(); code_ += "}\n"; 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) { const auto& field = **it; if (field.deprecated) continue; code_.SetValue("FIELDVAR", namer_.Variable(field)); code_.SetValue("VALUETYPE", GenType(field.value.type)); GenComment(field.doc_comment); if (IsArray(field.value.type)) { const auto fixed_length = NumToString(field.value.type.VectorType().fixed_length); code_.SetValue("FIXEDLENGTH", fixed_length); if (IsEnum(field.value.type.VectorType())) { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: InlineArray<{{FIXEDLENGTH}}, " "{{VALUETYPE}}> { InlineArray { {{VALUETYPE}}(rawValue: " "_{{FIELDVAR}}[$0])! } }"; } else { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: InlineArray<{{FIXEDLENGTH}}, " "{{VALUETYPE}}> { _{{FIELDVAR}} }"; } } else if (!IsEnum(field.value.type)) { code_ += GenReaderMainBody() + "_{{FIELDVAR}} }"; } else if (IsEnum(field.value.type)) { code_ += GenReaderMainBody() + "{{VALUETYPE}}(rawValue: _{{FIELDVAR}})! }"; } } code_ += ""; code_ += "{{ACCESS_TYPE}} static func verify(_ verifier: inout Verifier, at " "position: " "Int, of type: T.Type) throws where T: Verifiable {"; Indent(); code_ += "try verifier.inBuffer(position: position, of: {{STRUCTNAME}}.self)"; Outdent(); code_ += "}"; Outdent(); code_ += "}\n"; if (parser_.opts.gen_json_coders) GenerateJSONEncodingAPIs(struct_def); } void GenMutableStructReader(const StructDef& struct_def) { GenObjectHeader(struct_def); 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 offset = NumToString(field.value.offset); const auto type = GenType(field.value.type); code_.SetValue("FIELDVAR", namer_.Variable(field)); if (IsEnum(field.value.type)) { code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); } code_.SetValue("VALUETYPE", type); code_.SetValue("OFFSET", offset); if (IsArray(field.value.type)) { code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); code_.SetValue("SIZE", NumToString(InlineSize(field.value.type.VectorType()))); code_.SetValue("MUTABLE", IsStruct(field.value.type.VectorType()) ? Mutable() : ""); const auto fixed_length = NumToString(field.value.type.VectorType().fixed_length); code_.SetValue("FIXEDLENGTH", fixed_length); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: " "FlatbufferVector<{{VALUETYPE}}{{MUTABLE}}> " "{ return {{ACCESS}}.vector(at: {{OFFSET_VALUE}}, count: " "{{FIXEDLENGTH}}, size: {{SIZE}}) " "}"; } else 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}}.position + {{OFFSET}}"); } else if (IsVector(field.value.type.base_type)) { code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); code_ += GenReaderMainBody() + "return " + GenConstructor("{{ACCESS}}.position + {{OFFSET}}"); } if (parser_.opts.mutable_buffer) { if (!IsStruct(field.value.type) && !IsArray(field.value.type)) { code_ += GenMutate("{{OFFSET}}", "", IsEnum(field.value.type)); } else if (IsArray(field.value.type) && !IsStruct(field.value.type.VectorType())) { code_.SetValue("IS_RAW", IsEnum(field.value.type.VectorType()) ? ".rawValue" : ""); code_ += "@discardableResult {{ACCESS_TYPE}} func mutate({{FIELDVAR}}: " "{{VALUETYPE}}, at index: Int32) -> Bool { " "return {{ACCESS}}.mutate({{FIELDVAR}}{{IS_RAW}}, index: " "{{OFFSET_VALUE}} + (index * {{SIZE}})) }"; } } } if (parser_.opts.generate_object_based_api) { GenerateObjectAPIExtensionHeader(namer_.NamespacedType(struct_def), struct_def.fixed); code_ += "return builder.create(struct: obj)"; Outdent(); code_ += "}"; } Outdent(); code_ += "}\n"; } void GenerateStructArgs(const StructDef& struct_def, std::string* code_ptr, 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) { const auto& field = **it; 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), (object_name + "." + field.name), obj_api_named, is_obj_api); } else { const auto field_var = namer_.Variable(field); const auto field_field = namer_.Field(field); const auto type = GenType(field.value.type); if (!is_obj_api) { code += nameprefix + field_var + ": " + type; if (!IsEnum(field.value.type)) { code += " = "; code += SwiftConstant(field); } code += ", "; continue; } code += nameprefix + field_var + ": " + obj_api_named + object_name + "." + field_field; code += ", "; } } } // MARK: - Table Generator // 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); GenTableAccessors(struct_def); GenTableReader(struct_def); GenTableWriter(struct_def); if (parser_.opts.generate_object_based_api) GenerateObjectAPITableExtension(struct_def); code_ += ""; GenerateVerifier(struct_def); Outdent(); code_ += "}\n"; if (parser_.opts.gen_json_coders) GenerateJSONEncodingAPIs(struct_def); } // Generates the reader for swift void GenTableAccessors(const StructDef& struct_def) { // Generate field id constants. if (struct_def.fields.vec.size() > 0) { code_ += "private struct {{TABLEOFFSET}} {"; 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", namer_.Variable(field)); code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); code_ += "static let {{OFFSET_NAME}}: VOffset = {{OFFSET_VALUE}}"; } Outdent(); code_ += "}"; code_ += ""; } } void GenObjectHeader(const StructDef& struct_def) { GenComment(struct_def.doc_comment); code_.SetValue("SHORT_STRUCTNAME", namer_.Type(struct_def)); code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def)); code_.SetValue("OBJECTTYPE", struct_def.fixed ? "Struct" : "Table"); code_.SetValue("MUTABLE", struct_def.fixed ? Mutable() : ""); GenOSVersionChecks(); code_ += "{{ACCESS_TYPE}} struct {{STRUCTNAME}}{{MUTABLE}}: " "FlatBuffer{{OBJECTTYPE}}, FlatbuffersVectorInitializable\\"; if (!struct_def.fixed) code_ += ", Verifiable\\"; if (!struct_def.fixed && parser_.opts.generate_object_based_api) code_ += ", ObjectAPIPacker\\"; code_ += " {\n"; Indent(); code_ += ValidateFunc(); 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_ += "{{ACCESS_TYPE}} static var id: String { \"{{FILENAME}}\" } "; code_ += "{{ACCESS_TYPE}} static func finish(_ fbb: inout " "FlatBufferBuilder, end: " "Offset, prefix: Bool = false) { fbb.finish(offset: end, " "fileId: " "{{STRUCTNAME}}.id, addPrefix: prefix) }"; } code_ += "private init(_ t: Table) { {{ACCESS}} = t }"; } code_ += "{{ACCESS_TYPE}} init(_ bb: ByteBuffer, o: Int32) { {{ACCESS}} = " "{{OBJECTTYPE}}(bb: " "bb, position: o) }"; code_ += ""; } void GenTableWriter(const StructDef& struct_def) { flatbuffers::FieldDef* key_field = nullptr; std::vector require_fields; std::vector create_func_body; std::vector create_func_header; const auto should_generate_create = struct_def.fields.vec.size() != 0; code_.SetValue("NUMBEROFFIELDS", NumToString(struct_def.fields.vec.size())); code_ += "{{ACCESS_TYPE}} static func start{{SHORT_STRUCTNAME}}(_ fbb: inout " "FlatBufferBuilder) -> " "UOffset { fbb.startTable(with: {{NUMBEROFFIELDS}}) }"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto& field = **it; if (field.deprecated) continue; if (field.key) key_field = &field; if (field.IsRequired()) require_fields.push_back(NumToString(field.value.offset)); GenTableWriterFields(field, &create_func_body, &create_func_header); } code_ += "{{ACCESS_TYPE}} static func end{{SHORT_STRUCTNAME}}(_ fbb: inout " "FlatBufferBuilder, " "start: " "UOffset) -> Offset { let end = Offset(offset: " "fbb.endTable(at: start))\\"; if (require_fields.capacity() != 0) { std::string fields = ""; for (auto it = require_fields.begin(); it != require_fields.end(); ++it) fields += *it + ", "; code_.SetValue("FIELDS", fields.substr(0, fields.size() - 2)); code_ += "; fbb.require(table: end, fields: [{{FIELDS}}])\\"; } code_ += "; return end }"; if (should_generate_create) { code_ += "{{ACCESS_TYPE}} static func create{{SHORT_STRUCTNAME}}("; Indent(); code_ += "_ fbb: inout FlatBufferBuilder\\"; if (create_func_header.empty() == false) code_ += ","; 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("VOFFSET", NumToString(key_field->value.offset)); code_ += "{{ACCESS_TYPE}} static func " + namer_.Method("sort_vector_of", struct_def) + "(offsets:[Offset], " "_ fbb: inout FlatBufferBuilder) -> Offset {"; Indent(); code_ += spacing + "var off = offsets"; code_ += spacing + "off.sort { Table.compare(Table.offset(Int32($1.o), vOffset: " "{{VOFFSET}}, fbb: &fbb), Table.offset(Int32($0.o), vOffset: " "{{VOFFSET}}, fbb: &fbb), fbb: &fbb) < 0 } "; code_ += spacing + "return fbb.createVector(ofOffsets: off)"; Outdent(); code_ += "}"; GenLookup(*key_field, namer_.NamespacedType(struct_def)); } } 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; const auto field_field = namer_.Field(field); const auto field_var = namer_.Variable(field); const auto type = GenType(field.value.type); const auto opt_scalar = field.IsOptional() && IsScalar(field.value.type.base_type); const auto nullable_type = opt_scalar ? type + "?" : type; code_.SetValue("FIELDVAR", namer_.Variable(field)); code_.SetValue("VALUETYPE", nullable_type); code_.SetValue("OFFSET", namer_.Field(field)); code_.SetValue("CONSTANT", SwiftConstant(field)); std::string check_if_vector = (IsVector(field.value.type) || IsArray(field.value.type)) ? "VectorOf(" : "("; const auto body = "add" + check_if_vector + field_field + ": "; code_ += "{{ACCESS_TYPE}} static func " + body + "\\"; create_func_body.push_back("{{STRUCTNAME}}." + body + field_field + ", &fbb)"); if (IsScalar(field.value.type.base_type) && !IsBool(field.value.type.base_type)) { const std::string is_enum = IsEnum(field.value.type) ? ".rawValue" : ""; const std::string optional_enum = IsEnum(field.value.type) ? ("?" + is_enum) : ""; code_ += "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{FIELDVAR}}\\"; code_ += field.IsOptional() ? (optional_enum + "\\") : (is_enum + ", def: {{CONSTANT}}\\"); code_ += ", at: {{TABLEOFFSET}}.{{OFFSET}}) }"; const auto default_value = IsEnum(field.value.type) ? (field.IsOptional() ? "nil" : GenEnumDefaultValue(field)) : SwiftConstant(field); create_func_header.push_back( "" + field_field + ": " + nullable_type + " = " + (field.IsOptional() ? "nil" : default_value)); return; } if (IsBool(field.value.type.base_type)) { std::string default_value = SwiftConstant(field); code_.SetValue("CONSTANT", default_value); code_.SetValue("VALUETYPE", field.IsOptional() ? "Bool?" : "Bool"); code_ += "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{FIELDVAR}},\\"; code_ += field.IsOptional() ? "\\" : " def: {{CONSTANT}},"; code_ += " at: {{TABLEOFFSET}}.{{OFFSET}}) }"; create_func_header.push_back( field_var + ": " + nullable_type + " = " + (field.IsOptional() ? "nil" : default_value)); return; } if (IsStruct(field.value.type)) { const auto create_struct = "guard let {{FIELDVAR}} = {{FIELDVAR}} else { return };" " fbb.create(struct: {{FIELDVAR}}, position: " "{{TABLEOFFSET}}.{{OFFSET}}) }"; code_ += type + "?" + builder_string + create_struct; /// Optional hard coded since structs are always optional create_func_header.push_back(field_var + ": " + type + (field.IsOptional() ? "? = nil" : "")); return; } const auto arg_label = namer_.Variable(field) + (IsVector(field.value.type) || IsArray(field.value.type) ? "VectorOffset" : "Offset"); create_func_header.push_back(arg_label + " " + field_var + ": " + "Offset" + (field.IsRequired() ? "" : " = Offset()")); const auto reader_type = IsStruct(field.value.type) && field.value.type.struct_def->fixed ? "structOffset: {{TABLEOFFSET}}.{{OFFSET}}) }" : "offset: {{FIELDVAR}}, at: {{TABLEOFFSET}}.{{OFFSET}}) " "}"; code_ += "Offset" + builder_string + "fbb.add(" + reader_type; const 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))) { const auto field_name = namer_.NamespacedType(*vectortype.struct_def); code_ += "{{ACCESS_TYPE}} static func " + namer_.Method("start_vector_of", field_var) + "(_ 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) { const auto& field = **it; if (field.deprecated) continue; GenTableReaderFields(field); } } void GenTableReaderFields(const FieldDef& field) { const auto offset = NumToString(field.value.offset); const auto field_field = namer_.Field(field); const auto type = GenType(field.value.type); code_.SetValue("FIELDVAR", namer_.Variable(field)); code_.SetValue("FIELDMETHOD", namer_.Method(field)); code_.SetValue("VALUETYPE", type); code_.SetValue("OFFSET", namer_.Constant(field.name)); code_.SetValue("CONSTANT", SwiftConstant(field)); bool opt_scalar = field.IsOptional() && IsScalar(field.value.type.base_type); std::string def_Val = opt_scalar ? "nil" : "{{CONSTANT}}"; std::string optional = opt_scalar ? "?" : ""; const 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(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 = field.IsOptional() ? "nil" : SwiftConstant(field); code_.SetValue("CONSTANT", default_value); code_.SetValue("VALUETYPE", "Bool"); code_ += GenReaderMainBody(optional) + "\\"; code_ += GenOffset() + "return o == 0 ? {{CONSTANT}} : " + GenReader("VALUETYPE", "o") + " }"; if (parser_.opts.mutable_buffer) code_ += GenMutate("o", GenOffset()); return; } if (IsEnum(field.value.type)) { const auto default_value = field.IsOptional() ? "nil" : GenEnumDefaultValue(field); code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); code_ += GenReaderMainBody(optional) + "\\"; code_ += GenOffset() + "return o == 0 ? " + default_value + " : " + GenEnumConstructor("o") + "?? " + default_value + " }"; if (parser_.opts.mutable_buffer && !IsUnion(field.value.type)) code_ += GenMutate("o", GenOffset(), true); return; } const std::string is_required = field.IsRequired() ? "!" : "?"; const 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(is_required) + GenOffset() + required_reader + "{{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, at: o) }"; code_.SetValue("FIELDVAR", namer_.Variable("mutable", field_field)); code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); code_.SetValue("CONSTANT", "nil"); code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader + GenConstructor("o + {{ACCESS}}.position"); return; } switch (field.value.type.base_type) { case BASE_TYPE_STRUCT: code_.SetValue("VALUETYPE", GenType(field.value.type)); code_.SetValue("CONSTANT", "nil"); code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader + GenConstructor(GenIndirect("o + {{ACCESS}}.position")); break; case BASE_TYPE_STRING: { const auto default_string = "\"" + SwiftConstant(field) + "\""; code_.SetValue("VALUETYPE", GenType(field.value.type)); code_.SetValue("CONSTANT", field.IsDefault() ? default_string : "nil"); code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader + "{{ACCESS}}.string(at: o) }"; code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}SegmentArray: [UInt8]" + is_required + " { return " "{{ACCESS}}.getVector(at: {{TABLEOFFSET}}.{{OFFSET}}) }"; break; } case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru case BASE_TYPE_VECTOR: GenTableReaderVectorFields(field); break; case BASE_TYPE_UNION: code_.SetValue("CONSTANT", "nil"); code_ += "{{ACCESS_TYPE}} func {{FIELDVAR}}(type: " "T.Type) -> T" + is_required + " { " + GenOffset() + required_reader + "{{ACCESS}}.union(o) }"; break; default: FLATBUFFERS_ASSERT(0); } } void GenTableReaderVectorFields(const FieldDef& field) { const auto vectortype = field.value.type.VectorType(); code_.SetValue("SIZE", NumToString(InlineSize(vectortype))); if (vectortype.base_type == BASE_TYPE_STRING) { code_.SetValue("VALUETYPE", "String?"); } if (vectortype.base_type != BASE_TYPE_UNION) { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: " "FlatbufferVector<{{VALUETYPE}}> " "{ return {{ACCESS}}.vector(at: {{TABLEOFFSET}}.{{OFFSET}}, " "byteSize: {{SIZE}}) }"; } if (IsScalar(vectortype.base_type) && !IsEnum(vectortype) && !IsBool(field.value.type.base_type)) { if (parser_.opts.mutable_buffer) code_ += GenMutateArray(); GenUnsafeBufferPointer(field); return; } if (vectortype.base_type == BASE_TYPE_STRUCT && field.value.type.struct_def->fixed) { code_.SetValue("FIELDVAR", namer_.Method("mutable", field)); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: " "FlatbufferVector<{{VALUETYPE}}_Mutable> " "{ return {{ACCESS}}.vector(at: {{TABLEOFFSET}}.{{OFFSET}}, " "byteSize: {{SIZE}}) }"; GenUnsafeBufferPointer(field); return; } if (vectortype.base_type == BASE_TYPE_UNION) { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: UnionFlatbufferVector " "{ return {{ACCESS}}.unionVector(at: " "{{TABLEOFFSET}}.{{OFFSET}}, " "byteSize: {{SIZE}}) }"; code_ += "{{ACCESS_TYPE}} func {{FIELDVAR}}(at " "index: " "Int32, type: T.Type) -> T? { " + GenOffset() + "return o == 0 ? nil : \\"; code_ += "{{ACCESS}}.directUnion({{ACCESS}}.vector(at: o) + " "index * {{SIZE}}) }"; return; } if (vectortype.base_type == BASE_TYPE_STRUCT && !field.value.type.struct_def->fixed) { const auto& sd = *field.value.type.struct_def; const auto& fields = sd.fields.vec; for (auto kit = fields.begin(); kit != fields.end(); ++kit) { const auto& key_field = **kit; if (key_field.key) { GenByKeyFunctions(key_field); break; } } } } void GenUnsafeBufferPointer(const FieldDef& field) { code_.SetValue("functionName", namer_.Variable("withUnsafePointerTo", field)); code_ += "{{ACCESS_TYPE}} func {{functionName}}(_ body: " "(UnsafeRawBufferPointer, Int) throws -> T) rethrows -> T? { return " "try " "{{ACCESS}}.withUnsafePointerToSlice(at: " "{{TABLEOFFSET}}.{{OFFSET}}, " "body: body) }"; } std::vector GenerateCodingKeys(const StructDef& struct_def) { std::vector coding_keys; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto& field = **it; if (field.deprecated) continue; coding_keys.push_back("case " + namer_.Variable(field) + " = \"" + field.name + "\""); } return coding_keys; } void GenerateEncoderUnionBody(const FieldDef& field) { EnumDef& union_def = *field.value.type.enum_def; const auto is_vector = field.value.type.base_type == BASE_TYPE_VECTOR || field.value.type.base_type == BASE_TYPE_ARRAY; if (field.value.type.base_type == BASE_TYPE_UTYPE || (is_vector && field.value.type.VectorType().base_type == BASE_TYPE_UTYPE)) return; if (is_vector) { code_ += "try container.encode({{FIELDVAR}}Type, forKey: .charactersType)"; code_ += "var contentEncoder = container.nestedUnkeyedContainer(forKey: " ".{{FIELDVAR}})"; code_ += "let _{{FIELDVAR}} = {{FIELDVAR}}Type"; code_ += "for index in _{{FIELDVAR}}.startIndex..<_{{FIELDVAR}}.endIndex {"; Indent(); code_ += "switch _{{FIELDVAR}}[index] {"; for (auto it = union_def.Vals().begin(); it != union_def.Vals().end(); ++it) { const auto& ev = **it; const auto type = GenType(ev.union_type); code_.SetValue("KEY", namer_.LegacySwiftVariant(ev)); code_.SetValue("VALUETYPE", type); if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } code_ += "case .{{KEY}}:"; Indent(); code_ += "let _v = {{FIELDVAR}}(at: Int32(index), type: {{VALUETYPE}}.self)"; code_ += "try contentEncoder.encode(_v)"; Outdent(); } code_ += "default: break;"; code_ += "}"; Outdent(); code_ += "}"; return; } code_ += "switch {{FIELDVAR}}Type {"; for (auto it = union_def.Vals().begin(); it != union_def.Vals().end(); ++it) { const auto& ev = **it; const auto type = GenType(ev.union_type); code_.SetValue("KEY", namer_.LegacySwiftVariant(ev)); code_.SetValue("VALUETYPE", type); if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } code_ += "case .{{KEY}}:"; Indent(); code_ += "let _v = {{FIELDVAR}}(type: {{VALUETYPE}}.self)"; code_ += "try container.encodeIfPresent(_v, forKey: .{{FIELDVAR}})"; Outdent(); } code_ += "default: break;"; code_ += "}"; } void GenerateEncoderBody(const StructDef& struct_def) { code_ += "var container = encoder.container(keyedBy: CodingKeys.self)"; 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 type = field.value.type; code_.SetValue("FIELDVAR", namer_.Variable(field)); code_.SetValue("CONSTANT", SwiftConstant(field)); bool should_indent = true; if (IsEnum(type) && !field.IsOptional()) { code_.SetValue("CONSTANT", GenEnumDefaultValue(field)); code_ += "if {{FIELDVAR}} != {{CONSTANT}} {"; } else if (IsFloat(type.base_type) && StringIsFlatbufferNan(field.value.constant)) { code_ += "if !{{FIELDVAR}}.isNaN {"; } else if (IsScalar(type.base_type) && !IsEnum(type) && !IsBool(type.base_type) && !field.IsOptional()) { code_ += "if {{FIELDVAR}} != {{CONSTANT}} {"; } else if (IsBool(type.base_type) && !field.IsOptional()) { code_.SetValue("CONSTANT", SwiftConstant(field)); code_ += "if {{FIELDVAR}} != {{CONSTANT}} {"; } else { should_indent = false; } if (should_indent) Indent(); if (IsUnion(type) && !IsEnum(type)) { GenerateEncoderUnionBody(field); } else if (IsArray(type)) { code_ += "var {{FIELDVAR}}Container = " "container.nestedUnkeyedContainer(forKey: .{{FIELDVAR}})"; code_ += "for index in {{FIELDVAR}}.startIndex..<{{FIELDVAR}}.endIndex {"; Indent(); code_ += "try {{FIELDVAR}}Container.encode({{FIELDVAR}}[index])"; Outdent(); code_ += "}"; } else { code_ += "try container.encodeIfPresent({{FIELDVAR}}, forKey: " ".{{FIELDVAR}})"; } if (should_indent) Outdent(); if ((IsScalar(type.base_type) && !field.IsOptional())) { code_ += "}"; } } } void GenerateJSONEncodingAPIs(const StructDef& struct_def) { GenOSVersionChecks(); code_ += "extension {{STRUCTNAME}}: Encodable {"; Indent(); auto coding_keys = GenerateCodingKeys(struct_def); if (coding_keys.empty() == false) { code_ += "enum CodingKeys: String, CodingKey {"; Indent(); for (auto it = coding_keys.begin(); it != coding_keys.end(); ++it) { const auto& field = *it; code_ += field; } Outdent(); code_ += "}"; code_ += ""; } code_ += "{{ACCESS_TYPE}} func encode(to encoder: Encoder) throws {"; if (coding_keys.empty() == false) { Indent(); GenerateEncoderBody(struct_def); Outdent(); } code_ += "}"; Outdent(); code_ += "}"; code_ += ""; } void GenerateVerifier(const StructDef& struct_def) { code_ += "{{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)"; 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 offset = NumToString(field.value.offset); code_.SetValue("FIELDVAR", namer_.Variable(field)); code_.SetValue("VALUETYPE", GenerateVerifierType(field)); code_.SetValue("OFFSET", namer_.Field(field)); code_.SetValue("ISREQUIRED", field.IsRequired() ? "true" : "false"); if (IsUnion(field.value.type)) { GenerateUnionTypeVerifier(field); continue; } code_ += "try _v.visit(field: {{TABLEOFFSET}}.{{OFFSET}}, fieldName: " "\"{{FIELDVAR}}\", required: {{ISREQUIRED}}, type: " "{{VALUETYPE}}.self)"; } code_ += "_v.finish()"; Outdent(); code_ += "}"; } void GenerateUnionTypeVerifier(const FieldDef& field) { const auto is_vector = IsVector(field.value.type) || IsArray(field.value.type); if (field.value.type.base_type == BASE_TYPE_UTYPE || (is_vector && field.value.type.VectorType().base_type == BASE_TYPE_UTYPE)) return; EnumDef& union_def = *field.value.type.enum_def; code_.SetValue("VALUETYPE", namer_.NamespacedType(union_def)); code_.SetValue("FUNCTION_NAME", is_vector ? "visitUnionVector" : "visit"); code_ += "try _v.{{FUNCTION_NAME}}(unionKey: " "{{TABLEOFFSET}}.{{OFFSET}}Type, " "unionField: {{TABLEOFFSET}}.{{OFFSET}}, unionKeyName: " "\"{{FIELDVAR}}Type\", fieldName: \"{{FIELDVAR}}\", required: " "{{ISREQUIRED}}, completion: { (verifier, key: {{VALUETYPE}}, pos) in"; Indent(); code_ += "switch key {"; for (auto it = union_def.Vals().begin(); it != union_def.Vals().end(); ++it) { const auto& ev = **it; const auto type = GenType(ev.union_type); code_.SetValue("KEY", namer_.LegacySwiftVariant(ev)); code_.SetValue("VALUETYPE", type); code_ += "case .{{KEY}}:"; Indent(); if (ev.union_type.base_type == BASE_TYPE_NONE) { code_ += "break // NOTE - SWIFT doesnt support none"; } else if (ev.union_type.base_type == BASE_TYPE_STRING) { code_ += "try ForwardOffset.verify(&verifier, at: pos, of: " "String.self)"; } else { code_.SetValue("MAINTYPE", ev.union_type.struct_def->fixed ? type : "ForwardOffset<" + type + ">"); code_ += "try {{MAINTYPE}}.verify(&verifier, at: pos, of: " "{{VALUETYPE}}.self)"; } Outdent(); } code_ += "}"; Outdent(); code_ += "})"; } std::string GenerateVerifierType(const FieldDef& field) { const auto type = field.value.type; const auto is_vector = IsVector(type) || IsArray(type); if (is_vector) { const auto vector_type = field.value.type.VectorType(); return "ForwardOffset>"; } return GenerateNestedVerifierTypes(field.value.type); } std::string GenerateNestedVerifierTypes(const Type& type) { const auto string_type = GenType(type); if (IsScalar(type.base_type)) { return string_type; } if (IsString(type)) { return "ForwardOffset<" + string_type + ">"; } if (type.struct_def && type.struct_def->fixed) { return string_type; } return "ForwardOffset<" + string_type + ">"; } void GenByKeyFunctions(const FieldDef& key_field) { code_.SetValue("TYPE", GenType(key_field.value.type)); code_ += "{{ACCESS_TYPE}} func {{FIELDVAR}}By(key: {{TYPE}}) -> {{VALUETYPE}}? " "{ \\"; code_ += GenOffset() + "return o == 0 ? nil : {{VALUETYPE}}.lookupByKey(vector: " "{{ACCESS}}.vector(at: o), key: key, fbb: {{ACCESS}}.bb) }"; } void GenEnum(const EnumDef& enum_def) { if (enum_def.generated) return; const bool is_private_access = parser_.opts.swift_implementation_only || enum_def.attributes.Lookup("private") != nullptr; code_.SetValue("ENUM_TYPE", enum_def.is_union ? "UnionEnum" : "Enum, Verifiable"); code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public"); code_.SetValue("ENUM_NAME", namer_.NamespacedType(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}}, FlatbuffersVectorInitializable, {{ENUM_TYPE}} {"; Indent(); code_ += "{{ACCESS_TYPE}} typealias T = {{BASE_TYPE}}"; if (enum_def.is_union) { code_ += ""; code_ += "{{ACCESS_TYPE}} init?(value: T) {"; Indent(); code_ += "self.init(rawValue: value)"; Outdent(); code_ += "}\n"; } 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; code_.SetValue("KEY", namer_.LegacySwiftVariant(ev)); code_.SetValue("VALUE", enum_def.ToString(ev)); GenComment(ev.doc_comment); code_ += "case {{KEY}} = {{VALUE}}"; } code_ += ""; AddMinOrMaxEnumValue(namer_.LegacySwiftVariant(*enum_def.MaxValue()), "max"); AddMinOrMaxEnumValue(namer_.LegacySwiftVariant(*enum_def.MinValue()), "min"); Outdent(); code_ += "}\n"; if (parser_.opts.gen_json_coders) EnumEncoder(enum_def); code_ += ""; 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_ += "}"; } } void EnumEncoder(const EnumDef& enum_def) { code_ += "extension {{ENUM_NAME}}: Encodable {"; Indent(); code_ += "{{ACCESS_TYPE}} func encode(to encoder: Encoder) throws {"; Indent(); code_ += "var container = encoder.singleValueContainer()"; code_ += "switch self {"; for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { const auto& ev = **it; code_.SetValue("KEY", namer_.LegacySwiftVariant(ev)); code_.SetValue("RAWKEY", ev.name); code_ += "case .{{KEY}}: try container.encode(\"{{RAWKEY}}\")"; } code_ += "}"; Outdent(); code_ += "}"; Outdent(); code_ += "}"; } // MARK: - Object API void GenerateObjectAPIExtensionHeader(std::string type_name, const bool is_fixed_struct) { // code_.SetValue("MUTATING", is_fixed_struct ? "" : " mutating"); code_ += ""; code_ += "{{ACCESS_TYPE}} func unpack() -> " + type_name + " {"; Indent(); code_ += "return " + type_name + (is_fixed_struct ? "(self)" : "(self)"); Outdent(); code_ += "}"; code_ += "{{ACCESS_TYPE}} static func pack(_ builder: inout FlatBufferBuilder, " "obj: " "inout " + type_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 " + type_name + ") -> Offset {"; Indent(); } void GenerateObjectAPIStructConstructor(const StructDef& struct_def) { code_ += "{{ACCESS_TYPE}} init(_ _t: borrowing {{STRUCTNAME}}" + Mutable() + ") {"; Indent(); 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 type = GenType(field.value.type); code_.SetValue("FIELDVAR", namer_.Variable(field)); if (IsArray(field.value.type)) { code_.SetValue( "RAW_VALUE", IsStruct(field.value.type.VectorType()) ? ".unpack()" : (IsEnum(field.value.type.VectorType()) ? ".rawValue" : "")); code_ += "let _v{{FIELDVAR}} = _t.{{FIELDVAR}}"; code_ += "_{{FIELDVAR}} = InlineArray { _v{{FIELDVAR}}[$0]{{RAW_VALUE}} }"; continue; } else if (IsStruct(field.value.type)) { code_ += "let _v{{FIELDVAR}} = _t.{{FIELDVAR}}"; code_ += "_{{FIELDVAR}} = _v{{FIELDVAR}}.unpack()"; continue; } std::string is_enum = IsEnum(field.value.type) ? ".value" : ""; code_ += "_{{FIELDVAR}} = _t.{{FIELDVAR}}" + is_enum; } Outdent(); code_ += "}\n"; } void GenObjectAPI(const StructDef& struct_def) { GenOSVersionChecks(); code_ += "{{ACCESS_TYPE}} class " + namer_.NamespacedObjectType(struct_def) + ": 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) { const auto& field = **it; if (field.deprecated) continue; BuildObjectAPIConstructorBody(field, struct_def.fixed, buffer_constructor, base_constructor); } code_ += ""; BuildObjectConstructor( buffer_constructor, "_ _t: borrowing " + namer_.NamespacedType(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(namer_.NamespacedObjectType(struct_def), struct_def.fixed); std::vector unpack_body; std::string 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; const auto field_var = namer_.Variable(field); const auto field_field = namer_.Field(field); const auto field_method = namer_.Method(field); const 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 + field_method + ": "; switch (field.value.type.base_type) { case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); case BASE_TYPE_VECTOR: { GenerateVectorObjectAPITableExtension(field); unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var + builder); break; } case BASE_TYPE_UNION: { code_ += "let __" + field_var + " = obj." + field_var + "?.pack(builder: &builder) ?? Offset()"; unpack_body.push_back("if let o = obj." + field_var + "?.type {"); unpack_body.push_back(" {{STRUCTNAME}}.add(" + field_var + "Type: o" + builder); unpack_body.push_back(" {{STRUCTNAME}}." + body + "__" + field_var + 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." + field_field + builder); } else { code_ += "let __" + field_var + " = " + type + ".pack(&builder, obj: &obj." + field_field + ")"; unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var + builder); } break; } case BASE_TYPE_STRING: { unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var + builder); if (field.IsRequired()) { code_ += "let __" + field_var + " = builder.create(string: obj." + field_field + ")"; } else { BuildingOptionalObjects(field_field, "builder.create(string: s)"); } break; } case BASE_TYPE_UTYPE: break; default: unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." + field_field + builder); } } code_ += "let __root = {{STRUCTNAME}}.start{{SHORT_STRUCTNAME}}(&builder)"; for (auto it = unpack_body.begin(); it < unpack_body.end(); it++) code_ += *it; code_ += "return {{STRUCTNAME}}.end{{SHORT_STRUCTNAME}}(&builder, start: " "__root)"; Outdent(); code_ += "}"; } void GenerateVectorObjectAPITableExtension(const FieldDef& field_def) { const Type& field_type = field_def.value.type; const auto type = GenType(field_type); const auto var = namer_.Variable(field_def); const auto field = namer_.Field(field_def); const auto vectortype = field_type.VectorType(); switch (vectortype.base_type) { case BASE_TYPE_UNION: { code_ += "var __" + var + "__: [Offset] = []"; code_ += "for i in obj." + var + " {"; Indent(); code_ += "guard let off = i?.pack(builder: &builder) else { continue }"; code_ += "__" + var + "__.append(off)"; Outdent(); code_ += "}"; code_ += "let __" + var + " = builder.createVector(ofOffsets: __" + var + "__)"; code_ += "let __" + var + "Type = builder.createVector(obj." + field + ".compactMap { $0?.type })"; break; } case BASE_TYPE_UTYPE: break; case BASE_TYPE_STRUCT: { if (field_type.struct_def && !field_type.struct_def->fixed) { code_ += "var __" + var + "__: [Offset] = []"; code_ += "for var i in obj." + var + " {"; Indent(); code_ += "__" + var + "__.append(" + type + ".pack(&builder, obj: &i))"; Outdent(); code_ += "}"; code_ += "let __" + var + " = builder.createVector(ofOffsets: __" + var + "__)"; } else { code_ += "{{STRUCTNAME}}." + namer_.Method("start_vector_of", var) + "(obj." + field + ".count, in: &builder)"; std::string code; GenerateStructArgs(*field_type.struct_def, &code, "", "", "_o", true); code = code.substr(0, code.size() - 2); code_ += "for val in obj." + field + " {"; Indent(); code_ += "builder.create(struct: val)"; Outdent(); code_ += "}"; code_ += "let __" + var + " = builder.endVector(len: obj." + field + ".count)"; } break; } case BASE_TYPE_STRING: { code_ += "let __" + var + " = builder.createVector(ofStrings: obj." + var + ".compactMap({ $0 }) )"; break; } default: { code_ += "let __" + var + " = builder.createVector(obj." + field + ")"; break; } } } void BuildingOptionalObjects(const std::string& var, const std::string& body_front) { code_ += "let __" + var + ": Offset"; code_ += "if let s = obj." + var + " {"; Indent(); code_ += "__" + var + " = " + body_front; Outdent(); code_ += "} else {"; Indent(); code_ += "__" + var + " = 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) { const auto field_field = namer_.Field(field); const auto field_var = namer_.Variable(field); const auto type = GenType(field.value.type); code_.SetValue("FIELDVAR", field_field); code_.SetValue("VALUETYPE", type); std::string is_required = field.IsRequired() ? "" : "?"; switch (field.value.type.base_type) { case BASE_TYPE_STRUCT: { const auto objtype = GenType(field.value.type, true); code_.SetValue("VALUETYPE", objtype); const 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 {{FIELDVAR}}: {{VALUETYPE}}" + question_mark; base_constructor.push_back("" + field_var + " = " + objtype + "()"); if (field.value.type.struct_def->fixed) { buffer_constructor.push_back("" + field_var + " = _t." + field_field); } else { buffer_constructor.push_back( "" + field_var + " = _t." + field_var + (field.IsRequired() ? "!" : question_mark) + ".unpack()"); } break; } case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); case BASE_TYPE_VECTOR: { BuildObjectAPIConstructorBodyVectors(field, buffer_constructor, base_constructor, " "); break; } case BASE_TYPE_STRING: { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: String" + is_required; buffer_constructor.push_back(field_var + " = _t." + field_field); if (field.IsRequired()) { std::string default_value = field.IsDefault() ? SwiftConstant(field) : ""; base_constructor.push_back(field_var + " = \"" + default_value + "\""); break; } if (field.IsDefault() && !field.IsRequired()) { std::string value = field.IsDefault() ? SwiftConstant(field) : "nil"; base_constructor.push_back(field_var + " = \"" + value + "\""); } break; } case BASE_TYPE_UTYPE: break; case BASE_TYPE_UNION: { BuildUnionEnumSwitchCase(*field.value.type.enum_def, field_var, buffer_constructor); break; } default: { buffer_constructor.push_back(field_var + " = _t." + field_field); 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 {{FIELDVAR}}: {{VALUETYPE}}" + nullable; if (!field.IsOptional()) base_constructor.push_back(field_var + " = " + SwiftConstant(field)); break; } if (IsEnum(field.value.type)) { const auto default_value = IsEnum(field.value.type) ? GenEnumDefaultValue(field) : SwiftConstant(field); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}"; base_constructor.push_back(field_var + " = " + default_value); break; } if (IsBool(field.value.type.base_type)) { code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: Bool" + nullable; if (!field.IsOptional()) base_constructor.push_back(field_var + " = " + SwiftConstant(field)); } } } } void BuildObjectAPIConstructorBodyVectors( const FieldDef& field, std::vector& buffer_constructor, std::vector& base_constructor, const std::string& indentation) { const auto vectortype = field.value.type.VectorType(); const auto field_var = namer_.Field(field); const auto field_field = namer_.Field(field); if (vectortype.base_type != BASE_TYPE_UTYPE) { buffer_constructor.push_back(field_var + " = []"); base_constructor.push_back(field_var + " = []"); } switch (vectortype.base_type) { case BASE_TYPE_STRUCT: { code_.SetValue("VALUETYPE", GenType(vectortype, true)); code_.SetValue("OPTIONAL", !vectortype.struct_def->fixed ? "?" : ""); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}{{OPTIONAL}}]"; if (!vectortype.struct_def->fixed) { buffer_constructor.push_back("for val in _t." + field_field + "{"); buffer_constructor.push_back(indentation + field_var + ".append(val.unpack())"); buffer_constructor.push_back("}"); } else { buffer_constructor.push_back(field_var + ".append(contentsOf: _t." + field_field + ")"); } break; } case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); case BASE_TYPE_VECTOR: { break; } case BASE_TYPE_UNION: { BuildUnionEnumSwitchCase(*field.value.type.enum_def, field_var, buffer_constructor, indentation, true); break; } case BASE_TYPE_UTYPE: break; default: { code_.SetValue( "VALUETYPE", (IsString(vectortype) ? "String?" : GenType(vectortype))); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}]"; buffer_constructor.push_back(field_var + ".append(contentsOf: _t." + field_field + ")"); break; } } } void BuildUnionEnumSwitchCaseWritter(const EnumDef& ed) { code_ += "switch type {"; for (auto it = ed.Vals().begin(); it < ed.Vals().end(); ++it) { const auto ev = **it; const auto variant = namer_.LegacySwiftVariant(ev); const auto type = GenType(ev.union_type); const auto is_struct = IsStruct(ev.union_type) ? type + Mutable() : type; if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } code_ += "case ." + variant + ":"; Indent(); code_ += "var __obj = value as? " + GenType(ev.union_type, true); code_ += "return " + is_struct + ".pack(&builder, obj: &__obj)"; Outdent(); } code_ += "default: return Offset()"; code_ += "}"; } void BuildUnionEnumSwitchCase(const EnumDef& ed, const std::string& field, std::vector& buffer_constructor, const std::string& indentation = "", const bool is_vector = false) { const auto ns_type = namer_.NamespacedType(ed); code_.SetValue("VALUETYPE", ns_type); code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: \\"; code_ += is_vector ? "[{{VALUETYPE}}Union?]" : "{{VALUETYPE}}Union?"; const auto vector_reader = is_vector ? "[index]" : ""; if (is_vector) { buffer_constructor.push_back("let _" + field + "Type = _t." + field + "Type"); buffer_constructor.push_back("for index in _" + field + "Type.startIndex..<_" + field + "Type.endIndex {"); } buffer_constructor.push_back(indentation + "switch _t." + field + "Type" + vector_reader + " {"); for (auto it = ed.Vals().begin(); it < ed.Vals().end(); ++it) { const auto ev = **it; const auto variant = namer_.LegacySwiftVariant(ev); if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } const auto type = IsStruct(ev.union_type) ? GenType(ev.union_type) + Mutable() : GenType(ev.union_type); buffer_constructor.push_back(indentation + "case ." + variant + ":"); const auto constructor = ns_type + "Union(_v?.unpack(), type: ." + variant + ")"; if (is_vector) { buffer_constructor.push_back(indentation + " let _v = _t." + field + "(at: Int32(index), type: " + type + ".self)"); buffer_constructor.push_back(indentation + " " + field + ".append(" + constructor + ")"); } else { buffer_constructor.push_back(indentation + " let _v = _t." + field + "(" + "type: " + type + ".self)"); buffer_constructor.push_back(indentation + " " + field + " = " + constructor); } } buffer_constructor.push_back(indentation + "default: break"); buffer_constructor.push_back(indentation + "}"); if (is_vector) { buffer_constructor.push_back("}"); } } void AddMinOrMaxEnumValue(const std::string& str, const std::string& type) { const auto current_value = str; code_.SetValue(type, current_value); code_ += "{{ACCESS_TYPE}} static var " + type + ": {{ENUM_NAME}} { return .{{" + type + "}} }"; } void GenLookup(const FieldDef& key_field, const std::string& struct_type) { code_.SetValue("STRUCTTYPE", struct_type); code_.SetValue("OFFSET", NumToString(key_field.value.offset)); std::string offset_reader = "Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: {{OFFSET}}, " "fbb: fbb)"; code_.SetValue("TYPE", GenType(key_field.value.type)); code_ += "fileprivate static func lookupByKey(vector: Int32, key: {{TYPE}}, " "fbb: " "ByteBuffer) -> {{STRUCTTYPE}}? {"; 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_ += "let tableOffset = Table.indirect(vector + 4 * (start + middle), fbb)"; if (IsString(key_field.value.type)) { code_ += "let comp = Table.compare(" + offset_reader + ", key, fbb: fbb)"; } else { code_ += "let comp = fbb.read(def: {{TYPE}}.self, position: Int(" + offset_reader + "))"; } 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 {{STRUCTTYPE}}(fbb, o: tableOffset)"; Outdent(); code_ += "}"; Outdent(); code_ += "}"; code_ += "return nil"; Outdent(); code_ += "}"; } 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)) { const auto bits = (1 << i) * 8; code_ += "private var padding" + NumToString((*id)++) + "__: UInt" + NumToString(bits) + " = 0"; } } FLATBUFFERS_ASSERT(!(field.padding & ~0xF)); } } 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; } } void GenOSVersionChecks() { if (parser_.advanced_features_ == reflection::AdvancedArrayFeatures) { code_ += "@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)"; } } std::string GenOffset() { return "let o = {{ACCESS}}.offset({{TABLEOFFSET}}.{{OFFSET}}); "; } std::string GenReaderMainBody(const std::string& optional = "") { return "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}" + optional + " { "; } std::string GenReader(const std::string& type, const std::string& at = "{{OFFSET}}") { return "{{ACCESS}}.readBuffer(of: {{" + type + "}}.self, at: " + at + ")"; } std::string GenConstructor(const std::string& offset) { return "{{VALUETYPE}}({{ACCESS}}.bb, o: " + offset + ") }"; } std::string GenMutate(const std::string& offset, const std::string& get_offset, bool isRaw = false) { return "@discardableResult {{ACCESS_TYPE}} func mutate({{FIELDVAR}}: " "{{VALUETYPE}}) -> Bool {" + get_offset + " return {{ACCESS}}.mutate({{FIELDVAR}}" + (isRaw ? ".rawValue" : "") + ", index: " + offset + ") }"; } std::string GenMutateArray() { return "{{ACCESS_TYPE}} func mutate({{FIELDVAR}}: {{VALUETYPE}}, at " "index: Int32) -> Bool { " + GenOffset() + "return {{ACCESS}}.directMutate({{FIELDVAR}}, index: " "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; } std::string GenEnumDefaultValue(const FieldDef& field) { const auto& value = field.value; FLATBUFFERS_ASSERT(value.type.enum_def); const auto& enum_def = *value.type.enum_def; // Vector of enum defaults are always "[]" which never works. const std::string constant = IsVector(value.type) ? "0" : value.constant; const auto enum_val = enum_def.FindByValue(constant); if (enum_val) { return "." + namer_.LegacySwiftVariant(*enum_val); } else { const auto& ev = **enum_def.Vals().begin(); return "." + namer_.LegacySwiftVariant(ev); } } std::string SwiftConstant(const FieldDef& field) { const auto default_value = StringIsFlatbufferNan(field.value.constant) ? ".nan" : StringIsFlatbufferPositiveInfinity(field.value.constant) ? ".infinity" : StringIsFlatbufferNegativeInfinity(field.value.constant) ? "-.infinity" : IsBool(field.value.type.base_type) ? ("0" == field.value.constant ? "false" : "true") : field.value.constant; return default_value; } std::string GenEnumConstructor(const std::string& at) { return "{{VALUETYPE}}(rawValue: " + GenReader("BASEVALUE", at) + ") "; } std::string ValidateFunc() { return "static func validateVersion() { FlatBuffersVersion_25_12_19() }"; } 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, should_consider_suffix); } 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: { const auto& sd = *type.struct_def; if (should_consider_suffix && !sd.fixed) { return namer_.NamespacedObjectType(sd); } return namer_.NamespacedType(sd); } case BASE_TYPE_UNION: default: return "FlatbuffersInitializable"; } } std::string GenTypeBasic(const Type& type) const { return GenTypeBasic(type, true); } void Indent() { code_.IncrementIdentLevel(); } void Outdent() { code_.DecrementIdentLevel(); } std::string GenTypeBasic(const Type& type, bool can_override) const { // clang-format off static const char * const swift_type[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, STYPE, ...) \ #STYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD }; // clang-format on if (can_override) { if (type.enum_def) return namer_.NamespacedType(*type.enum_def); if (type.base_type == BASE_TYPE_BOOL) return "Bool"; } return swift_type[static_cast(type.base_type)]; } std::string Mutable() const { return "_Mutable"; } IdlNamer namer_; }; } // namespace swift static bool GenerateSwift(const Parser& parser, const std::string& path, const std::string& file_name) { swift::SwiftGenerator generator(parser, path, file_name); return generator.generate(); } namespace { class SwiftCodeGenerator : public CodeGenerator { public: Status GenerateCode(const Parser& parser, const std::string& path, const std::string& filename) override { if (!GenerateSwift(parser, path, filename)) { return Status::ERROR; } return Status::OK; } Status GenerateCode(const uint8_t*, int64_t, const CodeGenOptions&) override { return Status::NOT_IMPLEMENTED; } Status GenerateGrpcCode(const Parser& parser, const std::string& path, const std::string& filename) override { if (!GenerateSwiftGRPC(parser, path, filename)) { return Status::ERROR; } return Status::OK; } Status GenerateMakeRule(const Parser& parser, const std::string& path, const std::string& filename, std::string& output) override { (void)parser; (void)path; (void)filename; (void)output; return Status::NOT_IMPLEMENTED; } Status GenerateRootFile(const Parser& parser, const std::string& path) override { (void)parser; (void)path; return Status::NOT_IMPLEMENTED; } bool IsSchemaOnly() const override { return true; } bool SupportsBfbsGeneration() const override { return false; } bool SupportsRootFileGeneration() const override { return false; } IDLOptions::Language Language() const override { return IDLOptions::kSwift; } std::string LanguageName() const override { return "Swift"; } }; } // namespace std::unique_ptr NewSwiftCodeGenerator() { return std::unique_ptr(new SwiftCodeGenerator()); } } // namespace flatbuffers