• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Dan Field
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 #include <cassert>
19 
20 #include "flatbuffers/code_generators.h"
21 #include "flatbuffers/flatbuffers.h"
22 #include "flatbuffers/idl.h"
23 #include "flatbuffers/util.h"
24 #include "idl_namer.h"
25 
26 namespace flatbuffers {
27 
28 namespace dart {
29 
30 namespace {
31 
DartDefaultConfig()32 static Namer::Config DartDefaultConfig() {
33   return { /*types=*/Case::kUpperCamel,
34            /*constants=*/Case::kScreamingSnake,
35            /*methods=*/Case::kLowerCamel,
36            /*functions=*/Case::kUnknown,  // unused.
37            /*fields=*/Case::kLowerCamel,
38            /*variables=*/Case::kLowerCamel,
39            /*variants=*/Case::kKeep,
40            /*enum_variant_seperator=*/".",
41            /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
42            /*namespaces=*/Case::kSnake2,
43            /*namespace_seperator=*/".",
44            /*object_prefix=*/"",
45            /*object_suffix=*/"T",
46            /*keyword_prefix=*/"$",
47            /*keyword_suffix=*/"",
48            /*filenames=*/Case::kKeep,
49            /*directories=*/Case::kKeep,
50            /*output_path=*/"",
51            /*filename_suffix=*/"_generated",
52            /*filename_extension=*/".dart" };
53 }
54 
DartKeywords()55 static std::set<std::string> DartKeywords() {
56   // see https://www.dartlang.org/guides/language/language-tour#keywords
57   // yield*, async*, and sync* shouldn't be proble
58   return {
59     "abstract", "else",       "import",    "show",     "as",        "enum",
60     "in",       "static",     "assert",    "export",   "interface", "super",
61     "async",    "extends",    "is",        "switch",   "await",     "extension",
62     "late",     "sync",       "break",     "external", "library",   "this",
63     "case",     "factory",    "mixin",     "throw",    "catch",     "false",
64     "new",      "true",       "class",     "final",    "null",      "try",
65     "const",    "finally",    "on",        "typedef",  "continue",  "for",
66     "operator", "var",        "covariant", "Function", "part",      "void",
67     "default",  "get",        "required",  "while",    "deferred",  "hide",
68     "rethrow",  "with",       "do",        "if",       "return",    "yield",
69     "dynamic",  "implements", "set",
70   };
71 }
72 } // namespace
73 
74 const std::string _kFb = "fb";
75 
76 // Iterate through all definitions we haven't generate code for (enums, structs,
77 // and tables) and output them to a single file.
78 class DartGenerator : public BaseGenerator {
79  public:
80   typedef std::map<std::string, std::string> namespace_code_map;
81 
DartGenerator(const Parser & parser,const std::string & path,const std::string & file_name)82   DartGenerator(const Parser &parser, const std::string &path,
83                 const std::string &file_name)
84       : BaseGenerator(parser, path, file_name, "", ".", "dart"),
85         namer_(WithFlagOptions(DartDefaultConfig(), parser.opts, path),
86                DartKeywords()) {}
87   // Iterate through all definitions we haven't generate code for (enums,
88   // structs, and tables) and output them to a single file.
generate()89   bool generate() {
90     std::string code;
91     namespace_code_map namespace_code;
92     GenerateEnums(namespace_code);
93     GenerateStructs(namespace_code);
94 
95     for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) {
96       code.clear();
97       code = code + "// " + FlatBuffersGeneratedWarning() + "\n";
98       code = code +
99              "// ignore_for_file: unused_import, unused_field, unused_element, "
100              "unused_local_variable\n\n";
101 
102       if (!kv->first.empty()) { code += "library " + kv->first + ";\n\n"; }
103 
104       code += "import 'dart:typed_data' show Uint8List;\n";
105       code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb +
106               ";\n\n";
107 
108       for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end();
109            ++kv2) {
110         if (kv2->first != kv->first) {
111           code += "import './" + Filename(kv2->first, /*path=*/false) +
112                   "' as " + ImportAliasName(kv2->first) + ";\n";
113         }
114       }
115       code += "\n";
116       code += kv->second;
117 
118       if (!SaveFile(Filename(kv->first).c_str(), code, false)) { return false; }
119     }
120     return true;
121   }
122 
Filename(const std::string & suffix,bool path=true) const123   std::string Filename(const std::string &suffix, bool path = true) const {
124     return (path ? path_ : "") +
125            namer_.File(file_name_ + (suffix.empty() ? "" : "_" + suffix));
126   }
127 
128  private:
ImportAliasName(const std::string & ns)129   static std::string ImportAliasName(const std::string &ns) {
130     std::string ret;
131     ret.assign(ns);
132     size_t pos = ret.find('.');
133     while (pos != std::string::npos) {
134       ret.replace(pos, 1, "_");
135       pos = ret.find('.', pos + 1);
136     }
137 
138     return ret;
139   }
140 
GenerateEnums(namespace_code_map & namespace_code)141   void GenerateEnums(namespace_code_map &namespace_code) {
142     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
143          ++it) {
144       auto &enum_def = **it;
145       GenEnum(enum_def, namespace_code);
146     }
147   }
148 
GenerateStructs(namespace_code_map & namespace_code)149   void GenerateStructs(namespace_code_map &namespace_code) {
150     for (auto it = parser_.structs_.vec.begin();
151          it != parser_.structs_.vec.end(); ++it) {
152       auto &struct_def = **it;
153       GenStruct(struct_def, namespace_code);
154     }
155   }
156 
157   // Generate a documentation comment, if available.
GenDocComment(const std::vector<std::string> & dc,const char * indent,std::string & code)158   static void GenDocComment(const std::vector<std::string> &dc,
159                             const char *indent, std::string &code) {
160     for (auto it = dc.begin(); it != dc.end(); ++it) {
161       if (indent) code += indent;
162       code += "/// " + *it + "\n";
163     }
164   }
165 
166   // Generate an enum declaration and an enum string lookup table.
GenEnum(EnumDef & enum_def,namespace_code_map & namespace_code)167   void GenEnum(EnumDef &enum_def, namespace_code_map &namespace_code) {
168     if (enum_def.generated) return;
169     std::string &code =
170         namespace_code[namer_.Namespace(*enum_def.defined_namespace)];
171     GenDocComment(enum_def.doc_comment, "", code);
172 
173     const std::string enum_type =
174         namer_.Type(enum_def) + (enum_def.is_union ? "TypeId" : "");
175     const bool is_bit_flags =
176         enum_def.attributes.Lookup("bit_flags") != nullptr;
177     // The flatbuffer schema language allows bit flag enums to potentially have
178     // a default value of zero, even if it's not a valid enum value...
179     const bool permit_zero = is_bit_flags;
180 
181     code += "class " + enum_type + " {\n";
182     code += "  final int value;\n";
183     code += "  const " + enum_type + "._(this.value);\n\n";
184     code += "  factory " + enum_type + ".fromValue(int value) {\n";
185     code += "    final result = values[value];\n";
186     code += "    if (result == null) {\n";
187     if (permit_zero) {
188       code += "      if (value == 0) {\n";
189       code += "        return " + enum_type + "._(0);\n";
190       code += "      } else {\n";
191     }
192     code += "        throw StateError('Invalid value $value for bit flag enum ";
193     code += enum_type + "');\n";
194     if (permit_zero) { code += "      }\n"; }
195     code += "    }\n";
196 
197     code += "    return result;\n";
198     code += "  }\n\n";
199 
200     code += "  static " + enum_type + "? _createOrNull(int? value) => \n";
201     code +=
202         "      value == null ? null : " + enum_type + ".fromValue(value);\n\n";
203 
204     // this is meaningless for bit_flags
205     // however, note that unlike "regular" dart enums this enum can still have
206     // holes.
207     if (!is_bit_flags) {
208       code += "  static const int minValue = " +
209               enum_def.ToString(*enum_def.MinValue()) + ";\n";
210       code += "  static const int maxValue = " +
211               enum_def.ToString(*enum_def.MaxValue()) + ";\n";
212     }
213 
214     code +=
215         "  static bool containsValue(int value) =>"
216         " values.containsKey(value);\n\n";
217 
218     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
219       auto &ev = **it;
220       const auto enum_var = namer_.Variant(ev);
221 
222       if (!ev.doc_comment.empty()) {
223         if (it != enum_def.Vals().begin()) { code += '\n'; }
224         GenDocComment(ev.doc_comment, "  ", code);
225       }
226       code += "  static const " + enum_type + " " + enum_var + " = " +
227               enum_type + "._(" + enum_def.ToString(ev) + ");\n";
228     }
229 
230     code += "  static const Map<int, " + enum_type + "> values = {\n";
231     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
232       auto &ev = **it;
233       const auto enum_var = namer_.Variant(ev);
234       if (it != enum_def.Vals().begin()) code += ",\n";
235       code += "    " + enum_def.ToString(ev) + ": " + enum_var;
236     }
237     code += "};\n\n";
238 
239     code += "  static const " + _kFb + ".Reader<" + enum_type + "> reader = _" +
240             enum_type + "Reader();\n\n";
241     code += "  @override\n";
242     code += "  String toString() {\n";
243     code += "    return '" + enum_type + "{value: $value}';\n";
244     code += "  }\n";
245     code += "}\n\n";
246 
247     GenEnumReader(enum_def, enum_type, code);
248   }
249 
GenEnumReader(EnumDef & enum_def,const std::string & enum_type,std::string & code)250   void GenEnumReader(EnumDef &enum_def, const std::string &enum_type,
251                      std::string &code) {
252     code += "class _" + enum_type + "Reader extends " + _kFb + ".Reader<" +
253             enum_type + "> {\n";
254     code += "  const _" + enum_type + "Reader();\n\n";
255     code += "  @override\n";
256     code += "  int get size => " + EnumSize(enum_def.underlying_type) + ";\n\n";
257     code += "  @override\n";
258     code += "  " + enum_type + " read(" + _kFb +
259             ".BufferContext bc, int offset) =>\n";
260     code += "      " + enum_type + ".fromValue(const " + _kFb + "." +
261             GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n";
262     code += "}\n\n";
263   }
264 
GenType(const Type & type)265   std::string GenType(const Type &type) {
266     switch (type.base_type) {
267       case BASE_TYPE_BOOL: return "Bool";
268       case BASE_TYPE_CHAR: return "Int8";
269       case BASE_TYPE_UTYPE:
270       case BASE_TYPE_UCHAR: return "Uint8";
271       case BASE_TYPE_SHORT: return "Int16";
272       case BASE_TYPE_USHORT: return "Uint16";
273       case BASE_TYPE_INT: return "Int32";
274       case BASE_TYPE_UINT: return "Uint32";
275       case BASE_TYPE_LONG: return "Int64";
276       case BASE_TYPE_ULONG: return "Uint64";
277       case BASE_TYPE_FLOAT: return "Float32";
278       case BASE_TYPE_DOUBLE: return "Float64";
279       case BASE_TYPE_STRING: return "String";
280       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
281       case BASE_TYPE_STRUCT: return namer_.Type(*type.struct_def);
282       case BASE_TYPE_UNION: return namer_.Type(*type.enum_def) + "TypeId";
283       default: return "Table";
284     }
285   }
286 
EnumSize(const Type & type)287   static std::string EnumSize(const Type &type) {
288     switch (type.base_type) {
289       case BASE_TYPE_BOOL:
290       case BASE_TYPE_CHAR:
291       case BASE_TYPE_UTYPE:
292       case BASE_TYPE_UCHAR: return "1";
293       case BASE_TYPE_SHORT:
294       case BASE_TYPE_USHORT: return "2";
295       case BASE_TYPE_INT:
296       case BASE_TYPE_UINT:
297       case BASE_TYPE_FLOAT: return "4";
298       case BASE_TYPE_LONG:
299       case BASE_TYPE_ULONG:
300       case BASE_TYPE_DOUBLE: return "8";
301       default: return "1";
302     }
303   }
304 
GenReaderTypeName(const Type & type,Namespace * current_namespace,const FieldDef & def,bool parent_is_vector=false,bool lazy=true,bool constConstruct=true)305   std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
306                                 const FieldDef &def,
307                                 bool parent_is_vector = false, bool lazy = true,
308                                 bool constConstruct = true) {
309     std::string prefix = (constConstruct ? "const " : "") + _kFb;
310     if (type.base_type == BASE_TYPE_BOOL) {
311       return prefix + ".BoolReader()";
312     } else if (IsVector(type)) {
313       if (!type.VectorType().enum_def) {
314         if (type.VectorType().base_type == BASE_TYPE_CHAR) {
315           return prefix + ".Int8ListReader(" + (lazy ? ")" : "lazy: false)");
316         }
317         if (type.VectorType().base_type == BASE_TYPE_UCHAR) {
318           return prefix + ".Uint8ListReader(" + (lazy ? ")" : "lazy: false)");
319         }
320       }
321       return prefix + ".ListReader<" +
322              GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" +
323              GenReaderTypeName(type.VectorType(), current_namespace, def, true,
324                                true, false) +
325              (lazy ? ")" : ", lazy: false)");
326     } else if (IsString(type)) {
327       return prefix + ".StringReader()";
328     }
329     if (IsScalar(type.base_type)) {
330       if (type.enum_def && parent_is_vector) {
331         return GenDartTypeName(type, current_namespace, def) + ".reader";
332       }
333       return prefix + "." + GenType(type) + "Reader()";
334     } else {
335       return GenDartTypeName(type, current_namespace, def) + ".reader";
336     }
337   }
338 
GenDartTypeName(const Type & type,Namespace * current_namespace,const FieldDef & def,std::string struct_type_suffix="")339   std::string GenDartTypeName(const Type &type, Namespace *current_namespace,
340                               const FieldDef &def,
341                               std::string struct_type_suffix = "") {
342     if (type.enum_def) {
343       if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) {
344         return namer_.Type(*type.enum_def) + "TypeId";
345       } else if (type.enum_def->is_union) {
346         return "dynamic";
347       } else if (type.base_type != BASE_TYPE_VECTOR) {
348         return namer_.Type(*type.enum_def);
349       }
350     }
351 
352     switch (type.base_type) {
353       case BASE_TYPE_BOOL: return "bool";
354       case BASE_TYPE_LONG:
355       case BASE_TYPE_ULONG:
356       case BASE_TYPE_INT:
357       case BASE_TYPE_UINT:
358       case BASE_TYPE_SHORT:
359       case BASE_TYPE_USHORT:
360       case BASE_TYPE_CHAR:
361       case BASE_TYPE_UCHAR: return "int";
362       case BASE_TYPE_FLOAT:
363       case BASE_TYPE_DOUBLE: return "double";
364       case BASE_TYPE_STRING: return "String";
365       case BASE_TYPE_STRUCT:
366         return MaybeWrapNamespace(
367             namer_.Type(*type.struct_def) + struct_type_suffix,
368             current_namespace, def);
369       case BASE_TYPE_VECTOR:
370         return "List<" +
371                GenDartTypeName(type.VectorType(), current_namespace, def,
372                                struct_type_suffix) +
373                ">";
374       default: assert(0); return "dynamic";
375     }
376   }
377 
GenDartTypeName(const Type & type,Namespace * current_namespace,const FieldDef & def,bool nullable,std::string struct_type_suffix)378   std::string GenDartTypeName(const Type &type, Namespace *current_namespace,
379                               const FieldDef &def, bool nullable,
380                               std::string struct_type_suffix) {
381     std::string typeName =
382         GenDartTypeName(type, current_namespace, def, struct_type_suffix);
383     if (nullable && typeName != "dynamic") typeName += "?";
384     return typeName;
385   }
386 
MaybeWrapNamespace(const std::string & type_name,Namespace * current_ns,const FieldDef & field) const387   std::string MaybeWrapNamespace(const std::string &type_name,
388                                  Namespace *current_ns,
389                                  const FieldDef &field) const {
390     const std::string current_namespace = namer_.Namespace(*current_ns);
391     const std::string field_namespace =
392         field.value.type.struct_def
393             ? namer_.Namespace(*field.value.type.struct_def->defined_namespace)
394         : field.value.type.enum_def
395             ? namer_.Namespace(*field.value.type.enum_def->defined_namespace)
396             : "";
397 
398     if (field_namespace != "" && field_namespace != current_namespace) {
399       return ImportAliasName(field_namespace) + "." + type_name;
400     } else {
401       return type_name;
402     }
403   }
404 
405   // Generate an accessor struct with constructor for a flatbuffers struct.
GenStruct(const StructDef & struct_def,namespace_code_map & namespace_code)406   void GenStruct(const StructDef &struct_def,
407                  namespace_code_map &namespace_code) {
408     if (struct_def.generated) return;
409 
410     std::string &code =
411         namespace_code[namer_.Namespace(*struct_def.defined_namespace)];
412 
413     const auto &struct_type = namer_.Type(struct_def);
414 
415     // Emit constructor
416 
417     GenDocComment(struct_def.doc_comment, "", code);
418 
419     auto reader_name = "_" + struct_type + "Reader";
420     auto builder_name = struct_type + "Builder";
421     auto object_builder_name = struct_type + "ObjectBuilder";
422 
423     std::string reader_code, builder_code;
424 
425     code += "class " + struct_type + " {\n";
426 
427     code += "  " + struct_type + "._(this._bc, this._bcOffset);\n";
428     if (!struct_def.fixed) {
429       code += "  factory " + struct_type + "(List<int> bytes) {\n";
430       code +=
431           "    final rootRef = " + _kFb + ".BufferContext.fromBytes(bytes);\n";
432       code += "    return reader.read(rootRef, 0);\n";
433       code += "  }\n";
434     }
435 
436     code += "\n";
437     code += "  static const " + _kFb + ".Reader<" + struct_type +
438             "> reader = " + reader_name + "();\n\n";
439 
440     code += "  final " + _kFb + ".BufferContext _bc;\n";
441     code += "  final int _bcOffset;\n\n";
442 
443     std::vector<std::pair<int, FieldDef *>> non_deprecated_fields;
444     for (auto it = struct_def.fields.vec.begin();
445          it != struct_def.fields.vec.end(); ++it) {
446       FieldDef &field = **it;
447       if (field.deprecated) continue;
448       auto offset = static_cast<int>(it - struct_def.fields.vec.begin());
449       non_deprecated_fields.push_back(std::make_pair(offset, &field));
450     }
451 
452     GenImplementationGetters(struct_def, non_deprecated_fields, code);
453 
454     if (parser_.opts.generate_object_based_api) {
455       code +=
456           "\n" + GenStructObjectAPIUnpack(struct_def, non_deprecated_fields);
457 
458       code += "\n  static int pack(fb.Builder fbBuilder, " +
459               namer_.ObjectType(struct_def) + "? object) {\n";
460       code += "    if (object == null) return 0;\n";
461       code += "    return object.pack(fbBuilder);\n";
462       code += "  }\n";
463     }
464 
465     code += "}\n\n";
466 
467     if (parser_.opts.generate_object_based_api) {
468       code += GenStructObjectAPI(struct_def, non_deprecated_fields);
469     }
470 
471     GenReader(struct_def, reader_name, reader_code);
472     GenBuilder(struct_def, non_deprecated_fields, builder_name, builder_code);
473     GenObjectBuilder(struct_def, non_deprecated_fields, object_builder_name,
474                      builder_code);
475 
476     code += reader_code;
477     code += builder_code;
478   }
479 
480   // Generate an accessor struct with constructor for a flatbuffers struct.
GenStructObjectAPI(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields)481   std::string GenStructObjectAPI(
482       const StructDef &struct_def,
483       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields) {
484     std::string code;
485     GenDocComment(struct_def.doc_comment, "", code);
486 
487     std::string object_type = namer_.ObjectType(struct_def);
488     code += "class " + object_type + " implements " + _kFb + ".Packable {\n";
489 
490     std::string constructor_args;
491     for (auto it = non_deprecated_fields.begin();
492          it != non_deprecated_fields.end(); ++it) {
493       const FieldDef &field = *it->second;
494 
495       const std::string field_name = namer_.Field(field);
496       const std::string defaultValue = getDefaultValue(field.value);
497       const std::string type_name =
498           GenDartTypeName(field.value.type, struct_def.defined_namespace, field,
499                           defaultValue.empty() && !struct_def.fixed, "T");
500 
501       GenDocComment(field.doc_comment, "  ", code);
502       code += "  " + type_name + " " + field_name + ";\n";
503 
504       if (!constructor_args.empty()) constructor_args += ",\n";
505       constructor_args += "      ";
506       constructor_args += (struct_def.fixed ? "required " : "");
507       constructor_args += "this." + field_name;
508       if (!struct_def.fixed && !defaultValue.empty()) {
509         if (IsEnum(field.value.type)) {
510           auto &enum_def = *field.value.type.enum_def;
511           if (auto val = enum_def.FindByValue(defaultValue)) {
512             constructor_args += " = " + namer_.EnumVariant(enum_def, *val);
513           } else {
514             constructor_args += " = const " + namer_.Type(enum_def) + "._(" +
515                                 defaultValue + ")";
516           }
517         } else {
518           constructor_args += " = " + defaultValue;
519         }
520       }
521     }
522 
523     if (!constructor_args.empty()) {
524       code += "\n  " + object_type + "({\n" + constructor_args + "});\n\n";
525     }
526 
527     code += GenStructObjectAPIPack(struct_def, non_deprecated_fields);
528     code += "\n";
529     code += GenToString(object_type, non_deprecated_fields);
530 
531     code += "}\n\n";
532     return code;
533   }
534 
535   // Generate function `StructNameT unpack()`
GenStructObjectAPIUnpack(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields)536   std::string GenStructObjectAPIUnpack(
537       const StructDef &struct_def,
538       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields) {
539     std::string constructor_args;
540     for (auto it = non_deprecated_fields.begin();
541          it != non_deprecated_fields.end(); ++it) {
542       const FieldDef &field = *it->second;
543 
544       const std::string field_name = namer_.Field(field);
545       if (!constructor_args.empty()) constructor_args += ",\n";
546       constructor_args += "      " + field_name + ": ";
547 
548       const Type &type = field.value.type;
549       std::string defaultValue = getDefaultValue(field.value);
550       bool isNullable = defaultValue.empty() && !struct_def.fixed;
551       std::string nullableValueAccessOperator = isNullable ? "?" : "";
552       if (type.base_type == BASE_TYPE_STRUCT) {
553         constructor_args +=
554             field_name + nullableValueAccessOperator + ".unpack()";
555       } else if (type.base_type == BASE_TYPE_VECTOR) {
556         if (type.VectorType().base_type == BASE_TYPE_STRUCT) {
557           constructor_args += field_name + nullableValueAccessOperator +
558                               ".map((e) => e.unpack()).toList()";
559         } else {
560           constructor_args +=
561               GenReaderTypeName(field.value.type, struct_def.defined_namespace,
562                                 field, false, false);
563           constructor_args += ".vTableGet";
564           std::string offset = NumToString(field.value.offset);
565           constructor_args +=
566               isNullable
567                   ? "Nullable(_bc, _bcOffset, " + offset + ")"
568                   : "(_bc, _bcOffset, " + offset + ", " + defaultValue + ")";
569         }
570       } else {
571         constructor_args += field_name;
572       }
573     }
574 
575     const std::string object_type = namer_.ObjectType(struct_def);
576     std::string code = "  " + object_type + " unpack() => " + object_type + "(";
577     if (!constructor_args.empty()) code += "\n" + constructor_args;
578     code += ");\n";
579     return code;
580   }
581 
582   // Generate function `StructNameT pack()`
GenStructObjectAPIPack(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields)583   std::string GenStructObjectAPIPack(
584       const StructDef &struct_def,
585       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields) {
586     std::string code;
587 
588     code += "  @override\n";
589     code += "  int pack(fb.Builder fbBuilder) {\n";
590     code += GenObjectBuilderImplementation(struct_def, non_deprecated_fields,
591                                            false, true);
592     code += "  }\n";
593     return code;
594   }
595 
NamespaceAliasFromUnionType(Namespace * root_namespace,const Type & type)596   std::string NamespaceAliasFromUnionType(Namespace *root_namespace,
597                                           const Type &type) {
598     const std::vector<std::string> qualified_name_parts =
599         type.struct_def->defined_namespace->components;
600     if (std::equal(root_namespace->components.begin(),
601                    root_namespace->components.end(),
602                    qualified_name_parts.begin())) {
603       return namer_.Type(*type.struct_def);
604     }
605 
606     std::string ns;
607 
608     for (auto it = qualified_name_parts.begin();
609          it != qualified_name_parts.end(); ++it) {
610       auto &part = *it;
611 
612       for (size_t i = 0; i < part.length(); i++) {
613         if (i && !isdigit(part[i]) && part[i] == CharToUpper(part[i])) {
614           ns += "_";
615           ns += CharToLower(part[i]);
616         } else {
617           ns += CharToLower(part[i]);
618         }
619       }
620       if (it != qualified_name_parts.end() - 1) { ns += "_"; }
621     }
622 
623     return ns + "." + namer_.Type(*type.struct_def);
624   }
625 
GenImplementationGetters(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,std::string & code)626   void GenImplementationGetters(
627       const StructDef &struct_def,
628       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
629       std::string &code) {
630     for (auto it = non_deprecated_fields.begin();
631          it != non_deprecated_fields.end(); ++it) {
632       const FieldDef &field = *it->second;
633 
634       const std::string field_name = namer_.Field(field);
635       const std::string defaultValue = getDefaultValue(field.value);
636       const bool isNullable = defaultValue.empty() && !struct_def.fixed;
637       const std::string type_name =
638           GenDartTypeName(field.value.type, struct_def.defined_namespace, field,
639                           isNullable, "");
640 
641       GenDocComment(field.doc_comment, "  ", code);
642 
643       code += "  " + type_name + " get " + field_name;
644       if (field.value.type.base_type == BASE_TYPE_UNION) {
645         code += " {\n";
646         code += "    switch (" + field_name + "Type?.value) {\n";
647         const auto &enum_def = *field.value.type.enum_def;
648         for (auto en_it = enum_def.Vals().begin() + 1;
649              en_it != enum_def.Vals().end(); ++en_it) {
650           const auto &ev = **en_it;
651           const auto enum_name = NamespaceAliasFromUnionType(
652               enum_def.defined_namespace, ev.union_type);
653           code += "      case " + enum_def.ToString(ev) + ": return " +
654                   enum_name + ".reader.vTableGetNullable(_bc, _bcOffset, " +
655                   NumToString(field.value.offset) + ");\n";
656         }
657         code += "      default: return null;\n";
658         code += "    }\n";
659         code += "  }\n";
660       } else {
661         code += " => ";
662         if (field.value.type.enum_def &&
663             field.value.type.base_type != BASE_TYPE_VECTOR) {
664           code += GenDartTypeName(field.value.type,
665                                   struct_def.defined_namespace, field) +
666                   (isNullable ? "._createOrNull(" : ".fromValue(");
667         }
668 
669         code += GenReaderTypeName(field.value.type,
670                                   struct_def.defined_namespace, field);
671         if (struct_def.fixed) {
672           code +=
673               ".read(_bc, _bcOffset + " + NumToString(field.value.offset) + ")";
674         } else {
675           code += ".vTableGet";
676           std::string offset = NumToString(field.value.offset);
677           if (isNullable) {
678             code += "Nullable(_bc, _bcOffset, " + offset + ")";
679           } else {
680             code += "(_bc, _bcOffset, " + offset + ", " + defaultValue + ")";
681           }
682         }
683         if (field.value.type.enum_def &&
684             field.value.type.base_type != BASE_TYPE_VECTOR) {
685           code += ")";
686         }
687         code += ";\n";
688       }
689     }
690 
691     code += "\n";
692     code += GenToString(namer_.Type(struct_def), non_deprecated_fields);
693   }
694 
GenToString(const std::string & object_name,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields)695   std::string GenToString(
696       const std::string &object_name,
697       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields) {
698     std::string code;
699     code += "  @override\n";
700     code += "  String toString() {\n";
701     code += "    return '" + object_name + "{";
702     for (auto it = non_deprecated_fields.begin();
703          it != non_deprecated_fields.end(); ++it) {
704       const std::string field = namer_.Field(*it->second);
705       // We need to escape the fact that some fields have $ in the name which is
706       // also used in symbol/string substitution.
707       std::string escaped_field;
708       for (size_t i = 0; i < field.size(); i++) {
709         if (field[i] == '$') escaped_field.push_back('\\');
710         escaped_field.push_back(field[i]);
711       }
712       code += escaped_field + ": ${" + field + "}";
713       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
714     }
715     code += "}';\n";
716     code += "  }\n";
717     return code;
718   }
719 
getDefaultValue(const Value & value) const720   std::string getDefaultValue(const Value &value) const {
721     if (!value.constant.empty() && value.constant != "0") {
722       if (IsBool(value.type.base_type)) {
723         return "true";
724       } else if (value.constant == "nan" || value.constant == "+nan" ||
725                  value.constant == "-nan") {
726         return "double.nan";
727       } else if (value.constant == "inf" || value.constant == "+inf") {
728         return "double.infinity";
729       } else if (value.constant == "-inf") {
730         return "double.negativeInfinity";
731       } else {
732         return value.constant;
733       }
734     } else if (IsBool(value.type.base_type)) {
735       return "false";
736     } else if (IsScalar(value.type.base_type) && !IsUnion(value.type)) {
737       return "0";
738     } else {
739       return "";
740     }
741   }
742 
GenReader(const StructDef & struct_def,const std::string & reader_name,std::string & code)743   void GenReader(const StructDef &struct_def, const std::string &reader_name,
744                  std::string &code) {
745     const auto struct_type = namer_.Type(struct_def);
746 
747     code += "class " + reader_name + " extends " + _kFb;
748     if (struct_def.fixed) {
749       code += ".StructReader<";
750     } else {
751       code += ".TableReader<";
752     }
753     code += struct_type + "> {\n";
754     code += "  const " + reader_name + "();\n\n";
755 
756     if (struct_def.fixed) {
757       code += "  @override\n";
758       code += "  int get size => " + NumToString(struct_def.bytesize) + ";\n\n";
759     }
760     code += "  @override\n";
761     code += "  " + struct_type +
762             " createObject(fb.BufferContext bc, int offset) => \n    " +
763             struct_type + "._(bc, offset);\n";
764     code += "}\n\n";
765   }
766 
GenBuilder(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,const std::string & builder_name,std::string & code)767   void GenBuilder(
768       const StructDef &struct_def,
769       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
770       const std::string &builder_name, std::string &code) {
771     if (non_deprecated_fields.size() == 0) { return; }
772 
773     code += "class " + builder_name + " {\n";
774     code += "  " + builder_name + "(this.fbBuilder);\n\n";
775     code += "  final " + _kFb + ".Builder fbBuilder;\n\n";
776 
777     if (struct_def.fixed) {
778       StructBuilderBody(struct_def, non_deprecated_fields, code);
779     } else {
780       TableBuilderBody(struct_def, non_deprecated_fields, code);
781     }
782 
783     code += "}\n\n";
784   }
785 
StructBuilderBody(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,std::string & code)786   void StructBuilderBody(
787       const StructDef &struct_def,
788       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
789       std::string &code) {
790     code += "  int finish(";
791     for (auto it = non_deprecated_fields.begin();
792          it != non_deprecated_fields.end(); ++it) {
793       const FieldDef &field = *it->second;
794       const std::string field_name = namer_.Field(field);
795 
796       if (IsStruct(field.value.type)) {
797         code += "fb.StructBuilder";
798       } else {
799         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
800                                 field);
801       }
802       code += " " + field_name;
803       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
804     }
805     code += ") {\n";
806 
807     for (auto it = non_deprecated_fields.rbegin();
808          it != non_deprecated_fields.rend(); ++it) {
809       const FieldDef &field = *it->second;
810       const std::string field_name = namer_.Field(field);
811 
812       if (field.padding) {
813         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
814       }
815 
816       if (IsStruct(field.value.type)) {
817         code += "    " + field_name + "();\n";
818       } else {
819         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
820         code += field_name;
821         if (field.value.type.enum_def) { code += ".value"; }
822         code += ");\n";
823       }
824     }
825     code += "    return fbBuilder.offset;\n";
826     code += "  }\n\n";
827   }
828 
TableBuilderBody(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,std::string & code)829   void TableBuilderBody(
830       const StructDef &struct_def,
831       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
832       std::string &code) {
833     code += "  void begin() {\n";
834     code += "    fbBuilder.startTable(" +
835             NumToString(struct_def.fields.vec.size()) + ");\n";
836     code += "  }\n\n";
837 
838     for (auto it = non_deprecated_fields.begin();
839          it != non_deprecated_fields.end(); ++it) {
840       const auto &field = *it->second;
841       const auto offset = it->first;
842       const std::string add_field = namer_.Method("add", field);
843       const std::string field_var = namer_.Variable(field);
844 
845       if (IsScalar(field.value.type.base_type)) {
846         code += "  int " + add_field + "(";
847         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
848                                 field);
849         code += "? " + field_var + ") {\n";
850         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
851                 NumToString(offset) + ", ";
852         code += field_var;
853         if (field.value.type.enum_def) { code += "?.value"; }
854         code += ");\n";
855       } else if (IsStruct(field.value.type)) {
856         code += "  int " + add_field + "(int offset) {\n";
857         code +=
858             "    fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n";
859       } else {
860         code += "  int " + add_field + "Offset(int? offset) {\n";
861         code +=
862             "    fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n";
863       }
864       code += "    return fbBuilder.offset;\n";
865       code += "  }\n";
866     }
867 
868     code += "\n";
869     code += "  int finish() {\n";
870     code += "    return fbBuilder.endTable();\n";
871     code += "  }\n";
872   }
873 
GenObjectBuilder(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,const std::string & builder_name,std::string & code)874   void GenObjectBuilder(
875       const StructDef &struct_def,
876       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
877       const std::string &builder_name, std::string &code) {
878     code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n";
879     for (auto it = non_deprecated_fields.begin();
880          it != non_deprecated_fields.end(); ++it) {
881       const FieldDef &field = *it->second;
882 
883       code += "  final " +
884               GenDartTypeName(field.value.type, struct_def.defined_namespace,
885                               field, !struct_def.fixed, "ObjectBuilder") +
886               " _" + namer_.Variable(field) + ";\n";
887     }
888     code += "\n";
889     code += "  " + builder_name + "(";
890 
891     if (non_deprecated_fields.size() != 0) {
892       code += "{\n";
893       for (auto it = non_deprecated_fields.begin();
894            it != non_deprecated_fields.end(); ++it) {
895         const FieldDef &field = *it->second;
896 
897         code += "    ";
898         code += (struct_def.fixed ? "required " : "") +
899                 GenDartTypeName(field.value.type, struct_def.defined_namespace,
900                                 field, !struct_def.fixed, "ObjectBuilder") +
901                 " " + namer_.Variable(field) + ",\n";
902       }
903       code += "  })\n";
904       code += "      : ";
905       for (auto it = non_deprecated_fields.begin();
906            it != non_deprecated_fields.end(); ++it) {
907         const FieldDef &field = *it->second;
908 
909         code += "_" + namer_.Variable(field) + " = " + namer_.Variable(field);
910         if (it == non_deprecated_fields.end() - 1) {
911           code += ";\n\n";
912         } else {
913           code += ",\n        ";
914         }
915       }
916     } else {
917       code += ");\n\n";
918     }
919 
920     code += "  /// Finish building, and store into the [fbBuilder].\n";
921     code += "  @override\n";
922     code += "  int finish(" + _kFb + ".Builder fbBuilder) {\n";
923     code += GenObjectBuilderImplementation(struct_def, non_deprecated_fields);
924     code += "  }\n\n";
925 
926     code += "  /// Convenience method to serialize to byte list.\n";
927     code += "  @override\n";
928     code += "  Uint8List toBytes([String? fileIdentifier]) {\n";
929     code += "    final fbBuilder = " + _kFb +
930             ".Builder(deduplicateTables: false);\n";
931     code += "    fbBuilder.finish(finish(fbBuilder), fileIdentifier);\n";
932     code += "    return fbBuilder.buffer;\n";
933     code += "  }\n";
934     code += "}\n";
935   }
936 
GenObjectBuilderImplementation(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,bool prependUnderscore=true,bool pack=false)937   std::string GenObjectBuilderImplementation(
938       const StructDef &struct_def,
939       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
940       bool prependUnderscore = true, bool pack = false) {
941     std::string code;
942     for (auto it = non_deprecated_fields.begin();
943          it != non_deprecated_fields.end(); ++it) {
944       const FieldDef &field = *it->second;
945 
946       if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type))
947         continue;
948 
949       std::string offset_name = namer_.Variable(field) + "Offset";
950       std::string field_name =
951           (prependUnderscore ? "_" : "") + namer_.Variable(field);
952       // custom handling for fixed-sized struct in pack()
953       if (pack && IsVector(field.value.type) &&
954           field.value.type.VectorType().base_type == BASE_TYPE_STRUCT &&
955           field.value.type.struct_def->fixed) {
956         code += "    int? " + offset_name + ";\n";
957         code += "    if (" + field_name + " != null) {\n";
958         code +=
959             "      for (var e in " + field_name + "!) { e.pack(fbBuilder); }\n";
960         code += "      " + namer_.Variable(field) +
961                 "Offset = fbBuilder.endStructVector(" + field_name +
962                 "!.length);\n";
963         code += "    }\n";
964         continue;
965       }
966 
967       code += "    final int? " + offset_name;
968       if (IsVector(field.value.type)) {
969         code += " = " + field_name + " == null ? null\n";
970         code += "        : fbBuilder.writeList";
971         switch (field.value.type.VectorType().base_type) {
972           case BASE_TYPE_STRING:
973             code +=
974                 "(" + field_name + "!.map(fbBuilder.writeString).toList());\n";
975             break;
976           case BASE_TYPE_STRUCT:
977             if (field.value.type.struct_def->fixed) {
978               code += "OfStructs(" + field_name + "!);\n";
979             } else {
980               code += "(" + field_name + "!.map((b) => b." +
981                       (pack ? "pack" : "getOrCreateOffset") +
982                       "(fbBuilder)).toList());\n";
983             }
984             break;
985           default:
986             code +=
987                 GenType(field.value.type.VectorType()) + "(" + field_name + "!";
988             if (field.value.type.enum_def) {
989               code += ".map((f) => f.value).toList()";
990             }
991             code += ");\n";
992         }
993       } else if (IsString(field.value.type)) {
994         code += " = " + field_name + " == null ? null\n";
995         code += "        : fbBuilder.writeString(" + field_name + "!);\n";
996       } else {
997         code += " = " + field_name + "?." +
998                 (pack ? "pack" : "getOrCreateOffset") + "(fbBuilder);\n";
999       }
1000     }
1001 
1002     if (struct_def.fixed) {
1003       code += StructObjectBuilderBody(non_deprecated_fields, prependUnderscore,
1004                                       pack);
1005     } else {
1006       code += TableObjectBuilderBody(struct_def, non_deprecated_fields,
1007                                      prependUnderscore, pack);
1008     }
1009     return code;
1010   }
1011 
StructObjectBuilderBody(const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,bool prependUnderscore=true,bool pack=false)1012   std::string StructObjectBuilderBody(
1013       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
1014       bool prependUnderscore = true, bool pack = false) {
1015     std::string code;
1016 
1017     for (auto it = non_deprecated_fields.rbegin();
1018          it != non_deprecated_fields.rend(); ++it) {
1019       const FieldDef &field = *it->second;
1020       const std::string field_name = namer_.Field(field);
1021 
1022       if (field.padding) {
1023         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
1024       }
1025 
1026       if (IsStruct(field.value.type)) {
1027         code += "    ";
1028         if (prependUnderscore) { code += "_"; }
1029         code += field_name + (pack ? ".pack" : ".finish") + "(fbBuilder);\n";
1030       } else {
1031         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
1032         if (prependUnderscore) { code += "_"; }
1033         code += field_name;
1034         if (field.value.type.enum_def) { code += ".value"; }
1035         code += ");\n";
1036       }
1037     }
1038 
1039     code += "    return fbBuilder.offset;\n";
1040     return code;
1041   }
1042 
TableObjectBuilderBody(const StructDef & struct_def,const std::vector<std::pair<int,FieldDef * >> & non_deprecated_fields,bool prependUnderscore=true,bool pack=false)1043   std::string TableObjectBuilderBody(
1044       const StructDef &struct_def,
1045       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
1046       bool prependUnderscore = true, bool pack = false) {
1047     std::string code;
1048     code += "    fbBuilder.startTable(" +
1049             NumToString(struct_def.fields.vec.size()) + ");\n";
1050 
1051     for (auto it = non_deprecated_fields.begin();
1052          it != non_deprecated_fields.end(); ++it) {
1053       const FieldDef &field = *it->second;
1054       auto offset = it->first;
1055 
1056       std::string field_var =
1057           (prependUnderscore ? "_" : "") + namer_.Variable(field);
1058 
1059       if (IsScalar(field.value.type.base_type)) {
1060         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
1061                 NumToString(offset) + ", " + field_var;
1062         if (field.value.type.enum_def) {
1063           bool isNullable = getDefaultValue(field.value).empty();
1064           code += (isNullable || !pack) ? "?.value" : ".value";
1065         }
1066         code += ");\n";
1067       } else if (IsStruct(field.value.type)) {
1068         code += "    if (" + field_var + " != null) {\n";
1069         code += "      fbBuilder.addStruct(" + NumToString(offset) + ", " +
1070                 field_var + (pack ? "!.pack" : "!.finish") + "(fbBuilder));\n";
1071         code += "    }\n";
1072       } else {
1073         code += "    fbBuilder.addOffset(" + NumToString(offset) + ", " +
1074                 namer_.Variable(field) + "Offset);\n";
1075       }
1076     }
1077     code += "    return fbBuilder.endTable();\n";
1078     return code;
1079   }
1080 
1081   const IdlNamer namer_;
1082 };
1083 }  // namespace dart
1084 
GenerateDart(const Parser & parser,const std::string & path,const std::string & file_name)1085 bool GenerateDart(const Parser &parser, const std::string &path,
1086                   const std::string &file_name) {
1087   dart::DartGenerator generator(parser, path, file_name);
1088   return generator.generate();
1089 }
1090 
DartMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1091 std::string DartMakeRule(const Parser &parser, const std::string &path,
1092                          const std::string &file_name) {
1093   auto filebase =
1094       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1095   dart::DartGenerator generator(parser, path, file_name);
1096   auto make_rule = generator.Filename("") + ": ";
1097 
1098   auto included_files = parser.GetIncludedFilesRecursive(file_name);
1099   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1100     make_rule += " " + *it;
1101   }
1102   return make_rule;
1103 }
1104 
1105 }  // namespace flatbuffers
1106