• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // independent from idl_parser, since this code is not needed for most clients
18 
19 #include "idl_gen_csharp.h"
20 
21 #include <unordered_set>
22 
23 #include "flatbuffers/code_generators.h"
24 #include "flatbuffers/flatbuffers.h"
25 #include "flatbuffers/idl.h"
26 #include "flatbuffers/util.h"
27 
28 namespace flatbuffers {
29 
30 static TypedFloatConstantGenerator CSharpFloatGen("Double.", "Single.", "NaN",
31                                                   "PositiveInfinity",
32                                                   "NegativeInfinity");
33 static CommentConfig comment_config = {
34   nullptr,
35   "///",
36   nullptr,
37 };
38 
39 namespace csharp {
40 class CSharpGenerator : public BaseGenerator {
41   struct FieldArrayLength {
42     std::string name;
43     int length;
44   };
45 
46  public:
CSharpGenerator(const Parser & parser,const std::string & path,const std::string & file_name)47   CSharpGenerator(const Parser &parser, const std::string &path,
48                   const std::string &file_name)
49       : BaseGenerator(parser, path, file_name,
50                       parser.opts.cs_global_alias ? "global::" : "", ".", "cs"),
51         cur_name_space_(nullptr) {
52     // clang-format off
53 
54     // List of keywords retrieved from here:
55     // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
56 
57     // One per line to ease comparisons to that list are easier
58 
59     static const char *const keywords[] = {
60       "abstract",
61       "as",
62       "base",
63       "bool",
64       "break",
65       "byte",
66       "case",
67       "catch",
68       "char",
69       "checked",
70       "class",
71       "const",
72       "continue",
73       "decimal",
74       "default",
75       "delegate",
76       "do",
77       "double",
78       "else",
79       "enum",
80       "event",
81       "explicit",
82       "extern",
83       "false",
84       "finally",
85       "fixed",
86       "float",
87       "for",
88       "foreach",
89       "goto",
90       "if",
91       "implicit",
92       "in",
93       "int",
94       "interface",
95       "internal",
96       "is",
97       "lock",
98       "long",
99       "namespace",
100       "new",
101       "null",
102       "object",
103       "operator",
104       "out",
105       "override",
106       "params",
107       "private",
108       "protected",
109       "public",
110       "readonly",
111       "ref",
112       "return",
113       "sbyte",
114       "sealed",
115       "short",
116       "sizeof",
117       "stackalloc",
118       "static",
119       "string",
120       "struct",
121       "switch",
122       "this",
123       "throw",
124       "true",
125       "try",
126       "typeof",
127       "uint",
128       "ulong",
129       "unchecked",
130       "unsafe",
131       "ushort",
132       "using",
133       "virtual",
134       "void",
135       "volatile",
136       "while",
137       nullptr,
138       // clang-format on
139     };
140 
141     for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
142   }
143 
144   CSharpGenerator &operator=(const CSharpGenerator &);
145 
generate()146   bool generate() {
147     std::string one_file_code;
148     cur_name_space_ = parser_.current_namespace_;
149 
150     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
151          ++it) {
152       std::string enumcode;
153       auto &enum_def = **it;
154       if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
155       GenEnum(enum_def, &enumcode, parser_.opts);
156       if (parser_.opts.one_file) {
157         one_file_code += enumcode;
158       } else {
159         if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
160                       false, parser_.opts))
161           return false;
162       }
163     }
164 
165     for (auto it = parser_.structs_.vec.begin();
166          it != parser_.structs_.vec.end(); ++it) {
167       std::string declcode;
168       auto &struct_def = **it;
169       if (!parser_.opts.one_file)
170         cur_name_space_ = struct_def.defined_namespace;
171       GenStruct(struct_def, &declcode, parser_.opts);
172       GenStructVerifier(struct_def, &declcode);
173       if (parser_.opts.one_file) {
174         one_file_code += declcode;
175       } else {
176         if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
177                       true, parser_.opts))
178           return false;
179       }
180     }
181 
182     if (parser_.opts.one_file) {
183       return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
184                       true, parser_.opts);
185     }
186     return true;
187   }
188 
189  private:
190   std::unordered_set<std::string> keywords_;
191 
EscapeKeyword(const std::string & name) const192   std::string EscapeKeyword(const std::string &name) const {
193     return keywords_.find(name) == keywords_.end() ? name : "@" + name;
194   }
195 
Name(const FieldDef & field) const196   std::string Name(const FieldDef &field) const {
197     std::string name = ConvertCase(field.name, Case::kUpperCamel);
198     return EscapeKeyword(name);
199   }
200 
Name(const Definition & def) const201   std::string Name(const Definition &def) const {
202     return EscapeKeyword(def.name);
203   }
204 
NamespacedName(const Definition & def) const205   std::string NamespacedName(const Definition &def) const {
206     return WrapInNameSpace(def.defined_namespace, Name(def));
207   }
208 
Name(const EnumVal & ev) const209   std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
210 
211   // Save out the generated code for a single class while adding
212   // declaration boilerplate.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes,const IDLOptions & options) const213   bool SaveType(const std::string &defname, const Namespace &ns,
214                 const std::string &classcode, bool needs_includes,
215                 const IDLOptions &options) const {
216     if (!classcode.length()) return true;
217 
218     std::string code =
219         "// <auto-generated>\n"
220         "//  " +
221         std::string(FlatBuffersGeneratedWarning()) +
222         "\n"
223         "// </auto-generated>\n\n";
224 
225     std::string namespace_name = FullNamespace(".", ns);
226     if (!namespace_name.empty()) {
227       code += "namespace " + namespace_name + "\n{\n\n";
228     }
229     if (needs_includes) {
230       code += "using global::System;\n";
231       code += "using global::System.Collections.Generic;\n";
232       code += "using global::Google.FlatBuffers;\n\n";
233     }
234     code += classcode;
235     if (!namespace_name.empty()) { code += "\n}\n"; }
236     auto filename = NamespaceDir(ns) + defname;
237     if (options.one_file) { filename += options.filename_suffix; }
238     filename +=
239         options.filename_extension.empty() ? ".cs" : options.filename_extension;
240     return SaveFile(filename.c_str(), code, false);
241   }
242 
CurrentNameSpace() const243   const Namespace *CurrentNameSpace() const { return cur_name_space_; }
244 
GenTypeBasic(const Type & type,bool enableLangOverrides) const245   std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
246     // clang-format off
247     static const char * const csharp_typename[] = {
248       #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, ...) \
249         #NTYPE,
250         FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
251       #undef FLATBUFFERS_TD
252     };
253     // clang-format on
254 
255     if (enableLangOverrides) {
256       if (IsEnum(type)) return NamespacedName(*type.enum_def);
257       if (type.base_type == BASE_TYPE_STRUCT) {
258         return "Offset<" + NamespacedName(*type.struct_def) + ">";
259       }
260     }
261 
262     return csharp_typename[type.base_type];
263   }
264 
GenTypeBasic(const Type & type) const265   inline std::string GenTypeBasic(const Type &type) const {
266     return GenTypeBasic(type, true);
267   }
268 
GenTypePointer(const Type & type) const269   std::string GenTypePointer(const Type &type) const {
270     switch (type.base_type) {
271       case BASE_TYPE_STRING: return "string";
272       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
273       case BASE_TYPE_STRUCT: return NamespacedName(*type.struct_def);
274       case BASE_TYPE_UNION: return "TTable";
275       default: return "Table";
276     }
277   }
278 
GenTypeGet(const Type & type) const279   std::string GenTypeGet(const Type &type) const {
280     return IsScalar(type.base_type)
281                ? GenTypeBasic(type)
282                : (IsArray(type) ? GenTypeGet(type.VectorType())
283                                 : GenTypePointer(type));
284   }
285 
GenOffsetType(const StructDef & struct_def) const286   std::string GenOffsetType(const StructDef &struct_def) const {
287     return "Offset<" + NamespacedName(struct_def) + ">";
288   }
289 
GenOffsetConstruct(const StructDef & struct_def,const std::string & variable_name) const290   std::string GenOffsetConstruct(const StructDef &struct_def,
291                                  const std::string &variable_name) const {
292     return "new Offset<" + NamespacedName(struct_def) + ">(" + variable_name +
293            ")";
294   }
295 
296   // Casts necessary to correctly read serialized data
DestinationCast(const Type & type) const297   std::string DestinationCast(const Type &type) const {
298     if (IsSeries(type)) {
299       return DestinationCast(type.VectorType());
300     } else {
301       if (IsEnum(type)) return "(" + NamespacedName(*type.enum_def) + ")";
302     }
303     return "";
304   }
305 
306   // Cast statements for mutator method parameters.
307   // In Java, parameters representing unsigned numbers need to be cast down to
308   // their respective type. For example, a long holding an unsigned int value
309   // would be cast down to int before being put onto the buffer. In C#, one cast
310   // directly cast an Enum to its underlying type, which is essential before
311   // putting it onto the buffer.
SourceCast(const Type & type,const bool isOptional=false) const312   std::string SourceCast(const Type &type,
313                          const bool isOptional = false) const {
314     if (IsSeries(type)) {
315       return SourceCast(type.VectorType());
316     } else {
317       if (IsEnum(type))
318         return "(" + GenTypeBasic(type, false) + (isOptional ? "?" : "") + ")";
319     }
320     return "";
321   }
322 
SourceCastBasic(const Type & type,const bool isOptional) const323   std::string SourceCastBasic(const Type &type, const bool isOptional) const {
324     return IsScalar(type.base_type) ? SourceCast(type, isOptional) : "";
325   }
326 
GenEnumDefaultValue(const FieldDef & field) const327   std::string GenEnumDefaultValue(const FieldDef &field) const {
328     auto &value = field.value;
329     FLATBUFFERS_ASSERT(value.type.enum_def);
330     auto &enum_def = *value.type.enum_def;
331     auto enum_val = enum_def.FindByValue(value.constant);
332     return enum_val ? (NamespacedName(enum_def) + "." + Name(*enum_val))
333                     : value.constant;
334   }
335 
GenDefaultValue(const FieldDef & field,bool enableLangOverrides) const336   std::string GenDefaultValue(const FieldDef &field,
337                               bool enableLangOverrides) const {
338     // If it is an optional scalar field, the default is null
339     if (field.IsScalarOptional()) { return "null"; }
340 
341     auto &value = field.value;
342     if (enableLangOverrides) {
343       // handles both enum case and vector of enum case
344       if (value.type.enum_def != nullptr &&
345           value.type.base_type != BASE_TYPE_UNION) {
346         return GenEnumDefaultValue(field);
347       }
348     }
349 
350     auto longSuffix = "";
351     switch (value.type.base_type) {
352       case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
353       case BASE_TYPE_ULONG: return value.constant;
354       case BASE_TYPE_UINT:
355       case BASE_TYPE_LONG: return value.constant + longSuffix;
356       default:
357         if (IsFloat(value.type.base_type))
358           return CSharpFloatGen.GenFloatConstant(field);
359         else
360           return value.constant;
361     }
362   }
363 
GenDefaultValue(const FieldDef & field) const364   std::string GenDefaultValue(const FieldDef &field) const {
365     return GenDefaultValue(field, true);
366   }
367 
GenDefaultValueBasic(const FieldDef & field,bool enableLangOverrides) const368   std::string GenDefaultValueBasic(const FieldDef &field,
369                                    bool enableLangOverrides) const {
370     auto &value = field.value;
371     if (!IsScalar(value.type.base_type)) {
372       if (enableLangOverrides) {
373         switch (value.type.base_type) {
374           case BASE_TYPE_STRING: return "default(StringOffset)";
375           case BASE_TYPE_STRUCT:
376             return "default(Offset<" + NamespacedName(*value.type.struct_def) +
377                    ">)";
378           case BASE_TYPE_VECTOR: return "default(VectorOffset)";
379           default: break;
380         }
381       }
382       return "0";
383     }
384     return GenDefaultValue(field, enableLangOverrides);
385   }
386 
GenDefaultValueBasic(const FieldDef & field) const387   std::string GenDefaultValueBasic(const FieldDef &field) const {
388     return GenDefaultValueBasic(field, true);
389   }
390 
GenEnum(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const391   void GenEnum(EnumDef &enum_def, std::string *code_ptr,
392                const IDLOptions &opts) const {
393     std::string &code = *code_ptr;
394     if (enum_def.generated) return;
395 
396     // Generate enum definitions of the form:
397     // public static (final) int name = value;
398     // In Java, we use ints rather than the Enum feature, because we want them
399     // to map directly to how they're used in C/C++ and file formats.
400     // That, and Java Enums are expensive, and not universally liked.
401     GenComment(enum_def.doc_comment, code_ptr, &comment_config);
402 
403     if (opts.cs_gen_json_serializer && opts.generate_object_based_api) {
404       code +=
405           "[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters."
406           "StringEnumConverter))]\n";
407     }
408     // In C# this indicates enumeration values can be treated as bit flags.
409     if (enum_def.attributes.Lookup("bit_flags")) {
410       code += "[System.FlagsAttribute]\n";
411     }
412     if (enum_def.attributes.Lookup("private")) {
413       code += "internal ";
414     } else {
415       code += "public ";
416     }
417     code += "enum " + Name(enum_def);
418     code += " : " + GenTypeBasic(enum_def.underlying_type, false);
419     code += "\n{\n";
420     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
421       auto &ev = **it;
422       GenComment(ev.doc_comment, code_ptr, &comment_config, "  ");
423       code += "  ";
424       code += Name(ev) + " = ";
425       code += enum_def.ToString(ev);
426       code += ",\n";
427     }
428     // Close the class
429     code += "};\n\n";
430 
431     if (opts.generate_object_based_api) {
432       GenEnum_ObjectAPI(enum_def, code_ptr, opts);
433     }
434 
435     if (enum_def.is_union) {
436       code += GenUnionVerify(enum_def.underlying_type);
437     }
438   }
439 
HasUnionStringValue(const EnumDef & enum_def) const440   bool HasUnionStringValue(const EnumDef &enum_def) const {
441     if (!enum_def.is_union) return false;
442     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
443       auto &val = **it;
444       if (IsString(val.union_type)) { return true; }
445     }
446     return false;
447   }
448 
449   // Returns the function name that is able to read a value of the given type.
GenGetter(const Type & type) const450   std::string GenGetter(const Type &type) const {
451     switch (type.base_type) {
452       case BASE_TYPE_STRING: return "__p.__string";
453       case BASE_TYPE_STRUCT: return "__p.__struct";
454       case BASE_TYPE_UNION: return "__p.__union";
455       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
456       case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
457       default: {
458         std::string getter = "__p.bb.Get";
459         if (type.base_type == BASE_TYPE_BOOL) {
460           getter = "0!=" + getter;
461         } else if (GenTypeBasic(type, false) != "byte") {
462           getter += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel);
463         }
464         return getter;
465       }
466     }
467   }
468 
GetObjectConstructor(flatbuffers::StructDef & struct_def,const std::string & data_buffer,const std::string & offset) const469   std::string GetObjectConstructor(flatbuffers::StructDef &struct_def,
470                                    const std::string &data_buffer,
471                                    const std::string &offset) const {
472     // Use the generated type directly, to properly handle default values that
473     // might not be written to the buffer.
474     return "new " + Name(struct_def) + "().__assign(" + offset + ", " +
475            data_buffer + ")";
476   }
477 
478   // Returns the function name that is able to read a value of the given type.
GenGetterForLookupByKey(flatbuffers::StructDef & struct_def,flatbuffers::FieldDef * key_field,const std::string & data_buffer,const std::string & offset) const479   std::string GenGetterForLookupByKey(flatbuffers::StructDef &struct_def,
480                                       flatbuffers::FieldDef *key_field,
481                                       const std::string &data_buffer,
482                                       const std::string &offset) const {
483     // Use the generated type directly, to properly handle default values that
484     // might not be written to the buffer.
485     auto name = Name(*key_field);
486     if (name == struct_def.name) { name += "_"; }
487     return GetObjectConstructor(struct_def, data_buffer, offset) + "." + name;
488   }
489 
490   // Direct mutation is only allowed for scalar fields.
491   // Hence a setter method will only be generated for such fields.
GenSetter(const Type & type) const492   std::string GenSetter(const Type &type) const {
493     if (IsScalar(type.base_type)) {
494       std::string setter = "__p.bb.Put";
495       if (GenTypeBasic(type, false) != "byte" &&
496           type.base_type != BASE_TYPE_BOOL) {
497         setter += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel);
498       }
499       return setter;
500     } else {
501       return "";
502     }
503   }
504 
505   // Returns the method name for use with add/put calls.
GenMethod(const Type & type) const506   std::string GenMethod(const Type &type) const {
507     return IsScalar(type.base_type)
508                ? ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel)
509                : (IsStruct(type) ? "Struct" : "Offset");
510   }
511 
512   // Recursively generate arguments for a constructor, to deal with nested
513   // structs.
GenStructArgs(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t array_count=0) const514   void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
515                      const char *nameprefix, size_t array_count = 0) const {
516     std::string &code = *code_ptr;
517     for (auto it = struct_def.fields.vec.begin();
518          it != struct_def.fields.vec.end(); ++it) {
519       auto &field = **it;
520       const auto &field_type = field.value.type;
521       const auto array_field = IsArray(field_type);
522       const auto &type = array_field ? field_type.VectorType() : field_type;
523       const auto array_cnt = array_field ? (array_count + 1) : array_count;
524       if (IsStruct(type)) {
525         // Generate arguments for a struct inside a struct. To ensure names
526         // don't clash, and to make it obvious these arguments are constructing
527         // a nested struct, prefix the name with the field name.
528         GenStructArgs(*field_type.struct_def, code_ptr,
529                       (nameprefix + (EscapeKeyword(field.name) + "_")).c_str(),
530                       array_cnt);
531       } else {
532         code += ", ";
533         code += GenTypeBasic(type);
534         if (field.IsScalarOptional()) { code += "?"; }
535         if (array_cnt > 0) {
536           code += "[";
537           for (size_t i = 1; i < array_cnt; i++) code += ",";
538           code += "]";
539         }
540         code += " ";
541         code += nameprefix;
542         code += Name(field);
543       }
544     }
545   }
546 
547   // Recusively generate struct construction statements of the form:
548   // builder.putType(name);
549   // and insert manual padding.
GenStructBody(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t index=0,bool in_array=false) const550   void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
551                      const char *nameprefix, size_t index = 0,
552                      bool in_array = false) const {
553     std::string &code = *code_ptr;
554     std::string indent((index + 1) * 2, ' ');
555     code += indent + "  builder.Prep(";
556     code += NumToString(struct_def.minalign) + ", ";
557     code += NumToString(struct_def.bytesize) + ");\n";
558     for (auto it = struct_def.fields.vec.rbegin();
559          it != struct_def.fields.vec.rend(); ++it) {
560       auto &field = **it;
561       const auto &field_type = field.value.type;
562       if (field.padding) {
563         code += indent + "  builder.Pad(";
564         code += NumToString(field.padding) + ");\n";
565       }
566       if (IsStruct(field_type)) {
567         GenStructBody(*field_type.struct_def, code_ptr,
568                       (nameprefix + (field.name + "_")).c_str(), index,
569                       in_array);
570       } else {
571         const auto &type =
572             IsArray(field_type) ? field_type.VectorType() : field_type;
573         const auto index_var = "_idx" + NumToString(index);
574         if (IsArray(field_type)) {
575           code += indent + "  for (int " + index_var + " = ";
576           code += NumToString(field_type.fixed_length);
577           code += "; " + index_var + " > 0; " + index_var + "--) {\n";
578           in_array = true;
579         }
580         if (IsStruct(type)) {
581           GenStructBody(*field_type.struct_def, code_ptr,
582                         (nameprefix + (field.name + "_")).c_str(), index + 1,
583                         in_array);
584         } else {
585           code += IsArray(field_type) ? "  " : "";
586           code += indent + "  builder.Put";
587           code += GenMethod(type) + "(";
588           code += SourceCast(type);
589           auto argname = nameprefix + Name(field);
590           code += argname;
591           size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
592           if (array_cnt > 0) {
593             code += "[";
594             for (size_t i = 0; in_array && i < array_cnt; i++) {
595               code += "_idx" + NumToString(i) + "-1";
596               if (i != (array_cnt - 1)) code += ",";
597             }
598             code += "]";
599           }
600           code += ");\n";
601         }
602         if (IsArray(field_type)) { code += indent + "  }\n"; }
603       }
604     }
605   }
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const606   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
607                               const char *num = nullptr) const {
608     std::string key_offset =
609         "Table.__offset(" + NumToString(key_field->value.offset) + ", ";
610     if (num) {
611       key_offset += num;
612       key_offset += ".Value, builder.DataBuffer)";
613     } else {
614       key_offset += "bb.Length";
615       key_offset += " - tableOffset, bb)";
616     }
617     return key_offset;
618   }
619 
GenKeyGetter(flatbuffers::StructDef & struct_def,flatbuffers::FieldDef * key_field) const620   std::string GenKeyGetter(flatbuffers::StructDef &struct_def,
621                            flatbuffers::FieldDef *key_field) const {
622     // Get the getter for the key of the struct.
623     return GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
624                                    "builder.DataBuffer.Length - o1.Value") +
625            ".CompareTo(" +
626            GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
627                                    "builder.DataBuffer.Length - o2.Value") +
628            ")";
629   }
630 
631   // Get the value of a table verification function start
GetStartOfTableVerifier(const StructDef & struct_def,std::string * code_ptr)632   void GetStartOfTableVerifier(const StructDef &struct_def,
633                                std::string *code_ptr) {
634     std::string &code = *code_ptr;
635     code += "\n";
636     code += "static public class " + struct_def.name + "Verify\n";
637     code += "{\n";
638     code += "  static public bool Verify";
639     code += "(Google.FlatBuffers.Verifier verifier, uint tablePos)\n";
640     code += "  {\n";
641     code += "    return verifier.VerifyTableStart(tablePos)\n";
642   }
643 
644   // Get the value of a table verification function end
GetEndOfTableVerifier(std::string * code_ptr)645   void GetEndOfTableVerifier(std::string *code_ptr) {
646     std::string &code = *code_ptr;
647     code += "      && verifier.VerifyTableEnd(tablePos);\n";
648     code += "  }\n";
649     code += "}\n";
650   }
651 
GetNestedFlatBufferName(const FieldDef & field)652   std::string GetNestedFlatBufferName(const FieldDef &field) {
653     std::string name;
654     if (field.nested_flatbuffer) {
655       name = NamespacedName(*field.nested_flatbuffer);
656     } else {
657       name = "";
658     }
659     return name;
660   }
661 
662   // Generate the code to call the appropriate Verify function(s) for a field.
GenVerifyCall(CodeWriter & code_,const FieldDef & field,const char * prefix)663   void GenVerifyCall(CodeWriter &code_, const FieldDef &field,
664                      const char *prefix) {
665     code_.SetValue("PRE", prefix);
666     code_.SetValue("NAME", ConvertCase(field.name, Case::kUpperCamel));
667     code_.SetValue("REQUIRED", field.IsRequired() ? "Required" : "");
668     code_.SetValue("REQUIRED_FLAG", field.IsRequired() ? "true" : "false");
669     code_.SetValue("TYPE", GenTypeGet(field.value.type));
670     code_.SetValue("INLINESIZE", NumToString(InlineSize(field.value.type)));
671     code_.SetValue("OFFSET", NumToString(field.value.offset));
672 
673     if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) {
674       code_.SetValue("ALIGN", NumToString(InlineAlignment(field.value.type)));
675       code_ +=
676           "{{PRE}}      && verifier.VerifyField(tablePos, "
677           "{{OFFSET}} /*{{NAME}}*/, {{INLINESIZE}} /*{{TYPE}}*/, {{ALIGN}}, "
678           "{{REQUIRED_FLAG}})";
679     } else {
680       // TODO - probably code below should go to this 'else' - code_ +=
681       // "{{PRE}}VerifyOffset{{REQUIRED}}(verifier, {{OFFSET}})\\";
682     }
683 
684     switch (field.value.type.base_type) {
685       case BASE_TYPE_UNION: {
686         auto union_name = NamespacedName(*field.value.type.enum_def);
687         code_.SetValue("ENUM_NAME1", field.value.type.enum_def->name);
688         code_.SetValue("ENUM_NAME", union_name);
689         code_.SetValue("SUFFIX", UnionTypeFieldSuffix());
690         // Caution: This construction assumes, that UNION type id element has
691         // been created just before union data and its offset precedes union.
692         // Such assumption is common in flatbuffer implementation
693         code_.SetValue("TYPE_ID_OFFSET",
694                        NumToString(field.value.offset - sizeof(voffset_t)));
695         code_ +=
696             "{{PRE}}      && verifier.VerifyUnion(tablePos, "
697             "{{TYPE_ID_OFFSET}}, "
698             "{{OFFSET}} /*{{NAME}}*/, {{ENUM_NAME}}Verify.Verify, "
699             "{{REQUIRED_FLAG}})";
700         break;
701       }
702       case BASE_TYPE_STRUCT: {
703         if (!field.value.type.struct_def->fixed) {
704           code_ +=
705               "{{PRE}}      && verifier.VerifyTable(tablePos, "
706               "{{OFFSET}} /*{{NAME}}*/, {{TYPE}}Verify.Verify, "
707               "{{REQUIRED_FLAG}})";
708         }
709         break;
710       }
711       case BASE_TYPE_STRING: {
712         code_ +=
713             "{{PRE}}      && verifier.VerifyString(tablePos, "
714             "{{OFFSET}} /*{{NAME}}*/, {{REQUIRED_FLAG}})";
715         break;
716       }
717       case BASE_TYPE_VECTOR: {
718         switch (field.value.type.element) {
719           case BASE_TYPE_STRING: {
720             code_ +=
721                 "{{PRE}}      && verifier.VerifyVectorOfStrings(tablePos, "
722                 "{{OFFSET}} /*{{NAME}}*/, {{REQUIRED_FLAG}})";
723             break;
724           }
725           case BASE_TYPE_STRUCT: {
726             if (!field.value.type.struct_def->fixed) {
727               code_ +=
728                   "{{PRE}}      && verifier.VerifyVectorOfTables(tablePos, "
729                   "{{OFFSET}} /*{{NAME}}*/, {{TYPE}}Verify.Verify, "
730                   "{{REQUIRED_FLAG}})";
731             } else {
732               code_.SetValue(
733                   "VECTOR_ELEM_INLINESIZE",
734                   NumToString(InlineSize(field.value.type.VectorType())));
735               code_ +=
736                   "{{PRE}}      && "
737                   "verifier.VerifyVectorOfData(tablePos, "
738                   "{{OFFSET}} /*{{NAME}}*/, {{VECTOR_ELEM_INLINESIZE}} "
739                   "/*{{TYPE}}*/, {{REQUIRED_FLAG}})";
740             }
741             break;
742           }
743           case BASE_TYPE_UNION: {
744             // Vectors of unions are not yet supported for go
745             break;
746           }
747           default:
748             // Generate verifier for vector of data.
749             // It may be either nested flatbuffer of just vector of bytes
750             auto nfn = GetNestedFlatBufferName(field);
751             if (!nfn.empty()) {
752               code_.SetValue("CPP_NAME", nfn);
753               // FIXME: file_identifier.
754               code_ +=
755                   "{{PRE}}      && verifier.VerifyNestedBuffer(tablePos, "
756                   "{{OFFSET}} /*{{NAME}}*/, {{CPP_NAME}}Verify.Verify, "
757                   "{{REQUIRED_FLAG}})";
758             } else if (field.flexbuffer) {
759               code_ +=
760                   "{{PRE}}      && verifier.VerifyNestedBuffer(tablePos, "
761                   "{{OFFSET}} /*{{NAME}}*/, null, {{REQUIRED_FLAG}})";
762             } else {
763               code_.SetValue(
764                   "VECTOR_ELEM_INLINESIZE",
765                   NumToString(InlineSize(field.value.type.VectorType())));
766               code_ +=
767                   "{{PRE}}      && verifier.VerifyVectorOfData(tablePos, "
768                   "{{OFFSET}} /*{{NAME}}*/, {{VECTOR_ELEM_INLINESIZE}} "
769                   "/*{{TYPE}}*/, {{REQUIRED_FLAG}})";
770             }
771             break;
772         }
773 
774         break;
775       }
776       default: {
777         break;
778       }
779     }
780   }
781 
782   // Generate table constructors, conditioned on its members' types.
GenTableVerifier(const StructDef & struct_def,std::string * code_ptr)783   void GenTableVerifier(const StructDef &struct_def, std::string *code_ptr) {
784     CodeWriter code_;
785 
786     GetStartOfTableVerifier(struct_def, code_ptr);
787 
788     // Generate struct fields accessors
789     for (auto it = struct_def.fields.vec.begin();
790          it != struct_def.fields.vec.end(); ++it) {
791       auto &field = **it;
792       if (field.deprecated) continue;
793 
794       GenVerifyCall(code_, field, "");
795     }
796 
797     *code_ptr += code_.ToString();
798 
799     GetEndOfTableVerifier(code_ptr);
800   }
801 
802   // Generate struct or table methods.
GenStructVerifier(const StructDef & struct_def,std::string * code_ptr)803   void GenStructVerifier(const StructDef &struct_def, std::string *code_ptr) {
804     if (struct_def.generated) return;
805 
806     // cur_name_space_ = struct_def.defined_namespace;
807 
808     // Generate verifiers
809     if (struct_def.fixed) {
810       // Fixed size structures do not require table members
811       // verification - instead structure size is verified using VerifyField
812     } else {
813       // Create table verification function
814       GenTableVerifier(struct_def, code_ptr);
815     }
816   }
817 
GenStruct(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const818   void GenStruct(StructDef &struct_def, std::string *code_ptr,
819                  const IDLOptions &opts) const {
820     if (struct_def.generated) return;
821     std::string &code = *code_ptr;
822 
823     // Generate a struct accessor class, with methods of the form:
824     // public type name() { return bb.getType(i + offset); }
825     // or for tables of the form:
826     // public type name() {
827     //   int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
828     // }
829     GenComment(struct_def.doc_comment, code_ptr, &comment_config);
830     if (struct_def.attributes.Lookup("private")) {
831       code += "internal ";
832     } else {
833       code += "public ";
834     }
835     if (struct_def.attributes.Lookup("csharp_partial")) {
836       // generate a partial class for this C# struct/table
837       code += "partial ";
838     }
839     code += "struct " + struct_def.name;
840     code += " : IFlatbufferObject";
841     code += "\n{\n";
842     code += "  private ";
843     code += struct_def.fixed ? "Struct" : "Table";
844     code += " __p;\n";
845 
846     code += "  public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
847 
848     if (!struct_def.fixed) {
849       // Generate version check method.
850       // Force compile time error if not using the same version runtime.
851       code += "  public static void ValidateVersion() {";
852       code += " FlatBufferConstants.";
853       code += "FLATBUFFERS_25_1_24(); ";
854       code += "}\n";
855 
856       // Generate a special accessor for the table that when used as the root
857       // of a FlatBuffer
858       std::string method_name = "GetRootAs" + struct_def.name;
859       std::string method_signature =
860           "  public static " + struct_def.name + " " + method_name;
861 
862       // create convenience method that doesn't require an existing object
863       code += method_signature + "(ByteBuffer _bb) ";
864       code += "{ return " + method_name + "(_bb, new " + struct_def.name +
865               "()); }\n";
866 
867       // create method that allows object reuse
868       code +=
869           method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
870       code += "return (obj.__assign(_bb.GetInt(_bb.Position";
871       code += ") + _bb.Position";
872       code += ", _bb)); }\n";
873       if (parser_.root_struct_def_ == &struct_def) {
874         if (parser_.file_identifier_.length()) {
875           // Check if a buffer has the identifier.
876           code += "  public static ";
877           code += "bool " + struct_def.name;
878           code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
879           code += "Table.__has_identifier(_bb, \"";
880           code += parser_.file_identifier_;
881           code += "\"); }\n";
882         }
883 
884         // Generate the Verify method that checks if a ByteBuffer is save to
885         // access
886         code += "  public static ";
887         code += "bool Verify" + struct_def.name + "(ByteBuffer _bb) {";
888         code += "Google.FlatBuffers.Verifier verifier = new ";
889         code += "Google.FlatBuffers.Verifier(_bb); ";
890         code += "return verifier.VerifyBuffer(\"";
891         code += parser_.file_identifier_;
892         code += "\", false, " + struct_def.name + "Verify.Verify);";
893         code += " }\n";
894       }
895     }
896 
897     // Generate the __init method that sets the field in a pre-existing
898     // accessor object. This is to allow object reuse.
899     code += "  public void __init(int _i, ByteBuffer _bb) ";
900     code += "{ ";
901     code += "__p = new ";
902     code += struct_def.fixed ? "Struct" : "Table";
903     code += "(_i, _bb); ";
904     code += "}\n";
905     code +=
906         "  public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
907     code += "{ __init(_i, _bb); return this; }\n\n";
908     for (auto it = struct_def.fields.vec.begin();
909          it != struct_def.fields.vec.end(); ++it) {
910       auto &field = **it;
911       if (field.deprecated) continue;
912       GenComment(field.doc_comment, code_ptr, &comment_config, "  ");
913       std::string type_name = GenTypeGet(field.value.type);
914       std::string type_name_dest = GenTypeGet(field.value.type);
915       std::string conditional_cast = "";
916       std::string optional = "";
917       if (!struct_def.fixed &&
918           (field.value.type.base_type == BASE_TYPE_STRUCT ||
919            field.value.type.base_type == BASE_TYPE_UNION ||
920            (IsVector(field.value.type) &&
921             (field.value.type.element == BASE_TYPE_STRUCT ||
922              field.value.type.element == BASE_TYPE_UNION)))) {
923         optional = "?";
924         conditional_cast = "(" + type_name_dest + optional + ")";
925       }
926       if (field.IsScalarOptional()) { optional = "?"; }
927       std::string dest_mask = "";
928       std::string dest_cast = DestinationCast(field.value.type);
929       std::string src_cast = SourceCast(field.value.type);
930       std::string field_name_camel = Name(field);
931       if (field_name_camel == struct_def.name) { field_name_camel += "_"; }
932       std::string method_start =
933           "  public " + type_name_dest + optional + " " + field_name_camel;
934       std::string obj = "(new " + type_name + "())";
935 
936       // Most field accessors need to retrieve and test the field offset first,
937       // this is the prefix code for that:
938       auto offset_prefix =
939           IsArray(field.value.type)
940               ? " { return "
941               : (" { int o = __p.__offset(" + NumToString(field.value.offset) +
942                  "); return o != 0 ? ");
943       // Generate the accessors that don't do object reuse.
944       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
945       } else if (IsVector(field.value.type) &&
946                  field.value.type.element == BASE_TYPE_STRUCT) {
947       } else if (field.value.type.base_type == BASE_TYPE_UNION ||
948                  (IsVector(field.value.type) &&
949                   field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
950         method_start += "<TTable>";
951         type_name = type_name_dest;
952       }
953       std::string getter = dest_cast + GenGetter(field.value.type);
954       code += method_start;
955       std::string default_cast = "";
956       // only create default casts for c# scalars or vectors of scalars
957       if ((IsScalar(field.value.type.base_type) ||
958            (IsVector(field.value.type) &&
959             IsScalar(field.value.type.element)))) {
960         // For scalars, default value will be returned by GetDefaultValue().
961         // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
962         // that doesn't need to be casted. However, default values for enum
963         // elements of vectors are integer literals ("0") and are still casted
964         // for clarity.
965         // If the scalar is optional and enum, we still need the cast.
966         if ((field.value.type.enum_def == nullptr ||
967              IsVector(field.value.type)) ||
968             (IsEnum(field.value.type) && field.IsScalarOptional())) {
969           default_cast = "(" + type_name_dest + optional + ")";
970         }
971       }
972       std::string member_suffix = "; ";
973       if (IsScalar(field.value.type.base_type)) {
974         code += " { get";
975         member_suffix += "} ";
976         if (struct_def.fixed) {
977           code += " { return " + getter;
978           code += "(__p.bb_pos + ";
979           code += NumToString(field.value.offset) + ")";
980           code += dest_mask;
981         } else {
982           code += offset_prefix + getter;
983           code += "(o + __p.bb_pos)" + dest_mask;
984           code += " : " + default_cast;
985           code += GenDefaultValue(field);
986         }
987       } else {
988         switch (field.value.type.base_type) {
989           case BASE_TYPE_STRUCT:
990             code += " { get";
991             member_suffix += "} ";
992             if (struct_def.fixed) {
993               code += " { return " + obj + ".__assign(" + "__p.";
994               code += "bb_pos + " + NumToString(field.value.offset) + ", ";
995               code += "__p.bb)";
996             } else {
997               code += offset_prefix + conditional_cast;
998               code += obj + ".__assign(";
999               code += field.value.type.struct_def->fixed
1000                           ? "o + __p.bb_pos"
1001                           : "__p.__indirect(o + __p.bb_pos)";
1002               code += ", __p.bb) : null";
1003             }
1004             break;
1005           case BASE_TYPE_STRING:
1006             code += " { get";
1007             member_suffix += "} ";
1008             code += offset_prefix + getter + "(o + " + "__p.";
1009             code += "bb_pos) : null";
1010             break;
1011           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();  // fall thru
1012           case BASE_TYPE_VECTOR: {
1013             auto vectortype = field.value.type.VectorType();
1014             if (vectortype.base_type == BASE_TYPE_UNION) {
1015               conditional_cast = "(TTable?)";
1016               getter += "<TTable>";
1017             }
1018             code += "(";
1019             if (vectortype.base_type == BASE_TYPE_STRUCT) {
1020               getter = obj + ".__assign";
1021             } else if (vectortype.base_type == BASE_TYPE_UNION) {
1022             }
1023             code += "int j)";
1024             const auto body = offset_prefix + conditional_cast + getter + "(";
1025             if (vectortype.base_type == BASE_TYPE_UNION) {
1026               code += " where TTable : struct, IFlatbufferObject" + body;
1027             } else {
1028               code += body;
1029             }
1030             std::string index = "__p.";
1031             if (IsArray(field.value.type)) {
1032               index += "bb_pos + " + NumToString(field.value.offset) + " + ";
1033             } else {
1034               index += "__vector(o) + ";
1035             }
1036             index += "j * " + NumToString(InlineSize(vectortype));
1037             if (vectortype.base_type == BASE_TYPE_STRUCT) {
1038               code += vectortype.struct_def->fixed
1039                           ? index
1040                           : "__p.__indirect(" + index + ")";
1041               code += ", __p.bb";
1042             } else {
1043               code += index;
1044             }
1045             code += ")" + dest_mask;
1046             if (!IsArray(field.value.type)) {
1047               code += " : ";
1048               code +=
1049                   field.value.type.element == BASE_TYPE_BOOL
1050                       ? "false"
1051                       : (IsScalar(field.value.type.element) ? default_cast + "0"
1052                                                             : "null");
1053             }
1054             if (vectortype.base_type == BASE_TYPE_UNION &&
1055                 HasUnionStringValue(*vectortype.enum_def)) {
1056               code += member_suffix;
1057               code += "}\n";
1058               code += "  public string " + Name(field) + "AsString(int j)";
1059               code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
1060               code += "(" + index + ") : null";
1061             }
1062             break;
1063           }
1064           case BASE_TYPE_UNION:
1065             code += "() where TTable : struct, IFlatbufferObject";
1066             code += offset_prefix + "(TTable?)" + getter;
1067             code += "<TTable>(o + __p.bb_pos) : null";
1068             if (HasUnionStringValue(*field.value.type.enum_def)) {
1069               code += member_suffix;
1070               code += "}\n";
1071               code += "  public string " + Name(field) + "AsString()";
1072               code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
1073               code += "(o + __p.bb_pos) : null";
1074             }
1075             // As<> accesors for Unions
1076             // Loop through all the possible union types and generate an As
1077             // accessor that casts to the correct type.
1078             for (auto uit = field.value.type.enum_def->Vals().begin();
1079                  uit != field.value.type.enum_def->Vals().end(); ++uit) {
1080               auto val = *uit;
1081               if (val->union_type.base_type == BASE_TYPE_NONE) { continue; }
1082               auto union_field_type_name = GenTypeGet(val->union_type);
1083               code += member_suffix + "}\n";
1084               if (val->union_type.base_type == BASE_TYPE_STRUCT &&
1085                   val->union_type.struct_def->attributes.Lookup("private")) {
1086                 code += "  internal ";
1087               } else {
1088                 code += "  public ";
1089               }
1090               code += union_field_type_name + " ";
1091               code += field_name_camel + "As" + val->name + "() { return ";
1092               code += field_name_camel;
1093               if (IsString(val->union_type)) {
1094                 code += "AsString()";
1095               } else {
1096                 code += "<" + union_field_type_name + ">().Value";
1097               }
1098             }
1099             break;
1100           default: FLATBUFFERS_ASSERT(0);
1101         }
1102       }
1103       code += member_suffix;
1104       code += "}\n";
1105       if (IsVector(field.value.type)) {
1106         auto camel_name = Name(field);
1107         if (camel_name == struct_def.name) { camel_name += "_"; }
1108         code += "  public int " + camel_name;
1109         code += "Length";
1110         code += " { get";
1111         code += offset_prefix;
1112         code += "__p.__vector_len(o) : 0; ";
1113         code += "} ";
1114         code += "}\n";
1115         // See if we should generate a by-key accessor.
1116         if (field.value.type.element == BASE_TYPE_STRUCT &&
1117             !field.value.type.struct_def->fixed) {
1118           auto &sd = *field.value.type.struct_def;
1119           auto &fields = sd.fields.vec;
1120           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1121             auto &key_field = **kit;
1122             if (key_field.key) {
1123               auto qualified_name = NamespacedName(sd);
1124               code += "  public " + qualified_name + "? ";
1125               code += Name(field) + "ByKey(";
1126               code += GenTypeGet(key_field.value.type) + " key)";
1127               code += offset_prefix;
1128               code += qualified_name + ".__lookup_by_key(";
1129               code += "__p.__vector(o), key, ";
1130               code += "__p.bb) : null; ";
1131               code += "}\n";
1132               break;
1133             }
1134           }
1135         }
1136       }
1137       // Generate a ByteBuffer accessor for strings & vectors of scalars.
1138       if ((IsVector(field.value.type) &&
1139            IsScalar(field.value.type.VectorType().base_type)) ||
1140           IsString(field.value.type)) {
1141         code += "#if ENABLE_SPAN_T\n";
1142         code += "  public Span<" + GenTypeBasic(field.value.type.VectorType()) +
1143                 "> Get";
1144         code += Name(field);
1145         code += "Bytes() { return ";
1146         code += "__p.__vector_as_span<" +
1147                 GenTypeBasic(field.value.type.VectorType()) + ">(";
1148         code += NumToString(field.value.offset);
1149         code +=
1150             ", " + NumToString(SizeOf(field.value.type.VectorType().base_type));
1151         code += "); }\n";
1152         code += "#else\n";
1153         code += "  public ArraySegment<byte>? Get";
1154         code += Name(field);
1155         code += "Bytes() { return ";
1156         code += "__p.__vector_as_arraysegment(";
1157         code += NumToString(field.value.offset);
1158         code += "); }\n";
1159         code += "#endif\n";
1160 
1161         // For direct blockcopying the data into a typed array
1162         code += "  public ";
1163         code += GenTypeBasic(field.value.type.VectorType());
1164         code += "[] Get";
1165         code += Name(field);
1166         code += "Array() { ";
1167         if (IsEnum(field.value.type.VectorType())) {
1168           // Since __vector_as_array does not work for enum types,
1169           // fill array using an explicit loop.
1170           code += "int o = __p.__offset(";
1171           code += NumToString(field.value.offset);
1172           code += "); if (o == 0) return null; int p = ";
1173           code += "__p.__vector(o); int l = ";
1174           code += "__p.__vector_len(o); ";
1175           code += GenTypeBasic(field.value.type.VectorType());
1176           code += "[] a = new ";
1177           code += GenTypeBasic(field.value.type.VectorType());
1178           code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
1179           code += "(p + i * ";
1180           code += NumToString(InlineSize(field.value.type.VectorType()));
1181           code += "); } return a;";
1182         } else {
1183           code += "return ";
1184           code += "__p.__vector_as_array<";
1185           code += GenTypeBasic(field.value.type.VectorType());
1186           code += ">(";
1187           code += NumToString(field.value.offset);
1188           code += ");";
1189         }
1190         code += " }\n";
1191       }
1192       // generate object accessors if is nested_flatbuffer
1193       if (field.nested_flatbuffer) {
1194         auto nested_type_name = NamespacedName(*field.nested_flatbuffer);
1195         auto nested_method_name =
1196             Name(field) + "As" + field.nested_flatbuffer->name;
1197         auto get_nested_method_name = nested_method_name;
1198         get_nested_method_name = "Get" + nested_method_name;
1199         conditional_cast = "(" + nested_type_name + "?)";
1200         obj = "(new " + nested_type_name + "())";
1201         code += "  public " + nested_type_name + "? ";
1202         code += get_nested_method_name + "(";
1203         code += ") { int o = __p.__offset(";
1204         code += NumToString(field.value.offset) + "); ";
1205         code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
1206         code += "__p.";
1207         code += "__indirect(__p.__vector(o)), ";
1208         code += "__p.bb) : null; }\n";
1209       }
1210       // Generate mutators for scalar fields or vectors of scalars.
1211       if (parser_.opts.mutable_buffer) {
1212         auto is_series = (IsSeries(field.value.type));
1213         const auto &underlying_type =
1214             is_series ? field.value.type.VectorType() : field.value.type;
1215         // Boolean parameters have to be explicitly converted to byte
1216         // representation.
1217         auto setter_parameter =
1218             underlying_type.base_type == BASE_TYPE_BOOL
1219                 ? "(byte)(" + EscapeKeyword(field.name) + " ? 1 : 0)"
1220                 : EscapeKeyword(field.name);
1221         auto mutator_prefix = "Mutate";
1222         // A vector mutator also needs the index of the vector element it should
1223         // mutate.
1224         auto mutator_params = (is_series ? "(int j, " : "(") +
1225                               GenTypeGet(underlying_type) + " " +
1226                               EscapeKeyword(field.name) + ") { ";
1227         auto setter_index =
1228             is_series
1229                 ? "__p." +
1230                       (IsArray(field.value.type)
1231                            ? "bb_pos + " + NumToString(field.value.offset)
1232                            : "__vector(o)") +
1233                       +" + j * " + NumToString(InlineSize(underlying_type))
1234                 : (struct_def.fixed
1235                        ? "__p.bb_pos + " + NumToString(field.value.offset)
1236                        : "o + __p.bb_pos");
1237         if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
1238           code += "  public ";
1239           code += struct_def.fixed ? "void " : "bool ";
1240           code += mutator_prefix + Name(field);
1241           code += mutator_params;
1242           if (struct_def.fixed) {
1243             code += GenSetter(underlying_type) + "(" + setter_index + ", ";
1244             code += src_cast + setter_parameter + "); }\n";
1245           } else {
1246             code += "int o = __p.__offset(";
1247             code += NumToString(field.value.offset) + ");";
1248             code += " if (o != 0) { " + GenSetter(underlying_type);
1249             code += "(" + setter_index + ", " + src_cast + setter_parameter +
1250                     "); return true; } else { return false; } }\n";
1251           }
1252         }
1253       }
1254       if (parser_.opts.java_primitive_has_method &&
1255           IsScalar(field.value.type.base_type) && !struct_def.fixed) {
1256         auto vt_offset_constant =
1257             "  public static final int VT_" +
1258             ConvertCase(field.name, Case::kScreamingSnake) + " = " +
1259             NumToString(field.value.offset) + ";";
1260 
1261         code += vt_offset_constant;
1262         code += "\n";
1263       }
1264     }
1265     code += "\n";
1266     auto struct_has_create = false;
1267     std::set<flatbuffers::FieldDef *> field_has_create_set;
1268     flatbuffers::FieldDef *key_field = nullptr;
1269     if (struct_def.fixed) {
1270       struct_has_create = true;
1271       // create a struct constructor function
1272       code += "  public static " + GenOffsetType(struct_def) + " ";
1273       code += "Create";
1274       code += struct_def.name + "(FlatBufferBuilder builder";
1275       GenStructArgs(struct_def, code_ptr, "");
1276       code += ") {\n";
1277       GenStructBody(struct_def, code_ptr, "");
1278       code += "    return ";
1279       code += GenOffsetConstruct(struct_def, "builder.Offset");
1280       code += ";\n  }\n";
1281     } else {
1282       // Generate a method that creates a table in one go. This is only possible
1283       // when the table has no struct fields, since those have to be created
1284       // inline, and there's no way to do so in Java.
1285       bool has_no_struct_fields = true;
1286       int num_fields = 0;
1287       for (auto it = struct_def.fields.vec.begin();
1288            it != struct_def.fields.vec.end(); ++it) {
1289         auto &field = **it;
1290         if (field.deprecated) continue;
1291         if (IsStruct(field.value.type)) {
1292           has_no_struct_fields = false;
1293         } else {
1294           num_fields++;
1295         }
1296       }
1297       // JVM specifications restrict default constructor params to be < 255.
1298       // Longs and doubles take up 2 units, so we set the limit to be < 127.
1299       if ((has_no_struct_fields || opts.generate_object_based_api) &&
1300           num_fields && num_fields < 127) {
1301         struct_has_create = true;
1302         // Generate a table constructor of the form:
1303         // public static int createName(FlatBufferBuilder builder, args...)
1304         code += "  public static " + GenOffsetType(struct_def) + " ";
1305         code += "Create" + struct_def.name;
1306         code += "(FlatBufferBuilder builder";
1307         for (auto it = struct_def.fields.vec.begin();
1308              it != struct_def.fields.vec.end(); ++it) {
1309           auto &field = **it;
1310           if (field.deprecated) continue;
1311           code += ",\n      ";
1312           if (IsStruct(field.value.type) && opts.generate_object_based_api) {
1313             code += WrapInNameSpace(
1314                 field.value.type.struct_def->defined_namespace,
1315                 GenTypeName_ObjectAPI(field.value.type.struct_def->name, opts));
1316             code += " ";
1317             code += EscapeKeyword(field.name);
1318             code += " = null";
1319           } else {
1320             code += GenTypeBasic(field.value.type);
1321             if (field.IsScalarOptional()) { code += "?"; }
1322             code += " ";
1323             code += EscapeKeyword(field.name);
1324             if (!IsScalar(field.value.type.base_type)) code += "Offset";
1325 
1326             code += " = ";
1327             code += GenDefaultValueBasic(field);
1328           }
1329         }
1330         code += ") {\n    builder.";
1331         code += "StartTable(";
1332         code += NumToString(struct_def.fields.vec.size()) + ");\n";
1333         for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1334              size; size /= 2) {
1335           for (auto it = struct_def.fields.vec.rbegin();
1336                it != struct_def.fields.vec.rend(); ++it) {
1337             auto &field = **it;
1338             if (!field.deprecated &&
1339                 (!struct_def.sortbysize ||
1340                  size == SizeOf(field.value.type.base_type))) {
1341               code += "    " + struct_def.name + ".";
1342               code += "Add";
1343               code += Name(field) + "(builder, ";
1344               if (IsStruct(field.value.type) &&
1345                   opts.generate_object_based_api) {
1346                 code += GenTypePointer(field.value.type) + ".Pack(builder, " +
1347                         EscapeKeyword(field.name) + ")";
1348               } else {
1349                 code += EscapeKeyword(field.name);
1350                 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1351               }
1352 
1353               code += ");\n";
1354             }
1355           }
1356         }
1357         code += "    return " + struct_def.name + ".";
1358         code += "End" + struct_def.name;
1359         code += "(builder);\n  }\n\n";
1360       }
1361       // Generate a set of static methods that allow table construction,
1362       // of the form:
1363       // public static void addName(FlatBufferBuilder builder, short name)
1364       // { builder.addShort(id, name, default); }
1365       // Unlike the Create function, these always work.
1366       code += "  public static void Start";
1367       code += struct_def.name;
1368       code += "(FlatBufferBuilder builder) { builder.";
1369       code += "StartTable(";
1370       code += NumToString(struct_def.fields.vec.size()) + "); }\n";
1371       for (auto it = struct_def.fields.vec.begin();
1372            it != struct_def.fields.vec.end(); ++it) {
1373         auto &field = **it;
1374         if (field.deprecated) continue;
1375         if (field.key) key_field = &field;
1376         code += "  public static void Add";
1377         code += Name(field);
1378         code += "(FlatBufferBuilder builder, ";
1379         code += GenTypeBasic(field.value.type);
1380         auto argname = ConvertCase(field.name, Case::kLowerCamel);
1381         if (!IsScalar(field.value.type.base_type)) argname += "Offset";
1382         if (field.IsScalarOptional()) { code += "?"; }
1383         code += " " + EscapeKeyword(argname) + ") { builder.Add";
1384         code += GenMethod(field.value.type) + "(";
1385         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1386         code += SourceCastBasic(field.value.type, field.IsScalarOptional());
1387         code += EscapeKeyword(argname);
1388         if (!IsScalar(field.value.type.base_type) &&
1389             field.value.type.base_type != BASE_TYPE_UNION) {
1390           code += ".Value";
1391         }
1392         if (!field.IsScalarOptional()) {
1393           // When the scalar is optional, use the builder method that doesn't
1394           // supply a default value. Otherwise, we to continue to use the
1395           // default value method.
1396           code += ", ";
1397           code += GenDefaultValue(field, false);
1398         }
1399         code += "); }\n";
1400         if (IsVector(field.value.type)) {
1401           auto vector_type = field.value.type.VectorType();
1402           auto alignment = InlineAlignment(vector_type);
1403           auto elem_size = InlineSize(vector_type);
1404           if (!IsStruct(vector_type)) {
1405             field_has_create_set.insert(&field);
1406             code += "  public static VectorOffset ";
1407             code += "Create";
1408             code += Name(field);
1409             code += "Vector(FlatBufferBuilder builder, ";
1410             code += GenTypeBasic(vector_type) + "[] data) ";
1411             code += "{ builder.StartVector(";
1412             code += NumToString(elem_size);
1413             code += ", data.Length, ";
1414             code += NumToString(alignment);
1415             code += "); for (int i = data.";
1416             code += "Length - 1; i >= 0; i--) builder.";
1417             code += "Add";
1418             code += GenMethod(vector_type);
1419             code += "(";
1420             // At the moment there is no support of the type Vector with
1421             // optional enum, e.g. if we have enum type SomeEnum there is no way
1422             // to define `SomeEmum?[] enums` in FlatBuffer schema, so isOptional
1423             // = false
1424             code += SourceCastBasic(vector_type, false);
1425             code += "data[i]";
1426             if (vector_type.base_type == BASE_TYPE_STRUCT ||
1427                 IsString(vector_type))
1428               code += ".Value";
1429             code += "); return ";
1430             code += "builder.EndVector(); }\n";
1431 
1432             // add Create...VectorBlock() overloads for T[], ArraySegment<T> and
1433             // IntPtr
1434             code += "  public static VectorOffset ";
1435             code += "Create";
1436             code += Name(field);
1437             code += "VectorBlock(FlatBufferBuilder builder, ";
1438             code += GenTypeBasic(vector_type) + "[] data) ";
1439             code += "{ builder.StartVector(";
1440             code += NumToString(elem_size);
1441             code += ", data.Length, ";
1442             code += NumToString(alignment);
1443             code += "); builder.Add(data); return builder.EndVector(); }\n";
1444 
1445             code += "  public static VectorOffset ";
1446             code += "Create";
1447             code += Name(field);
1448             code += "VectorBlock(FlatBufferBuilder builder, ";
1449             code += "ArraySegment<" + GenTypeBasic(vector_type) + "> data) ";
1450             code += "{ builder.StartVector(";
1451             code += NumToString(elem_size);
1452             code += ", data.Count, ";
1453             code += NumToString(alignment);
1454             code += "); builder.Add(data); return builder.EndVector(); }\n";
1455 
1456             code += "  public static VectorOffset ";
1457             code += "Create";
1458             code += Name(field);
1459             code += "VectorBlock(FlatBufferBuilder builder, ";
1460             code += "IntPtr dataPtr, int sizeInBytes) ";
1461             code += "{ builder.StartVector(1, sizeInBytes, 1); ";
1462             code += "builder.Add<" + GenTypeBasic(vector_type) +
1463                     ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n";
1464           }
1465           // Generate a method to start a vector, data to be added manually
1466           // after.
1467           code += "  public static void Start";
1468           code += Name(field);
1469           code += "Vector(FlatBufferBuilder builder, int numElems) ";
1470           code += "{ builder.StartVector(";
1471           code += NumToString(elem_size);
1472           code += ", numElems, " + NumToString(alignment);
1473           code += "); }\n";
1474         }
1475       }
1476       code += "  public static " + GenOffsetType(struct_def) + " ";
1477       code += "End" + struct_def.name;
1478       code += "(FlatBufferBuilder builder) {\n    int o = builder.";
1479       code += "EndTable();\n";
1480       for (auto it = struct_def.fields.vec.begin();
1481            it != struct_def.fields.vec.end(); ++it) {
1482         auto &field = **it;
1483         if (!field.deprecated && field.IsRequired()) {
1484           code += "    builder.Required(o, ";
1485           code += NumToString(field.value.offset);
1486           code += ");  // " + field.name + "\n";
1487         }
1488       }
1489       code += "    return " + GenOffsetConstruct(struct_def, "o") + ";\n  }\n";
1490       if (parser_.root_struct_def_ == &struct_def) {
1491         std::string size_prefix[] = { "", "SizePrefixed" };
1492         for (int i = 0; i < 2; ++i) {
1493           code += "  public static void ";
1494           code += "Finish" + size_prefix[i] + struct_def.name;
1495           code +=
1496               "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1497           code += " offset) {";
1498           code += " builder.Finish" + size_prefix[i] + "(offset";
1499           code += ".Value";
1500 
1501           if (parser_.file_identifier_.length())
1502             code += ", \"" + parser_.file_identifier_ + "\"";
1503           code += "); }\n";
1504         }
1505       }
1506     }
1507     // Only generate key compare function for table,
1508     // because `key_field` is not set for struct
1509     if (struct_def.has_key && !struct_def.fixed) {
1510       FLATBUFFERS_ASSERT(key_field);
1511       auto name = Name(*key_field);
1512       if (name == struct_def.name) { name += "_"; }
1513       code += "\n  public static VectorOffset ";
1514       code += "CreateSortedVectorOf" + struct_def.name;
1515       code += "(FlatBufferBuilder builder, ";
1516       code += "Offset<" + struct_def.name + ">";
1517       code += "[] offsets) {\n";
1518       code += "    Array.Sort(offsets,\n";
1519       code += "      (Offset<" + struct_def.name + "> o1, Offset<" +
1520               struct_def.name + "> o2) =>\n";
1521       code += "        " + GenKeyGetter(struct_def, key_field);
1522       code += ");\n";
1523       code += "    return builder.CreateVectorOfTables(offsets);\n  }\n";
1524 
1525       code += "\n  public static " + struct_def.name + "?";
1526       code += " __lookup_by_key(";
1527       code += "int vectorLocation, ";
1528       code += GenTypeGet(key_field->value.type);
1529       code += " key, ByteBuffer bb) {\n";
1530       code +=
1531           "    " + struct_def.name + " obj_ = new " + struct_def.name + "();\n";
1532       code += "    int span = ";
1533       code += "bb.GetInt(vectorLocation - 4);\n";
1534       code += "    int start = 0;\n";
1535       code += "    while (span != 0) {\n";
1536       code += "      int middle = span / 2;\n";
1537       code +=
1538           "      int tableOffset = Table.__indirect(vectorLocation + 4 * "
1539           "(start + middle), bb);\n";
1540 
1541       code += "      obj_.__assign(tableOffset, bb);\n";
1542       code += "      int comp = obj_." + name + ".CompareTo(key);\n";
1543       code += "      if (comp > 0) {\n";
1544       code += "        span = middle;\n";
1545       code += "      } else if (comp < 0) {\n";
1546       code += "        middle++;\n";
1547       code += "        start += middle;\n";
1548       code += "        span -= middle;\n";
1549       code += "      } else {\n";
1550       code += "        return obj_;\n";
1551       code += "      }\n    }\n";
1552       code += "    return null;\n";
1553       code += "  }\n";
1554     }
1555 
1556     if (opts.generate_object_based_api) {
1557       GenPackUnPack_ObjectAPI(struct_def, code_ptr, opts, struct_has_create,
1558                               field_has_create_set);
1559     }
1560     code += "}\n\n";
1561 
1562     if (opts.generate_object_based_api) {
1563       GenStruct_ObjectAPI(struct_def, code_ptr, opts);
1564     }
1565   }
1566 
GenVectorAccessObject(StructDef & struct_def,std::string * code_ptr) const1567   void GenVectorAccessObject(StructDef &struct_def,
1568                              std::string *code_ptr) const {
1569     auto &code = *code_ptr;
1570     // Generate a vector of structs accessor class.
1571     code += "\n";
1572     code += "  ";
1573     if (!struct_def.attributes.Lookup("private")) code += "public ";
1574     code += "static struct Vector : BaseVector\n{\n";
1575 
1576     // Generate the __assign method that sets the field in a pre-existing
1577     // accessor object. This is to allow object reuse.
1578     std::string method_indent = "    ";
1579     code += method_indent + "public Vector ";
1580     code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1581     code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1582 
1583     auto type_name = struct_def.name;
1584     auto method_start = method_indent + "public " + type_name + " Get";
1585     // Generate the accessors that don't do object reuse.
1586     code += method_start + "(int j) { return Get";
1587     code += "(new " + type_name + "(), j); }\n";
1588     code += method_start + "(" + type_name + " obj, int j) { ";
1589     code += " return obj.__assign(";
1590     code += struct_def.fixed ? "__p.__element(j)"
1591                              : "__p.__indirect(__p.__element(j), bb)";
1592     code += ", __p.bb); }\n";
1593     // See if we should generate a by-key accessor.
1594     if (!struct_def.fixed) {
1595       auto &fields = struct_def.fields.vec;
1596       for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1597         auto &key_field = **kit;
1598         if (key_field.key) {
1599           auto nullable_annotation =
1600               parser_.opts.gen_nullable ? "@Nullable " : "";
1601           code += method_indent + nullable_annotation;
1602           code += "public " + type_name + "? ";
1603           code += "GetByKey(";
1604           code += GenTypeGet(key_field.value.type) + " key) { ";
1605           code += " return __lookup_by_key(null, ";
1606           code += "__p.__vector(), key, ";
1607           code += "__p.bb); ";
1608           code += "}\n";
1609           code += method_indent + nullable_annotation;
1610           code += "public " + type_name + "?" + " ";
1611           code += "GetByKey(";
1612           code += type_name + "? obj, ";
1613           code += GenTypeGet(key_field.value.type) + " key) { ";
1614           code += " return __lookup_by_key(obj, ";
1615           code += "__p.__vector(), key, ";
1616           code += "__p.bb); ";
1617           code += "}\n";
1618           break;
1619         }
1620       }
1621     }
1622     code += "  }\n";
1623   }
1624 
GenUnionVerify(const Type & union_type) const1625   std::string GenUnionVerify(const Type &union_type) const {
1626     if (union_type.enum_def) {
1627       const auto &enum_def = *union_type.enum_def;
1628 
1629       auto ret = "\n\nstatic public class " + enum_def.name + "Verify\n";
1630       ret += "{\n";
1631       ret +=
1632           "  static public bool Verify(Google.FlatBuffers.Verifier verifier, "
1633           "byte typeId, uint tablePos)\n";
1634       ret += "  {\n";
1635       ret += "    bool result = true;\n";
1636 
1637       const auto union_enum_loop = [&]() {
1638         ret += "    switch((" + enum_def.name + ")typeId)\n";
1639         ret += "    {\n";
1640 
1641         for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
1642              ++it) {
1643           const auto &ev = **it;
1644           if (ev.IsZero()) { continue; }
1645 
1646           ret += "      case " + Name(enum_def) + "." + Name(ev) + ":\n";
1647 
1648           if (IsString(ev.union_type)) {
1649             ret += "       result = verifier.VerifyUnionString(tablePos);\n";
1650             ret += "        break;";
1651           } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
1652             if (!ev.union_type.struct_def->fixed) {
1653               auto type = GenTypeGet(ev.union_type);
1654               ret += "        result = " + type +
1655                      "Verify.Verify(verifier, tablePos);\n";
1656             } else {
1657               ret += "        result = verifier.VerifyUnionData(tablePos, " +
1658                      NumToString(InlineSize(ev.union_type)) + ", " +
1659                      NumToString(InlineAlignment(ev.union_type)) + ");\n";
1660               ;
1661             }
1662             ret += "        break;";
1663           } else {
1664             FLATBUFFERS_ASSERT(false);
1665           }
1666           ret += "\n";
1667         }
1668 
1669         ret += "      default: result = true;\n";
1670         ret += "        break;\n";
1671         ret += "    }\n";
1672         ret += "    return result;\n";
1673       };
1674 
1675       union_enum_loop();
1676       ret += "  }\n";
1677       ret += "}\n";
1678       ret += "\n";
1679 
1680       return ret;
1681     }
1682     FLATBUFFERS_ASSERT(0);
1683     return "";
1684   }
1685 
GenEnum_ObjectAPI(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const1686   void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
1687                          const IDLOptions &opts) const {
1688     auto &code = *code_ptr;
1689     if (enum_def.generated) return;
1690     if (!enum_def.is_union) return;
1691     if (enum_def.attributes.Lookup("private")) {
1692       code += "internal ";
1693     } else {
1694       code += "public ";
1695     }
1696     auto union_name = enum_def.name + "Union";
1697     auto class_member = std::string("Value");
1698     if (class_member == enum_def.name) { class_member += "_"; };
1699     code += "class " + union_name + " {\n";
1700     // Type
1701     code += "  public " + enum_def.name + " Type { get; set; }\n";
1702     // Value
1703     code += "  public object " + class_member + " { get; set; }\n";
1704     code += "\n";
1705     // Constructor
1706     code += "  public " + union_name + "() {\n";
1707     code += "    this.Type = " + enum_def.name + "." +
1708             enum_def.Vals()[0]->name + ";\n";
1709     code += "    this." + class_member + " = null;\n";
1710     code += "  }\n\n";
1711     // As<T>
1712     code += "  public T As<T>() where T : class { return this." + class_member +
1713             " as T; }\n";
1714     // As, From
1715     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1716       auto &ev = **it;
1717       if (ev.union_type.base_type == BASE_TYPE_NONE) continue;
1718       auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1719       std::string accessibility =
1720           (ev.union_type.base_type == BASE_TYPE_STRUCT &&
1721            ev.union_type.struct_def->attributes.Lookup("private"))
1722               ? "internal"
1723               : "public";
1724       // As
1725       code += "  " + accessibility + " " + type_name + " As" + ev.name +
1726               "() { return this.As<" + type_name + ">(); }\n";
1727       // From
1728       auto lower_ev_name = ev.name;
1729       std::transform(lower_ev_name.begin(), lower_ev_name.end(),
1730                      lower_ev_name.begin(), CharToLower);
1731       code += "  " + accessibility + " static " + union_name + " From" +
1732               ev.name + "(" + type_name + " _" + lower_ev_name +
1733               ") { return new " + union_name + "{ Type = " + Name(enum_def) +
1734               "." + Name(ev) + ", " + class_member + " = _" + lower_ev_name +
1735               " }; }\n";
1736     }
1737     code += "\n";
1738     // Pack()
1739     code +=
1740         "  public static int Pack(Google.FlatBuffers.FlatBufferBuilder "
1741         "builder, " +
1742         union_name + " _o) {\n";
1743     code += "    switch (_o.Type) {\n";
1744     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1745       auto &ev = **it;
1746       if (ev.union_type.base_type == BASE_TYPE_NONE) {
1747         code += "      default: return 0;\n";
1748       } else {
1749         code += "      case " + Name(enum_def) + "." + Name(ev) + ": return ";
1750         if (IsString(ev.union_type)) {
1751           code += "builder.CreateString(_o.As" + ev.name + "()).Value;\n";
1752         } else {
1753           code += GenTypeGet(ev.union_type) + ".Pack(builder, _o.As" + ev.name +
1754                   "()).Value;\n";
1755         }
1756       }
1757     }
1758     code += "    }\n";
1759     code += "  }\n";
1760     code += "}\n\n";
1761 
1762     // JsonConverter
1763     if (opts.cs_gen_json_serializer) {
1764       if (enum_def.attributes.Lookup("private")) {
1765         code += "internal ";
1766       } else {
1767         code += "public ";
1768       }
1769       code += "class " + union_name +
1770               "_JsonConverter : Newtonsoft.Json.JsonConverter {\n";
1771       code += "  public override bool CanConvert(System.Type objectType) {\n";
1772       code += "    return objectType == typeof(" + union_name +
1773               ") || objectType == typeof(System.Collections.Generic.List<" +
1774               union_name + ">);\n";
1775       code += "  }\n";
1776       code +=
1777           "  public override void WriteJson(Newtonsoft.Json.JsonWriter writer, "
1778           "object value, "
1779           "Newtonsoft.Json.JsonSerializer serializer) {\n";
1780       code += "    var _olist = value as System.Collections.Generic.List<" +
1781               union_name + ">;\n";
1782       code += "    if (_olist != null) {\n";
1783       code += "      writer.WriteStartArray();\n";
1784       code +=
1785           "      foreach (var _o in _olist) { this.WriteJson(writer, _o, "
1786           "serializer); }\n";
1787       code += "      writer.WriteEndArray();\n";
1788       code += "    } else {\n";
1789       code += "      this.WriteJson(writer, value as " + union_name +
1790               ", serializer);\n";
1791       code += "    }\n";
1792       code += "  }\n";
1793       code += "  public void WriteJson(Newtonsoft.Json.JsonWriter writer, " +
1794               union_name +
1795               " _o, "
1796               "Newtonsoft.Json.JsonSerializer serializer) {\n";
1797       code += "    if (_o == null) return;\n";
1798       code += "    serializer.Serialize(writer, _o." + class_member + ");\n";
1799       code += "  }\n";
1800       code +=
1801           "  public override object ReadJson(Newtonsoft.Json.JsonReader "
1802           "reader, "
1803           "System.Type objectType, "
1804           "object existingValue, Newtonsoft.Json.JsonSerializer serializer) "
1805           "{\n";
1806       code +=
1807           "    var _olist = existingValue as System.Collections.Generic.List<" +
1808           union_name + ">;\n";
1809       code += "    if (_olist != null) {\n";
1810       code += "      for (var _j = 0; _j < _olist.Count; ++_j) {\n";
1811       code += "        reader.Read();\n";
1812       code +=
1813           "        _olist[_j] = this.ReadJson(reader, _olist[_j], "
1814           "serializer);\n";
1815       code += "      }\n";
1816       code += "      reader.Read();\n";
1817       code += "      return _olist;\n";
1818       code += "    } else {\n";
1819       code += "      return this.ReadJson(reader, existingValue as " +
1820               union_name + ", serializer);\n";
1821       code += "    }\n";
1822       code += "  }\n";
1823       code += "  public " + union_name +
1824               " ReadJson(Newtonsoft.Json.JsonReader reader, " + union_name +
1825               " _o, Newtonsoft.Json.JsonSerializer serializer) {\n";
1826       code += "    if (_o == null) return null;\n";
1827       code += "    switch (_o.Type) {\n";
1828       for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
1829            ++it) {
1830         auto &ev = **it;
1831         if (ev.union_type.base_type == BASE_TYPE_NONE) {
1832           code += "      default: break;\n";
1833         } else {
1834           auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1835           code += "      case " + Name(enum_def) + "." + Name(ev) + ": _o." +
1836                   class_member + " = serializer.Deserialize<" + type_name +
1837                   ">(reader); break;\n";
1838         }
1839       }
1840       code += "    }\n";
1841       code += "    return _o;\n";
1842       code += "  }\n";
1843       code += "}\n\n";
1844     }
1845   }
1846 
GenTypeName_ObjectAPI(const std::string & name,const IDLOptions & opts) const1847   std::string GenTypeName_ObjectAPI(const std::string &name,
1848                                     const IDLOptions &opts) const {
1849     return opts.object_prefix + name + opts.object_suffix;
1850   }
1851 
GenUnionUnPack_ObjectAPI(const EnumDef & enum_def,std::string * code_ptr,const std::string & camel_name,const std::string & camel_name_short,bool is_vector) const1852   void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
1853                                 const std::string &camel_name,
1854                                 const std::string &camel_name_short,
1855                                 bool is_vector) const {
1856     auto &code = *code_ptr;
1857     std::string varialbe_name = "_o." + camel_name;
1858     std::string class_member = "Value";
1859     if (class_member == enum_def.name) class_member += "_";
1860     std::string type_suffix = "";
1861     std::string func_suffix = "()";
1862     std::string indent = "    ";
1863     if (is_vector) {
1864       varialbe_name = "_o_" + camel_name;
1865       type_suffix = "(_j)";
1866       func_suffix = "(_j)";
1867       indent = "      ";
1868     }
1869     if (is_vector) {
1870       code += indent + "var " + varialbe_name + " = new ";
1871     } else {
1872       code += indent + varialbe_name + " = new ";
1873     }
1874     code += NamespacedName(enum_def) + "Union();\n";
1875     code += indent + varialbe_name + ".Type = this." + camel_name_short +
1876             "Type" + type_suffix + ";\n";
1877     code += indent + "switch (this." + camel_name_short + "Type" + type_suffix +
1878             ") {\n";
1879     for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end();
1880          ++eit) {
1881       auto &ev = **eit;
1882       if (ev.union_type.base_type == BASE_TYPE_NONE) {
1883         code += indent + "  default: break;\n";
1884       } else {
1885         code += indent + "  case " + NamespacedName(enum_def) + "." + ev.name +
1886                 ":\n";
1887         code += indent + "    " + varialbe_name + "." + class_member +
1888                 " = this." + camel_name;
1889         if (IsString(ev.union_type)) {
1890           code += "AsString" + func_suffix + ";\n";
1891         } else {
1892           code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix;
1893           code += ".HasValue ? this." + camel_name;
1894           code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix +
1895                   ".Value.UnPack() : null;\n";
1896         }
1897         code += indent + "    break;\n";
1898       }
1899     }
1900     code += indent + "}\n";
1901     if (is_vector) {
1902       code += indent + "_o." + camel_name + ".Add(" + varialbe_name + ");\n";
1903     }
1904   }
1905 
GenPackUnPack_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts,bool struct_has_create,const std::set<FieldDef * > & field_has_create) const1906   void GenPackUnPack_ObjectAPI(
1907       StructDef &struct_def, std::string *code_ptr, const IDLOptions &opts,
1908       bool struct_has_create,
1909       const std::set<FieldDef *> &field_has_create) const {
1910     auto &code = *code_ptr;
1911     auto struct_name = GenTypeName_ObjectAPI(struct_def.name, opts);
1912     // UnPack()
1913     code += "  public " + struct_name + " UnPack() {\n";
1914     code += "    var _o = new " + struct_name + "();\n";
1915     code += "    this.UnPackTo(_o);\n";
1916     code += "    return _o;\n";
1917     code += "  }\n";
1918     // UnPackTo()
1919     code += "  public void UnPackTo(" + struct_name + " _o) {\n";
1920     for (auto it = struct_def.fields.vec.begin();
1921          it != struct_def.fields.vec.end(); ++it) {
1922       auto &field = **it;
1923       if (field.deprecated) continue;
1924       auto camel_name = Name(field);
1925       if (camel_name == struct_def.name) { camel_name += "_"; }
1926       auto camel_name_short = Name(field);
1927       auto start = "    _o." + camel_name + " = ";
1928       switch (field.value.type.base_type) {
1929         case BASE_TYPE_STRUCT: {
1930           auto fixed = struct_def.fixed && field.value.type.struct_def->fixed;
1931           if (fixed) {
1932             code += start + "this." + camel_name + ".UnPack();\n";
1933           } else {
1934             code += start + "this." + camel_name + ".HasValue ? this." +
1935                     camel_name + ".Value.UnPack() : null;\n";
1936           }
1937           break;
1938         }
1939         case BASE_TYPE_ARRAY: {
1940           auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
1941           auto length_str = NumToString(field.value.type.fixed_length);
1942           auto unpack_method = field.value.type.struct_def == nullptr ? ""
1943                                : field.value.type.struct_def->fixed
1944                                    ? ".UnPack()"
1945                                    : "?.UnPack()";
1946           code += start + "new " + type_name.substr(0, type_name.length() - 1) +
1947                   length_str + "];\n";
1948           code += "    for (var _j = 0; _j < " + length_str + "; ++_j) { _o." +
1949                   camel_name + "[_j] = this." + camel_name + "(_j)" +
1950                   unpack_method + "; }\n";
1951           break;
1952         }
1953         case BASE_TYPE_VECTOR:
1954           if (field.value.type.element == BASE_TYPE_UNION) {
1955             code += start + "new " +
1956                     GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1957             code += "    for (var _j = 0; _j < this." + camel_name +
1958                     "Length; ++_j) {\n";
1959             GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1960                                      camel_name, camel_name_short, true);
1961             code += "    }\n";
1962           } else if (field.value.type.element != BASE_TYPE_UTYPE) {
1963             auto fixed = field.value.type.struct_def == nullptr;
1964             code += start + "new " +
1965                     GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1966             code += "    for (var _j = 0; _j < this." + camel_name +
1967                     "Length; ++_j) {";
1968             code += "_o." + camel_name + ".Add(";
1969             if (fixed) {
1970               code += "this." + camel_name + "(_j)";
1971             } else {
1972               code += "this." + camel_name + "(_j).HasValue ? this." +
1973                       camel_name + "(_j).Value.UnPack() : null";
1974             }
1975             code += ");}\n";
1976           }
1977           break;
1978         case BASE_TYPE_UTYPE: break;
1979         case BASE_TYPE_UNION: {
1980           GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1981                                    camel_name, camel_name_short, false);
1982           break;
1983         }
1984         default: {
1985           code += start + "this." + camel_name + ";\n";
1986           break;
1987         }
1988       }
1989     }
1990     code += "  }\n";
1991     // Pack()
1992     code += "  public static " + GenOffsetType(struct_def) +
1993             " Pack(FlatBufferBuilder builder, " + struct_name + " _o) {\n";
1994     code += "    if (_o == null) return default(" + GenOffsetType(struct_def) +
1995             ");\n";
1996     for (auto it = struct_def.fields.vec.begin();
1997          it != struct_def.fields.vec.end(); ++it) {
1998       auto &field = **it;
1999       if (field.deprecated) continue;
2000       auto camel_name = Name(field);
2001       if (camel_name == struct_def.name) { camel_name += "_"; }
2002       auto camel_name_short = Name(field);
2003       // pre
2004       switch (field.value.type.base_type) {
2005         case BASE_TYPE_STRUCT: {
2006           if (!field.value.type.struct_def->fixed) {
2007             code += "    var _" + field.name + " = _o." + camel_name +
2008                     " == null ? default(" +
2009                     GenOffsetType(*field.value.type.struct_def) +
2010                     ") : " + GenTypeGet(field.value.type) +
2011                     ".Pack(builder, _o." + camel_name + ");\n";
2012           } else if (struct_def.fixed && struct_has_create) {
2013             std::vector<FieldArrayLength> array_lengths;
2014             FieldArrayLength tmp_array_length = {
2015               field.name,
2016               field.value.type.fixed_length,
2017             };
2018             array_lengths.push_back(tmp_array_length);
2019             GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
2020                                         array_lengths);
2021           }
2022           break;
2023         }
2024         case BASE_TYPE_STRING: {
2025           std::string create_string =
2026               field.shared ? "CreateSharedString" : "CreateString";
2027           code += "    var _" + field.name + " = _o." + camel_name +
2028                   " == null ? default(StringOffset) : "
2029                   "builder." +
2030                   create_string + "(_o." + camel_name + ");\n";
2031           break;
2032         }
2033         case BASE_TYPE_VECTOR: {
2034           if (field_has_create.find(&field) != field_has_create.end()) {
2035             auto property_name = camel_name;
2036             auto gen_for_loop = true;
2037             std::string array_name = "__" + field.name;
2038             std::string array_type = "";
2039             std::string to_array = "";
2040             switch (field.value.type.element) {
2041               case BASE_TYPE_STRING: {
2042                 std::string create_string =
2043                     field.shared ? "CreateSharedString" : "CreateString";
2044                 array_type = "StringOffset";
2045                 to_array += "builder." + create_string + "(_o." +
2046                             property_name + "[_j])";
2047                 break;
2048               }
2049               case BASE_TYPE_STRUCT:
2050                 array_type = "Offset<" + GenTypeGet(field.value.type) + ">";
2051                 to_array = GenTypeGet(field.value.type) + ".Pack(builder, _o." +
2052                            property_name + "[_j])";
2053                 break;
2054               case BASE_TYPE_UTYPE:
2055                 property_name = camel_name.substr(0, camel_name.size() - 4);
2056                 array_type = NamespacedName(*field.value.type.enum_def);
2057                 to_array = "_o." + property_name + "[_j].Type";
2058                 break;
2059               case BASE_TYPE_UNION:
2060                 array_type = "int";
2061                 to_array = NamespacedName(*field.value.type.enum_def) +
2062                            "Union.Pack(builder,  _o." + property_name + "[_j])";
2063                 break;
2064               default: gen_for_loop = false; break;
2065             }
2066             code += "    var _" + field.name + " = default(VectorOffset);\n";
2067             code += "    if (_o." + property_name + " != null) {\n";
2068             if (gen_for_loop) {
2069               code += "      var " + array_name + " = new " + array_type +
2070                       "[_o." + property_name + ".Count];\n";
2071               code += "      for (var _j = 0; _j < " + array_name +
2072                       ".Length; ++_j) { ";
2073               code += array_name + "[_j] = " + to_array + "; }\n";
2074             } else {
2075               code += "      var " + array_name + " = _o." + property_name +
2076                       ".ToArray();\n";
2077             }
2078             code += "      _" + field.name + " = Create" + camel_name_short +
2079                     "Vector(builder, " + array_name + ");\n";
2080             code += "    }\n";
2081           } else {
2082             auto pack_method =
2083                 field.value.type.struct_def == nullptr
2084                     ? "builder.Add" + GenMethod(field.value.type.VectorType()) +
2085                           "(_o." + camel_name + "[_j]);"
2086                     : GenTypeGet(field.value.type) + ".Pack(builder, _o." +
2087                           camel_name + "[_j]);";
2088             code += "    var _" + field.name + " = default(VectorOffset);\n";
2089             code += "    if (_o." + camel_name + " != null) {\n";
2090             code += "      Start" + camel_name_short + "Vector(builder, _o." +
2091                     camel_name + ".Count);\n";
2092             code += "      for (var _j = _o." + camel_name +
2093                     ".Count - 1; _j >= 0; --_j) { " + pack_method + " }\n";
2094             code += "      _" + field.name + " = builder.EndVector();\n";
2095             code += "    }\n";
2096           }
2097           break;
2098         }
2099         case BASE_TYPE_ARRAY: {
2100           if (field.value.type.struct_def != nullptr) {
2101             std::vector<FieldArrayLength> array_lengths;
2102             FieldArrayLength tmp_array_length = {
2103               field.name,
2104               field.value.type.fixed_length,
2105             };
2106             array_lengths.push_back(tmp_array_length);
2107             GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
2108                                         array_lengths);
2109           } else {
2110             code += "    var _" + field.name + " = _o." + camel_name + ";\n";
2111           }
2112           break;
2113         }
2114         case BASE_TYPE_UNION: {
2115           code += "    var _" + field.name + "_type = _o." + camel_name +
2116                   " == null ? " + NamespacedName(*field.value.type.enum_def) +
2117                   ".NONE : " + "_o." + camel_name + ".Type;\n";
2118           code +=
2119               "    var _" + field.name + " = _o." + camel_name +
2120               " == null ? 0 : " + GenTypeGet_ObjectAPI(field.value.type, opts) +
2121               ".Pack(builder, _o." + camel_name + ");\n";
2122           break;
2123         }
2124         default: break;
2125       }
2126     }
2127     if (struct_has_create) {
2128       // Create
2129       code += "    return Create" + struct_def.name + "(\n";
2130       code += "      builder";
2131       for (auto it = struct_def.fields.vec.begin();
2132            it != struct_def.fields.vec.end(); ++it) {
2133         auto &field = **it;
2134         if (field.deprecated) continue;
2135         auto camel_name = Name(field);
2136         if (camel_name == struct_def.name) { camel_name += "_"; }
2137         switch (field.value.type.base_type) {
2138           case BASE_TYPE_STRUCT: {
2139             if (struct_def.fixed) {
2140               GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
2141                                           code_ptr,
2142                                           "      _" + field.name + "_");
2143             } else {
2144               code += ",\n";
2145               if (field.value.type.struct_def->fixed) {
2146                 if (opts.generate_object_based_api)
2147                   code += "      _o." + camel_name;
2148                 else
2149                   code += "      " + GenTypeGet(field.value.type) +
2150                           ".Pack(builder, _o." + camel_name + ")";
2151               } else {
2152                 code += "      _" + field.name;
2153               }
2154             }
2155             break;
2156           }
2157           case BASE_TYPE_ARRAY: {
2158             if (field.value.type.struct_def != nullptr) {
2159               GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
2160                                           code_ptr,
2161                                           "      _" + field.name + "_");
2162             } else {
2163               code += ",\n";
2164               code += "      _" + field.name;
2165             }
2166             break;
2167           }
2168           case BASE_TYPE_UNION: FLATBUFFERS_FALLTHROUGH();   // fall thru
2169           case BASE_TYPE_UTYPE: FLATBUFFERS_FALLTHROUGH();   // fall thru
2170           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
2171           case BASE_TYPE_VECTOR: {
2172             code += ",\n";
2173             code += "      _" + field.name;
2174             break;
2175           }
2176           default:  // scalar
2177             code += ",\n";
2178             code += "      _o." + camel_name;
2179             break;
2180         }
2181       }
2182       code += ");\n";
2183     } else {
2184       // Start, End
2185       code += "    Start" + struct_def.name + "(builder);\n";
2186       for (auto it = struct_def.fields.vec.begin();
2187            it != struct_def.fields.vec.end(); ++it) {
2188         auto &field = **it;
2189         if (field.deprecated) continue;
2190         auto camel_name = Name(field);
2191         switch (field.value.type.base_type) {
2192           case BASE_TYPE_STRUCT: {
2193             if (field.value.type.struct_def->fixed) {
2194               code += "    Add" + camel_name + "(builder, " +
2195                       GenTypeGet(field.value.type) + ".Pack(builder, _o." +
2196                       camel_name + "));\n";
2197             } else {
2198               code +=
2199                   "    Add" + camel_name + "(builder, _" + field.name + ");\n";
2200             }
2201             break;
2202           }
2203           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
2204           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();   // fall thru
2205           case BASE_TYPE_VECTOR: {
2206             code +=
2207                 "    Add" + camel_name + "(builder, _" + field.name + ");\n";
2208             break;
2209           }
2210           case BASE_TYPE_UTYPE: break;
2211           case BASE_TYPE_UNION: {
2212             code += "    Add" + camel_name + "Type(builder, _" + field.name +
2213                     "_type);\n";
2214             code +=
2215                 "    Add" + camel_name + "(builder, _" + field.name + ");\n";
2216             break;
2217           }
2218           // scalar
2219           default: {
2220             code +=
2221                 "    Add" + camel_name + "(builder, _o." + camel_name + ");\n";
2222             break;
2223           }
2224         }
2225       }
2226       code += "    return End" + struct_def.name + "(builder);\n";
2227     }
2228     code += "  }\n";
2229   }
2230 
GenStructPackDecl_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::vector<FieldArrayLength> & array_lengths) const2231   void GenStructPackDecl_ObjectAPI(
2232       const StructDef &struct_def, std::string *code_ptr,
2233       std::vector<FieldArrayLength> &array_lengths) const {
2234     auto &code = *code_ptr;
2235     for (auto it = struct_def.fields.vec.begin();
2236          it != struct_def.fields.vec.end(); ++it) {
2237       auto &field = **it;
2238       auto is_array = IsArray(field.value.type);
2239       const auto &field_type =
2240           is_array ? field.value.type.VectorType() : field.value.type;
2241       FieldArrayLength tmp_array_length = {
2242         field.name,
2243         field_type.fixed_length,
2244       };
2245       array_lengths.push_back(tmp_array_length);
2246       if (field_type.struct_def != nullptr) {
2247         GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr,
2248                                     array_lengths);
2249       } else {
2250         std::vector<FieldArrayLength> array_only_lengths;
2251         for (size_t i = 0; i < array_lengths.size(); ++i) {
2252           if (array_lengths[i].length > 0) {
2253             array_only_lengths.push_back(array_lengths[i]);
2254           }
2255         }
2256         std::string name;
2257         for (size_t i = 0; i < array_lengths.size(); ++i) {
2258           name += "_" + array_lengths[i].name;
2259         }
2260         code += "    var " + name + " = ";
2261         if (array_only_lengths.size() > 0) {
2262           code += "new " + GenTypeBasic(field_type) + "[";
2263           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
2264             if (i != 0) { code += ","; }
2265             code += NumToString(array_only_lengths[i].length);
2266           }
2267           code += "];\n";
2268           code += "    ";
2269           // initialize array
2270           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
2271             auto idx = "idx" + NumToString(i);
2272             code += "for (var " + idx + " = 0; " + idx + " < " +
2273                     NumToString(array_only_lengths[i].length) + "; ++" + idx +
2274                     ") {";
2275           }
2276           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
2277             auto idx = "idx" + NumToString(i);
2278             if (i == 0) {
2279               code += name + "[" + idx;
2280             } else {
2281               code += "," + idx;
2282             }
2283           }
2284           code += "] = _o";
2285           for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) {
2286             code += "." + ConvertCase(array_lengths[i].name, Case::kUpperCamel);
2287             if (array_lengths[i].length <= 0) continue;
2288             code += "[idx" + NumToString(j++) + "]";
2289           }
2290           code += ";";
2291           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
2292             code += "}";
2293           }
2294         } else {
2295           code += "_o";
2296           for (size_t i = 0; i < array_lengths.size(); ++i) {
2297             code += "." + ConvertCase(array_lengths[i].name, Case::kUpperCamel);
2298           }
2299           code += ";";
2300         }
2301         code += "\n";
2302       }
2303       array_lengths.pop_back();
2304     }
2305   }
2306 
GenStructPackCall_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::string prefix) const2307   void GenStructPackCall_ObjectAPI(const StructDef &struct_def,
2308                                    std::string *code_ptr,
2309                                    std::string prefix) const {
2310     auto &code = *code_ptr;
2311     for (auto it = struct_def.fields.vec.begin();
2312          it != struct_def.fields.vec.end(); ++it) {
2313       auto &field = **it;
2314       const auto &field_type = field.value.type;
2315       if (field_type.struct_def != nullptr) {
2316         GenStructPackCall_ObjectAPI(*field_type.struct_def, code_ptr,
2317                                     prefix + field.name + "_");
2318       } else {
2319         code += ",\n";
2320         code += prefix + field.name;
2321       }
2322     }
2323   }
2324 
GenTypeGet_ObjectAPI(flatbuffers::Type type,const IDLOptions & opts) const2325   std::string GenTypeGet_ObjectAPI(flatbuffers::Type type,
2326                                    const IDLOptions &opts) const {
2327     auto type_name = GenTypeGet(type);
2328     // Replace to ObjectBaseAPI Type Name
2329     switch (type.base_type) {
2330       case BASE_TYPE_STRUCT: FLATBUFFERS_FALLTHROUGH();  // fall thru
2331       case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();   // fall thru
2332       case BASE_TYPE_VECTOR: {
2333         if (type.struct_def != nullptr) {
2334           auto type_name_length = type.struct_def->name.length();
2335           auto new_type_name =
2336               GenTypeName_ObjectAPI(type.struct_def->name, opts);
2337           type_name.replace(type_name.length() - type_name_length,
2338                             type_name_length, new_type_name);
2339         } else if (type.element == BASE_TYPE_UNION) {
2340           type_name = NamespacedName(*type.enum_def) + "Union";
2341         }
2342         break;
2343       }
2344 
2345       case BASE_TYPE_UNION: {
2346         type_name = NamespacedName(*type.enum_def) + "Union";
2347         break;
2348       }
2349       default: break;
2350     }
2351 
2352     switch (type.base_type) {
2353       case BASE_TYPE_ARRAY: {
2354         type_name = type_name + "[]";
2355         break;
2356       }
2357       case BASE_TYPE_VECTOR: {
2358         type_name = "List<" + type_name + ">";
2359         break;
2360       }
2361       default: break;
2362     }
2363     return type_name;
2364   }
2365 
GenStruct_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const2366   void GenStruct_ObjectAPI(StructDef &struct_def, std::string *code_ptr,
2367                            const IDLOptions &opts) const {
2368     auto &code = *code_ptr;
2369     if (struct_def.attributes.Lookup("private")) {
2370       code += "internal ";
2371     } else {
2372       code += "public ";
2373     }
2374     if (struct_def.attributes.Lookup("csharp_partial")) {
2375       // generate a partial class for this C# struct/table
2376       code += "partial ";
2377     }
2378     auto class_name = GenTypeName_ObjectAPI(struct_def.name, opts);
2379     code += "class " + class_name;
2380     code += "\n{\n";
2381     // Generate Properties
2382     for (auto it = struct_def.fields.vec.begin();
2383          it != struct_def.fields.vec.end(); ++it) {
2384       auto &field = **it;
2385       if (field.deprecated) continue;
2386       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2387       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2388       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2389       if (field.IsScalarOptional()) type_name += "?";
2390       auto camel_name = Name(field);
2391       if (camel_name == struct_def.name) { camel_name += "_"; }
2392       if (opts.cs_gen_json_serializer) {
2393         if (IsUnion(field.value.type)) {
2394           auto utype_name = NamespacedName(*field.value.type.enum_def);
2395           code +=
2396               "  [Newtonsoft.Json.JsonProperty(\"" + field.name + "_type\")]\n";
2397           if (IsVector(field.value.type)) {
2398             code += "  private " + utype_name + "[] " + camel_name + "Type {\n";
2399             code += "    get {\n";
2400             code += "      if (this." + camel_name + " == null) return null;\n";
2401             code += "      var _o = new " + utype_name + "[this." + camel_name +
2402                     ".Count];\n";
2403             code +=
2404                 "      for (var _j = 0; _j < _o.Length; ++_j) { _o[_j] = "
2405                 "this." +
2406                 camel_name + "[_j].Type; }\n";
2407             code += "      return _o;\n";
2408             code += "    }\n";
2409             code += "    set {\n";
2410             code += "      this." + camel_name + " = new List<" + utype_name +
2411                     "Union>();\n";
2412             code += "      for (var _j = 0; _j < value.Length; ++_j) {\n";
2413             code += "        var _o = new " + utype_name + "Union();\n";
2414             code += "        _o.Type = value[_j];\n";
2415             code += "        this." + camel_name + ".Add(_o);\n";
2416             code += "      }\n";
2417             code += "    }\n";
2418             code += "  }\n";
2419           } else {
2420             code += "  private " + utype_name + " " + camel_name + "Type {\n";
2421             code += "    get {\n";
2422             code += "      return this." + camel_name + " != null ? this." +
2423                     camel_name + ".Type : " + utype_name + ".NONE;\n";
2424             code += "    }\n";
2425             code += "    set {\n";
2426             code += "      this." + camel_name + " = new " + utype_name +
2427                     "Union();\n";
2428             code += "      this." + camel_name + ".Type = value;\n";
2429             code += "    }\n";
2430             code += "  }\n";
2431           }
2432         }
2433         code += "  [Newtonsoft.Json.JsonProperty(\"" + field.name + "\")]\n";
2434         if (IsUnion(field.value.type)) {
2435           auto union_name =
2436               (IsVector(field.value.type))
2437                   ? GenTypeGet_ObjectAPI(field.value.type.VectorType(), opts)
2438                   : type_name;
2439           code += "  [Newtonsoft.Json.JsonConverter(typeof(" + union_name +
2440                   "_JsonConverter))]\n";
2441         }
2442         if (field.attributes.Lookup("hash")) {
2443           code += "  [Newtonsoft.Json.JsonIgnore()]\n";
2444         }
2445       }
2446       code += "  public " + type_name + " " + camel_name + " { get; set; }\n";
2447     }
2448     // Generate Constructor
2449     code += "\n";
2450     code += "  public " + class_name + "() {\n";
2451     for (auto it = struct_def.fields.vec.begin();
2452          it != struct_def.fields.vec.end(); ++it) {
2453       auto &field = **it;
2454       if (field.deprecated) continue;
2455       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2456       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2457       auto camel_name = Name(field);
2458       if (camel_name == struct_def.name) { camel_name += "_"; }
2459       code += "    this." + camel_name + " = ";
2460       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2461       if (IsScalar(field.value.type.base_type)) {
2462         code += GenDefaultValue(field) + ";\n";
2463       } else {
2464         switch (field.value.type.base_type) {
2465           case BASE_TYPE_STRUCT: {
2466             if (IsStruct(field.value.type)) {
2467               code += "new " + type_name + "();\n";
2468             } else {
2469               code += "null;\n";
2470             }
2471             break;
2472           }
2473           case BASE_TYPE_ARRAY: {
2474             code += "new " + type_name.substr(0, type_name.length() - 1) +
2475                     NumToString(field.value.type.fixed_length) + "];\n";
2476             break;
2477           }
2478           default: {
2479             code += "null;\n";
2480             break;
2481           }
2482         }
2483       }
2484     }
2485     code += "  }\n";
2486     // Generate Serialization
2487     if (opts.cs_gen_json_serializer &&
2488         parser_.root_struct_def_ == &struct_def) {
2489       code += "\n";
2490       code += "  public static " + class_name +
2491               " DeserializeFromJson(string jsonText) {\n";
2492       code += "    return Newtonsoft.Json.JsonConvert.DeserializeObject<" +
2493               class_name + ">(jsonText);\n";
2494       code += "  }\n";
2495       code += "  public string SerializeToJson() {\n";
2496       code +=
2497           "    return Newtonsoft.Json.JsonConvert.SerializeObject(this, "
2498           "Newtonsoft.Json.Formatting.Indented);\n";
2499       code += "  }\n";
2500     }
2501     if (parser_.root_struct_def_ == &struct_def) {
2502       code += "  public static " + class_name +
2503               " DeserializeFromBinary(byte[] fbBuffer) {\n";
2504       code += "    return " + struct_def.name + ".GetRootAs" + struct_def.name +
2505               "(new ByteBuffer(fbBuffer)).UnPack();\n";
2506       code += "  }\n";
2507       code += "  public byte[] SerializeToBinary() {\n";
2508       code += "    var fbb = new FlatBufferBuilder(0x10000);\n";
2509       code += "    " + struct_def.name + ".Finish" + struct_def.name +
2510               "Buffer(fbb, " + struct_def.name + ".Pack(fbb, this));\n";
2511       code += "    return fbb.DataBuffer.ToSizedArray();\n";
2512       code += "  }\n";
2513     }
2514     code += "}\n\n";
2515   }
2516 
2517   // This tracks the current namespace used to determine if a type need to be
2518   // prefixed by its namespace
2519   const Namespace *cur_name_space_;
2520 };
2521 }  // namespace csharp
2522 
GenerateCSharp(const Parser & parser,const std::string & path,const std::string & file_name)2523 static bool GenerateCSharp(const Parser &parser, const std::string &path,
2524                            const std::string &file_name) {
2525   csharp::CSharpGenerator generator(parser, path, file_name);
2526   return generator.generate();
2527 }
2528 
2529 namespace {
2530 
2531 class CSharpCodeGenerator : public CodeGenerator {
2532  public:
GenerateCode(const Parser & parser,const std::string & path,const std::string & filename)2533   Status GenerateCode(const Parser &parser, const std::string &path,
2534                       const std::string &filename) override {
2535     if (!GenerateCSharp(parser, path, filename)) { return Status::ERROR; }
2536     return Status::OK;
2537   }
2538 
GenerateCode(const uint8_t *,int64_t,const CodeGenOptions &)2539   Status GenerateCode(const uint8_t *, int64_t,
2540                       const CodeGenOptions &) override {
2541     return Status::NOT_IMPLEMENTED;
2542   }
2543 
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)2544   Status GenerateMakeRule(const Parser &parser, const std::string &path,
2545                           const std::string &filename,
2546                           std::string &output) override {
2547     output = JavaCSharpMakeRule(false, parser, path, filename);
2548     return Status::OK;
2549   }
2550 
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)2551   Status GenerateGrpcCode(const Parser &parser, const std::string &path,
2552                           const std::string &filename) override {
2553     (void)parser;
2554     (void)path;
2555     (void)filename;
2556     return Status::NOT_IMPLEMENTED;
2557   }
2558 
GenerateRootFile(const Parser & parser,const std::string & path)2559   Status GenerateRootFile(const Parser &parser,
2560                           const std::string &path) override {
2561     (void)parser;
2562     (void)path;
2563     return Status::NOT_IMPLEMENTED;
2564   }
2565 
IsSchemaOnly() const2566   bool IsSchemaOnly() const override { return true; }
2567 
SupportsBfbsGeneration() const2568   bool SupportsBfbsGeneration() const override { return false; }
2569 
SupportsRootFileGeneration() const2570   bool SupportsRootFileGeneration() const override { return false; }
2571 
Language() const2572   IDLOptions::Language Language() const override { return IDLOptions::kCSharp; }
2573 
LanguageName() const2574   std::string LanguageName() const override { return "CSharp"; }
2575 };
2576 }  // namespace
2577 
NewCSharpCodeGenerator()2578 std::unique_ptr<CodeGenerator> NewCSharpCodeGenerator() {
2579   return std::unique_ptr<CSharpCodeGenerator>(new CSharpCodeGenerator());
2580 }
2581 
2582 }  // namespace flatbuffers
2583