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