| /* | |
| * 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 <cctype> | |
| #include <unordered_set> | |
| #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 { | |
| 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=*/"_", | |
| /*filenames=*/Case::kKeep, | |
| /*directories=*/Case::kKeep, | |
| /*output_path=*/"", | |
| /*filename_suffix=*/"_generated", | |
| /*filename_extension=*/".swift" }; | |
| } | |
| std::set<std::string> 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", | |
| }; | |
| } | |
| inline std::string GenIndirect(const std::string &reading) { | |
| return "{{ACCESS}}.indirect(" + reading + ")"; | |
| } | |
| inline std::string GenArrayMainBody(const std::string &optional) { | |
| return "{{ACCESS_TYPE}} func {{FIELDMETHOD}}(at index: Int32) -> " | |
| "{{VALUETYPE}}" + | |
| optional + " { "; | |
| } | |
| class SwiftGenerator : public BaseGenerator { | |
| private: | |
| CodeWriter code_; | |
| std::unordered_set<std::string> 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", "VTOFFSET"); | |
| 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.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(); | |
| ++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); | |
| } | |
| } | |
| } | |
| const auto filename = GeneratedFileName(path_, file_name_, parser_.opts); | |
| const auto final_code = code_.ToString(); | |
| return SaveFile(filename.c_str(), final_code, false); | |
| } | |
| 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)); | |
| code_ += | |
| "{{ACCESS_TYPE}} struct {{STRUCTNAME}}: NativeStruct, 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<std::string> base_constructor; | |
| std::vector<std::string> 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); | |
| const auto type = GenType(field.value.type); | |
| code_.SetValue("FIELDVAR", field_var); | |
| 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 _{{FIELDVAR}}: " + valueType; | |
| const auto accessing_value = IsEnum(field.value.type) ? ".value" : ""; | |
| const auto is_bool = IsBool(field.value.type.base_type); | |
| const auto base_value = | |
| IsStruct(field.value.type) ? (type + "()") | |
| : is_bool ? ("0" == field.value.constant ? "false" : "true") | |
| : field.value.constant; | |
| 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_ += ""; | |
| BuildStructConstructor(struct_def); | |
| 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 (!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<T>(_ 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 BuildStructConstructor(const StructDef &struct_def) { | |
| code_ += "{{ACCESS_TYPE}} init(_ bb: ByteBuffer, o: Int32) {"; | |
| Indent(); | |
| code_ += "let {{ACCESS}} = Struct(bb: bb, position: o)"; | |
| 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("VALUETYPE", GenType(type)); | |
| code_.SetValue("OFFSET", NumToString(field.value.offset)); | |
| if (IsScalar(type.base_type)) { | |
| if (IsEnum(type)) | |
| code_.SetValue("VALUETYPE", GenTypeBasic(field.value.type, false)); | |
| code_ += | |
| "_{{FIELDVAR}} = {{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, " | |
| "at: {{OFFSET}})"; | |
| } else { | |
| code_ += | |
| "_{{FIELDVAR}} = {{VALUETYPE}}({{ACCESS}}.bb, o: " | |
| "{{ACCESS}}.postion + {{OFFSET}})"; | |
| } | |
| } | |
| Outdent(); | |
| code_ += "}\n"; | |
| } | |
| 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 (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(namer_.NamespacedType(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) { | |
| 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)); | |
| code_.SetValue("SHORT_STRUCTNAME", namer_.Method(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 &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 += " = "; | |
| const auto is_bool = IsBool(field.value.type.base_type); | |
| const auto constant = | |
| is_bool ? ("0" == field.value.constant ? "false" : "true") | |
| : field.value.constant; | |
| code += constant; | |
| } | |
| 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 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", namer_.Variable(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("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() : ""); | |
| code_ += | |
| "{{ACCESS_TYPE}} struct {{STRUCTNAME}}{{MUTABLE}}: FlatBufferObject\\"; | |
| 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_ += | |
| "{{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_ += "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<std::string> require_fields; | |
| std::vector<std::string> create_func_body; | |
| std::vector<std::string> 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,"; | |
| 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.buffer), Table.offset(Int32($0.o), vOffset: " | |
| "{{VOFFSET}}, fbb: fbb.buffer), fbb: fbb.buffer) < 0 } "; | |
| code_ += spacing + "return fbb.createVector(ofOffsets: off)"; | |
| Outdent(); | |
| code_ += "}"; | |
| GenLookup(*key_field, namer_.NamespacedType(struct_def)); | |
| } | |
| } | |
| void GenTableWriterFields(const FieldDef &field, | |
| std::vector<std::string> *create_body, | |
| std::vector<std::string> *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", field.value.constant); | |
| 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}}.p) }"; | |
| const auto default_value = | |
| IsEnum(field.value.type) | |
| ? (field.IsOptional() ? "nil" : GenEnumDefaultValue(field)) | |
| : field.value.constant; | |
| 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 = | |
| "0" == field.value.constant ? "false" : "true"; | |
| 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}}.p) }"; | |
| 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}}.p) }"; | |
| 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}}.p) }" | |
| : "offset: {{FIELDVAR}}, at: {{TABLEOFFSET}}.{{OFFSET}}.p) }"; | |
| 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", field.value.constant); | |
| 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" | |
| : ("0" == field.value.constant ? "false" : "true"); | |
| code_.SetValue("CONSTANT", default_value); | |
| code_.SetValue("VALUETYPE", "Bool"); | |
| code_ += GenReaderMainBody(optional) + "\\"; | |
| code_.SetValue("VALUETYPE", "Byte"); | |
| 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)) { | |
| 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}}.postion"); | |
| 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}}.postion")); | |
| break; | |
| case BASE_TYPE_STRING: { | |
| const auto default_string = "\"" + field.value.constant + "\""; | |
| 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}}.v) }"; | |
| 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}}<T: " | |
| "FlatbuffersInitializable>(type: " | |
| "T.Type) -> T" + | |
| is_required + " { " + GenOffset() + required_reader + | |
| "{{ACCESS}}.union(o) }"; | |
| break; | |
| default: FLATBUFFERS_ASSERT(0); | |
| } | |
| } | |
| void GenTableReaderVectorFields(const FieldDef &field) { | |
| std::string const_string = "return o == 0 ? {{CONSTANT}} : "; | |
| const auto vectortype = field.value.type.VectorType(); | |
| code_.SetValue("SIZE", NumToString(InlineSize(vectortype))); | |
| code_.SetValue("HAS_FIELDVAR", namer_.Variable("has", field)); | |
| code_ += "{{ACCESS_TYPE}} var {{HAS_FIELDVAR}}: Bool { " + GenOffset() + | |
| "return o == 0 ? false : true }"; | |
| code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}Count: Int32 { " + GenOffset() + | |
| "return o == 0 ? 0 : {{ACCESS}}.vector(count: o) }"; | |
| code_.SetValue("CONSTANT", IsScalar(vectortype.base_type) ? "0" : "nil"); | |
| const auto nullable = | |
| IsScalar(vectortype.base_type) && !IsEnum(vectortype) ? "" : "?"; | |
| if (vectortype.base_type != BASE_TYPE_UNION) { | |
| code_ += GenArrayMainBody(nullable) + GenOffset() + "\\"; | |
| } else { | |
| code_ += | |
| "{{ACCESS_TYPE}} func {{FIELDVAR}}<T: FlatbuffersInitializable>(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", "Bool"); | |
| } | |
| if (!IsEnum(vectortype)) code_ += const_string + "\\"; | |
| if (IsScalar(vectortype.base_type) && !IsEnum(vectortype) && | |
| !IsBool(field.value.type.base_type)) { | |
| code_ += | |
| "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: " | |
| "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; | |
| code_ += | |
| "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{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_ += | |
| "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: " | |
| "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; | |
| code_.SetValue("FIELDMETHOD", namer_.Method("mutable", field)); | |
| code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable()); | |
| code_ += GenArrayMainBody(nullable) + GenOffset() + const_string + | |
| GenConstructor("{{ACCESS}}.vector(at: o) + index * {{SIZE}}"); | |
| return; | |
| } | |
| if (IsString(vectortype)) { | |
| code_ += | |
| "{{ACCESS}}.directString(at: {{ACCESS}}.vector(at: o) + " | |
| "index * {{SIZE}}) }"; | |
| return; | |
| } | |
| if (IsEnum(vectortype)) { | |
| code_.SetValue("BASEVALUE", GenTypeBasic(vectortype, false)); | |
| code_ += "return o == 0 ? {{VALUETYPE}}" + GenEnumDefaultValue(field) + | |
| " : {{VALUETYPE}}(rawValue: {{ACCESS}}.directRead(of: " | |
| "{{BASEVALUE}}.self, offset: {{ACCESS}}.vector(at: o) + " | |
| "index * {{SIZE}})) }"; | |
| return; | |
| } | |
| if (vectortype.base_type == BASE_TYPE_UNION) { | |
| code_ += | |
| "{{ACCESS}}.directUnion({{ACCESS}}.vector(at: o) + " | |
| "index * {{SIZE}}) }"; | |
| return; | |
| } | |
| if (vectortype.base_type == BASE_TYPE_STRUCT && | |
| !field.value.type.struct_def->fixed) { | |
| code_ += GenConstructor( | |
| "{{ACCESS}}.indirect({{ACCESS}}.vector(at: o) + index * " | |
| "{{SIZE}})"); | |
| 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 GenerateCodingKeys(const StructDef &struct_def) { | |
| code_ += "enum CodingKeys: String, CodingKey {"; | |
| 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("RAWVALUENAME", field.name); | |
| code_.SetValue("FIELDVAR", namer_.Variable(field)); | |
| code_ += "case {{FIELDVAR}} = \"{{RAWVALUENAME}}\""; | |
| } | |
| Outdent(); | |
| code_ += "}"; | |
| } | |
| 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_ += | |
| "var enumsEncoder = container.nestedUnkeyedContainer(forKey: " | |
| ".{{FIELDVAR}}Type)"; | |
| code_ += | |
| "var contentEncoder = container.nestedUnkeyedContainer(forKey: " | |
| ".{{FIELDVAR}})"; | |
| code_ += "for index in 0..<{{FIELDVAR}}Count {"; | |
| Indent(); | |
| code_ += "guard let type = {{FIELDVAR}}Type(at: index) else { continue }"; | |
| code_ += "try enumsEncoder.encode(type)"; | |
| code_ += "switch 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}}(at: 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; | |
| const auto is_non_union_vector = | |
| (field.value.type.base_type == BASE_TYPE_ARRAY || | |
| field.value.type.base_type == BASE_TYPE_VECTOR) && | |
| field.value.type.VectorType().base_type != BASE_TYPE_UTYPE; | |
| code_.SetValue("FIELDVAR", namer_.Variable(field)); | |
| code_.SetValue("CONSTANT", field.value.constant); | |
| bool should_indent = true; | |
| if (is_non_union_vector) { | |
| code_ += "if {{FIELDVAR}}Count > 0 {"; | |
| } else if (IsEnum(type) && !field.IsOptional()) { | |
| code_.SetValue("CONSTANT", GenEnumDefaultValue(field)); | |
| code_ += "if {{FIELDVAR}} != {{CONSTANT}} {"; | |
| } 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()) { | |
| std::string default_value = | |
| "0" == field.value.constant ? "false" : "true"; | |
| code_.SetValue("CONSTANT", default_value); | |
| code_ += "if {{FIELDVAR}} != {{CONSTANT}} {"; | |
| } else { | |
| should_indent = false; | |
| } | |
| if (should_indent) Indent(); | |
| if (IsUnion(type) && !IsEnum(type)) { | |
| GenerateEncoderUnionBody(field); | |
| } else if (is_non_union_vector && | |
| (!IsScalar(type.VectorType().base_type) || | |
| IsEnum(type.VectorType()))) { | |
| code_ += | |
| "var contentEncoder = container.nestedUnkeyedContainer(forKey: " | |
| ".{{FIELDVAR}})"; | |
| code_ += "for index in 0..<{{FIELDVAR}}Count {"; | |
| Indent(); | |
| code_ += "guard let type = {{FIELDVAR}}(at: index) else { continue }"; | |
| code_ += "try contentEncoder.encode(type)"; | |
| Outdent(); | |
| code_ += "}"; | |
| } else { | |
| code_ += | |
| "try container.encodeIfPresent({{FIELDVAR}}, forKey: " | |
| ".{{FIELDVAR}})"; | |
| } | |
| if (should_indent) Outdent(); | |
| if (is_non_union_vector || | |
| (IsScalar(type.base_type) && !field.IsOptional())) { | |
| code_ += "}"; | |
| } | |
| } | |
| } | |
| void GenerateJSONEncodingAPIs(const StructDef &struct_def) { | |
| code_ += "extension {{STRUCTNAME}}: Encodable {"; | |
| Indent(); | |
| code_ += ""; | |
| if (struct_def.fields.vec.empty() == false) GenerateCodingKeys(struct_def); | |
| code_ += "{{ACCESS_TYPE}} func encode(to encoder: Encoder) throws {"; | |
| Indent(); | |
| if (struct_def.fields.vec.empty() == false) GenerateEncoderBody(struct_def); | |
| Outdent(); | |
| code_ += "}"; | |
| Outdent(); | |
| code_ += "}"; | |
| code_ += ""; | |
| } | |
| void GenerateVerifier(const StructDef &struct_def) { | |
| code_ += | |
| "{{ACCESS_TYPE}} static func verify<T>(_ 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}}.p, 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.p, " | |
| "unionField: {{TABLEOFFSET}}.{{OFFSET}}.p, 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<String>.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<Vector<" + | |
| GenerateNestedVerifierTypes(vector_type) + ", " + | |
| GenType(vector_type) + ">>"; | |
| } | |
| 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 auto is_private_access = enum_def.attributes.Lookup("private"); | |
| 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}}, {{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) { | |
| code_ += "\n"; | |
| code_ += "{{ACCESS_TYPE}} mutating func unpack() -> " + type_name + " {"; | |
| Indent(); | |
| code_ += "return " + type_name + "(&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: inout {{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 (IsStruct(field.value.type)) { | |
| code_ += "var _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) { | |
| code_ += "{{ACCESS_TYPE}} class " + | |
| namer_.NamespacedObjectType(struct_def) + ": NativeObject {\n"; | |
| std::vector<std::string> buffer_constructor; | |
| std::vector<std::string> 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: inout " + 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)); | |
| std::vector<std::string> 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 i in obj." + field + " {"; | |
| Indent(); | |
| code_ += "guard let _o = i else { continue }"; | |
| code_ += "builder.create(struct: _o)"; | |
| 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<std::string> &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<std::string> &buffer_constructor, | |
| std::vector<std::string> &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("var __" + field_var + " = _t." + | |
| field_field); | |
| buffer_constructor.push_back( | |
| "" + field_var + " = __" + 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() ? field.value.constant : ""; | |
| base_constructor.push_back(field_var + " = \"" + default_value + | |
| "\""); | |
| break; | |
| } | |
| if (field.IsDefault() && !field.IsRequired()) { | |
| std::string value = field.IsDefault() ? field.value.constant : "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 + " = " + | |
| field.value.constant); | |
| break; | |
| } | |
| if (IsEnum(field.value.type)) { | |
| const auto default_value = IsEnum(field.value.type) | |
| ? GenEnumDefaultValue(field) | |
| : field.value.constant; | |
| 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; | |
| std::string default_value = | |
| "0" == field.value.constant ? "false" : "true"; | |
| if (!field.IsOptional()) | |
| base_constructor.push_back(field_var + " = " + default_value); | |
| } | |
| } | |
| } | |
| } | |
| void BuildObjectAPIConstructorBodyVectors( | |
| const FieldDef &field, std::vector<std::string> &buffer_constructor, | |
| std::vector<std::string> &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 + " = []"); | |
| buffer_constructor.push_back("for index in 0..<_t." + field_field + | |
| "Count {"); | |
| base_constructor.push_back(field_var + " = []"); | |
| } | |
| switch (vectortype.base_type) { | |
| case BASE_TYPE_STRUCT: { | |
| code_.SetValue("VALUETYPE", GenType(vectortype, true)); | |
| code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}?]"; | |
| if (!vectortype.struct_def->fixed) { | |
| buffer_constructor.push_back(indentation + "var __v_ = _t." + | |
| field_field + "(at: index)"); | |
| buffer_constructor.push_back(indentation + field_var + | |
| ".append(__v_?.unpack())"); | |
| } else { | |
| buffer_constructor.push_back(indentation + field_var + ".append(_t." + | |
| field_var + "(at: index))"); | |
| } | |
| 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}}]"; | |
| if (IsEnum(vectortype) && vectortype.base_type != BASE_TYPE_UNION) { | |
| const auto default_value = IsEnum(field.value.type) | |
| ? GenEnumDefaultValue(field) | |
| : field.value.constant; | |
| buffer_constructor.push_back(indentation + field_var + ".append(_t." + | |
| field_field + "(at: index)!)"); | |
| break; | |
| } | |
| buffer_constructor.push_back(indentation + field_var + ".append(_t." + | |
| field_field + "(at: index))"); | |
| break; | |
| } | |
| } | |
| if (vectortype.base_type != BASE_TYPE_UTYPE) | |
| buffer_constructor.push_back("}"); | |
| } | |
| 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<std::string> &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 ? "(at: index" : ""; | |
| buffer_constructor.push_back(indentation + "switch _t." + field + "Type" + | |
| vector_reader + (is_vector ? ")" : "") + " {"); | |
| 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 + ":"); | |
| buffer_constructor.push_back( | |
| indentation + " var _v = _t." + field + (is_vector ? "" : "(") + | |
| vector_reader + (is_vector ? ", " : "") + "type: " + type + ".self)"); | |
| const auto constructor = | |
| ns_type + "Union(_v?.unpack(), type: ." + variant + ")"; | |
| buffer_constructor.push_back( | |
| indentation + " " + field + | |
| (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) { | |
| 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<int>(field.padding) & (1 << i)) { | |
| const auto bits = (1 << i) * 8; | |
| code_ += "private let padding" + NumToString((*id)++) + "__: UInt" + | |
| NumToString(bits) + " = 0"; | |
| } | |
| } | |
| FLATBUFFERS_ASSERT(!(field.padding & ~0xF)); | |
| } | |
| } | |
| void GenComment(const std::vector<std::string> &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 "{{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 GenEnumConstructor(const std::string &at) { | |
| return "{{VALUETYPE}}(rawValue: " + GenReader("BASEVALUE", at) + ") "; | |
| } | |
| std::string ValidateFunc() { | |
| return "static func validateVersion() { FlatBuffersVersion_2_0_0() }"; | |
| } | |
| 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<int>(type.base_type)]; | |
| } | |
| std::string Mutable() const { return "_Mutable"; } | |
| IdlNamer namer_; | |
| }; | |
| } // namespace swift | |
| 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 flatbuffers |