...

Text file src/github.com/google/flatbuffers/src/idl_gen_ts.cpp

Documentation: github.com/google/flatbuffers/src

     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#include "idl_gen_ts.h"
    18
    19#include <algorithm>
    20#include <cassert>
    21#include <cmath>
    22#include <iostream>
    23#include <unordered_map>
    24#include <unordered_set>
    25
    26#include "flatbuffers/code_generators.h"
    27#include "flatbuffers/flatbuffers.h"
    28#include "flatbuffers/flatc.h"
    29#include "flatbuffers/idl.h"
    30#include "flatbuffers/util.h"
    31#include "idl_namer.h"
    32
    33namespace flatbuffers {
    34namespace {
    35struct ImportDefinition {
    36  std::string name;
    37  std::string import_statement;
    38  std::string export_statement;
    39  std::string bare_file_path;
    40  std::string rel_file_path;
    41  std::string object_name;
    42  const Definition *dependent = nullptr;
    43  const Definition *dependency = nullptr;
    44};
    45
    46struct NsDefinition {
    47  std::string path;
    48  std::string filepath;
    49  std::string symbolic_name;
    50  const Namespace *ns;
    51  std::map<std::string, const Definition *> definitions;
    52};
    53
    54Namer::Config TypeScriptDefaultConfig() {
    55  return { /*types=*/Case::kKeep,
    56           /*constants=*/Case::kUnknown,
    57           /*methods=*/Case::kLowerCamel,
    58           /*functions=*/Case::kLowerCamel,
    59           /*fields=*/Case::kLowerCamel,
    60           /*variables=*/Case::kLowerCamel,
    61           /*variants=*/Case::kKeep,
    62           /*enum_variant_seperator=*/"::",
    63           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
    64           /*namespaces=*/Case::kKeep,
    65           /*namespace_seperator=*/"_",
    66           /*object_prefix=*/"",
    67           /*object_suffix=*/"T",
    68           /*keyword_prefix=*/"",
    69           /*keyword_suffix=*/"_",
    70           /*filenames=*/Case::kDasher,
    71           /*directories=*/Case::kDasher,
    72           /*output_path=*/"",
    73           /*filename_suffix=*/"_generated",
    74           /*filename_extension=*/".ts" };
    75}
    76
    77std::set<std::string> TypescriptKeywords() {
    78  // List of keywords retrieved from here:
    79  // https://github.com/microsoft/TypeScript/issues/2536
    80  return {
    81    "arguments", "break",    "case",      "catch",      "class",      "const",
    82    "continue",  "debugger", "default",   "delete",     "do",         "else",
    83    "enum",      "export",   "extends",   "false",      "finally",    "for",
    84    "function",  "if",       "import",    "in",         "instanceof", "new",
    85    "null",      "Object",   "return",    "super",      "switch",     "this",
    86    "throw",     "true",     "try",       "typeof",     "var",        "void",
    87    "while",     "with",     "as",        "implements", "interface",  "let",
    88    "package",   "private",  "protected", "public",     "static",     "yield",
    89  };
    90}
    91
    92enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
    93
    94template<typename T> struct SupportsObjectAPI : std::false_type {};
    95
    96template<> struct SupportsObjectAPI<StructDef> : std::true_type {};
    97
    98}  // namespace
    99
   100namespace ts {
   101// Iterate through all definitions we haven't generate code for (enums, structs,
   102// and tables) and output them to a single file.
   103class TsGenerator : public BaseGenerator {
   104 public:
   105  typedef std::map<std::string, ImportDefinition> import_set;
   106
   107  TsGenerator(const Parser &parser, const std::string &path,
   108              const std::string &file_name)
   109      : BaseGenerator(parser, path, file_name, "", "_", "ts"),
   110        namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
   111               TypescriptKeywords()) {}
   112
   113  bool generate() {
   114    generateEnums();
   115    generateStructs();
   116    generateEntry();
   117    if (!generateBundle()) return false;
   118    return true;
   119  }
   120
   121  std::string GetTypeName(const EnumDef &def, const bool = false,
   122                          const bool force_ns_wrap = false) {
   123    if (force_ns_wrap) { return namer_.NamespacedType(def); }
   124    return namer_.Type(def);
   125  }
   126
   127  std::string GetTypeName(const StructDef &def, const bool object_api = false,
   128                          const bool force_ns_wrap = false) {
   129    if (object_api && parser_.opts.generate_object_based_api) {
   130      if (force_ns_wrap) {
   131        return namer_.NamespacedObjectType(def);
   132      } else {
   133        return namer_.ObjectType(def);
   134      }
   135    } else {
   136      if (force_ns_wrap) {
   137        return namer_.NamespacedType(def);
   138      } else {
   139        return namer_.Type(def);
   140      }
   141    }
   142  }
   143
   144  // Save out the generated code for a single class while adding
   145  // declaration boilerplate.
   146  bool SaveType(const Definition &definition, const std::string &class_code,
   147                import_set &imports, import_set &bare_imports) {
   148    if (!class_code.length()) return true;
   149
   150    std::string code;
   151
   152    code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
   153
   154    for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
   155      code += it->second.import_statement + "\n";
   156    }
   157    if (!bare_imports.empty()) code += "\n";
   158
   159    for (auto it = imports.begin(); it != imports.end(); it++) {
   160      if (it->second.dependency != &definition) {
   161        code += it->second.import_statement + "\n";
   162      }
   163    }
   164    if (!imports.empty()) code += "\n\n";
   165
   166    code += class_code;
   167
   168    auto dirs = namer_.Directories(*definition.defined_namespace);
   169    EnsureDirExists(dirs);
   170    auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
   171
   172    return SaveFile(basename.c_str(), code, false);
   173  }
   174
   175  void TrackNsDef(const Definition &definition, std::string type_name) {
   176    std::string path;
   177    std::string filepath;
   178    std::string symbolic_name;
   179    if (definition.defined_namespace->components.size() > 0) {
   180      path = namer_.Directories(*definition.defined_namespace,
   181                                SkipDir::TrailingPathSeperator);
   182      filepath = path + ".ts";
   183      path = namer_.Directories(*definition.defined_namespace,
   184                                SkipDir::OutputPathAndTrailingPathSeparator);
   185      symbolic_name = definition.defined_namespace->components.back();
   186    } else {
   187      auto def_mod_name = namer_.File(definition, SkipFile::SuffixAndExtension);
   188      symbolic_name = file_name_;
   189      filepath = path_ + symbolic_name + ".ts";
   190    }
   191    if (ns_defs_.count(path) == 0) {
   192      NsDefinition nsDef;
   193      nsDef.path = path;
   194      nsDef.filepath = filepath;
   195      nsDef.ns = definition.defined_namespace;
   196      nsDef.definitions.insert(std::make_pair(type_name, &definition));
   197      nsDef.symbolic_name = symbolic_name;
   198      ns_defs_[path] = nsDef;
   199    } else {
   200      ns_defs_[path].definitions.insert(std::make_pair(type_name, &definition));
   201    }
   202  }
   203
   204 private:
   205  IdlNamer namer_;
   206
   207  std::map<std::string, NsDefinition> ns_defs_;
   208
   209  // Generate code for all enums.
   210  void generateEnums() {
   211    for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
   212         ++it) {
   213      import_set bare_imports;
   214      import_set imports;
   215      std::string enumcode;
   216      auto &enum_def = **it;
   217      GenEnum(enum_def, &enumcode, imports, false);
   218      GenEnum(enum_def, &enumcode, imports, true);
   219      std::string type_name = GetTypeName(enum_def);
   220      TrackNsDef(enum_def, type_name);
   221      SaveType(enum_def, enumcode, imports, bare_imports);
   222    }
   223  }
   224
   225  // Generate code for all structs.
   226  void generateStructs() {
   227    for (auto it = parser_.structs_.vec.begin();
   228         it != parser_.structs_.vec.end(); ++it) {
   229      import_set bare_imports;
   230      import_set imports;
   231      AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
   232      auto &struct_def = **it;
   233      std::string declcode;
   234      GenStruct(parser_, struct_def, &declcode, imports);
   235      std::string type_name = GetTypeName(struct_def);
   236      TrackNsDef(struct_def, type_name);
   237      SaveType(struct_def, declcode, imports, bare_imports);
   238    }
   239  }
   240
   241  // Generate code for a single entry point module.
   242  void generateEntry() {
   243    std::string code;
   244
   245    // add root namespace def if not already existing from defs tracking
   246    std::string root;
   247    if (ns_defs_.count(root) == 0) {
   248      NsDefinition nsDef;
   249      nsDef.path = root;
   250      nsDef.symbolic_name = file_name_;
   251      nsDef.filepath = path_ + file_name_ + ".ts";
   252      nsDef.ns = new Namespace();
   253      ns_defs_[nsDef.path] = nsDef;
   254    }
   255
   256    for (const auto &it : ns_defs_) {
   257      code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
   258      // export all definitions in ns entry point module
   259      int export_counter = 0;
   260      for (const auto &def : it.second.definitions) {
   261        std::vector<std::string> rel_components;
   262        // build path for root level vs child level
   263        if (it.second.ns->components.size() > 1)
   264          std::copy(it.second.ns->components.begin() + 1,
   265                    it.second.ns->components.end(),
   266                    std::back_inserter(rel_components));
   267        else
   268          std::copy(it.second.ns->components.begin(),
   269                    it.second.ns->components.end(),
   270                    std::back_inserter(rel_components));
   271        auto base_file_name =
   272            namer_.File(*(def.second), SkipFile::SuffixAndExtension);
   273        auto base_name =
   274            namer_.Directories(it.second.ns->components, SkipDir::OutputPath) +
   275            base_file_name;
   276        auto ts_file_path = base_name + ".ts";
   277        auto base_name_rel = std::string("./");
   278        base_name_rel +=
   279            namer_.Directories(rel_components, SkipDir::OutputPath);
   280        base_name_rel += base_file_name;
   281        auto ts_file_path_rel = base_name_rel + ".ts";
   282        auto type_name = def.first;
   283        auto fully_qualified_type_name =
   284            it.second.ns->GetFullyQualifiedName(type_name);
   285        auto is_struct = parser_.structs_.Lookup(fully_qualified_type_name);
   286        code += "export { " + type_name;
   287        if (parser_.opts.generate_object_based_api && is_struct) {
   288          code += ", " + type_name + parser_.opts.object_suffix;
   289        }
   290        code += " } from '";
   291        std::string import_extension =
   292            parser_.opts.ts_no_import_ext ? "" : ".js";
   293        code += base_name_rel + import_extension + "';\n";
   294        export_counter++;
   295      }
   296
   297      // re-export child namespace(s) in parent
   298      const auto child_ns_level = it.second.ns->components.size() + 1;
   299      for (const auto &it2 : ns_defs_) {
   300        if (it2.second.ns->components.size() != child_ns_level) continue;
   301        auto ts_file_path = it2.second.path + ".ts";
   302        code += "export * as " + it2.second.symbolic_name + " from './";
   303        std::string rel_path = it2.second.path;
   304        code += rel_path + ".js';\n";
   305        export_counter++;
   306      }
   307
   308      if (export_counter > 0) SaveFile(it.second.filepath.c_str(), code, false);
   309    }
   310  }
   311
   312  bool generateBundle() {
   313    if (parser_.opts.ts_flat_files) {
   314      std::string inputpath;
   315      std::string symbolic_name = file_name_;
   316      inputpath = path_ + file_name_ + ".ts";
   317      std::string bundlepath =
   318          GeneratedFileName(path_, file_name_, parser_.opts);
   319      bundlepath = bundlepath.substr(0, bundlepath.size() - 3) + ".js";
   320      std::string cmd = "esbuild";
   321      cmd += " ";
   322      cmd += inputpath;
   323      // cmd += " --minify";
   324      cmd += " --format=cjs --bundle --outfile=";
   325      cmd += bundlepath;
   326      cmd += " --external:flatbuffers";
   327      std::cout << "Entry point " << inputpath << " generated." << std::endl;
   328      std::cout << "A single file bundle can be created using fx. esbuild with:"
   329                << std::endl;
   330      std::cout << "> " << cmd << std::endl;
   331    }
   332    return true;
   333  }
   334
   335  // Generate a documentation comment, if available.
   336  static void GenDocComment(const std::vector<std::string> &dc,
   337                            std::string *code_ptr,
   338                            const char *indent = nullptr) {
   339    if (dc.empty()) {
   340      // Don't output empty comment blocks with 0 lines of comment content.
   341      return;
   342    }
   343
   344    std::string &code = *code_ptr;
   345    if (indent) code += indent;
   346    code += "/**\n";
   347    for (auto it = dc.begin(); it != dc.end(); ++it) {
   348      if (indent) code += indent;
   349      code += " *" + *it + "\n";
   350    }
   351    if (indent) code += indent;
   352    code += " */\n";
   353  }
   354
   355  static void GenDocComment(std::string *code_ptr) {
   356    GenDocComment(std::vector<std::string>(), code_ptr);
   357  }
   358
   359  // Generate an enum declaration and an enum string lookup table.
   360  void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports,
   361               bool reverse) {
   362    if (enum_def.generated) return;
   363    if (reverse) return;  // FIXME.
   364    std::string &code = *code_ptr;
   365    GenDocComment(enum_def.doc_comment, code_ptr);
   366    code += "export enum ";
   367    code += GetTypeName(enum_def);
   368    code += " {\n";
   369    for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
   370      auto &ev = **it;
   371      if (!ev.doc_comment.empty()) {
   372        if (it != enum_def.Vals().begin()) { code += '\n'; }
   373        GenDocComment(ev.doc_comment, code_ptr, "  ");
   374      }
   375
   376      // Generate mapping between EnumName: EnumValue(int)
   377      if (reverse) {
   378        code += "  '" + enum_def.ToString(ev) + "'";
   379        code += " = ";
   380        code += "'" + namer_.Variant(ev) + "'";
   381      } else {
   382        code += "  " + namer_.Variant(ev);
   383        code += " = ";
   384        // Unfortunately, because typescript does not support bigint enums,
   385        // for 64-bit enums, we instead map the enum names to strings.
   386        switch (enum_def.underlying_type.base_type) {
   387          case BASE_TYPE_LONG:
   388          case BASE_TYPE_ULONG: {
   389            code += "'" + enum_def.ToString(ev) + "'";
   390            break;
   391          }
   392          default: code += enum_def.ToString(ev);
   393        }
   394      }
   395
   396      code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
   397    }
   398    code += "}";
   399
   400    if (enum_def.is_union) {
   401      code += GenUnionConvFunc(enum_def.underlying_type, imports);
   402    }
   403
   404    code += "\n";
   405  }
   406
   407  static std::string GenType(const Type &type) {
   408    switch (type.base_type) {
   409      case BASE_TYPE_BOOL:
   410      case BASE_TYPE_CHAR: return "Int8";
   411      case BASE_TYPE_UTYPE: return GenType(GetUnionUnderlyingType(type));
   412      case BASE_TYPE_UCHAR: return "Uint8";
   413      case BASE_TYPE_SHORT: return "Int16";
   414      case BASE_TYPE_USHORT: return "Uint16";
   415      case BASE_TYPE_INT: return "Int32";
   416      case BASE_TYPE_UINT: return "Uint32";
   417      case BASE_TYPE_LONG: return "Int64";
   418      case BASE_TYPE_ULONG: return "Uint64";
   419      case BASE_TYPE_FLOAT: return "Float32";
   420      case BASE_TYPE_DOUBLE: return "Float64";
   421      case BASE_TYPE_STRING: return "String";
   422      case BASE_TYPE_VECTOR: return GenType(type.VectorType());
   423      case BASE_TYPE_STRUCT: return type.struct_def->name;
   424      default: return "flatbuffers.Table";
   425    }
   426  }
   427
   428  std::string GenGetter(const Type &type, const std::string &arguments) {
   429    switch (type.base_type) {
   430      case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
   431      case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
   432      case BASE_TYPE_UNION:
   433        if (!UnionHasStringType(*type.enum_def)) {
   434          return GenBBAccess() + ".__union" + arguments;
   435        }
   436        return GenBBAccess() + ".__union_with_string" + arguments;
   437      case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
   438      default: {
   439        auto getter = GenBBAccess() + "." + "read" + GenType(type) + arguments;
   440        if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
   441        return getter;
   442      }
   443    }
   444  }
   445
   446  std::string GenBBAccess() const { return "this.bb!"; }
   447
   448  std::string GenDefaultValue(const FieldDef &field, import_set &imports) {
   449    if (field.IsScalarOptional()) { return "null"; }
   450
   451    const auto &value = field.value;
   452    if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
   453        value.type.base_type != BASE_TYPE_VECTOR) {
   454      switch (value.type.base_type) {
   455        case BASE_TYPE_ARRAY: {
   456          std::string ret = "[";
   457          for (auto i = 0; i < value.type.fixed_length; ++i) {
   458            std::string enum_name =
   459                AddImport(imports, *value.type.enum_def, *value.type.enum_def)
   460                    .name;
   461            std::string enum_value = namer_.Variant(
   462                *value.type.enum_def->FindByValue(value.constant));
   463            ret += enum_name + "." + enum_value +
   464                   (i < value.type.fixed_length - 1 ? ", " : "");
   465          }
   466          ret += "]";
   467          return ret;
   468        }
   469        case BASE_TYPE_LONG:
   470        case BASE_TYPE_ULONG: {
   471          // If the value is an enum with a 64-bit base type, we have to just
   472          // return the bigint value directly since typescript does not support
   473          // enums with bigint backing types.
   474          return "BigInt('" + value.constant + "')";
   475        }
   476        default: {
   477          EnumVal *val = value.type.enum_def->FindByValue(value.constant);
   478          if (val == nullptr)
   479            val = const_cast<EnumVal *>(value.type.enum_def->MinValue());
   480          return AddImport(imports, *value.type.enum_def, *value.type.enum_def)
   481                     .name +
   482                 "." + namer_.Variant(*val);
   483        }
   484      }
   485    }
   486
   487    switch (value.type.base_type) {
   488      case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
   489
   490      case BASE_TYPE_STRING:
   491      case BASE_TYPE_UNION:
   492      case BASE_TYPE_STRUCT: {
   493        return "null";
   494      }
   495
   496      case BASE_TYPE_ARRAY:
   497      case BASE_TYPE_VECTOR: return "[]";
   498
   499      case BASE_TYPE_LONG:
   500      case BASE_TYPE_ULONG: {
   501        return "BigInt('" + value.constant + "')";
   502      }
   503
   504      default: {
   505        if (StringIsFlatbufferNan(value.constant)) {
   506          return "NaN";
   507        } else if (StringIsFlatbufferPositiveInfinity(value.constant)) {
   508          return "Infinity";
   509        } else if (StringIsFlatbufferNegativeInfinity(value.constant)) {
   510          return "-Infinity";
   511        }
   512        return value.constant;
   513      }
   514    }
   515  }
   516
   517  std::string GenTypeName(import_set &imports, const Definition &owner,
   518                          const Type &type, bool input,
   519                          bool allowNull = false) {
   520    if (!input) {
   521      if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
   522        std::string name;
   523        if (IsString(type)) {
   524          name = "string|Uint8Array";
   525        } else {
   526          name = AddImport(imports, owner, *type.struct_def).name;
   527        }
   528        return allowNull ? (name + "|null") : name;
   529      }
   530    }
   531
   532    switch (type.base_type) {
   533      case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean";
   534      case BASE_TYPE_LONG:
   535      case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint";
   536      case BASE_TYPE_ARRAY: {
   537        std::string name;
   538        if (type.element == BASE_TYPE_LONG || type.element == BASE_TYPE_ULONG) {
   539          name = "bigint[]";
   540        } else if (type.element != BASE_TYPE_STRUCT) {
   541          name = "number[]";
   542        } else {
   543          name = "any[]";
   544          if (parser_.opts.generate_object_based_api) {
   545            name = "(any|" +
   546                   GetTypeName(*type.struct_def, /*object_api =*/true) + ")[]";
   547          }
   548        }
   549
   550        return name + (allowNull ? "|null" : "");
   551      }
   552      default:
   553        if (IsScalar(type.base_type)) {
   554          if (type.enum_def) {
   555            const auto enum_name =
   556                AddImport(imports, owner, *type.enum_def).name;
   557            return allowNull ? (enum_name + "|null") : enum_name;
   558          }
   559          return allowNull ? "number|null" : "number";
   560        }
   561        return "flatbuffers.Offset";
   562    }
   563  }
   564
   565  static Type GetUnionUnderlyingType(const Type &type)
   566  {
   567    if (type.enum_def != nullptr && 
   568        type.enum_def->underlying_type.base_type != type.base_type) {
   569      return type.enum_def->underlying_type;
   570    } else {
   571        return Type(BASE_TYPE_UCHAR);
   572    }
   573  }
   574
   575  static Type GetUnderlyingVectorType(const Type &vector_type)
   576  {
   577    return (vector_type.base_type == BASE_TYPE_UTYPE) ? GetUnionUnderlyingType(vector_type) : vector_type;
   578  }
   579
   580  // Returns the method name for use with add/put calls.
   581  std::string GenWriteMethod(const Type &type) {
   582    // Forward to signed versions since unsigned versions don't exist
   583    switch (type.base_type) {
   584      case BASE_TYPE_UTYPE: return GenWriteMethod(GetUnionUnderlyingType(type));
   585      case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
   586      case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
   587      case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
   588      case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
   589      default: break;
   590    }
   591
   592    return IsScalar(type.base_type) ? namer_.Type(GenType(type))
   593                                    : (IsStruct(type) ? "Struct" : "Offset");
   594  }
   595
   596  template<typename T> static std::string MaybeAdd(T value) {
   597    return value != 0 ? " + " + NumToString(value) : "";
   598  }
   599
   600  template<typename T> static std::string MaybeScale(T value) {
   601    return value != 1 ? " * " + NumToString(value) : "";
   602  }
   603
   604  void GenStructArgs(import_set &imports, const StructDef &struct_def,
   605                     std::string *arguments, const std::string &nameprefix) {
   606    for (auto it = struct_def.fields.vec.begin();
   607         it != struct_def.fields.vec.end(); ++it) {
   608      auto &field = **it;
   609      if (IsStruct(field.value.type)) {
   610        // Generate arguments for a struct inside a struct. To ensure names
   611        // don't clash, and to make it obvious these arguments are constructing
   612        // a nested struct, prefix the name with the field name.
   613        GenStructArgs(imports, *field.value.type.struct_def, arguments,
   614                      nameprefix + field.name + "_");
   615      } else {
   616        *arguments += ", " + nameprefix + field.name + ": " +
   617                      GenTypeName(imports, field, field.value.type, true,
   618                                  field.IsOptional());
   619      }
   620    }
   621  }
   622
   623  void GenStructBody(const StructDef &struct_def, std::string *body,
   624                     const std::string &nameprefix) {
   625    *body += "  builder.prep(";
   626    *body += NumToString(struct_def.minalign) + ", ";
   627    *body += NumToString(struct_def.bytesize) + ");\n";
   628
   629    for (auto it = struct_def.fields.vec.rbegin();
   630         it != struct_def.fields.vec.rend(); ++it) {
   631      auto &field = **it;
   632      if (field.padding) {
   633        *body += "  builder.pad(" + NumToString(field.padding) + ");\n";
   634      }
   635      if (IsStruct(field.value.type)) {
   636        // Generate arguments for a struct inside a struct. To ensure names
   637        // don't clash, and to make it obvious these arguments are constructing
   638        // a nested struct, prefix the name with the field name.
   639        GenStructBody(
   640            *field.value.type.struct_def, body,
   641            nameprefix.length() ? nameprefix + "_" + field.name : field.name);
   642      } else {
   643        auto element_type = field.value.type.element;
   644
   645        if (field.value.type.base_type == BASE_TYPE_ARRAY) {
   646          switch (field.value.type.element) {
   647            case BASE_TYPE_STRUCT: {
   648              std::string str_last_item_idx =
   649                  NumToString(field.value.type.fixed_length - 1);
   650              *body += "\n  for (let i = " + str_last_item_idx +
   651                       "; i >= 0; --i" + ") {\n";
   652
   653              std::string fname = nameprefix.length()
   654                                      ? nameprefix + "_" + field.name
   655                                      : field.name;
   656
   657              *body += "    const item = " + fname + "?.[i];\n\n";
   658
   659              if (parser_.opts.generate_object_based_api) {
   660                *body += "    if (item instanceof " +
   661                         GetTypeName(*field.value.type.struct_def,
   662                                     /*object_api =*/true) +
   663                         ") {\n";
   664                *body += "      item.pack(builder);\n";
   665                *body += "      continue;\n";
   666                *body += "    }\n\n";
   667              }
   668
   669              std::string class_name =
   670                  GetPrefixedName(*field.value.type.struct_def);
   671              std::string pack_func_create_call =
   672                  class_name + ".create" + class_name + "(builder,\n";
   673              pack_func_create_call +=
   674                  "    " +
   675                  GenStructMemberValueTS(*field.value.type.struct_def, "item",
   676                                         ",\n    ", false) +
   677                  "\n  ";
   678              *body += "    " + pack_func_create_call;
   679              *body += "  );\n  }\n\n";
   680
   681              break;
   682            }
   683            default: {
   684              std::string str_last_item_idx =
   685                  NumToString(field.value.type.fixed_length - 1);
   686              std::string fname = nameprefix.length()
   687                                      ? nameprefix + "_" + field.name
   688                                      : field.name;
   689
   690              *body += "\n  for (let i = " + str_last_item_idx +
   691                       "; i >= 0; --i) {\n";
   692              *body += "    builder.write";
   693              *body += GenWriteMethod(
   694                  static_cast<flatbuffers::Type>(field.value.type.element));
   695              *body += "(";
   696              *body += element_type == BASE_TYPE_BOOL ? "+" : "";
   697
   698              if (element_type == BASE_TYPE_LONG ||
   699                  element_type == BASE_TYPE_ULONG) {
   700                *body += "BigInt(" + fname + "?.[i] ?? 0));\n";
   701              } else {
   702                *body += "(" + fname + "?.[i] ?? 0));\n\n";
   703              }
   704              *body += "  }\n\n";
   705              break;
   706            }
   707          }
   708        } else {
   709          std::string fname =
   710              nameprefix.length() ? nameprefix + "_" + field.name : field.name;
   711
   712          *body += "  builder.write" + GenWriteMethod(field.value.type) + "(";
   713          if (field.value.type.base_type == BASE_TYPE_BOOL) {
   714            *body += "Number(Boolean(" + fname + ")));\n";
   715            continue;
   716          } else if (field.value.type.base_type == BASE_TYPE_LONG ||
   717                     field.value.type.base_type == BASE_TYPE_ULONG) {
   718            *body += "BigInt(" + fname + " ?? 0));\n";
   719            continue;
   720          }
   721
   722          *body += fname + ");\n";
   723        }
   724      }
   725    }
   726  }
   727
   728  std::string GenerateNewExpression(const std::string &object_name) {
   729    return "new " + namer_.Type(object_name) + "()";
   730  }
   731
   732  void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
   733                            std::string &code, const std::string &object_name,
   734                            bool size_prefixed) {
   735    if (!struct_def.fixed) {
   736      GenDocComment(code_ptr);
   737      std::string sizePrefixed("SizePrefixed");
   738      code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
   739              GetPrefixedName(struct_def, "As");
   740      code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
   741              "):" + object_name + " {\n";
   742      if (size_prefixed) {
   743        code +=
   744            "  bb.setPosition(bb.position() + "
   745            "flatbuffers.SIZE_PREFIX_LENGTH);\n";
   746      }
   747      code += "  return (obj || " + GenerateNewExpression(object_name);
   748      code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
   749      code += "}\n\n";
   750    }
   751  }
   752
   753  void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
   754                        std::string &code, bool size_prefixed) {
   755    if (parser_.root_struct_def_ == &struct_def) {
   756      std::string sizePrefixed("SizePrefixed");
   757      GenDocComment(code_ptr);
   758
   759      code += "static finish" + (size_prefixed ? sizePrefixed : "") +
   760              GetPrefixedName(struct_def) + "Buffer";
   761      code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
   762      code += "  builder.finish(offset";
   763      if (!parser_.file_identifier_.empty()) {
   764        code += ", '" + parser_.file_identifier_ + "'";
   765      }
   766      if (size_prefixed) {
   767        if (parser_.file_identifier_.empty()) { code += ", undefined"; }
   768        code += ", true";
   769      }
   770      code += ");\n";
   771      code += "}\n\n";
   772    }
   773  }
   774
   775  bool UnionHasStringType(const EnumDef &union_enum) {
   776    return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
   777                       [](const EnumVal *ev) {
   778                         return !ev->IsZero() && IsString(ev->union_type);
   779                       });
   780  }
   781
   782  std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
   783    // TODO: make it work without any
   784    // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
   785    // "");
   786    return std::string("any") +
   787           (UnionHasStringType(union_enum) ? "|string" : "");
   788  }
   789
   790  std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) {
   791    std::string ret;
   792    std::set<std::string> type_list;
   793
   794    for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
   795         ++it) {
   796      const auto &ev = **it;
   797      if (ev.IsZero()) { continue; }
   798
   799      std::string type = "";
   800      if (IsString(ev.union_type)) {
   801        type = "string";  // no need to wrap string type in namespace
   802      } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
   803        type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
   804      } else {
   805        FLATBUFFERS_ASSERT(false);
   806      }
   807      type_list.insert(type);
   808    }
   809
   810    for (auto it = type_list.begin(); it != type_list.end(); ++it) {
   811      ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
   812    }
   813
   814    return ret;
   815  }
   816
   817  static bool CheckIfNameClashes(const import_set &imports,
   818                                 const std::string &name) {
   819    // TODO: this would be better as a hashset.
   820    for (auto it = imports.begin(); it != imports.end(); it++) {
   821      if (it->second.name == name) { return true; }
   822    }
   823    return false;
   824  }
   825
   826  std::string GenSymbolExpression(const StructDef &struct_def,
   827                                  const bool has_name_clash,
   828                                  const std::string &import_name,
   829                                  const std::string &name,
   830                                  const std::string &object_name) {
   831    std::string symbols_expression;
   832
   833    if (has_name_clash) {
   834      // We have a name clash
   835      symbols_expression += import_name + " as " + name;
   836
   837      if (parser_.opts.generate_object_based_api) {
   838        symbols_expression += ", " +
   839                              GetTypeName(struct_def, /*object_api =*/true) +
   840                              " as " + object_name;
   841      }
   842    } else {
   843      // No name clash, use the provided name
   844      symbols_expression += name;
   845
   846      if (parser_.opts.generate_object_based_api) {
   847        symbols_expression += ", " + object_name;
   848      }
   849    }
   850
   851    return symbols_expression;
   852  }
   853
   854  std::string GenSymbolExpression(const EnumDef &enum_def,
   855                                  const bool has_name_clash,
   856                                  const std::string &import_name,
   857                                  const std::string &name,
   858                                  const std::string &) {
   859    std::string symbols_expression;
   860    if (has_name_clash) {
   861      symbols_expression += import_name + " as " + name;
   862    } else {
   863      symbols_expression += name;
   864    }
   865
   866    if (enum_def.is_union) {
   867      symbols_expression += (", " + namer_.Function("unionTo" + name));
   868      symbols_expression += (", " + namer_.Function("unionListTo" + name));
   869    }
   870
   871    return symbols_expression;
   872  }
   873
   874  template<typename DefinitionT>
   875  ImportDefinition AddImport(import_set &imports, const Definition &dependent,
   876                             const DefinitionT &dependency) {
   877    // The unique name of the dependency, fully qualified in its namespace.
   878    const std::string unique_name = GetTypeName(
   879        dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
   880
   881    // Look if we have already added this import and return its name if found.
   882    const auto import_pair = imports.find(unique_name);
   883    if (import_pair != imports.end()) { return import_pair->second; }
   884
   885    // Check if this name would have a name clash with another type. Just use
   886    // the "base" name (properly escaped) without any namespacing applied.
   887    const std::string import_name = GetTypeName(dependency);
   888    const bool has_name_clash = CheckIfNameClashes(imports, import_name);
   889
   890    // If we have a name clash, use the unique name, otherwise use simple name.
   891    std::string name = has_name_clash ? unique_name : import_name;
   892
   893    const std::string object_name =
   894        GetTypeName(dependency, /*object_api=*/true, has_name_clash);
   895
   896    const std::string symbols_expression = GenSymbolExpression(
   897        dependency, has_name_clash, import_name, name, object_name);
   898
   899    std::string bare_file_path;
   900    std::string rel_file_path;
   901    const auto &dep_comps = dependent.defined_namespace->components;
   902    for (size_t i = 0; i < dep_comps.size(); i++) {
   903      rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
   904    }
   905    if (dep_comps.size() == 0) { rel_file_path += "."; }
   906
   907    bare_file_path +=
   908        kPathSeparator +
   909        namer_.Directories(dependency.defined_namespace->components,
   910                           SkipDir::OutputPath) +
   911        namer_.File(dependency, SkipFile::SuffixAndExtension);
   912    rel_file_path += bare_file_path;
   913
   914    ImportDefinition import;
   915    import.name = name;
   916    import.object_name = object_name;
   917    import.bare_file_path = bare_file_path;
   918    import.rel_file_path = rel_file_path;
   919    std::string import_extension = parser_.opts.ts_no_import_ext ? "" : ".js";
   920    import.import_statement = "import { " + symbols_expression + " } from '" +
   921                              rel_file_path + import_extension + "';";
   922    import.export_statement = "export { " + symbols_expression + " } from '." +
   923                              bare_file_path + import_extension + "';";
   924    import.dependency = &dependency;
   925    import.dependent = &dependent;
   926
   927    imports.insert(std::make_pair(unique_name, import));
   928
   929    return import;
   930  }
   931
   932  void AddImport(import_set &imports, std::string import_name,
   933                 std::string fileName) {
   934    ImportDefinition import;
   935    import.name = import_name;
   936    import.import_statement =
   937        "import " + import_name + " from '" + fileName + "';";
   938    imports.insert(std::make_pair(import_name, import));
   939  }
   940
   941  // Generate a TS union type based on a union's enum
   942  std::string GenObjApiUnionTypeTS(import_set &imports,
   943                                   const StructDef &dependent,
   944                                   const IDLOptions &,
   945                                   const EnumDef &union_enum) {
   946    std::string ret = "";
   947    std::set<std::string> type_list;
   948
   949    for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
   950         ++it) {
   951      const auto &ev = **it;
   952      if (ev.IsZero()) { continue; }
   953
   954      std::string type = "";
   955      if (IsString(ev.union_type)) {
   956        type = "string";  // no need to wrap string type in namespace
   957      } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
   958        type = AddImport(imports, dependent, *ev.union_type.struct_def)
   959                   .object_name;
   960      } else {
   961        FLATBUFFERS_ASSERT(false);
   962      }
   963      type_list.insert(type);
   964    }
   965
   966    size_t totalPrinted = 0;
   967    for (auto it = type_list.begin(); it != type_list.end(); ++it) {
   968      ++totalPrinted;
   969      ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
   970    }
   971
   972    return ret;
   973  }
   974
   975  std::string GenUnionConvFuncName(const EnumDef &enum_def) {
   976    return namer_.Function("unionTo", enum_def);
   977  }
   978
   979  std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
   980    return namer_.Function("unionListTo", enum_def);
   981  }
   982
   983  std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
   984    if (union_type.enum_def) {
   985      const auto &enum_def = *union_type.enum_def;
   986
   987      const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
   988      const auto valid_union_type_with_null = valid_union_type + "|null";
   989
   990      auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
   991                 "(\n  type: " + GetTypeName(enum_def) +
   992                 ",\n  accessor: (obj:" + valid_union_type + ") => " +
   993                 valid_union_type_with_null +
   994                 "\n): " + valid_union_type_with_null + " {\n";
   995
   996      const auto enum_type = AddImport(imports, enum_def, enum_def).name;
   997
   998      const auto union_enum_loop = [&](const std::string &accessor_str) {
   999        ret += "  switch(" + enum_type + "[type]) {\n";
  1000        ret += "    case 'NONE': return null; \n";
  1001
  1002        for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
  1003             ++it) {
  1004          const auto &ev = **it;
  1005          if (ev.IsZero()) { continue; }
  1006
  1007          ret += "    case '" + namer_.Variant(ev) + "': ";
  1008
  1009          if (IsString(ev.union_type)) {
  1010            ret += "return " + accessor_str + "'') as string;";
  1011          } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
  1012            const auto type =
  1013                AddImport(imports, enum_def, *ev.union_type.struct_def).name;
  1014            ret += "return " + accessor_str + "new " + type + "())! as " +
  1015                   type + ";";
  1016          } else {
  1017            FLATBUFFERS_ASSERT(false);
  1018          }
  1019          ret += "\n";
  1020        }
  1021
  1022        ret += "    default: return null;\n";
  1023        ret += "  }\n";
  1024      };
  1025
  1026      union_enum_loop("accessor(");
  1027      ret += "}";
  1028
  1029      ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
  1030             "(\n  type: " + GetTypeName(enum_def) +
  1031             ", \n  accessor: (index: number, obj:" + valid_union_type +
  1032             ") => " + valid_union_type_with_null +
  1033             ", \n  index: number\n): " + valid_union_type_with_null + " {\n";
  1034      union_enum_loop("accessor(index, ");
  1035      ret += "}";
  1036
  1037      return ret;
  1038    }
  1039    FLATBUFFERS_ASSERT(0);
  1040    return "";
  1041  }
  1042
  1043  // Used for generating a short function that returns the correct class
  1044  // based on union enum type. Assume the context is inside the non object api
  1045  // type
  1046  std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
  1047                            const std::string &field_name,
  1048                            const Type &union_type,
  1049                            const bool is_array = false) {
  1050    if (union_type.enum_def) {
  1051      const auto &enum_def = *union_type.enum_def;
  1052      const auto enum_type = AddImport(imports, dependent, enum_def).name;
  1053      const std::string union_accessor = "this." + field_name;
  1054
  1055      const auto union_has_string = UnionHasStringType(enum_def);
  1056      const auto field_binded_method = "this." + field_name + ".bind(this)";
  1057
  1058      std::string ret;
  1059
  1060      if (!is_array) {
  1061        const auto conversion_function = GenUnionConvFuncName(enum_def);
  1062
  1063        ret = "(() => {\n";
  1064        ret += "      const temp = " + conversion_function + "(this." +
  1065               namer_.Method(field_name, "Type") + "(), " +
  1066               field_binded_method + ");\n";
  1067        ret += "      if(temp === null) { return null; }\n";
  1068        ret += union_has_string
  1069                   ? "      if(typeof temp === 'string') { return temp; }\n"
  1070                   : "";
  1071        ret += "      return temp.unpack()\n";
  1072        ret += "  })()";
  1073      } else {
  1074        const auto conversion_function = GenUnionListConvFuncName(enum_def);
  1075
  1076        ret = "(() => {\n";
  1077        ret += "    const ret: (" +
  1078               GenObjApiUnionTypeTS(imports, *union_type.struct_def,
  1079                                    parser_.opts, *union_type.enum_def) +
  1080               ")[] = [];\n";
  1081        ret += "    for(let targetEnumIndex = 0; targetEnumIndex < this." +
  1082               namer_.Method(field_name, "TypeLength") + "()" +
  1083               "; "
  1084               "++targetEnumIndex) {\n";
  1085        ret += "      const targetEnum = this." +
  1086               namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
  1087        ret += "      if(targetEnum === null || " + enum_type +
  1088               "[targetEnum!] === 'NONE') { "
  1089               "continue; }\n\n";
  1090        ret += "      const temp = " + conversion_function + "(targetEnum, " +
  1091               field_binded_method + ", targetEnumIndex);\n";
  1092        ret += "      if(temp === null) { continue; }\n";
  1093        ret += union_has_string ? "      if(typeof temp === 'string') { "
  1094                                  "ret.push(temp); continue; }\n"
  1095                                : "";
  1096        ret += "      ret.push(temp.unpack());\n";
  1097        ret += "    }\n";
  1098        ret += "    return ret;\n";
  1099        ret += "  })()";
  1100      }
  1101
  1102      return ret;
  1103    }
  1104
  1105    FLATBUFFERS_ASSERT(0);
  1106    return "";
  1107  }
  1108
  1109  static std::string GenNullCheckConditional(
  1110      const std::string &nullCheckVar, const std::string &trueVal,
  1111      const std::string &falseVal = "null") {
  1112    return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
  1113           ")";
  1114  }
  1115
  1116  std::string GenStructMemberValueTS(const StructDef &struct_def,
  1117                                     const std::string &prefix,
  1118                                     const std::string &delimiter,
  1119                                     const bool nullCheck = true) {
  1120    std::string ret;
  1121    for (auto it = struct_def.fields.vec.begin();
  1122         it != struct_def.fields.vec.end(); ++it) {
  1123      auto &field = **it;
  1124
  1125      auto curr_member_accessor = prefix + "." + namer_.Method(field);
  1126      if (prefix != "this") {
  1127        curr_member_accessor = prefix + "?." + namer_.Method(field);
  1128      }
  1129      if (IsStruct(field.value.type)) {
  1130        ret += GenStructMemberValueTS(*field.value.type.struct_def,
  1131                                      curr_member_accessor, delimiter);
  1132      } else {
  1133        if (nullCheck) {
  1134          std::string nullValue = "0";
  1135          if (field.value.type.base_type == BASE_TYPE_BOOL) {
  1136            nullValue = "false";
  1137          } else if (field.value.type.base_type == BASE_TYPE_LONG ||
  1138                     field.value.type.base_type == BASE_TYPE_ULONG) {
  1139            nullValue = "BigInt(0)";
  1140          } else if (field.value.type.base_type == BASE_TYPE_ARRAY) {
  1141            nullValue = "[]";
  1142          }
  1143          ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
  1144        } else {
  1145          ret += curr_member_accessor;
  1146        }
  1147      }
  1148
  1149      if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
  1150    }
  1151
  1152    return ret;
  1153  }
  1154
  1155  void GenObjApi(const Parser &parser, StructDef &struct_def,
  1156                 std::string &obj_api_unpack_func, std::string &obj_api_class,
  1157                 import_set &imports) {
  1158    const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
  1159
  1160    std::string unpack_func = "\nunpack(): " + class_name +
  1161                              " {\n  return new " + class_name + "(" +
  1162                              (struct_def.fields.vec.empty() ? "" : "\n");
  1163    std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
  1164                                 +(struct_def.fields.vec.empty() ? "" : "\n");
  1165
  1166    std::string constructor_func = "constructor(";
  1167    constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
  1168
  1169    const auto has_create =
  1170        struct_def.fixed || CanCreateFactoryMethod(struct_def);
  1171
  1172    std::string pack_func_prototype =
  1173        "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
  1174
  1175    std::string pack_func_offset_decl;
  1176    std::string pack_func_create_call;
  1177
  1178    const auto struct_name = AddImport(imports, struct_def, struct_def).name;
  1179
  1180    if (has_create) {
  1181      pack_func_create_call = "  return " + struct_name + ".create" +
  1182                              GetPrefixedName(struct_def) + "(builder" +
  1183                              (struct_def.fields.vec.empty() ? "" : ",\n    ");
  1184    } else {
  1185      pack_func_create_call = "  " + struct_name + ".start" +
  1186                              GetPrefixedName(struct_def) + "(builder);\n";
  1187    }
  1188
  1189    if (struct_def.fixed) {
  1190      // when packing struct, nested struct's members instead of the struct's
  1191      // offset are used
  1192      pack_func_create_call +=
  1193          GenStructMemberValueTS(struct_def, "this", ",\n    ", false) + "\n  ";
  1194    }
  1195
  1196    for (auto it = struct_def.fields.vec.begin();
  1197         it != struct_def.fields.vec.end(); ++it) {
  1198      auto &field = **it;
  1199      if (field.deprecated) continue;
  1200
  1201      const auto field_method = namer_.Method(field);
  1202      const auto field_field = namer_.Field(field);
  1203      const std::string field_binded_method =
  1204          "this." + field_method + ".bind(this)";
  1205
  1206      std::string field_val;
  1207      std::string field_type;
  1208      // a string that declares a variable containing the
  1209      // offset for things that can't be generated inline
  1210      // empty otw
  1211      std::string field_offset_decl;
  1212      // a string that contains values for things that can be created inline or
  1213      // the variable name from field_offset_decl
  1214      std::string field_offset_val;
  1215      const auto field_default_val = GenDefaultValue(field, imports);
  1216
  1217      // Emit a scalar field
  1218      const auto is_string = IsString(field.value.type);
  1219      if (IsScalar(field.value.type.base_type) || is_string) {
  1220        const auto has_null_default = is_string || HasNullDefault(field);
  1221
  1222        field_type += GenTypeName(imports, field, field.value.type, false,
  1223                                  has_null_default);
  1224        field_val = "this." + namer_.Method(field) + "()";
  1225
  1226        if (field.value.type.base_type != BASE_TYPE_STRING) {
  1227          field_offset_val = "this." + namer_.Field(field);
  1228        } else {
  1229          field_offset_decl = GenNullCheckConditional(
  1230              "this." + namer_.Field(field),
  1231              "builder.createString(this." + field_field + "!)", "0");
  1232        }
  1233      }
  1234
  1235      // Emit an object field
  1236      else {
  1237        auto is_vector = false;
  1238        switch (field.value.type.base_type) {
  1239          case BASE_TYPE_STRUCT: {
  1240            const auto &sd = *field.value.type.struct_def;
  1241            field_type += AddImport(imports, struct_def, sd).object_name;
  1242
  1243            const std::string field_accessor =
  1244                "this." + namer_.Method(field) + "()";
  1245            field_val = GenNullCheckConditional(field_accessor,
  1246                                                field_accessor + "!.unpack()");
  1247            auto packing = GenNullCheckConditional(
  1248                "this." + field_field,
  1249                "this." + field_field + "!.pack(builder)", "0");
  1250
  1251            if (sd.fixed) {
  1252              field_offset_val = std::move(packing);
  1253            } else {
  1254              field_offset_decl = std::move(packing);
  1255            }
  1256
  1257            break;
  1258          }
  1259
  1260          case BASE_TYPE_ARRAY: {
  1261            auto vectortype = field.value.type.VectorType();
  1262            auto vectortypename =
  1263                GenTypeName(imports, struct_def, vectortype, false);
  1264            is_vector = true;
  1265
  1266            field_type = "(";
  1267
  1268            switch (vectortype.base_type) {
  1269              case BASE_TYPE_STRUCT: {
  1270                const auto &sd = *field.value.type.struct_def;
  1271                const auto field_type_name =
  1272                    GetTypeName(sd, /*object_api=*/true);
  1273                field_type += field_type_name;
  1274                field_type += ")[]";
  1275
  1276                field_val = GenBBAccess() + ".createObjList<" + vectortypename +
  1277                            ", " + field_type_name + ">(" +
  1278                            field_binded_method + ", " +
  1279                            NumToString(field.value.type.fixed_length) + ")";
  1280
  1281                if (sd.fixed) {
  1282                  field_offset_decl =
  1283                      "builder.createStructOffsetList(this." + field_field +
  1284                      ", " + AddImport(imports, struct_def, struct_def).name +
  1285                      "." + namer_.Method("start", field, "Vector") + ")";
  1286                } else {
  1287                  field_offset_decl =
  1288                      AddImport(imports, struct_def, struct_def).name + "." +
  1289                      namer_.Method("create", field, "Vector") +
  1290                      "(builder, builder.createObjectOffsetList(" + "this." +
  1291                      field_field + "))";
  1292                }
  1293
  1294                break;
  1295              }
  1296
  1297              case BASE_TYPE_STRING: {
  1298                field_type += "string)[]";
  1299                field_val = GenBBAccess() + ".createScalarList<string>(" +
  1300                            field_binded_method + ", this." +
  1301                            namer_.Field(field, "Length") + "())";
  1302                field_offset_decl =
  1303                    AddImport(imports, struct_def, struct_def).name + "." +
  1304                    namer_.Method("create", field, "Vector") +
  1305                    "(builder, builder.createObjectOffsetList(" + "this." +
  1306                    namer_.Field(field) + "))";
  1307                break;
  1308              }
  1309
  1310              case BASE_TYPE_UNION: {
  1311                field_type += GenObjApiUnionTypeTS(
  1312                    imports, struct_def, parser.opts, *(vectortype.enum_def));
  1313                field_type += ")[]";
  1314                field_val = GenUnionValTS(imports, struct_def, field_method,
  1315                                          vectortype, true);
  1316
  1317                field_offset_decl =
  1318                    AddImport(imports, struct_def, struct_def).name + "." +
  1319                    namer_.Method("create", field, "Vector") +
  1320                    "(builder, builder.createObjectOffsetList(" + "this." +
  1321                    namer_.Field(field) + "))";
  1322
  1323                break;
  1324              }
  1325              default: {
  1326                if (vectortype.enum_def) {
  1327                  field_type += GenTypeName(imports, struct_def, vectortype,
  1328                                            false, HasNullDefault(field));
  1329                } else {
  1330                  field_type += vectortypename;
  1331                }
  1332                field_type += ")[]";
  1333                field_val = GenBBAccess() + ".createScalarList<" +
  1334                            vectortypename + ">(" + field_binded_method + ", " +
  1335                            NumToString(field.value.type.fixed_length) + ")";
  1336
  1337                field_offset_decl =
  1338                    AddImport(imports, struct_def, struct_def).name + "." +
  1339                    namer_.Method("create", field, "Vector") +
  1340                    "(builder, this." + field_field + ")";
  1341
  1342                break;
  1343              }
  1344            }
  1345
  1346            break;
  1347          }
  1348
  1349          case BASE_TYPE_VECTOR: {
  1350            auto vectortype = field.value.type.VectorType();
  1351            auto vectortypename =
  1352                GenTypeName(imports, struct_def, vectortype, false);
  1353            is_vector = true;
  1354
  1355            field_type = "(";
  1356
  1357            switch (vectortype.base_type) {
  1358              case BASE_TYPE_STRUCT: {
  1359                const auto &sd = *field.value.type.struct_def;
  1360                const auto field_type_name =
  1361                    GetTypeName(sd, /*object_api=*/true);
  1362                field_type += field_type_name;
  1363                field_type += ")[]";
  1364
  1365                field_val = GenBBAccess() + ".createObjList<" + vectortypename +
  1366                            ", " + field_type_name + ">(" +
  1367                            field_binded_method + ", this." +
  1368                            namer_.Method(field, "Length") + "())";
  1369
  1370                if (sd.fixed) {
  1371                  field_offset_decl =
  1372                      "builder.createStructOffsetList(this." + field_field +
  1373                      ", " + AddImport(imports, struct_def, struct_def).name +
  1374                      "." + namer_.Method("start", field, "Vector") + ")";
  1375                } else {
  1376                  field_offset_decl =
  1377                      AddImport(imports, struct_def, struct_def).name + "." +
  1378                      namer_.Method("create", field, "Vector") +
  1379                      "(builder, builder.createObjectOffsetList(" + "this." +
  1380                      field_field + "))";
  1381                }
  1382
  1383                break;
  1384              }
  1385
  1386              case BASE_TYPE_STRING: {
  1387                field_type += "string)[]";
  1388                field_val = GenBBAccess() + ".createScalarList<string>(" +
  1389                            field_binded_method + ", this." +
  1390                            namer_.Field(field, "Length") + "())";
  1391                field_offset_decl =
  1392                    AddImport(imports, struct_def, struct_def).name + "." +
  1393                    namer_.Method("create", field, "Vector") +
  1394                    "(builder, builder.createObjectOffsetList(" + "this." +
  1395                    namer_.Field(field) + "))";
  1396                break;
  1397              }
  1398
  1399              case BASE_TYPE_UNION: {
  1400                field_type += GenObjApiUnionTypeTS(
  1401                    imports, struct_def, parser.opts, *(vectortype.enum_def));
  1402                field_type += ")[]";
  1403                field_val = GenUnionValTS(imports, struct_def, field_method,
  1404                                          vectortype, true);
  1405
  1406                field_offset_decl =
  1407                    AddImport(imports, struct_def, struct_def).name + "." +
  1408                    namer_.Method("create", field, "Vector") +
  1409                    "(builder, builder.createObjectOffsetList(" + "this." +
  1410                    namer_.Field(field) + "))";
  1411
  1412                break;
  1413              }
  1414              default: {
  1415                if (vectortype.enum_def) {
  1416                  field_type += GenTypeName(imports, struct_def, vectortype,
  1417                                            false, HasNullDefault(field));
  1418                } else {
  1419                  field_type += vectortypename;
  1420                }
  1421                field_type += ")[]";
  1422                field_val = GenBBAccess() + ".createScalarList<" +
  1423                            vectortypename + ">(" + field_binded_method +
  1424                            ", this." + namer_.Method(field, "Length") + "())";
  1425
  1426                field_offset_decl =
  1427                    AddImport(imports, struct_def, struct_def).name + "." +
  1428                    namer_.Method("create", field, "Vector") +
  1429                    "(builder, this." + field_field + ")";
  1430
  1431                break;
  1432              }
  1433            }
  1434
  1435            break;
  1436          }
  1437
  1438          case BASE_TYPE_UNION: {
  1439            field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
  1440                                               *(field.value.type.enum_def));
  1441
  1442            field_val = GenUnionValTS(imports, struct_def, field_method,
  1443                                      field.value.type);
  1444            field_offset_decl =
  1445                "builder.createObjectOffset(this." + field_field + ")";
  1446            break;
  1447          }
  1448
  1449          default: FLATBUFFERS_ASSERT(0); break;
  1450        }
  1451
  1452        // length 0 vector is simply empty instead of null
  1453        field_type += is_vector ? "" : "|null";
  1454      }
  1455
  1456      if (!field_offset_decl.empty()) {
  1457        field_offset_decl =
  1458            "  const " + field_field + " = " + field_offset_decl + ";";
  1459      }
  1460      if (field_offset_val.empty()) { field_offset_val = field_field; }
  1461
  1462      unpack_func += "    " + field_val;
  1463      unpack_to_func += "  _o." + field_field + " = " + field_val + ";";
  1464
  1465      // FIXME: if field_type and field_field are identical, then
  1466      // this generates invalid typescript.
  1467      constructor_func += "  public " + field_field + ": " + field_type +
  1468                          " = " + field_default_val;
  1469
  1470      if (!struct_def.fixed) {
  1471        if (!field_offset_decl.empty()) {
  1472          pack_func_offset_decl += field_offset_decl + "\n";
  1473        }
  1474
  1475        if (has_create) {
  1476          pack_func_create_call += field_offset_val;
  1477        } else {
  1478          if (field.IsScalarOptional()) {
  1479            pack_func_create_call +=
  1480                "  if (" + field_offset_val + " !== null)\n  ";
  1481          }
  1482          pack_func_create_call += "  " + struct_name + "." +
  1483                                   namer_.Method("add", field) + "(builder, " +
  1484                                   field_offset_val + ");\n";
  1485        }
  1486      }
  1487
  1488      if (std::next(it) != struct_def.fields.vec.end()) {
  1489        constructor_func += ",\n";
  1490
  1491        if (!struct_def.fixed && has_create) {
  1492          pack_func_create_call += ",\n    ";
  1493        }
  1494
  1495        unpack_func += ",\n";
  1496        unpack_to_func += "\n";
  1497      } else {
  1498        constructor_func += "\n";
  1499        if (!struct_def.fixed) {
  1500          pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
  1501          pack_func_create_call += "\n  ";
  1502        }
  1503
  1504        unpack_func += "\n  ";
  1505        unpack_to_func += "\n";
  1506      }
  1507    }
  1508
  1509    constructor_func += "){}\n\n";
  1510
  1511    if (has_create) {
  1512      pack_func_create_call += ");";
  1513    } else {
  1514      pack_func_create_call += "return " + struct_name + ".end" +
  1515                               GetPrefixedName(struct_def) + "(builder);";
  1516    }
  1517    obj_api_class = "\n";
  1518    obj_api_class += "export class ";
  1519    obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
  1520    obj_api_class += " implements flatbuffers.IGeneratedObject {\n";
  1521    obj_api_class += constructor_func;
  1522    obj_api_class += pack_func_prototype + pack_func_offset_decl +
  1523                     pack_func_create_call + "\n}";
  1524
  1525    obj_api_class += "\n}\n";
  1526
  1527    unpack_func += ");\n}";
  1528    unpack_to_func += "}\n";
  1529
  1530    obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
  1531  }
  1532
  1533  static bool CanCreateFactoryMethod(const StructDef &struct_def) {
  1534    // to preserve backwards compatibility, we allow the first field to be a
  1535    // struct
  1536    return struct_def.fields.vec.size() < 2 ||
  1537           std::all_of(std::begin(struct_def.fields.vec) + 1,
  1538                       std::end(struct_def.fields.vec),
  1539                       [](const FieldDef *f) -> bool {
  1540                         FLATBUFFERS_ASSERT(f != nullptr);
  1541                         return f->value.type.base_type != BASE_TYPE_STRUCT;
  1542                       });
  1543  }
  1544
  1545  // Generate an accessor struct with constructor for a flatbuffers struct.
  1546  void GenStruct(const Parser &parser, StructDef &struct_def,
  1547                 std::string *code_ptr, import_set &imports) {
  1548    if (struct_def.generated) return;
  1549    std::string &code = *code_ptr;
  1550
  1551    // Special case for the root struct, since no one will necessarily reference
  1552    // it, we have to explicitly add it to the import list.
  1553    if (&struct_def == parser_.root_struct_def_) {
  1554      AddImport(imports, struct_def, struct_def);
  1555    }
  1556
  1557    const std::string object_name = GetTypeName(struct_def);
  1558    const std::string object_api_name = GetTypeName(struct_def, true);
  1559
  1560    // Emit constructor
  1561    GenDocComment(struct_def.doc_comment, code_ptr);
  1562    code += "export class ";
  1563    code += object_name;
  1564    if (parser.opts.generate_object_based_api)
  1565      code += " implements flatbuffers.IUnpackableObject<" + object_api_name +
  1566              "> {\n";
  1567    else
  1568      code += " {\n";
  1569    code += "  bb: flatbuffers.ByteBuffer|null = null;\n";
  1570    code += "  bb_pos = 0;\n";
  1571
  1572    // Generate the __init method that sets the field in a pre-existing
  1573    // accessor object. This is to allow object reuse.
  1574    code +=
  1575        "  __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
  1576    code += "  this.bb_pos = i;\n";
  1577    code += "  this.bb = bb;\n";
  1578    code += "  return this;\n";
  1579    code += "}\n\n";
  1580
  1581    // Generate special accessors for the table that when used as the root of a
  1582    // FlatBuffer
  1583    GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
  1584    GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
  1585
  1586    // Generate the identifier check method
  1587    if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
  1588        !parser_.file_identifier_.empty()) {
  1589      GenDocComment(code_ptr);
  1590      code +=
  1591          "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
  1592          "{\n";
  1593      code += "  return bb.__has_identifier('" + parser_.file_identifier_;
  1594      code += "');\n}\n\n";
  1595    }
  1596
  1597    // Emit field accessors
  1598    for (auto it = struct_def.fields.vec.begin();
  1599         it != struct_def.fields.vec.end(); ++it) {
  1600      auto &field = **it;
  1601      if (field.deprecated) continue;
  1602      std::string offset_prefix = "";
  1603
  1604      if (field.value.type.base_type == BASE_TYPE_ARRAY) {
  1605        offset_prefix = "    return ";
  1606      } else {
  1607        offset_prefix = "  const offset = " + GenBBAccess() +
  1608                        ".__offset(this.bb_pos, " +
  1609                        NumToString(field.value.offset) + ");\n";
  1610        offset_prefix += "  return offset ? ";
  1611      }
  1612
  1613      // Emit a scalar field
  1614      const auto is_string = IsString(field.value.type);
  1615      if (IsScalar(field.value.type.base_type) || is_string) {
  1616        const auto has_null_default = is_string || HasNullDefault(field);
  1617
  1618        GenDocComment(field.doc_comment, code_ptr);
  1619        std::string prefix = namer_.Method(field) + "(";
  1620        if (is_string) {
  1621          code += prefix + "):string|null\n";
  1622          code +=
  1623              prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
  1624              GenTypeName(imports, struct_def, field.value.type, false, true) +
  1625              "\n";
  1626          code += prefix + "optionalEncoding?:any";
  1627        } else {
  1628          code += prefix;
  1629        }
  1630        if (field.value.type.enum_def) {
  1631          code += "):" +
  1632                  GenTypeName(imports, struct_def, field.value.type, false,
  1633                              field.IsOptional()) +
  1634                  " {\n";
  1635        } else {
  1636          code += "):" +
  1637                  GenTypeName(imports, struct_def, field.value.type, false,
  1638                              has_null_default) +
  1639                  " {\n";
  1640        }
  1641
  1642        if (struct_def.fixed) {
  1643          code +=
  1644              "  return " +
  1645              GenGetter(field.value.type,
  1646                        "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
  1647              ";\n";
  1648        } else {
  1649          std::string index = "this.bb_pos + offset";
  1650          if (is_string) { index += ", optionalEncoding"; }
  1651          code +=
  1652              offset_prefix + GenGetter(field.value.type, "(" + index + ")");
  1653          if (field.value.type.base_type != BASE_TYPE_ARRAY) {
  1654            code += " : " + GenDefaultValue(field, imports);
  1655          }
  1656          code += ";\n";
  1657        }
  1658      }
  1659
  1660      // Emit an object field
  1661      else {
  1662        switch (field.value.type.base_type) {
  1663          case BASE_TYPE_STRUCT: {
  1664            const auto type =
  1665                AddImport(imports, struct_def, *field.value.type.struct_def)
  1666                    .name;
  1667            GenDocComment(field.doc_comment, code_ptr);
  1668            code += namer_.Method(field);
  1669            code += "(obj?:" + type + "):" + type + "|null {\n";
  1670
  1671            if (struct_def.fixed) {
  1672              code += "  return (obj || " + GenerateNewExpression(type);
  1673              code += ").__init(this.bb_pos";
  1674              code +=
  1675                  MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
  1676            } else {
  1677              code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
  1678                      ").__init(";
  1679              code += field.value.type.struct_def->fixed
  1680                          ? "this.bb_pos + offset"
  1681                          : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
  1682              code += ", " + GenBBAccess() + ") : null;\n";
  1683            }
  1684
  1685            break;
  1686          }
  1687
  1688          case BASE_TYPE_ARRAY: {
  1689            auto vectortype = field.value.type.VectorType();
  1690            auto vectortypename =
  1691                GenTypeName(imports, struct_def, vectortype, false);
  1692            auto inline_size = InlineSize(vectortype);
  1693            auto index = "this.bb_pos + " + NumToString(field.value.offset) +
  1694                         " + index" + MaybeScale(inline_size);
  1695            std::string ret_type;
  1696            bool is_union = false;
  1697            switch (vectortype.base_type) {
  1698              case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
  1699              case BASE_TYPE_STRING: ret_type = vectortypename; break;
  1700              case BASE_TYPE_UNION:
  1701                ret_type = "?flatbuffers.Table";
  1702                is_union = true;
  1703                break;
  1704              default: ret_type = vectortypename;
  1705            }
  1706            GenDocComment(field.doc_comment, code_ptr);
  1707            std::string prefix = namer_.Method(field);
  1708            // TODO: make it work without any
  1709            // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
  1710            if (is_union) { prefix += ""; }
  1711            prefix += "(index: number";
  1712            if (is_union) {
  1713              const auto union_type =
  1714                  GenUnionGenericTypeTS(*(field.value.type.enum_def));
  1715
  1716              vectortypename = union_type;
  1717              code += prefix + ", obj:" + union_type;
  1718            } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
  1719              code += prefix + ", obj?:" + vectortypename;
  1720            } else if (IsString(vectortype)) {
  1721              code += prefix + "):string\n";
  1722              code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
  1723                      "):" + vectortypename + "\n";
  1724              code += prefix + ",optionalEncoding?:any";
  1725            } else {
  1726              code += prefix;
  1727            }
  1728            code += "):" + vectortypename + "|null {\n";
  1729
  1730            if (vectortype.base_type == BASE_TYPE_STRUCT) {
  1731              code += offset_prefix + "(obj || " +
  1732                      GenerateNewExpression(vectortypename);
  1733              code += ").__init(";
  1734              code += vectortype.struct_def->fixed
  1735                          ? index
  1736                          : GenBBAccess() + ".__indirect(" + index + ")";
  1737              code += ", " + GenBBAccess() + ")";
  1738            } else {
  1739              if (is_union) {
  1740                index = "obj, " + index;
  1741              } else if (IsString(vectortype)) {
  1742                index += ", optionalEncoding";
  1743              }
  1744              code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
  1745            }
  1746
  1747            switch (field.value.type.base_type) {
  1748              case BASE_TYPE_ARRAY: {
  1749                break;
  1750              }
  1751              case BASE_TYPE_BOOL: {
  1752                code += " : false";
  1753                break;
  1754              }
  1755              case BASE_TYPE_LONG:
  1756              case BASE_TYPE_ULONG: {
  1757                code += " : BigInt(0)";
  1758                break;
  1759              }
  1760              default: {
  1761                if (IsScalar(field.value.type.element)) {
  1762                  if (field.value.type.enum_def) {
  1763                    code += field.value.constant;
  1764                  } else {
  1765                    code += " : 0";
  1766                  }
  1767                } else {
  1768                  code += ": null";
  1769                }
  1770                break;
  1771              }
  1772            }
  1773            code += ";\n";
  1774            break;
  1775          }
  1776
  1777          case BASE_TYPE_VECTOR: {
  1778            auto vectortype = field.value.type.VectorType();
  1779            auto vectortypename =
  1780                GenTypeName(imports, struct_def, vectortype, false);
  1781            auto type = GetUnderlyingVectorType(vectortype);
  1782            auto inline_size = InlineSize(type);
  1783            auto index = GenBBAccess() +
  1784                         ".__vector(this.bb_pos + offset) + index" +
  1785                         MaybeScale(inline_size);
  1786            std::string ret_type;
  1787            bool is_union = false;
  1788            switch (vectortype.base_type) {
  1789              case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
  1790              case BASE_TYPE_STRING: ret_type = vectortypename; break;
  1791              case BASE_TYPE_UNION:
  1792                ret_type = "?flatbuffers.Table";
  1793                is_union = true;
  1794                break;
  1795              default: ret_type = vectortypename;
  1796            }
  1797            GenDocComment(field.doc_comment, code_ptr);
  1798            std::string prefix = namer_.Method(field);
  1799            // TODO: make it work without any
  1800            // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
  1801            if (is_union) { prefix += ""; }
  1802            prefix += "(index: number";
  1803            if (is_union) {
  1804              const auto union_type =
  1805                  GenUnionGenericTypeTS(*(field.value.type.enum_def));
  1806
  1807              vectortypename = union_type;
  1808              code += prefix + ", obj:" + union_type;
  1809            } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
  1810              code += prefix + ", obj?:" + vectortypename;
  1811            } else if (IsString(vectortype)) {
  1812              code += prefix + "):string\n";
  1813              code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
  1814                      "):" + vectortypename + "\n";
  1815              code += prefix + ",optionalEncoding?:any";
  1816            } else {
  1817              code += prefix;
  1818            }
  1819            code += "):" + vectortypename + "|null {\n";
  1820
  1821            if (vectortype.base_type == BASE_TYPE_STRUCT) {
  1822              code += offset_prefix + "(obj || " +
  1823                      GenerateNewExpression(vectortypename);
  1824              code += ").__init(";
  1825              code += vectortype.struct_def->fixed
  1826                          ? index
  1827                          : GenBBAccess() + ".__indirect(" + index + ")";
  1828              code += ", " + GenBBAccess() + ")";
  1829            } else {
  1830              if (is_union) {
  1831                index = "obj, " + index;
  1832              } else if (IsString(vectortype)) {
  1833                index += ", optionalEncoding";
  1834              }
  1835              code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
  1836            }
  1837            code += " : ";
  1838            if (field.value.type.element == BASE_TYPE_BOOL) {
  1839              code += "false";
  1840            } else if (field.value.type.element == BASE_TYPE_LONG ||
  1841                       field.value.type.element == BASE_TYPE_ULONG) {
  1842              code += "BigInt(0)";
  1843            } else if (IsScalar(field.value.type.element)) {
  1844              if (field.value.type.enum_def) {
  1845                code += field.value.constant;
  1846              } else {
  1847                code += "0";
  1848              }
  1849            } else {
  1850              code += "null";
  1851            }
  1852            code += ";\n";
  1853            break;
  1854          }
  1855
  1856          case BASE_TYPE_UNION: {
  1857            GenDocComment(field.doc_comment, code_ptr);
  1858            code += namer_.Method(field);
  1859
  1860            const auto &union_enum = *(field.value.type.enum_def);
  1861            const auto union_type = GenUnionGenericTypeTS(union_enum);
  1862            code += "<T extends flatbuffers.Table>(obj:" + union_type +
  1863                    "):" + union_type +
  1864                    "|null "
  1865                    "{\n";
  1866
  1867            code += offset_prefix +
  1868                    GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
  1869                    " : null;\n";
  1870            break;
  1871          }
  1872          default: FLATBUFFERS_ASSERT(0);
  1873        }
  1874      }
  1875      code += "}\n\n";
  1876
  1877      // Adds the mutable scalar value to the output
  1878      if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
  1879          !IsUnion(field.value.type)) {
  1880        std::string type =
  1881            GenTypeName(imports, struct_def, field.value.type, true);
  1882
  1883        code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
  1884                "):boolean {\n";
  1885
  1886        const std::string write_method =
  1887            "." + namer_.Method("write", GenType(field.value.type));
  1888
  1889        if (struct_def.fixed) {
  1890          code += "  " + GenBBAccess() + write_method + "(this.bb_pos + " +
  1891                  NumToString(field.value.offset) + ", ";
  1892        } else {
  1893          code += "  const offset = " + GenBBAccess() +
  1894                  ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
  1895                  ");\n\n";
  1896          code += "  if (offset === 0) {\n";
  1897          code += "    return false;\n";
  1898          code += "  }\n\n";
  1899
  1900          // special case for bools, which are treated as uint8
  1901          code +=
  1902              "  " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
  1903          if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
  1904        }
  1905
  1906        code += "value);\n";
  1907        code += "  return true;\n";
  1908        code += "}\n\n";
  1909      }
  1910
  1911      // Emit vector helpers
  1912      if (IsVector(field.value.type)) {
  1913        // Emit a length helper
  1914        GenDocComment(code_ptr);
  1915        code += namer_.Method(field, "Length");
  1916        code += "():number {\n" + offset_prefix;
  1917
  1918        code +=
  1919            GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
  1920
  1921        // For scalar types, emit a typed array helper
  1922        auto vectorType = field.value.type.VectorType();
  1923        if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
  1924          GenDocComment(code_ptr);
  1925
  1926          code += namer_.Method(field, "Array");
  1927          code +=
  1928              "():" + GenType(vectorType) + "Array|null {\n" + offset_prefix;
  1929
  1930          code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
  1931                  ".bytes().buffer, " + GenBBAccess() +
  1932                  ".bytes().byteOffset + " + GenBBAccess() +
  1933                  ".__vector(this.bb_pos + offset), " + GenBBAccess() +
  1934                  ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n";
  1935        }
  1936      }
  1937    }
  1938
  1939    // Emit the fully qualified name
  1940    if (parser_.opts.generate_name_strings) {
  1941      GenDocComment(code_ptr);
  1942      code += "static getFullyQualifiedName():string {\n";
  1943      code +=
  1944          "  return '" +
  1945          struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name) +
  1946          "';\n";
  1947      code += "}\n\n";
  1948    }
  1949
  1950    // Emit the size of the struct.
  1951    if (struct_def.fixed) {
  1952      GenDocComment(code_ptr);
  1953      code += "static sizeOf():number {\n";
  1954      code += "  return " + NumToString(struct_def.bytesize) + ";\n";
  1955      code += "}\n\n";
  1956    }
  1957
  1958    // Emit a factory constructor
  1959    if (struct_def.fixed) {
  1960      std::string arguments;
  1961      GenStructArgs(imports, struct_def, &arguments, "");
  1962      GenDocComment(code_ptr);
  1963
  1964      code += "static create" + GetPrefixedName(struct_def) +
  1965              "(builder:flatbuffers.Builder";
  1966      code += arguments + "):flatbuffers.Offset {\n";
  1967
  1968      GenStructBody(struct_def, &code, "");
  1969      code += "  return builder.offset();\n}\n\n";
  1970    } else {
  1971      // Generate a method to start building a new object
  1972      GenDocComment(code_ptr);
  1973
  1974      code += "static start" + GetPrefixedName(struct_def) +
  1975              "(builder:flatbuffers.Builder) {\n";
  1976
  1977      code += "  builder.startObject(" +
  1978              NumToString(struct_def.fields.vec.size()) + ");\n";
  1979      code += "}\n\n";
  1980
  1981      // Generate a set of static methods that allow table construction
  1982      for (auto it = struct_def.fields.vec.begin();
  1983           it != struct_def.fields.vec.end(); ++it) {
  1984        auto &field = **it;
  1985        if (field.deprecated) continue;
  1986        const auto argname = GetArgName(field);
  1987
  1988        // Generate the field insertion method
  1989        GenDocComment(code_ptr);
  1990        code += "static " + namer_.Method("add", field);
  1991        code += "(builder:flatbuffers.Builder, " + argname + ":" +
  1992                GetArgType(imports, struct_def, field, false) + ") {\n";
  1993        code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
  1994        code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
  1995        if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
  1996        code += argname + ", ";
  1997        if (!IsScalar(field.value.type.base_type)) {
  1998          code += "0";
  1999        } else if (HasNullDefault(field)) {
  2000          if (IsLong(field.value.type.base_type)) {
  2001            code += "BigInt(0)";
  2002          } else {
  2003            code += "0";
  2004          }
  2005        } else {
  2006          if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
  2007          code += GenDefaultValue(field, imports);
  2008        }
  2009        code += ");\n}\n\n";
  2010
  2011        if (IsVector(field.value.type)) {
  2012          auto vector_type = field.value.type.VectorType();
  2013          auto type = GetUnderlyingVectorType(vector_type);
  2014          auto alignment = InlineAlignment(type);
  2015          auto elem_size = InlineSize(type);
  2016
  2017          // Generate a method to create a vector from a JavaScript array
  2018          if (!IsStruct(vector_type)) {
  2019            GenDocComment(code_ptr);
  2020
  2021            const std::string sig_begin =
  2022                "static " + namer_.Method("create", field, "Vector") +
  2023                "(builder:flatbuffers.Builder, data:";
  2024            const std::string sig_end = "):flatbuffers.Offset";
  2025            std::string type =
  2026                GenTypeName(imports, struct_def, vector_type, true) + "[]";
  2027            if (type == "number[]") {
  2028              const auto &array_type = GenType(vector_type);
  2029              // the old type should be deprecated in the future
  2030              std::string type_old = "number[]|Uint8Array";
  2031              std::string type_new = "number[]|" + array_type + "Array";
  2032              if (type_old == type_new) {
  2033                type = type_new;
  2034              } else {
  2035                // add function overloads
  2036                code += sig_begin + type_new + sig_end + ";\n";
  2037                code +=
  2038                    "/**\n * @deprecated This Uint8Array overload will "
  2039                    "be removed in the future.\n */\n";
  2040                code += sig_begin + type_old + sig_end + ";\n";
  2041                type = type_new + "|Uint8Array";
  2042              }
  2043            }
  2044            code += sig_begin + type + sig_end + " {\n";
  2045            code += "  builder.startVector(" + NumToString(elem_size);
  2046            code += ", data.length, " + NumToString(alignment) + ");\n";
  2047            code += "  for (let i = data.length - 1; i >= 0; i--) {\n";
  2048            code += "    builder.add" + GenWriteMethod(vector_type) + "(";
  2049            if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
  2050            code += "data[i]!);\n";
  2051            code += "  }\n";
  2052            code += "  return builder.endVector();\n";
  2053            code += "}\n\n";
  2054          }
  2055
  2056          // Generate a method to start a vector, data to be added manually
  2057          // after
  2058          GenDocComment(code_ptr);
  2059
  2060          code += "static ";
  2061          code += namer_.Method("start", field, "Vector");
  2062          code += "(builder:flatbuffers.Builder, numElems:number) {\n";
  2063          code += "  builder.startVector(" + NumToString(elem_size);
  2064          code += ", numElems, " + NumToString(alignment) + ");\n";
  2065          code += "}\n\n";
  2066        }
  2067      }
  2068
  2069      // Generate a method to stop building a new object
  2070      GenDocComment(code_ptr);
  2071
  2072      code += "static end" + GetPrefixedName(struct_def);
  2073      code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
  2074
  2075      code += "  const offset = builder.endObject();\n";
  2076      for (auto it = struct_def.fields.vec.begin();
  2077           it != struct_def.fields.vec.end(); ++it) {
  2078        auto &field = **it;
  2079        if (!field.deprecated && field.IsRequired()) {
  2080          code += "  builder.requiredField(offset, ";
  2081          code += NumToString(field.value.offset);
  2082          code += ") // " + field.name + "\n";
  2083        }
  2084      }
  2085      code += "  return offset;\n";
  2086      code += "}\n\n";
  2087
  2088      // Generate the methods to complete buffer construction
  2089      GenerateFinisher(struct_def, code_ptr, code, false);
  2090      GenerateFinisher(struct_def, code_ptr, code, true);
  2091
  2092      // Generate a convenient CreateX function
  2093      if (CanCreateFactoryMethod(struct_def)) {
  2094        code += "static create" + GetPrefixedName(struct_def);
  2095        code += "(builder:flatbuffers.Builder";
  2096        for (auto it = struct_def.fields.vec.begin();
  2097             it != struct_def.fields.vec.end(); ++it) {
  2098          const auto &field = **it;
  2099          if (field.deprecated) continue;
  2100          code += ", " + GetArgName(field) + ":" +
  2101                  GetArgType(imports, struct_def, field, true);
  2102        }
  2103
  2104        code += "):flatbuffers.Offset {\n";
  2105        code += "  " + object_name + ".start" + GetPrefixedName(struct_def) +
  2106                "(builder);\n";
  2107
  2108        std::string methodPrefix = object_name;
  2109        for (auto it = struct_def.fields.vec.begin();
  2110             it != struct_def.fields.vec.end(); ++it) {
  2111          const auto &field = **it;
  2112          if (field.deprecated) continue;
  2113
  2114          const auto arg_name = GetArgName(field);
  2115
  2116          if (field.IsScalarOptional()) {
  2117            code += "  if (" + arg_name + " !== null)\n  ";
  2118          }
  2119
  2120          code += "  " + methodPrefix + "." + namer_.Method("add", field) + "(";
  2121          code += "builder, " + arg_name + ");\n";
  2122        }
  2123
  2124        code += "  return " + methodPrefix + ".end" +
  2125                GetPrefixedName(struct_def) + "(builder);\n";
  2126        code += "}\n";
  2127      }
  2128    }
  2129
  2130    if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
  2131      auto name = GetPrefixedName(struct_def, "");
  2132      code += "\n";
  2133      code += "serialize():Uint8Array {\n";
  2134      code += "  return this.bb!.bytes();\n";
  2135      code += "}\n";
  2136
  2137      code += "\n";
  2138      code += "static deserialize(buffer: Uint8Array):" +
  2139              namer_.EscapeKeyword(name) + " {\n";
  2140      code += "  return " + AddImport(imports, struct_def, struct_def).name +
  2141              ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
  2142      code += "}\n";
  2143    }
  2144
  2145    if (parser_.opts.generate_object_based_api) {
  2146      std::string obj_api_class;
  2147      std::string obj_api_unpack_func;
  2148      GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
  2149                imports);
  2150
  2151      code += obj_api_unpack_func + "}\n" + obj_api_class;
  2152    } else {
  2153      code += "}\n";
  2154    }
  2155  }
  2156
  2157  static bool HasNullDefault(const FieldDef &field) {
  2158    return field.IsOptional() && field.value.constant == "null";
  2159  }
  2160
  2161  std::string GetArgType(import_set &imports, const Definition &owner,
  2162                         const FieldDef &field, bool allowNull) {
  2163    return GenTypeName(imports, owner, field.value.type, true,
  2164                       allowNull && field.IsOptional());
  2165  }
  2166
  2167  std::string GetArgName(const FieldDef &field) {
  2168    auto argname = namer_.Variable(field);
  2169    if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
  2170    return argname;
  2171  }
  2172
  2173  std::string GetPrefixedName(const StructDef &struct_def,
  2174                              const char *prefix = "") {
  2175    return prefix + struct_def.name;
  2176  }
  2177};  // namespace ts
  2178}  // namespace ts
  2179
  2180static bool GenerateTS(const Parser &parser, const std::string &path,
  2181                       const std::string &file_name) {
  2182  ts::TsGenerator generator(parser, path, file_name);
  2183  return generator.generate();
  2184}
  2185
  2186static std::string TSMakeRule(const Parser &parser, const std::string &path,
  2187                              const std::string &file_name) {
  2188  std::string filebase =
  2189      flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
  2190  ts::TsGenerator generator(parser, path, file_name);
  2191  std::string make_rule =
  2192      generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
  2193
  2194  auto included_files = parser.GetIncludedFilesRecursive(file_name);
  2195  for (auto it = included_files.begin(); it != included_files.end(); ++it) {
  2196    make_rule += " " + *it;
  2197  }
  2198  return make_rule;
  2199}
  2200
  2201namespace {
  2202
  2203class TsCodeGenerator : public CodeGenerator {
  2204 public:
  2205  Status GenerateCode(const Parser &parser, const std::string &path,
  2206                      const std::string &filename) override {
  2207    if (!GenerateTS(parser, path, filename)) { return Status::ERROR; }
  2208    return Status::OK;
  2209  }
  2210
  2211  Status GenerateCode(const uint8_t *, int64_t,
  2212                      const CodeGenOptions &) override {
  2213    return Status::NOT_IMPLEMENTED;
  2214  }
  2215
  2216  Status GenerateMakeRule(const Parser &parser, const std::string &path,
  2217                          const std::string &filename,
  2218                          std::string &output) override {
  2219    output = TSMakeRule(parser, path, filename);
  2220    return Status::OK;
  2221  }
  2222
  2223  Status GenerateGrpcCode(const Parser &parser, const std::string &path,
  2224                          const std::string &filename) override {
  2225    if (!GenerateTSGRPC(parser, path, filename)) { return Status::ERROR; }
  2226    return Status::OK;
  2227  }
  2228
  2229  Status GenerateRootFile(const Parser &parser,
  2230                          const std::string &path) override {
  2231    (void)parser;
  2232    (void)path;
  2233    return Status::NOT_IMPLEMENTED;
  2234  }
  2235  bool IsSchemaOnly() const override { return true; }
  2236
  2237  bool SupportsBfbsGeneration() const override { return false; }
  2238  bool SupportsRootFileGeneration() const override { return false; }
  2239
  2240  IDLOptions::Language Language() const override { return IDLOptions::kTs; }
  2241
  2242  std::string LanguageName() const override { return "TS"; }
  2243};
  2244}  // namespace
  2245
  2246std::unique_ptr<CodeGenerator> NewTsCodeGenerator() {
  2247  return std::unique_ptr<TsCodeGenerator>(new TsCodeGenerator());
  2248}
  2249
  2250}  // namespace flatbuffers

View as plain text