blob: faf43458d4c4cf4456563396fe9baf2621d1cc49 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// independent from idl_parser, since this code is not needed for most clients
18#include <algorithm>
19#include <cassert>
20#include <unordered_map>
21#include <unordered_set>
22
23#include "flatbuffers/code_generators.h"
24#include "flatbuffers/flatbuffers.h"
25#include "flatbuffers/idl.h"
26#include "flatbuffers/util.h"
27
28namespace flatbuffers {
29
30struct ImportDefinition {
31 std::string name;
32 std::string import_statement;
33 std::string export_statement;
34 std::string bare_file_path;
35 std::string rel_file_path;
36 const Definition *dependent = nullptr;
37 const Definition *dependency = nullptr;
38};
39
40enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
41
42namespace ts {
43// Iterate through all definitions we haven't generate code for (enums, structs,
44// and tables) and output them to a single file.
45class TsGenerator : public BaseGenerator {
46 public:
47 typedef std::map<std::string, ImportDefinition> import_set;
48
49 TsGenerator(const Parser &parser, const std::string &path,
50 const std::string &file_name)
51 : BaseGenerator(parser, path, file_name, "", ".", "ts") {
52 // clang-format off
53
54 // List of keywords retrieved from here:
55 // https://github.com/microsoft/TypeScript/issues/2536
56 // One per line to ease comparisons to that list are easier
57 static const char *const keywords[] = {
58 "argument",
59 "break",
60 "case",
61 "catch",
62 "class",
63 "const",
64 "continue",
65 "debugger",
66 "default",
67 "delete",
68 "do",
69 "else",
70 "enum",
71 "export",
72 "extends",
73 "false",
74 "finally",
75 "for",
76 "function",
77 "if",
78 "import",
79 "in",
80 "instanceof",
81 "new",
82 "null",
83 "Object",
84 "return",
85 "super",
86 "switch",
87 "this",
88 "throw",
89 "true",
90 "try",
91 "typeof",
92 "var",
93 "void",
94 "while",
95 "with",
96 "as",
97 "implements",
98 "interface",
99 "let",
100 "package",
101 "private",
102 "protected",
103 "public",
104 "static",
105 "yield",
106 nullptr,
107 // clang-format on
108 };
109
110 for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
111 }
112 bool generate() {
113 if (parser_.opts.ts_flat_file && parser_.opts.generate_all) {
114 // Not implemented; warning message should have beem emitted by flatc.
115 return false;
116 }
117 generateEnums();
118 generateStructs();
119 generateEntry();
120 return true;
121 }
122
123 // Save out the generated code for a single class while adding
124 // declaration boilerplate.
125 bool SaveType(const Definition &definition, const std::string &classcode,
126 import_set &imports, import_set &bare_imports) {
127 if (!classcode.length()) return true;
128
129 std::string code;
130
131 if (!parser_.opts.ts_flat_file) {
132 code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
133
134 for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
135 code += it->second.import_statement + "\n";
136 }
137 if (!bare_imports.empty()) code += "\n";
138
139 for (auto it = imports.begin(); it != imports.end(); it++) {
140 if (it->second.dependency != &definition) {
141 code += it->second.import_statement + "\n";
142 }
143 }
144 if (!imports.empty()) code += "\n\n";
145 }
146
147 code += classcode;
148 auto filename =
149 NamespaceDir(*definition.defined_namespace, true) +
150 ConvertCase(definition.name, Case::kDasher, Case::kUpperCamel) + ".ts";
151 if (parser_.opts.ts_flat_file) {
152 flat_file_ += code;
153 flat_file_definitions_.insert(&definition);
154 return true;
155 } else {
156 return SaveFile(filename.c_str(), code, false);
157 }
158 }
159
160 private:
161 std::unordered_set<std::string> keywords_;
162
163 std::string EscapeKeyword(const std::string &name) const {
164 return keywords_.find(name) == keywords_.end() ? name : name + "_";
165 }
166
167 import_set imports_all_;
168
169 // The following three members are used when generating typescript code into a
170 // single file rather than creating separate files for each type.
171
172 // flat_file_ contains the aggregated contents of the file prior to being
173 // written to disk.
174 std::string flat_file_;
175 // flat_file_definitions_ tracks which types have been written to flat_file_.
176 std::unordered_set<const Definition *> flat_file_definitions_;
177 // This maps from import names to types to import.
178 std::map<std::string, std::map<std::string, std::string>>
179 flat_file_import_declarations_;
180
181 // Generate code for all enums.
182 void generateEnums() {
183 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
184 ++it) {
185 import_set bare_imports;
186 import_set imports;
187 std::string enumcode;
188 auto &enum_def = **it;
189 GenEnum(enum_def, &enumcode, imports, false);
190 GenEnum(enum_def, &enumcode, imports, true);
191 SaveType(enum_def, enumcode, imports, bare_imports);
192 imports_all_.insert(imports.begin(), imports.end());
193 }
194 }
195
196 // Generate code for all structs.
197 void generateStructs() {
198 for (auto it = parser_.structs_.vec.begin();
199 it != parser_.structs_.vec.end(); ++it) {
200 import_set bare_imports;
201 import_set imports;
202 AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
203 auto &struct_def = **it;
204 std::string declcode;
205 GenStruct(parser_, struct_def, &declcode, imports);
206 SaveType(struct_def, declcode, imports, bare_imports);
207 imports_all_.insert(imports.begin(), imports.end());
208 }
209 }
210
211 // Generate code for a single entry point module.
212 void generateEntry() {
213 std::string code;
214 if (parser_.opts.ts_flat_file) {
215 code += "import * as flatbuffers from 'flatbuffers';\n";
216 for (const auto &it : flat_file_import_declarations_) {
217 // Note that we do end up generating an import for ourselves, which
218 // should generally be harmless.
219 // TODO: Make it so we don't generate a self-import; this will also
220 // require modifying AddImport to ensure that we don't use
221 // namespace-prefixed names anywhere...
222 std::string file = it.first;
223 if (file.empty()) {
224 continue;
225 }
226 std::string noext = flatbuffers::StripExtension(file);
227 std::string basename = flatbuffers::StripPath(noext);
228 std::string include_file = GeneratedFileName(
229 parser_.opts.include_prefix,
230 parser_.opts.keep_include_path ? noext : basename, parser_.opts);
231 // TODO: what is the right behavior when different include flags are
232 // specified here? Should we always be adding the "./" for a relative
233 // path or turn it off if --include-prefix is specified, or something
234 // else?
235 std::string include_name = "./" + flatbuffers::StripExtension(include_file);
236 code += "import {";
237 for (const auto &pair : it.second) {
238 code += EscapeKeyword(pair.first) + " as " +
239 EscapeKeyword(pair.second) + ", ";
240 }
241 code.resize(code.size() - 2);
242 code += "} from '" + include_name + "';\n";
243 }
244 code += "\n\n";
245 code += flat_file_;
246 const std::string filename =
247 GeneratedFileName(path_, file_name_, parser_.opts);
248 SaveFile(filename.c_str(), code, false);
249 } else {
250 for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
251 code += it->second.export_statement + "\n";
252 }
253 std::string path = "./" + path_ + file_name_ + ".ts";
254 SaveFile(path.c_str(), code, false);
255 }
256 }
257
258 // Generate a documentation comment, if available.
259 static void GenDocComment(const std::vector<std::string> &dc,
260 std::string *code_ptr,
261 const char *indent = nullptr) {
262 if (dc.empty()) {
263 // Don't output empty comment blocks with 0 lines of comment content.
264 return;
265 }
266
267 std::string &code = *code_ptr;
268 if (indent) code += indent;
269 code += "/**\n";
270 for (auto it = dc.begin(); it != dc.end(); ++it) {
271 if (indent) code += indent;
272 code += " *" + *it + "\n";
273 }
274 if (indent) code += indent;
275 code += " */\n";
276 }
277
278 static void GenDocComment(std::string *code_ptr) {
279 GenDocComment(std::vector<std::string>(), code_ptr);
280 }
281
282 // Generate an enum declaration and an enum string lookup table.
283 void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports,
284 bool reverse) {
285 if (enum_def.generated) return;
286 if (reverse) return; // FIXME.
287 std::string &code = *code_ptr;
288 GenDocComment(enum_def.doc_comment, code_ptr);
289 code += "export enum " + EscapeKeyword(enum_def.name) + "{\n";
290 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
291 auto &ev = **it;
292 if (!ev.doc_comment.empty()) {
293 if (it != enum_def.Vals().begin()) { code += '\n'; }
294 GenDocComment(ev.doc_comment, code_ptr, " ");
295 }
296
297 const std::string escaped_name = EscapeKeyword(ev.name);
298
299 // Generate mapping between EnumName: EnumValue(int)
300 if (reverse) {
301 code += " '" + enum_def.ToString(ev) + "'";
302 code += " = ";
303 code += "'" + escaped_name + "'";
304 } else {
305 code += " " + escaped_name;
306 code += " = ";
307 // Unfortunately, because typescript does not support bigint enums,
308 // for 64-bit enums, we instead map the enum names to strings.
309 switch (enum_def.underlying_type.base_type) {
310 case BASE_TYPE_LONG:
311 case BASE_TYPE_ULONG: {
312 code += "'" + enum_def.ToString(ev) + "'";
313 break;
314 }
315 default: code += enum_def.ToString(ev);
316 }
317 }
318
319 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
320 }
321 code += "}";
322
323 if (enum_def.is_union) {
324 code += GenUnionConvFunc(enum_def.underlying_type, imports);
325 }
326
327 code += "\n\n";
328 }
329
330 static std::string GenType(const Type &type) {
331 switch (type.base_type) {
332 case BASE_TYPE_BOOL:
333 case BASE_TYPE_CHAR: return "Int8";
334 case BASE_TYPE_UTYPE:
335 case BASE_TYPE_UCHAR: return "Uint8";
336 case BASE_TYPE_SHORT: return "Int16";
337 case BASE_TYPE_USHORT: return "Uint16";
338 case BASE_TYPE_INT: return "Int32";
339 case BASE_TYPE_UINT: return "Uint32";
340 case BASE_TYPE_LONG: return "Int64";
341 case BASE_TYPE_ULONG: return "Uint64";
342 case BASE_TYPE_FLOAT: return "Float32";
343 case BASE_TYPE_DOUBLE: return "Float64";
344 case BASE_TYPE_STRING: return "String";
345 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
346 case BASE_TYPE_STRUCT: return type.struct_def->name;
347 default: return "flatbuffers.Table";
348 }
349 }
350
351 std::string GenGetter(const Type &type, const std::string &arguments) {
352 switch (type.base_type) {
353 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
354 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
355 case BASE_TYPE_UNION:
356 if (!UnionHasStringType(*type.enum_def)) {
357 return GenBBAccess() + ".__union" + arguments;
358 }
359 return GenBBAccess() + ".__union_with_string" + arguments;
360 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
361 default: {
362 auto getter = GenBBAccess() + ".read" +
363 ConvertCase(GenType(type), Case::kUpperCamel) + arguments;
364 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
365 return getter;
366 }
367 }
368 }
369
370 std::string GenBBAccess() const { return "this.bb!"; }
371
372 std::string GenDefaultValue(const FieldDef &field, import_set &imports) {
373 if (field.IsScalarOptional()) { return "null"; }
374
375 const auto &value = field.value;
376 if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
377 value.type.base_type != BASE_TYPE_VECTOR) {
378 // If the value is an enum with a 64-bit base type, we have to just
379 // return the bigint value directly since typescript does not support
380 // enums with bigint backing types.
381 switch (value.type.base_type) {
382 case BASE_TYPE_LONG:
383 case BASE_TYPE_ULONG: {
384 return "BigInt('" + value.constant + "')";
385 }
386 default: {
387 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
388 return EscapeKeyword(AddImport(imports, *value.type.enum_def,
389 *value.type.enum_def)) +
390 "." + EscapeKeyword(val->name);
391 } else {
392 return value.constant;
393 }
394 }
395 }
396 }
397
398 switch (value.type.base_type) {
399 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
400
401 case BASE_TYPE_STRING:
402 case BASE_TYPE_UNION:
403 case BASE_TYPE_STRUCT: {
404 return "null";
405 }
406
407 case BASE_TYPE_VECTOR: return "[]";
408
409 case BASE_TYPE_LONG:
410 case BASE_TYPE_ULONG: {
411 return "BigInt('" + value.constant + "')";
412 }
413
414 default: return value.constant;
415 }
416 }
417
418 std::string GenTypeName(import_set &imports, const Definition &owner,
419 const Type &type, bool input,
420 bool allowNull = false) {
421 if (!input) {
422 if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
423 std::string name;
424 if (IsString(type)) {
425 name = "string|Uint8Array";
426 } else {
427 name = EscapeKeyword(AddImport(imports, owner, *type.struct_def));
428 }
429 return allowNull ? (name + "|null") : name;
430 }
431 }
432
433 switch (type.base_type) {
434 case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean";
435 case BASE_TYPE_LONG:
436 case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint";
437 default:
438 if (IsScalar(type.base_type)) {
439 if (type.enum_def) {
440 const auto enum_name = AddImport(imports, owner, *type.enum_def);
441 return allowNull ? (enum_name + "|null") : enum_name;
442 }
443 return allowNull ? "number|null" : "number";
444 }
445 return "flatbuffers.Offset";
446 }
447 }
448
449 // Returns the method name for use with add/put calls.
450 static std::string GenWriteMethod(const Type &type) {
451 // Forward to signed versions since unsigned versions don't exist
452 switch (type.base_type) {
453 case BASE_TYPE_UTYPE:
454 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
455 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
456 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
457 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
458 default: break;
459 }
460
461 return IsScalar(type.base_type)
462 ? ConvertCase(GenType(type), Case::kUpperCamel)
463 : (IsStruct(type) ? "Struct" : "Offset");
464 }
465
466 template<typename T> static std::string MaybeAdd(T value) {
467 return value != 0 ? " + " + NumToString(value) : "";
468 }
469
470 template<typename T> static std::string MaybeScale(T value) {
471 return value != 1 ? " * " + NumToString(value) : "";
472 }
473
474 void GenStructArgs(import_set &imports, const StructDef &struct_def,
475 std::string *arguments, const std::string &nameprefix) {
476 for (auto it = struct_def.fields.vec.begin();
477 it != struct_def.fields.vec.end(); ++it) {
478 auto &field = **it;
479 if (IsStruct(field.value.type)) {
480 // Generate arguments for a struct inside a struct. To ensure names
481 // don't clash, and to make it obvious these arguments are constructing
482 // a nested struct, prefix the name with the field name.
483 GenStructArgs(imports, *field.value.type.struct_def, arguments,
484 nameprefix + field.name + "_");
485 } else {
486 *arguments += ", " + nameprefix + field.name + ": " +
487 GenTypeName(imports, field, field.value.type, true,
488 field.IsOptional());
489 }
490 }
491 }
492
493 static void GenStructBody(const StructDef &struct_def, std::string *body,
494 const std::string &nameprefix) {
495 *body += " builder.prep(";
496 *body += NumToString(struct_def.minalign) + ", ";
497 *body += NumToString(struct_def.bytesize) + ");\n";
498
499 for (auto it = struct_def.fields.vec.rbegin();
500 it != struct_def.fields.vec.rend(); ++it) {
501 auto &field = **it;
502 if (field.padding) {
503 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
504 }
505 if (IsStruct(field.value.type)) {
506 // Generate arguments for a struct inside a struct. To ensure names
507 // don't clash, and to make it obvious these arguments are constructing
508 // a nested struct, prefix the name with the field name.
509 GenStructBody(*field.value.type.struct_def, body,
510 nameprefix + field.name + "_");
511 } else {
512 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
513 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
514 *body += nameprefix + field.name + ");\n";
515 }
516 }
517 }
518
519 std::string GenerateNewExpression(const std::string &object_name) {
520 return "new " + EscapeKeyword(object_name) + "()";
521 }
522
523 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
524 std::string &code, const std::string &object_name,
525 bool size_prefixed) {
526 if (!struct_def.fixed) {
527 GenDocComment(code_ptr);
528 std::string sizePrefixed("SizePrefixed");
529 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
530 GetPrefixedName(struct_def, "As");
531 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
532 "):" + object_name + " {\n";
533 if (size_prefixed) {
534 code +=
535 " bb.setPosition(bb.position() + "
536 "flatbuffers.SIZE_PREFIX_LENGTH);\n";
537 }
538 code += " return (obj || " + GenerateNewExpression(object_name);
539 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
540 code += "}\n\n";
541 }
542 }
543
544 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
545 std::string &code, bool size_prefixed) {
546 if (parser_.root_struct_def_ == &struct_def) {
547 std::string sizePrefixed("SizePrefixed");
548 GenDocComment(code_ptr);
549
550 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
551 GetPrefixedName(struct_def) + "Buffer";
552 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
553 code += " builder.finish(offset";
554 if (!parser_.file_identifier_.empty()) {
555 code += ", '" + parser_.file_identifier_ + "'";
556 }
557 if (size_prefixed) {
558 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
559 code += ", true";
560 }
561 code += ");\n";
562 code += "}\n\n";
563 }
564 }
565
566 static std::string GetObjApiClassName(const StructDef &sd,
567 const IDLOptions &opts) {
568 return GetObjApiClassName(sd.name, opts);
569 }
570
571 static std::string GetObjApiClassName(const std::string &name,
572 const IDLOptions &opts) {
573 return opts.object_prefix + name + opts.object_suffix;
574 }
575
576 bool UnionHasStringType(const EnumDef &union_enum) {
577 return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
578 [](const EnumVal *ev) {
579 return !ev->IsZero() && IsString(ev->union_type);
580 });
581 }
582
583 std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
584 // TODO: make it work without any
585 // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
586 // "");
587 return std::string("any") +
588 (UnionHasStringType(union_enum) ? "|string" : "");
589 }
590
591 std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) {
592 std::string ret;
593 std::set<std::string> type_list;
594
595 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
596 ++it) {
597 const auto &ev = **it;
598 if (ev.IsZero()) { continue; }
599
600 std::string type = "";
601 if (IsString(ev.union_type)) {
602 type = "string"; // no need to wrap string type in namespace
603 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
604 type = AddImport(imports, union_enum, *ev.union_type.struct_def);
605 } else {
606 FLATBUFFERS_ASSERT(false);
607 }
608 type_list.insert(type);
609 }
610
611 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
612 ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
613 }
614
615 return ret;
616 }
617
618 std::string AddImport(import_set &imports, const Definition &dependent,
619 const StructDef &dependency) {
620 std::string ns;
621 const auto &depc_comps = dependency.defined_namespace->components;
622 for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
623 ns += *it;
624 }
625 std::string unique_name = ns + dependency.name;
626 std::string import_name = dependency.name;
627 std::string long_import_name;
628 if (imports.find(unique_name) != imports.end())
629 return imports.find(unique_name)->second.name;
630 for (auto it = imports.begin(); it != imports.end(); it++) {
631 if (it->second.name == import_name) {
632 long_import_name = ns + import_name;
633 break;
634 }
635 }
636
637 if (parser_.opts.ts_flat_file) {
638 std::string file = dependency.declaration_file == nullptr
639 ? dependency.file
640 : dependency.declaration_file->substr(2);
641 file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
642 dependency.file).substr(2);
643 long_import_name = ns + import_name;
644 flat_file_import_declarations_[file][import_name] = long_import_name;
645 if (parser_.opts.generate_object_based_api) {
646 flat_file_import_declarations_[file][import_name + "T"] = long_import_name + "T";
647 }
648 }
649
650 std::string import_statement;
651 std::string export_statement;
652 import_statement += "import { ";
653 export_statement += "export { ";
654 std::string symbols_expression;
655 if (long_import_name.empty()) {
656 symbols_expression += EscapeKeyword(import_name);
657 if (parser_.opts.generate_object_based_api)
658 symbols_expression += ", " + import_name + "T";
659 } else {
660 symbols_expression += EscapeKeyword(dependency.name) + " as " +
661 EscapeKeyword(long_import_name);
662 if (parser_.opts.generate_object_based_api)
663 symbols_expression +=
664 ", " + dependency.name + "T as " + long_import_name + "T";
665 }
666 import_statement += symbols_expression + " } from '";
667 export_statement += symbols_expression + " } from '";
668 std::string bare_file_path;
669 std::string rel_file_path;
670 const auto &dep_comps = dependent.defined_namespace->components;
671 for (size_t i = 0; i < dep_comps.size(); i++)
672 rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
673 if (dep_comps.size() == 0) rel_file_path += ".";
674 for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
675 bare_file_path +=
676 kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
677 bare_file_path +=
678 kPathSeparator +
679 ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
680 rel_file_path += bare_file_path;
681 import_statement += rel_file_path + "';";
682 export_statement += "." + bare_file_path + "';";
683 ImportDefinition import;
684 import.name = long_import_name.empty() ? import_name : long_import_name;
685 import.bare_file_path = bare_file_path;
686 import.rel_file_path = rel_file_path;
687 import.import_statement = import_statement;
688 import.export_statement = export_statement;
689 import.dependency = &dependency;
690 import.dependent = &dependent;
691 imports.insert(std::make_pair(unique_name, import));
692 return import.name;
693 }
694
695 // TODO: largely (but not identical) duplicated code from above couln't find a
696 // good way to refactor
697 std::string AddImport(import_set &imports, const Definition &dependent,
698 const EnumDef &dependency) {
699 std::string ns;
700 const auto &depc_comps = dependency.defined_namespace->components;
701 for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
702 ns += *it;
703 }
704 std::string unique_name = ns + dependency.name;
705 std::string import_name = EscapeKeyword(dependency.name);
706 std::string long_import_name;
707 if (imports.find(unique_name) != imports.end()) {
708 return imports.find(unique_name)->second.name;
709 }
710 for (auto it = imports.begin(); it != imports.end(); it++) {
711 if (it->second.name == import_name) {
712 long_import_name = ns + import_name;
713 break;
714 }
715 }
716
717 if (parser_.opts.ts_flat_file) {
718 std::string file = dependency.declaration_file == nullptr
719 ? dependency.file
720 : dependency.declaration_file->substr(2);
721 file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
722 dependency.file).substr(2);
723 long_import_name = ns + import_name;
724 flat_file_import_declarations_[file][import_name] = long_import_name;
725 }
726
727 std::string import_statement;
728 std::string export_statement;
729 import_statement += "import { ";
730 export_statement += "export { ";
731 std::string symbols_expression;
732 if (long_import_name.empty())
733 symbols_expression += import_name;
734 else
735 symbols_expression += EscapeKeyword(dependency.name) + " as " +
736 EscapeKeyword(long_import_name);
737 if (dependency.is_union) {
738 symbols_expression += ", unionTo" + import_name;
739 symbols_expression += ", unionListTo" + import_name;
740 }
741 import_statement += symbols_expression + " } from '";
742 export_statement += symbols_expression + " } from '";
743 std::string bare_file_path;
744 std::string rel_file_path;
745 const auto &dep_comps = dependent.defined_namespace->components;
746 for (size_t i = 0; i < dep_comps.size(); i++)
747 rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
748 if (dep_comps.size() == 0) rel_file_path += ".";
749 for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
750 bare_file_path +=
751 kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
752 bare_file_path +=
753 kPathSeparator +
754 ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
755 rel_file_path += bare_file_path;
756 import_statement += rel_file_path + "';";
757 export_statement += "." + bare_file_path + "';";
758 ImportDefinition import;
759 import.name = long_import_name.empty() ? import_name : long_import_name;
760 import.bare_file_path = bare_file_path;
761 import.rel_file_path = rel_file_path;
762 import.import_statement = import_statement;
763 import.export_statement = export_statement;
764 import.dependency = &dependency;
765 import.dependent = &dependent;
766 imports.insert(std::make_pair(unique_name, import));
767 return import.name;
768 }
769
770 void AddImport(import_set &imports, std::string import_name,
771 std::string fileName) {
772 ImportDefinition import;
773 import.name = import_name;
774 import.import_statement =
775 "import " + import_name + " from '" + fileName + "';";
776 imports.insert(std::make_pair(import_name, import));
777 }
778
779 // Generate a TS union type based on a union's enum
780 std::string GenObjApiUnionTypeTS(import_set &imports, const IDLOptions &opts,
781 const EnumDef &union_enum) {
782 std::string ret = "";
783 std::set<std::string> type_list;
784
785 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
786 ++it) {
787 const auto &ev = **it;
788 if (ev.IsZero()) { continue; }
789
790 std::string type = "";
791 if (IsString(ev.union_type)) {
792 type = "string"; // no need to wrap string type in namespace
793 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
794 type = GetObjApiClassName(
795 AddImport(imports, union_enum, *ev.union_type.struct_def), opts);
796 } else {
797 FLATBUFFERS_ASSERT(false);
798 }
799 type_list.insert(type);
800 }
801
802 size_t totalPrinted = 0;
803 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
804 ++totalPrinted;
805 ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
806 }
807
808 return ret;
809 }
810
811 std::string GenUnionConvFuncName(const EnumDef &enum_def) {
812 return "unionTo" + enum_def.name;
813 }
814
815 std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
816 return "unionListTo" + enum_def.name;
817 }
818
819 std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
820 if (union_type.enum_def) {
821 const auto &enum_def = *union_type.enum_def;
822
823 const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
824 const auto valid_union_type_with_null = valid_union_type + "|null";
825
826 auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
827 "(\n type: " + enum_def.name +
828 ",\n accessor: (obj:" + valid_union_type + ") => " +
829 valid_union_type_with_null +
830 "\n): " + valid_union_type_with_null + " {\n";
831
832 const auto enum_type = AddImport(imports, enum_def, enum_def);
833
834 const auto union_enum_loop = [&](const std::string &accessor_str) {
835 ret += " switch(" + enum_type + "[type]) {\n";
836 ret += " case 'NONE': return null; \n";
837
838 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
839 ++it) {
840 const auto &ev = **it;
841 if (ev.IsZero()) { continue; }
842
843 ret += " case '" + ev.name + "': ";
844
845 if (IsString(ev.union_type)) {
846 ret += "return " + accessor_str + "'') as string;";
847 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
848 const auto type =
849 AddImport(imports, enum_def, *ev.union_type.struct_def);
850 ret += "return " + accessor_str + "new " + type + "())! as " +
851 type + ";";
852 } else {
853 FLATBUFFERS_ASSERT(false);
854 }
855 ret += "\n";
856 }
857
858 ret += " default: return null;\n";
859 ret += " }\n";
860 };
861
862 union_enum_loop("accessor(");
863 ret += "}";
864
865 ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
866 "(\n type: " + enum_def.name +
867 ", \n accessor: (index: number, obj:" + valid_union_type +
868 ") => " + valid_union_type_with_null +
869 ", \n index: number\n): " + valid_union_type_with_null + " {\n";
870 union_enum_loop("accessor(index, ");
871 ret += "}";
872
873 return ret;
874 }
875 FLATBUFFERS_ASSERT(0);
876 return "";
877 }
878
879 // Used for generating a short function that returns the correct class
880 // based on union enum type. Assume the context is inside the non object api
881 // type
882 std::string GenUnionValTS(import_set &imports, const std::string &field_name,
883 const Type &union_type,
884 const bool is_array = false) {
885 if (union_type.enum_def) {
886 const auto &enum_def = *union_type.enum_def;
887 const auto enum_type = AddImport(imports, enum_def, enum_def);
888 const std::string union_accessor = "this." + field_name;
889
890 const auto union_has_string = UnionHasStringType(enum_def);
891 const auto field_binded_method = "this." + field_name + ".bind(this)";
892
893 std::string ret;
894
895 if (!is_array) {
896 const auto conversion_function = GenUnionConvFuncName(enum_def);
897 const auto target_enum = "this." + field_name + "Type()";
898
899 ret = "(() => {\n";
900 ret += " let temp = " + conversion_function + "(" + target_enum +
901 ", " + field_binded_method + ");\n";
902 ret += " if(temp === null) { return null; }\n";
903 ret += union_has_string
904 ? " if(typeof temp === 'string') { return temp; }\n"
905 : "";
906 ret += " return temp.unpack()\n";
907 ret += " })()";
908 } else {
909 const auto conversion_function = GenUnionListConvFuncName(enum_def);
910 const auto target_enum_accesor = "this." + field_name + "Type";
911 const auto target_enum_length = target_enum_accesor + "Length()";
912
913 ret = "(() => {\n";
914 ret += " let ret = [];\n";
915 ret += " for(let targetEnumIndex = 0; targetEnumIndex < " +
916 target_enum_length +
917 "; "
918 "++targetEnumIndex) {\n";
919 ret += " let targetEnum = " + target_enum_accesor +
920 "(targetEnumIndex);\n";
921 ret += " if(targetEnum === null || " + enum_type +
922 "[targetEnum!] === 'NONE') { "
923 "continue; }\n\n";
924 ret += " let temp = " + conversion_function + "(targetEnum, " +
925 field_binded_method + ", targetEnumIndex);\n";
926 ret += " if(temp === null) { continue; }\n";
927 ret += union_has_string ? " if(typeof temp === 'string') { "
928 "ret.push(temp); continue; }\n"
929 : "";
930 ret += " ret.push(temp.unpack());\n";
931 ret += " }\n";
932 ret += " return ret;\n";
933 ret += " })()";
934 }
935
936 return ret;
937 }
938
939 FLATBUFFERS_ASSERT(0);
940 return "";
941 }
942
943 static std::string GenNullCheckConditional(
944 const std::string &nullCheckVar, const std::string &trueVal,
945 const std::string &falseVal = "null") {
946 return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
947 ")";
948 }
949
950 std::string GenStructMemberValueTS(const StructDef &struct_def,
951 const std::string &prefix,
952 const std::string &delimiter,
953 const bool nullCheck = true) {
954 std::string ret;
955 for (auto it = struct_def.fields.vec.begin();
956 it != struct_def.fields.vec.end(); ++it) {
957 auto &field = **it;
958
959 const auto curr_member_accessor =
960 prefix + "." + ConvertCase(field.name, Case::kLowerCamel);
961 if (IsStruct(field.value.type)) {
962 ret += GenStructMemberValueTS(*field.value.type.struct_def,
963 curr_member_accessor, delimiter);
964 } else {
965 if (nullCheck) {
966 ret +=
967 "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)";
968 } else {
969 ret += curr_member_accessor;
970 }
971 }
972
973 if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
974 }
975
976 return ret;
977 }
978
979 void GenObjApi(const Parser &parser, StructDef &struct_def,
980 std::string &obj_api_unpack_func, std::string &obj_api_class,
981 import_set &imports) {
982 const auto class_name = GetObjApiClassName(struct_def, parser.opts);
983
984 std::string unpack_func = "\nunpack(): " + class_name +
985 " {\n return new " + class_name + "(" +
986 (struct_def.fields.vec.empty() ? "" : "\n");
987 std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
988 +(struct_def.fields.vec.empty() ? "" : "\n");
989
990 std::string constructor_func = "constructor(";
991 constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
992
993 const auto has_create =
994 struct_def.fixed || CanCreateFactoryMethod(struct_def);
995
996 std::string pack_func_prototype =
997 "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
998
999 std::string pack_func_offset_decl;
1000 std::string pack_func_create_call;
1001
1002 const auto struct_name =
1003 EscapeKeyword(AddImport(imports, struct_def, struct_def));
1004
1005 if (has_create) {
1006 pack_func_create_call = " return " + struct_name + ".create" +
1007 GetPrefixedName(struct_def) + "(builder" +
1008 (struct_def.fields.vec.empty() ? "" : ",\n ");
1009 } else {
1010 pack_func_create_call = " " + struct_name + ".start" +
1011 GetPrefixedName(struct_def) + "(builder);\n";
1012 }
1013
1014 if (struct_def.fixed) {
1015 // when packing struct, nested struct's members instead of the struct's
1016 // offset are used
1017 pack_func_create_call +=
1018 GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n ";
1019 }
1020
1021 for (auto it = struct_def.fields.vec.begin();
1022 it != struct_def.fields.vec.end(); ++it) {
1023 auto &field = **it;
1024 if (field.deprecated) continue;
1025
1026 const auto field_name = ConvertCase(field.name, Case::kLowerCamel);
1027 const auto field_name_escaped = EscapeKeyword(field_name);
1028 const std::string field_binded_method =
1029 "this." + field_name + ".bind(this)";
1030
1031 std::string field_val;
1032 std::string field_type;
1033 // a string that declares a variable containing the
1034 // offset for things that can't be generated inline
1035 // empty otw
1036 std::string field_offset_decl;
1037 // a string that contains values for things that can be created inline or
1038 // the variable name from field_offset_decl
1039 std::string field_offset_val;
1040 const auto field_default_val = GenDefaultValue(field, imports);
1041
1042 // Emit a scalar field
1043 const auto is_string = IsString(field.value.type);
1044 if (IsScalar(field.value.type.base_type) || is_string) {
1045 const auto has_null_default = is_string || HasNullDefault(field);
1046
1047 field_type += GenTypeName(imports, field, field.value.type, false,
1048 has_null_default);
1049 field_val = "this." + field_name + "()";
1050
1051 if (field.value.type.base_type != BASE_TYPE_STRING) {
1052 field_offset_val = "this." + field_name_escaped;
1053 } else {
1054 field_offset_decl = GenNullCheckConditional(
1055 "this." + field_name_escaped,
1056 "builder.createString(this." + field_name_escaped + "!)", "0");
1057 }
1058 }
1059
1060 // Emit an object field
1061 else {
1062 auto is_vector = false;
1063 switch (field.value.type.base_type) {
1064 case BASE_TYPE_STRUCT: {
1065 const auto &sd = *field.value.type.struct_def;
1066 field_type += GetObjApiClassName(AddImport(imports, struct_def, sd),
1067 parser.opts);
1068
1069 const std::string field_accessor =
1070 "this." + field_name_escaped + "()";
1071 field_val = GenNullCheckConditional(field_accessor,
1072 field_accessor + "!.unpack()");
1073 auto packing = GenNullCheckConditional(
1074 "this." + field_name_escaped,
1075 "this." + field_name_escaped + "!.pack(builder)",
1076 "0");
1077
1078 if (sd.fixed) {
1079 field_offset_val = std::move(packing);
1080 } else {
1081 field_offset_decl = std::move(packing);
1082 }
1083
1084 break;
1085 }
1086
1087 case BASE_TYPE_VECTOR: {
1088 auto vectortype = field.value.type.VectorType();
1089 auto vectortypename =
1090 GenTypeName(imports, struct_def, vectortype, false);
1091 is_vector = true;
1092
1093 field_type = "(";
1094
1095 switch (vectortype.base_type) {
1096 case BASE_TYPE_STRUCT: {
1097 const auto &sd = *field.value.type.struct_def;
1098 field_type += GetObjApiClassName(sd, parser.opts);
1099 field_type += ")[]";
1100
1101 field_val = GenBBAccess() + ".createObjList(" +
1102 field_binded_method + ", this." + field_name +
1103 "Length())";
1104
1105 if (sd.fixed) {
1106 field_offset_decl =
1107 "builder.createStructOffsetList(this." +
1108 field_name_escaped + ", " +
1109 EscapeKeyword(
1110 AddImport(imports, struct_def, struct_def)) +
1111 ".start" + ConvertCase(field_name, Case::kUpperCamel) +
1112 "Vector)";
1113 } else {
1114 field_offset_decl =
1115 EscapeKeyword(
1116 AddImport(imports, struct_def, struct_def)) +
1117 ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1118 "Vector(builder, builder.createObjectOffsetList(" +
1119 "this." + field_name_escaped + "))";
1120 }
1121
1122 break;
1123 }
1124
1125 case BASE_TYPE_STRING: {
1126 field_type += "string)[]";
1127 field_val = GenBBAccess() + ".createScalarList(" +
1128 field_binded_method + ", this." + field_name +
1129 "Length())";
1130 field_offset_decl =
1131 EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
1132 ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1133 "Vector(builder, builder.createObjectOffsetList(" +
1134 "this." + field_name_escaped + "))";
1135 break;
1136 }
1137
1138 case BASE_TYPE_UNION: {
1139 field_type += GenObjApiUnionTypeTS(imports, parser.opts,
1140 *(vectortype.enum_def));
1141 field_type += ")[]";
1142 field_val =
1143 GenUnionValTS(imports, field_name, vectortype, true);
1144
1145 field_offset_decl =
1146 EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
1147 ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1148 "Vector(builder, builder.createObjectOffsetList(" +
1149 "this." + field_name_escaped + "))";
1150
1151 break;
1152 }
1153 default: {
1154 if (vectortype.enum_def) {
1155 field_type += GenTypeName(imports, struct_def, vectortype,
1156 false, HasNullDefault(field));
1157 } else {
1158 field_type += vectortypename;
1159 }
1160 field_type += ")[]";
1161 field_val = GenBBAccess() + ".createScalarList(" +
1162 field_binded_method + ", this." + field_name +
1163 "Length())";
1164
1165 field_offset_decl =
1166 EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
1167 ".create" + ConvertCase(field_name, Case::kUpperCamel) +
1168 "Vector(builder, this." + field_name_escaped + ")";
1169
1170 break;
1171 }
1172 }
1173
1174 break;
1175 }
1176
1177 case BASE_TYPE_UNION: {
1178 field_type += GenObjApiUnionTypeTS(imports, parser.opts,
1179 *(field.value.type.enum_def));
1180
1181 field_val = GenUnionValTS(imports, field_name, field.value.type);
1182 field_offset_decl =
1183 "builder.createObjectOffset(this." + field_name_escaped + ")";
1184 break;
1185 }
1186
1187 default: FLATBUFFERS_ASSERT(0); break;
1188 }
1189
1190 // length 0 vector is simply empty instead of null
1191 field_type += is_vector ? "" : "|null";
1192 }
1193
1194 if (!field_offset_decl.empty()) {
1195 field_offset_decl =
1196 " const " + field_name_escaped + " = " + field_offset_decl + ";";
1197 }
1198 if (field_offset_val.empty()) { field_offset_val = field_name_escaped; }
1199
1200 unpack_func += " " + field_val;
1201 unpack_to_func += " _o." + field_name_escaped + " = " + field_val + ";";
1202
1203 // FIXME: if field_type and field_name_escaped are identical, then
1204 // this generates invalid typescript.
1205 constructor_func += " public " + field_name_escaped + ": " + field_type +
1206 " = " +
1207 field_default_val;
1208
1209 if (!struct_def.fixed) {
1210 if (!field_offset_decl.empty()) {
1211 pack_func_offset_decl += field_offset_decl + "\n";
1212 }
1213
1214 if (has_create) {
1215 pack_func_create_call += field_offset_val;
1216 } else {
1217 if (field.IsScalarOptional()) {
1218 pack_func_create_call += " if (" + field_offset_val + " !== null)\n ";
1219 }
1220 pack_func_create_call += " " + struct_name + ".add" +
1221 ConvertCase(field.name, Case::kUpperCamel) +
1222 "(builder, " + field_offset_val + ");\n";
1223 }
1224 }
1225
1226 if (std::next(it) != struct_def.fields.vec.end()) {
1227 constructor_func += ",\n";
1228
1229 if (!struct_def.fixed && has_create) {
1230 pack_func_create_call += ",\n ";
1231 }
1232
1233 unpack_func += ",\n";
1234 unpack_to_func += "\n";
1235 } else {
1236 constructor_func += "\n";
1237 if (!struct_def.fixed) {
1238 pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
1239 pack_func_create_call += "\n ";
1240 }
1241
1242 unpack_func += "\n ";
1243 unpack_to_func += "\n";
1244 }
1245 }
1246
1247 constructor_func += "){}\n\n";
1248
1249 if (has_create) {
1250 pack_func_create_call += ");";
1251 } else {
1252 pack_func_create_call += "return " + struct_name + ".end" +
1253 GetPrefixedName(struct_def) + "(builder);";
1254 }
1255
1256 obj_api_class = "\nexport class " +
1257 GetObjApiClassName(struct_def, parser.opts) + " {\n";
1258
1259 obj_api_class += constructor_func;
1260 obj_api_class += pack_func_prototype + pack_func_offset_decl +
1261 pack_func_create_call + "\n}";
1262
1263 obj_api_class += "\n}\n";
1264
1265 unpack_func += ");\n}";
1266 unpack_to_func += "}\n";
1267
1268 obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
1269 }
1270
1271 static bool CanCreateFactoryMethod(const StructDef &struct_def) {
1272 // to preserve backwards compatibility, we allow the first field to be a
1273 // struct
1274 return struct_def.fields.vec.size() < 2 ||
1275 std::all_of(std::begin(struct_def.fields.vec) + 1,
1276 std::end(struct_def.fields.vec),
1277 [](const FieldDef *f) -> bool {
1278 FLATBUFFERS_ASSERT(f != nullptr);
1279 return f->value.type.base_type != BASE_TYPE_STRUCT;
1280 });
1281 }
1282
1283 // Generate an accessor struct with constructor for a flatbuffers struct.
1284 void GenStruct(const Parser &parser, StructDef &struct_def,
1285 std::string *code_ptr, import_set &imports) {
1286 if (struct_def.generated) return;
1287 std::string &code = *code_ptr;
1288
1289 std::string object_name;
1290 std::string object_namespace = GetNameSpace(struct_def);
1291
1292 // Emit constructor
1293 object_name = EscapeKeyword(struct_def.name);
1294 GenDocComment(struct_def.doc_comment, code_ptr);
1295 code += "export class " + object_name;
1296 code += " {\n";
1297 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
1298 code += " bb_pos = 0;\n";
1299
1300 // Generate the __init method that sets the field in a pre-existing
1301 // accessor object. This is to allow object reuse.
1302 code +=
1303 "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
1304 code += " this.bb_pos = i;\n";
1305 code += " this.bb = bb;\n";
1306 code += " return this;\n";
1307 code += "}\n\n";
1308
1309 // Generate special accessors for the table that when used as the root of a
1310 // FlatBuffer
1311 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
1312 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
1313
1314 // Generate the identifier check method
1315 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
1316 !parser_.file_identifier_.empty()) {
1317 GenDocComment(code_ptr);
1318 code +=
1319 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
1320 "{\n";
1321 code += " return bb.__has_identifier('" + parser_.file_identifier_;
1322 code += "');\n}\n\n";
1323 }
1324
1325 // Emit field accessors
1326 for (auto it = struct_def.fields.vec.begin();
1327 it != struct_def.fields.vec.end(); ++it) {
1328 auto &field = **it;
1329 if (field.deprecated) continue;
1330 auto offset_prefix =
1331 " const offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1332 NumToString(field.value.offset) + ");\n return offset ? ";
1333
1334 // Emit a scalar field
1335 const auto is_string = IsString(field.value.type);
1336 if (IsScalar(field.value.type.base_type) || is_string) {
1337 const auto has_null_default = is_string || HasNullDefault(field);
1338
1339 GenDocComment(field.doc_comment, code_ptr);
1340 std::string prefix = ConvertCase(field.name, Case::kLowerCamel) + "(";
1341 if (is_string) {
1342 code += prefix + "):string|null\n";
1343 code +=
1344 prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
1345 GenTypeName(imports, struct_def, field.value.type, false, true) +
1346 "\n";
1347 code += prefix + "optionalEncoding?:any";
1348 } else {
1349 code += prefix;
1350 }
1351 if (field.value.type.enum_def) {
1352 code += "):" +
1353 GenTypeName(imports, struct_def, field.value.type, false,
1354 field.IsOptional()) +
1355 " {\n";
1356 } else {
1357 code += "):" +
1358 GenTypeName(imports, struct_def, field.value.type, false,
1359 has_null_default) +
1360 " {\n";
1361 }
1362
1363 if (struct_def.fixed) {
1364 code +=
1365 " return " +
1366 GenGetter(field.value.type,
1367 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
1368 ";\n";
1369 } else {
1370 std::string index = "this.bb_pos + offset";
1371 if (is_string) { index += ", optionalEncoding"; }
1372 code += offset_prefix +
1373 GenGetter(field.value.type, "(" + index + ")") + " : " +
1374 GenDefaultValue(field, imports);
1375 code += ";\n";
1376 }
1377 }
1378
1379 // Emit an object field
1380 else {
1381 switch (field.value.type.base_type) {
1382 case BASE_TYPE_STRUCT: {
1383 const auto type = EscapeKeyword(
1384 AddImport(imports, struct_def, *field.value.type.struct_def));
1385 GenDocComment(field.doc_comment, code_ptr);
1386 code += ConvertCase(field.name, Case::kLowerCamel);
1387 code += "(obj?:" + type + "):" + type + "|null {\n";
1388
1389 if (struct_def.fixed) {
1390 code += " return (obj || " + GenerateNewExpression(type);
1391 code += ").__init(this.bb_pos";
1392 code +=
1393 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
1394 } else {
1395 code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1396 ").__init(";
1397 code += field.value.type.struct_def->fixed
1398 ? "this.bb_pos + offset"
1399 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
1400 code += ", " + GenBBAccess() + ") : null;\n";
1401 }
1402
1403 break;
1404 }
1405
1406 case BASE_TYPE_VECTOR: {
1407 auto vectortype = field.value.type.VectorType();
1408 auto vectortypename =
1409 GenTypeName(imports, struct_def, vectortype, false);
1410 auto inline_size = InlineSize(vectortype);
1411 auto index = GenBBAccess() +
1412 ".__vector(this.bb_pos + offset) + index" +
1413 MaybeScale(inline_size);
1414 std::string ret_type;
1415 bool is_union = false;
1416 switch (vectortype.base_type) {
1417 case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
1418 case BASE_TYPE_STRING: ret_type = vectortypename; break;
1419 case BASE_TYPE_UNION:
1420 ret_type = "?flatbuffers.Table";
1421 is_union = true;
1422 break;
1423 default: ret_type = vectortypename;
1424 }
1425 GenDocComment(field.doc_comment, code_ptr);
1426 std::string prefix = ConvertCase(field.name, Case::kLowerCamel);
1427 // TODO: make it work without any
1428 // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1429 if (is_union) { prefix += ""; }
1430 prefix += "(index: number";
1431 if (is_union) {
1432 const auto union_type =
1433 GenUnionGenericTypeTS(*(field.value.type.enum_def));
1434
1435 vectortypename = union_type;
1436 code += prefix + ", obj:" + union_type;
1437 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1438 code += prefix + ", obj?:" + vectortypename;
1439 } else if (IsString(vectortype)) {
1440 code += prefix + "):string\n";
1441 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1442 "):" + vectortypename + "\n";
1443 code += prefix + ",optionalEncoding?:any";
1444 } else {
1445 code += prefix;
1446 }
1447 code += "):" + vectortypename + "|null {\n";
1448
1449 if (vectortype.base_type == BASE_TYPE_STRUCT) {
1450 code += offset_prefix + "(obj || " +
1451 GenerateNewExpression(vectortypename);
1452 code += ").__init(";
1453 code += vectortype.struct_def->fixed
1454 ? index
1455 : GenBBAccess() + ".__indirect(" + index + ")";
1456 code += ", " + GenBBAccess() + ")";
1457 } else {
1458 if (is_union) {
1459 index = "obj, " + index;
1460 } else if (IsString(vectortype)) {
1461 index += ", optionalEncoding";
1462 }
1463 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1464 }
1465 code += " : ";
1466 if (field.value.type.element == BASE_TYPE_BOOL) {
1467 code += "false";
1468 } else if (field.value.type.element == BASE_TYPE_LONG ||
1469 field.value.type.element == BASE_TYPE_ULONG) {
1470 code += "BigInt(0)";
1471 } else if (IsScalar(field.value.type.element)) {
1472 if (field.value.type.enum_def) {
1473 code += field.value.constant;
1474 } else {
1475 code += "0";
1476 }
1477 } else {
1478 code += "null";
1479 }
1480 code += ";\n";
1481 break;
1482 }
1483
1484 case BASE_TYPE_UNION: {
1485 GenDocComment(field.doc_comment, code_ptr);
1486 code += ConvertCase(field.name, Case::kLowerCamel);
1487
1488 const auto &union_enum = *(field.value.type.enum_def);
1489 const auto union_type = GenUnionGenericTypeTS(union_enum);
1490 code += "<T extends flatbuffers.Table>(obj:" + union_type +
1491 "):" + union_type +
1492 "|null "
1493 "{\n";
1494
1495 code += offset_prefix +
1496 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1497 " : null;\n";
1498 break;
1499 }
1500 default: FLATBUFFERS_ASSERT(0);
1501 }
1502 }
1503 code += "}\n\n";
1504
1505 // Adds the mutable scalar value to the output
1506 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1507 !IsUnion(field.value.type)) {
1508 std::string type =
1509 GenTypeName(imports, struct_def, field.value.type, true);
1510
1511 code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1512
1513 if (struct_def.fixed) {
1514 code += " " + GenBBAccess() + ".write" +
1515 ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
1516 "(this.bb_pos + " + NumToString(field.value.offset) + ", ";
1517 } else {
1518 code += " const offset = " + GenBBAccess() +
1519 ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
1520 ");\n\n";
1521 code += " if (offset === 0) {\n";
1522 code += " return false;\n";
1523 code += " }\n\n";
1524
1525 // special case for bools, which are treated as uint8
1526 code += " " + GenBBAccess() + ".write" +
1527 ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
1528 "(this.bb_pos + offset, ";
1529 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1530 }
1531
1532 code += "value);\n";
1533 code += " return true;\n";
1534 code += "}\n\n";
1535 }
1536
1537 // Emit vector helpers
1538 if (IsVector(field.value.type)) {
1539 // Emit a length helper
1540 GenDocComment(code_ptr);
1541 code += ConvertCase(field.name, Case::kLowerCamel);
1542 code += "Length():number {\n" + offset_prefix;
1543
1544 code +=
1545 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
1546
1547 // For scalar types, emit a typed array helper
1548 auto vectorType = field.value.type.VectorType();
1549 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1550 GenDocComment(code_ptr);
1551
1552 code += ConvertCase(field.name, Case::kLowerCamel);
1553 code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1554 offset_prefix;
1555
1556 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1557 ".bytes().buffer, " + GenBBAccess() +
1558 ".bytes().byteOffset + " + GenBBAccess() +
1559 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1560 ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n";
1561 }
1562 }
1563 }
1564
1565 // Emit the fully qualified name
1566 if (parser_.opts.generate_name_strings) {
1567 GenDocComment(code_ptr);
1568 code += "static getFullyQualifiedName():string {\n";
1569 code += " return '" + WrapInNameSpace(struct_def) + "';\n";
1570 code += "}\n\n";
1571 }
1572
1573 // Emit the size of the struct.
1574 if (struct_def.fixed) {
1575 GenDocComment(code_ptr);
1576 code += "static sizeOf():number {\n";
1577 code += " return " + NumToString(struct_def.bytesize) + ";\n";
1578 code += "}\n\n";
1579 }
1580
1581 // Emit a factory constructor
1582 if (struct_def.fixed) {
1583 std::string arguments;
1584 GenStructArgs(imports, struct_def, &arguments, "");
1585 GenDocComment(code_ptr);
1586
1587 code += "static create" + GetPrefixedName(struct_def) +
1588 "(builder:flatbuffers.Builder";
1589 code += arguments + "):flatbuffers.Offset {\n";
1590
1591 GenStructBody(struct_def, &code, "");
1592 code += " return builder.offset();\n}\n\n";
1593 } else {
1594 // Generate a method to start building a new object
1595 GenDocComment(code_ptr);
1596
1597 code += "static start" + GetPrefixedName(struct_def) +
1598 "(builder:flatbuffers.Builder) {\n";
1599
1600 code += " builder.startObject(" +
1601 NumToString(struct_def.fields.vec.size()) + ");\n";
1602 code += "}\n\n";
1603
1604 // Generate a set of static methods that allow table construction
1605 for (auto it = struct_def.fields.vec.begin();
1606 it != struct_def.fields.vec.end(); ++it) {
1607 auto &field = **it;
1608 if (field.deprecated) continue;
1609 const auto argname = GetArgName(field);
1610
1611 // Generate the field insertion method
1612 GenDocComment(code_ptr);
1613 code += "static add" + ConvertCase(field.name, Case::kUpperCamel);
1614 code += "(builder:flatbuffers.Builder, " + argname + ":" +
1615 GetArgType(imports, struct_def, field, false) + ") {\n";
1616 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1617 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1618 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1619 code += argname + ", ";
1620 if (!IsScalar(field.value.type.base_type)) {
1621 code += "0";
1622 } else if (HasNullDefault(field)) {
1623 if (IsLong(field.value.type.base_type)) {
1624 code += "BigInt(0)";
1625 } else {
1626 code += "0";
1627 }
1628 } else {
1629 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1630 code += GenDefaultValue(field, imports);
1631 }
1632 code += ");\n}\n\n";
1633
1634 if (IsVector(field.value.type)) {
1635 auto vector_type = field.value.type.VectorType();
1636 auto alignment = InlineAlignment(vector_type);
1637 auto elem_size = InlineSize(vector_type);
1638
1639 // Generate a method to create a vector from a JavaScript array
1640 if (!IsStruct(vector_type)) {
1641 GenDocComment(code_ptr);
1642
1643 const std::string sig_begin =
1644 "static create" + ConvertCase(field.name, Case::kUpperCamel) +
1645 "Vector(builder:flatbuffers.Builder, data:";
1646 const std::string sig_end = "):flatbuffers.Offset";
1647 std::string type =
1648 GenTypeName(imports, struct_def, vector_type, true) + "[]";
1649 if (type == "number[]") {
1650 const auto &array_type = GenType(vector_type);
1651 // the old type should be deprecated in the future
1652 std::string type_old = "number[]|Uint8Array";
1653 std::string type_new = "number[]|" + array_type + "Array";
1654 if (type_old == type_new) {
1655 type = type_new;
1656 } else {
1657 // add function overloads
1658 code += sig_begin + type_new + sig_end + ";\n";
1659 code +=
1660 "/**\n * @deprecated This Uint8Array overload will "
1661 "be removed in the future.\n */\n";
1662 code += sig_begin + type_old + sig_end + ";\n";
1663 type = type_new + "|Uint8Array";
1664 }
1665 }
1666 code += sig_begin + type + sig_end + " {\n";
1667 code += " builder.startVector(" + NumToString(elem_size);
1668 code += ", data.length, " + NumToString(alignment) + ");\n";
1669 code += " for (let i = data.length - 1; i >= 0; i--) {\n";
1670 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1671 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1672 code += "data[i]!);\n";
1673 code += " }\n";
1674 code += " return builder.endVector();\n";
1675 code += "}\n\n";
1676 }
1677
1678 // Generate a method to start a vector, data to be added manually
1679 // after
1680 GenDocComment(code_ptr);
1681
1682 code += "static start" + ConvertCase(field.name, Case::kUpperCamel);
1683 code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1684 code += " builder.startVector(" + NumToString(elem_size);
1685 code += ", numElems, " + NumToString(alignment) + ");\n";
1686 code += "}\n\n";
1687 }
1688 }
1689
1690 // Generate a method to stop building a new object
1691 GenDocComment(code_ptr);
1692
1693 code += "static end" + GetPrefixedName(struct_def);
1694 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1695
1696 code += " const offset = builder.endObject();\n";
1697 for (auto it = struct_def.fields.vec.begin();
1698 it != struct_def.fields.vec.end(); ++it) {
1699 auto &field = **it;
1700 if (!field.deprecated && field.IsRequired()) {
1701 code += " builder.requiredField(offset, ";
1702 code += NumToString(field.value.offset);
1703 code += ") // " + field.name + "\n";
1704 }
1705 }
1706 code += " return offset;\n";
1707 code += "}\n\n";
1708
1709 // Generate the methods to complete buffer construction
1710 GenerateFinisher(struct_def, code_ptr, code, false);
1711 GenerateFinisher(struct_def, code_ptr, code, true);
1712
1713 // Generate a convenient CreateX function
1714 if (CanCreateFactoryMethod(struct_def)) {
1715 code += "static create" + GetPrefixedName(struct_def);
1716 code += "(builder:flatbuffers.Builder";
1717 for (auto it = struct_def.fields.vec.begin();
1718 it != struct_def.fields.vec.end(); ++it) {
1719 const auto &field = **it;
1720 if (field.deprecated) continue;
1721 code += ", " + GetArgName(field) + ":" +
1722 GetArgType(imports, struct_def, field, true);
1723 }
1724
1725 code += "):flatbuffers.Offset {\n";
1726 code += " " + object_name + ".start" +
1727 GetPrefixedName(struct_def) + "(builder);\n";
1728
1729 std::string methodPrefix = object_name;
1730 for (auto it = struct_def.fields.vec.begin();
1731 it != struct_def.fields.vec.end(); ++it) {
1732 const auto &field = **it;
1733 if (field.deprecated) continue;
1734
1735 const auto arg_name = GetArgName(field);
1736
1737 if (field.IsScalarOptional()) {
1738 code += " if (" + arg_name + " !== null)\n ";
1739 }
1740
1741 code += " " + methodPrefix + ".add" +
1742 ConvertCase(field.name, Case::kUpperCamel) + "(";
1743 code += "builder, " + arg_name + ");\n";
1744 }
1745
1746 code += " return " + methodPrefix + ".end" +
1747 GetPrefixedName(struct_def) + "(builder);\n";
1748 code += "}\n";
1749 }
1750 }
1751
1752 if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
1753 auto name = GetPrefixedName(struct_def, "");
1754 code += "\n";
1755 code += "serialize():Uint8Array {\n";
1756 code += " return this.bb!.bytes();\n";
1757 code += "}\n";
1758
1759 code += "\n";
1760 code += "static deserialize(buffer: Uint8Array):" + EscapeKeyword(name) +
1761 " {\n";
1762 code += " return " +
1763 EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
1764 ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
1765 code += "}\n";
1766 }
1767
1768 if (parser_.opts.generate_object_based_api) {
1769 std::string obj_api_class;
1770 std::string obj_api_unpack_func;
1771 GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
1772 imports);
1773
1774 code += obj_api_unpack_func + "}\n" + obj_api_class;
1775 } else {
1776 code += "}\n";
1777 }
1778 }
1779
1780 static bool HasNullDefault(const FieldDef &field) {
1781 return field.IsOptional() && field.value.constant == "null";
1782 }
1783
1784 std::string GetArgType(import_set &imports, const Definition &owner,
1785 const FieldDef &field, bool allowNull) {
1786 return GenTypeName(imports, owner, field.value.type, true,
1787 allowNull && field.IsOptional());
1788 }
1789
1790 std::string GetArgName(const FieldDef &field) {
1791 auto argname = ConvertCase(field.name, Case::kLowerCamel);
1792 if (!IsScalar(field.value.type.base_type)) {
1793 argname += "Offset";
1794 } else {
1795 argname = EscapeKeyword(argname);
1796 }
1797 return argname;
1798 }
1799
1800 std::string GetPrefixedName(const StructDef &struct_def,
1801 const char *prefix = "") {
1802 return prefix + struct_def.name;
1803 }
1804}; // namespace ts
1805} // namespace ts
1806
1807bool GenerateTS(const Parser &parser, const std::string &path,
1808 const std::string &file_name) {
1809 ts::TsGenerator generator(parser, path, file_name);
1810 return generator.generate();
1811}
1812
1813std::string TSMakeRule(const Parser &parser, const std::string &path,
1814 const std::string &file_name) {
1815 std::string filebase =
1816 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1817 ts::TsGenerator generator(parser, path, file_name);
1818 std::string make_rule =
1819 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
1820
1821 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1822 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1823 make_rule += " " + *it;
1824 }
1825 return make_rule;
1826}
1827
1828} // namespace flatbuffers