blob: 32ab863edc945bf516a33b5165eca438a03fa6d0 [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
James Kuszmaul8e62b022022-03-22 09:33:25 -070017#include <algorithm>
18#include <cassert>
19#include <unordered_map>
20#include <unordered_set>
21
22#include "flatbuffers/code_generators.h"
23#include "flatbuffers/flatbuffers.h"
24#include "flatbuffers/idl.h"
25#include "flatbuffers/util.h"
Austin Schuh2dd86a92022-09-14 21:19:23 -070026#include "idl_namer.h"
James Kuszmaul8e62b022022-03-22 09:33:25 -070027
28namespace flatbuffers {
Austin Schuh2dd86a92022-09-14 21:19:23 -070029namespace {
James Kuszmaul8e62b022022-03-22 09:33:25 -070030struct 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;
Austin Schuh2dd86a92022-09-14 21:19:23 -070036 std::string object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -070037 const Definition *dependent = nullptr;
38 const Definition *dependency = nullptr;
39};
40
Austin Schuh2dd86a92022-09-14 21:19:23 -070041Namer::Config TypeScriptDefaultConfig() {
42 return { /*types=*/Case::kKeep,
43 /*constants=*/Case::kUnknown,
44 /*methods=*/Case::kLowerCamel,
45 /*functions=*/Case::kLowerCamel,
46 /*fields=*/Case::kLowerCamel,
47 /*variables=*/Case::kLowerCamel,
48 /*variants=*/Case::kKeep,
49 /*enum_variant_seperator=*/"::",
50 /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
51 /*namespaces=*/Case::kKeep,
52 /*namespace_seperator=*/"_",
53 /*object_prefix=*/"",
54 /*object_suffix=*/"T",
55 /*keyword_prefix=*/"",
56 /*keyword_suffix=*/"_",
57 /*filenames=*/Case::kDasher,
58 /*directories=*/Case::kDasher,
59 /*output_path=*/"",
60 /*filename_suffix=*/"_generated",
61 /*filename_extension=*/".ts" };
62}
63
64std::set<std::string> TypescriptKeywords() {
65 // List of keywords retrieved from here:
66 // https://github.com/microsoft/TypeScript/issues/2536
67 return {
68 "arguments", "break", "case", "catch", "class", "const",
69 "continue", "debugger", "default", "delete", "do", "else",
70 "enum", "export", "extends", "false", "finally", "for",
71 "function", "if", "import", "in", "instanceof", "new",
72 "null", "Object", "return", "super", "switch", "this",
73 "throw", "true", "try", "typeof", "var", "void",
74 "while", "with", "as", "implements", "interface", "let",
75 "package", "private", "protected", "public", "static", "yield",
76 };
77}
78
James Kuszmaul8e62b022022-03-22 09:33:25 -070079enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
80
Austin Schuh2dd86a92022-09-14 21:19:23 -070081template<typename T> struct SupportsObjectAPI : std::false_type {};
82
83template<> struct SupportsObjectAPI<StructDef> : std::true_type {};
84
85} // namespace
86
James Kuszmaul8e62b022022-03-22 09:33:25 -070087namespace ts {
88// Iterate through all definitions we haven't generate code for (enums, structs,
89// and tables) and output them to a single file.
90class TsGenerator : public BaseGenerator {
91 public:
92 typedef std::map<std::string, ImportDefinition> import_set;
93
94 TsGenerator(const Parser &parser, const std::string &path,
95 const std::string &file_name)
Austin Schuh2dd86a92022-09-14 21:19:23 -070096 : BaseGenerator(parser, path, file_name, "", "_", "ts"),
97 namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
98 TypescriptKeywords()) {}
James Kuszmaul8e62b022022-03-22 09:33:25 -070099
James Kuszmaul8e62b022022-03-22 09:33:25 -0700100 bool generate() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700101 generateEnums();
102 generateStructs();
103 generateEntry();
104 return true;
105 }
106
Austin Schuh2dd86a92022-09-14 21:19:23 -0700107 bool IncludeNamespace() const {
108 // When generating a single flat file and all its includes, namespaces are
109 // important to avoid type name clashes.
110 return parser_.opts.ts_flat_file && parser_.opts.generate_all;
111 }
112
113 std::string GetTypeName(const EnumDef &def, const bool = false,
114 const bool force_ns_wrap = false) {
115 if (IncludeNamespace() || force_ns_wrap) {
116 return namer_.NamespacedType(def);
117 }
118 return namer_.Type(def);
119 }
120
121 std::string GetTypeName(const StructDef &def, const bool object_api = false,
122 const bool force_ns_wrap = false) {
123 if (object_api && parser_.opts.generate_object_based_api) {
124 if (IncludeNamespace() || force_ns_wrap) {
125 return namer_.NamespacedObjectType(def);
126 } else {
127 return namer_.ObjectType(def);
128 }
129 } else {
130 if (IncludeNamespace() || force_ns_wrap) {
131 return namer_.NamespacedType(def);
132 } else {
133 return namer_.Type(def);
134 }
135 }
136 }
137
James Kuszmaul8e62b022022-03-22 09:33:25 -0700138 // Save out the generated code for a single class while adding
139 // declaration boilerplate.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700140 bool SaveType(const Definition &definition, const std::string &class_code,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700141 import_set &imports, import_set &bare_imports) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700142 if (!class_code.length()) return true;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700143
144 std::string code;
145
146 if (!parser_.opts.ts_flat_file) {
147 code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
148
149 for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
150 code += it->second.import_statement + "\n";
151 }
152 if (!bare_imports.empty()) code += "\n";
153
154 for (auto it = imports.begin(); it != imports.end(); it++) {
155 if (it->second.dependency != &definition) {
156 code += it->second.import_statement + "\n";
157 }
158 }
159 if (!imports.empty()) code += "\n\n";
160 }
161
Austin Schuh2dd86a92022-09-14 21:19:23 -0700162 code += class_code;
163
James Kuszmaul8e62b022022-03-22 09:33:25 -0700164 if (parser_.opts.ts_flat_file) {
165 flat_file_ += code;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700166 flat_file_ += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700167 flat_file_definitions_.insert(&definition);
168 return true;
169 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700170 auto dirs = namer_.Directories(*definition.defined_namespace);
171 EnsureDirExists(dirs);
172 auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
173
174 return SaveFile(basename.c_str(), code, false);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700175 }
176 }
177
178 private:
Austin Schuh2dd86a92022-09-14 21:19:23 -0700179 IdlNamer namer_;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700180
181 import_set imports_all_;
182
183 // The following three members are used when generating typescript code into a
184 // single file rather than creating separate files for each type.
185
186 // flat_file_ contains the aggregated contents of the file prior to being
187 // written to disk.
188 std::string flat_file_;
189 // flat_file_definitions_ tracks which types have been written to flat_file_.
190 std::unordered_set<const Definition *> flat_file_definitions_;
191 // This maps from import names to types to import.
192 std::map<std::string, std::map<std::string, std::string>>
193 flat_file_import_declarations_;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700194 // For flat file codegen, tracks whether we need to import the flatbuffers
195 // library itself (not necessary for files that solely consist of enum
196 // definitions).
197 bool import_flatbuffers_lib_ = false;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700198
199 // Generate code for all enums.
200 void generateEnums() {
201 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
202 ++it) {
203 import_set bare_imports;
204 import_set imports;
205 std::string enumcode;
206 auto &enum_def = **it;
207 GenEnum(enum_def, &enumcode, imports, false);
208 GenEnum(enum_def, &enumcode, imports, true);
209 SaveType(enum_def, enumcode, imports, bare_imports);
210 imports_all_.insert(imports.begin(), imports.end());
211 }
212 }
213
214 // Generate code for all structs.
215 void generateStructs() {
216 for (auto it = parser_.structs_.vec.begin();
217 it != parser_.structs_.vec.end(); ++it) {
218 import_set bare_imports;
219 import_set imports;
220 AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
Austin Schuh2dd86a92022-09-14 21:19:23 -0700221 import_flatbuffers_lib_ = true;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700222 auto &struct_def = **it;
223 std::string declcode;
224 GenStruct(parser_, struct_def, &declcode, imports);
225 SaveType(struct_def, declcode, imports, bare_imports);
226 imports_all_.insert(imports.begin(), imports.end());
227 }
228 }
229
230 // Generate code for a single entry point module.
231 void generateEntry() {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700232 std::string code =
233 "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700234 if (parser_.opts.ts_flat_file) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700235 if (import_flatbuffers_lib_) {
236 code += "import * as flatbuffers from 'flatbuffers';\n";
237 code += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700238 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700239 // Only include import statements when not generating all.
240 if (!parser_.opts.generate_all) {
241 for (const auto &it : flat_file_import_declarations_) {
242 // Note that we do end up generating an import for ourselves, which
243 // should generally be harmless.
244 // TODO: Make it so we don't generate a self-import; this will also
245 // require modifying AddImport to ensure that we don't use
246 // namespace-prefixed names anywhere...
247 std::string file = it.first;
248 if (file.empty()) { continue; }
249 std::string noext = flatbuffers::StripExtension(file);
250 std::string basename = flatbuffers::StripPath(noext);
251 std::string include_file = GeneratedFileName(
252 parser_.opts.include_prefix,
253 parser_.opts.keep_prefix ? noext : basename, parser_.opts);
254 // TODO: what is the right behavior when different include flags are
255 // specified here? Should we always be adding the "./" for a relative
256 // path or turn it off if --include-prefix is specified, or something
257 // else?
258 std::string include_name =
259 "./" + flatbuffers::StripExtension(include_file);
260 code += "import {";
261 for (const auto &pair : it.second) {
262 code += namer_.EscapeKeyword(pair.first) + " as " +
263 namer_.EscapeKeyword(pair.second) + ", ";
264 }
265 code.resize(code.size() - 2);
266 code += "} from '" + include_name + ".js';\n";
267 }
268 code += "\n";
269 }
270
James Kuszmaul8e62b022022-03-22 09:33:25 -0700271 code += flat_file_;
272 const std::string filename =
273 GeneratedFileName(path_, file_name_, parser_.opts);
274 SaveFile(filename.c_str(), code, false);
275 } else {
276 for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
277 code += it->second.export_statement + "\n";
278 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700279 const std::string path =
280 GeneratedFileName(path_, file_name_, parser_.opts);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700281 SaveFile(path.c_str(), code, false);
282 }
283 }
284
285 // Generate a documentation comment, if available.
286 static void GenDocComment(const std::vector<std::string> &dc,
287 std::string *code_ptr,
288 const char *indent = nullptr) {
289 if (dc.empty()) {
290 // Don't output empty comment blocks with 0 lines of comment content.
291 return;
292 }
293
294 std::string &code = *code_ptr;
295 if (indent) code += indent;
296 code += "/**\n";
297 for (auto it = dc.begin(); it != dc.end(); ++it) {
298 if (indent) code += indent;
299 code += " *" + *it + "\n";
300 }
301 if (indent) code += indent;
302 code += " */\n";
303 }
304
305 static void GenDocComment(std::string *code_ptr) {
306 GenDocComment(std::vector<std::string>(), code_ptr);
307 }
308
309 // Generate an enum declaration and an enum string lookup table.
310 void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports,
311 bool reverse) {
312 if (enum_def.generated) return;
313 if (reverse) return; // FIXME.
314 std::string &code = *code_ptr;
315 GenDocComment(enum_def.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -0700316 code += "export enum ";
317 code += GetTypeName(enum_def);
318 code += " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700319 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
320 auto &ev = **it;
321 if (!ev.doc_comment.empty()) {
322 if (it != enum_def.Vals().begin()) { code += '\n'; }
323 GenDocComment(ev.doc_comment, code_ptr, " ");
324 }
325
James Kuszmaul8e62b022022-03-22 09:33:25 -0700326 // Generate mapping between EnumName: EnumValue(int)
327 if (reverse) {
328 code += " '" + enum_def.ToString(ev) + "'";
329 code += " = ";
Austin Schuh2dd86a92022-09-14 21:19:23 -0700330 code += "'" + namer_.Variant(ev) + "'";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700331 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700332 code += " " + namer_.Variant(ev);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700333 code += " = ";
334 // Unfortunately, because typescript does not support bigint enums,
335 // for 64-bit enums, we instead map the enum names to strings.
336 switch (enum_def.underlying_type.base_type) {
337 case BASE_TYPE_LONG:
338 case BASE_TYPE_ULONG: {
339 code += "'" + enum_def.ToString(ev) + "'";
340 break;
341 }
342 default: code += enum_def.ToString(ev);
343 }
344 }
345
346 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
347 }
348 code += "}";
349
350 if (enum_def.is_union) {
351 code += GenUnionConvFunc(enum_def.underlying_type, imports);
352 }
353
Austin Schuh2dd86a92022-09-14 21:19:23 -0700354 code += "\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700355 }
356
357 static std::string GenType(const Type &type) {
358 switch (type.base_type) {
359 case BASE_TYPE_BOOL:
360 case BASE_TYPE_CHAR: return "Int8";
361 case BASE_TYPE_UTYPE:
362 case BASE_TYPE_UCHAR: return "Uint8";
363 case BASE_TYPE_SHORT: return "Int16";
364 case BASE_TYPE_USHORT: return "Uint16";
365 case BASE_TYPE_INT: return "Int32";
366 case BASE_TYPE_UINT: return "Uint32";
367 case BASE_TYPE_LONG: return "Int64";
368 case BASE_TYPE_ULONG: return "Uint64";
369 case BASE_TYPE_FLOAT: return "Float32";
370 case BASE_TYPE_DOUBLE: return "Float64";
371 case BASE_TYPE_STRING: return "String";
372 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
373 case BASE_TYPE_STRUCT: return type.struct_def->name;
374 default: return "flatbuffers.Table";
375 }
376 }
377
378 std::string GenGetter(const Type &type, const std::string &arguments) {
379 switch (type.base_type) {
380 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
381 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
382 case BASE_TYPE_UNION:
383 if (!UnionHasStringType(*type.enum_def)) {
384 return GenBBAccess() + ".__union" + arguments;
385 }
386 return GenBBAccess() + ".__union_with_string" + arguments;
387 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
388 default: {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700389 auto getter = GenBBAccess() + "." +
390 namer_.Method("read_" + GenType(type)) + arguments;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700391 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
392 return getter;
393 }
394 }
395 }
396
397 std::string GenBBAccess() const { return "this.bb!"; }
398
399 std::string GenDefaultValue(const FieldDef &field, import_set &imports) {
400 if (field.IsScalarOptional()) { return "null"; }
401
402 const auto &value = field.value;
403 if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
404 value.type.base_type != BASE_TYPE_VECTOR) {
405 // If the value is an enum with a 64-bit base type, we have to just
406 // return the bigint value directly since typescript does not support
407 // enums with bigint backing types.
408 switch (value.type.base_type) {
409 case BASE_TYPE_LONG:
410 case BASE_TYPE_ULONG: {
411 return "BigInt('" + value.constant + "')";
412 }
413 default: {
414 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700415 return AddImport(imports, *value.type.enum_def,
416 *value.type.enum_def)
417 .name +
418 "." + namer_.Variant(*val);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700419 } else {
420 return value.constant;
421 }
422 }
423 }
424 }
425
426 switch (value.type.base_type) {
427 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
428
429 case BASE_TYPE_STRING:
430 case BASE_TYPE_UNION:
431 case BASE_TYPE_STRUCT: {
432 return "null";
433 }
434
435 case BASE_TYPE_VECTOR: return "[]";
436
437 case BASE_TYPE_LONG:
438 case BASE_TYPE_ULONG: {
439 return "BigInt('" + value.constant + "')";
440 }
441
Austin Schuh2dd86a92022-09-14 21:19:23 -0700442 default:
443 if (value.constant == "nan") { return "NaN"; }
444 return value.constant;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700445 }
446 }
447
448 std::string GenTypeName(import_set &imports, const Definition &owner,
449 const Type &type, bool input,
450 bool allowNull = false) {
451 if (!input) {
452 if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
453 std::string name;
454 if (IsString(type)) {
455 name = "string|Uint8Array";
456 } else {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700457 name = AddImport(imports, owner, *type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700458 }
459 return allowNull ? (name + "|null") : name;
460 }
461 }
462
463 switch (type.base_type) {
464 case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean";
465 case BASE_TYPE_LONG:
466 case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint";
467 default:
468 if (IsScalar(type.base_type)) {
469 if (type.enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700470 const auto enum_name =
471 AddImport(imports, owner, *type.enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700472 return allowNull ? (enum_name + "|null") : enum_name;
473 }
474 return allowNull ? "number|null" : "number";
475 }
476 return "flatbuffers.Offset";
477 }
478 }
479
480 // Returns the method name for use with add/put calls.
Austin Schuh2dd86a92022-09-14 21:19:23 -0700481 std::string GenWriteMethod(const Type &type) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700482 // Forward to signed versions since unsigned versions don't exist
483 switch (type.base_type) {
484 case BASE_TYPE_UTYPE:
485 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
486 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
487 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
488 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
489 default: break;
490 }
491
Austin Schuh2dd86a92022-09-14 21:19:23 -0700492 return IsScalar(type.base_type) ? namer_.Type(GenType(type))
493 : (IsStruct(type) ? "Struct" : "Offset");
James Kuszmaul8e62b022022-03-22 09:33:25 -0700494 }
495
496 template<typename T> static std::string MaybeAdd(T value) {
497 return value != 0 ? " + " + NumToString(value) : "";
498 }
499
500 template<typename T> static std::string MaybeScale(T value) {
501 return value != 1 ? " * " + NumToString(value) : "";
502 }
503
504 void GenStructArgs(import_set &imports, const StructDef &struct_def,
505 std::string *arguments, const std::string &nameprefix) {
506 for (auto it = struct_def.fields.vec.begin();
507 it != struct_def.fields.vec.end(); ++it) {
508 auto &field = **it;
509 if (IsStruct(field.value.type)) {
510 // Generate arguments for a struct inside a struct. To ensure names
511 // don't clash, and to make it obvious these arguments are constructing
512 // a nested struct, prefix the name with the field name.
513 GenStructArgs(imports, *field.value.type.struct_def, arguments,
514 nameprefix + field.name + "_");
515 } else {
516 *arguments += ", " + nameprefix + field.name + ": " +
517 GenTypeName(imports, field, field.value.type, true,
518 field.IsOptional());
519 }
520 }
521 }
522
Austin Schuh2dd86a92022-09-14 21:19:23 -0700523 void GenStructBody(const StructDef &struct_def, std::string *body,
524 const std::string &nameprefix) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700525 *body += " builder.prep(";
526 *body += NumToString(struct_def.minalign) + ", ";
527 *body += NumToString(struct_def.bytesize) + ");\n";
528
529 for (auto it = struct_def.fields.vec.rbegin();
530 it != struct_def.fields.vec.rend(); ++it) {
531 auto &field = **it;
532 if (field.padding) {
533 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
534 }
535 if (IsStruct(field.value.type)) {
536 // Generate arguments for a struct inside a struct. To ensure names
537 // don't clash, and to make it obvious these arguments are constructing
538 // a nested struct, prefix the name with the field name.
539 GenStructBody(*field.value.type.struct_def, body,
540 nameprefix + field.name + "_");
541 } else {
542 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
543 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
544 *body += nameprefix + field.name + ");\n";
545 }
546 }
547 }
548
549 std::string GenerateNewExpression(const std::string &object_name) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700550 return "new " + namer_.Type(object_name) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700551 }
552
553 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
554 std::string &code, const std::string &object_name,
555 bool size_prefixed) {
556 if (!struct_def.fixed) {
557 GenDocComment(code_ptr);
558 std::string sizePrefixed("SizePrefixed");
559 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
560 GetPrefixedName(struct_def, "As");
561 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
562 "):" + object_name + " {\n";
563 if (size_prefixed) {
564 code +=
565 " bb.setPosition(bb.position() + "
566 "flatbuffers.SIZE_PREFIX_LENGTH);\n";
567 }
568 code += " return (obj || " + GenerateNewExpression(object_name);
569 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
570 code += "}\n\n";
571 }
572 }
573
574 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
575 std::string &code, bool size_prefixed) {
576 if (parser_.root_struct_def_ == &struct_def) {
577 std::string sizePrefixed("SizePrefixed");
578 GenDocComment(code_ptr);
579
580 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
581 GetPrefixedName(struct_def) + "Buffer";
582 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
583 code += " builder.finish(offset";
584 if (!parser_.file_identifier_.empty()) {
585 code += ", '" + parser_.file_identifier_ + "'";
586 }
587 if (size_prefixed) {
588 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
589 code += ", true";
590 }
591 code += ");\n";
592 code += "}\n\n";
593 }
594 }
595
James Kuszmaul8e62b022022-03-22 09:33:25 -0700596 bool UnionHasStringType(const EnumDef &union_enum) {
597 return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
598 [](const EnumVal *ev) {
599 return !ev->IsZero() && IsString(ev->union_type);
600 });
601 }
602
603 std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
604 // TODO: make it work without any
605 // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
606 // "");
607 return std::string("any") +
608 (UnionHasStringType(union_enum) ? "|string" : "");
609 }
610
611 std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) {
612 std::string ret;
613 std::set<std::string> type_list;
614
615 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
616 ++it) {
617 const auto &ev = **it;
618 if (ev.IsZero()) { continue; }
619
620 std::string type = "";
621 if (IsString(ev.union_type)) {
622 type = "string"; // no need to wrap string type in namespace
623 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700624 type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700625 } else {
626 FLATBUFFERS_ASSERT(false);
627 }
628 type_list.insert(type);
629 }
630
631 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
632 ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
633 }
634
635 return ret;
636 }
637
Austin Schuh2dd86a92022-09-14 21:19:23 -0700638 static bool CheckIfNameClashes(const import_set &imports,
639 const std::string &name) {
640 // TODO: this would be better as a hashset.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700641 for (auto it = imports.begin(); it != imports.end(); it++) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700642 if (it->second.name == name) { return true; }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700643 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700644 return false;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700645 }
646
Austin Schuh2dd86a92022-09-14 21:19:23 -0700647 std::string GenSymbolExpression(const StructDef &struct_def,
648 const bool has_name_clash,
649 const std::string &import_name,
650 const std::string &name,
651 const std::string &object_name) {
652 std::string symbols_expression;
653
654 if (has_name_clash) {
655 // We have a name clash
656 symbols_expression += import_name + " as " + name;
657
658 if (parser_.opts.generate_object_based_api) {
659 symbols_expression += ", " +
660 GetTypeName(struct_def, /*object_api =*/true) +
661 " as " + object_name;
662 }
663 } else {
664 // No name clash, use the provided name
665 symbols_expression += name;
666
667 if (parser_.opts.generate_object_based_api) {
668 symbols_expression += ", " + object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700669 }
670 }
671
Austin Schuh2dd86a92022-09-14 21:19:23 -0700672 return symbols_expression;
673 }
674
675 std::string GenSymbolExpression(const EnumDef &enum_def,
676 const bool has_name_clash,
677 const std::string &import_name,
678 const std::string &name,
679 const std::string &) {
680 std::string symbols_expression;
681 if (has_name_clash) {
682 symbols_expression += import_name + " as " + name;
683 } else {
684 symbols_expression += name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700685 }
686
Austin Schuh2dd86a92022-09-14 21:19:23 -0700687 if (enum_def.is_union) {
688 symbols_expression += ", unionTo" + name;
689 symbols_expression += ", unionListTo" + name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700690 }
Austin Schuh2dd86a92022-09-14 21:19:23 -0700691
692 return symbols_expression;
693 }
694
695 template<typename DefinitionT>
696 ImportDefinition AddImport(import_set &imports, const Definition &dependent,
697 const DefinitionT &dependency) {
698 // The unique name of the dependency, fully qualified in its namespace.
699 const std::string unique_name = GetTypeName(
700 dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
701
702 // Look if we have already added this import and return its name if found.
703 const auto import_pair = imports.find(unique_name);
704 if (import_pair != imports.end()) { return import_pair->second; }
705
706 // Check if this name would have a name clash with another type. Just use
707 // the "base" name (properly escaped) without any namespacing applied.
708 const std::string import_name = GetTypeName(dependency);
709 const bool has_name_clash = CheckIfNameClashes(imports, import_name);
710
711 // If we have a name clash, use the unique name, otherwise use simple name.
712 std::string name = has_name_clash ? unique_name : import_name;
713
714 const std::string object_name =
715 GetTypeName(dependency, /*object_api=*/true, has_name_clash);
716
717 if (parser_.opts.ts_flat_file) {
718 // In flat-file generation, do not attempt to import things from ourselves
719 // *and* do not wrap namespaces (note that this does override the logic
720 // above, but since we force all non-self-imports to use namespace-based
721 // names in flat file generation, it's fine).
722 if (dependent.file == dependency.file) {
723 name = import_name;
724 } else {
725 const std::string file =
726 RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
727 dependency.file)
728 // Strip the leading //
729 .substr(2);
730 flat_file_import_declarations_[file][import_name] = name;
731
732 if (parser_.opts.generate_object_based_api &&
733 SupportsObjectAPI<DefinitionT>::value) {
734 flat_file_import_declarations_[file][import_name + "T"] = object_name;
735 }
736 }
737 }
738
739 const std::string symbols_expression = GenSymbolExpression(
740 dependency, has_name_clash, import_name, name, object_name);
741
James Kuszmaul8e62b022022-03-22 09:33:25 -0700742 std::string bare_file_path;
743 std::string rel_file_path;
744 const auto &dep_comps = dependent.defined_namespace->components;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700745 for (size_t i = 0; i < dep_comps.size(); i++) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700746 rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
Austin Schuh2dd86a92022-09-14 21:19:23 -0700747 }
748 if (dep_comps.size() == 0) { rel_file_path += "."; }
749
James Kuszmaul8e62b022022-03-22 09:33:25 -0700750 bare_file_path +=
751 kPathSeparator +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700752 namer_.Directories(dependency.defined_namespace->components,
753 SkipDir::OutputPath) +
754 namer_.File(dependency, SkipFile::SuffixAndExtension);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700755 rel_file_path += bare_file_path;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700756
James Kuszmaul8e62b022022-03-22 09:33:25 -0700757 ImportDefinition import;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700758 import.name = name;
759 import.object_name = object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700760 import.bare_file_path = bare_file_path;
761 import.rel_file_path = rel_file_path;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700762 import.import_statement =
763 "import { " + symbols_expression + " } from '" + rel_file_path + ".js';";
764 import.export_statement =
765 "export { " + symbols_expression + " } from '." + bare_file_path + ".js';";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700766 import.dependency = &dependency;
767 import.dependent = &dependent;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700768
James Kuszmaul8e62b022022-03-22 09:33:25 -0700769 imports.insert(std::make_pair(unique_name, import));
Austin Schuh2dd86a92022-09-14 21:19:23 -0700770
771 return import;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700772 }
773
774 void AddImport(import_set &imports, std::string import_name,
775 std::string fileName) {
776 ImportDefinition import;
777 import.name = import_name;
778 import.import_statement =
779 "import " + import_name + " from '" + fileName + "';";
780 imports.insert(std::make_pair(import_name, import));
781 }
782
783 // Generate a TS union type based on a union's enum
Austin Schuh2dd86a92022-09-14 21:19:23 -0700784 std::string GenObjApiUnionTypeTS(import_set &imports,
785 const StructDef &dependent,
786 const IDLOptions &,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700787 const EnumDef &union_enum) {
788 std::string ret = "";
789 std::set<std::string> type_list;
790
791 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
792 ++it) {
793 const auto &ev = **it;
794 if (ev.IsZero()) { continue; }
795
796 std::string type = "";
797 if (IsString(ev.union_type)) {
798 type = "string"; // no need to wrap string type in namespace
799 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700800 type = AddImport(imports, dependent, *ev.union_type.struct_def)
801 .object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700802 } else {
803 FLATBUFFERS_ASSERT(false);
804 }
805 type_list.insert(type);
806 }
807
808 size_t totalPrinted = 0;
809 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
810 ++totalPrinted;
811 ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
812 }
813
814 return ret;
815 }
816
817 std::string GenUnionConvFuncName(const EnumDef &enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700818 return namer_.Function("unionTo", enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700819 }
820
821 std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700822 return namer_.Function("unionListTo", enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700823 }
824
825 std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
826 if (union_type.enum_def) {
827 const auto &enum_def = *union_type.enum_def;
828
829 const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
830 const auto valid_union_type_with_null = valid_union_type + "|null";
831
832 auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700833 "(\n type: " + GetTypeName(enum_def) +
James Kuszmaul8e62b022022-03-22 09:33:25 -0700834 ",\n accessor: (obj:" + valid_union_type + ") => " +
835 valid_union_type_with_null +
836 "\n): " + valid_union_type_with_null + " {\n";
837
Austin Schuh2dd86a92022-09-14 21:19:23 -0700838 const auto enum_type = AddImport(imports, enum_def, enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700839
840 const auto union_enum_loop = [&](const std::string &accessor_str) {
841 ret += " switch(" + enum_type + "[type]) {\n";
842 ret += " case 'NONE': return null; \n";
843
844 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
845 ++it) {
846 const auto &ev = **it;
847 if (ev.IsZero()) { continue; }
848
Austin Schuh2dd86a92022-09-14 21:19:23 -0700849 ret += " case '" + namer_.Variant(ev) + "': ";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700850
851 if (IsString(ev.union_type)) {
852 ret += "return " + accessor_str + "'') as string;";
853 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
854 const auto type =
Austin Schuh2dd86a92022-09-14 21:19:23 -0700855 AddImport(imports, enum_def, *ev.union_type.struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700856 ret += "return " + accessor_str + "new " + type + "())! as " +
857 type + ";";
858 } else {
859 FLATBUFFERS_ASSERT(false);
860 }
861 ret += "\n";
862 }
863
864 ret += " default: return null;\n";
865 ret += " }\n";
866 };
867
868 union_enum_loop("accessor(");
869 ret += "}";
870
871 ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
Austin Schuh2dd86a92022-09-14 21:19:23 -0700872 "(\n type: " + GetTypeName(enum_def) +
James Kuszmaul8e62b022022-03-22 09:33:25 -0700873 ", \n accessor: (index: number, obj:" + valid_union_type +
874 ") => " + valid_union_type_with_null +
875 ", \n index: number\n): " + valid_union_type_with_null + " {\n";
876 union_enum_loop("accessor(index, ");
877 ret += "}";
878
879 return ret;
880 }
881 FLATBUFFERS_ASSERT(0);
882 return "";
883 }
884
885 // Used for generating a short function that returns the correct class
886 // based on union enum type. Assume the context is inside the non object api
887 // type
Austin Schuh2dd86a92022-09-14 21:19:23 -0700888 std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
889 const std::string &field_name,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700890 const Type &union_type,
891 const bool is_array = false) {
892 if (union_type.enum_def) {
893 const auto &enum_def = *union_type.enum_def;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700894 const auto enum_type = AddImport(imports, dependent, enum_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700895 const std::string union_accessor = "this." + field_name;
896
897 const auto union_has_string = UnionHasStringType(enum_def);
898 const auto field_binded_method = "this." + field_name + ".bind(this)";
899
900 std::string ret;
901
902 if (!is_array) {
903 const auto conversion_function = GenUnionConvFuncName(enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700904
905 ret = "(() => {\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -0700906 ret += " let temp = " + conversion_function + "(this." +
907 namer_.Method(field_name, "Type") + "(), " +
908 field_binded_method + ");\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700909 ret += " if(temp === null) { return null; }\n";
910 ret += union_has_string
911 ? " if(typeof temp === 'string') { return temp; }\n"
912 : "";
913 ret += " return temp.unpack()\n";
914 ret += " })()";
915 } else {
916 const auto conversion_function = GenUnionListConvFuncName(enum_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700917
918 ret = "(() => {\n";
919 ret += " let ret = [];\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -0700920 ret += " for(let targetEnumIndex = 0; targetEnumIndex < this." +
921 namer_.Method(field_name, "TypeLength") + "()" +
James Kuszmaul8e62b022022-03-22 09:33:25 -0700922 "; "
923 "++targetEnumIndex) {\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -0700924 ret += " let targetEnum = this." +
925 namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700926 ret += " if(targetEnum === null || " + enum_type +
927 "[targetEnum!] === 'NONE') { "
928 "continue; }\n\n";
929 ret += " let temp = " + conversion_function + "(targetEnum, " +
930 field_binded_method + ", targetEnumIndex);\n";
931 ret += " if(temp === null) { continue; }\n";
932 ret += union_has_string ? " if(typeof temp === 'string') { "
933 "ret.push(temp); continue; }\n"
934 : "";
935 ret += " ret.push(temp.unpack());\n";
936 ret += " }\n";
937 ret += " return ret;\n";
938 ret += " })()";
939 }
940
941 return ret;
942 }
943
944 FLATBUFFERS_ASSERT(0);
945 return "";
946 }
947
948 static std::string GenNullCheckConditional(
949 const std::string &nullCheckVar, const std::string &trueVal,
950 const std::string &falseVal = "null") {
951 return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
952 ")";
953 }
954
955 std::string GenStructMemberValueTS(const StructDef &struct_def,
956 const std::string &prefix,
957 const std::string &delimiter,
958 const bool nullCheck = true) {
959 std::string ret;
960 for (auto it = struct_def.fields.vec.begin();
961 it != struct_def.fields.vec.end(); ++it) {
962 auto &field = **it;
963
Austin Schuh2dd86a92022-09-14 21:19:23 -0700964 auto curr_member_accessor = prefix + "." + namer_.Method(field);
965 if (prefix != "this") {
966 curr_member_accessor = prefix + "?." + namer_.Method(field);
967 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700968 if (IsStruct(field.value.type)) {
969 ret += GenStructMemberValueTS(*field.value.type.struct_def,
970 curr_member_accessor, delimiter);
971 } else {
972 if (nullCheck) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700973 std::string nullValue = "0";
974 if (field.value.type.base_type == BASE_TYPE_BOOL) {
975 nullValue = "false";
976 }
977 ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -0700978 } else {
979 ret += curr_member_accessor;
980 }
981 }
982
983 if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
984 }
985
986 return ret;
987 }
988
989 void GenObjApi(const Parser &parser, StructDef &struct_def,
990 std::string &obj_api_unpack_func, std::string &obj_api_class,
991 import_set &imports) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700992 const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700993
994 std::string unpack_func = "\nunpack(): " + class_name +
995 " {\n return new " + class_name + "(" +
996 (struct_def.fields.vec.empty() ? "" : "\n");
997 std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
998 +(struct_def.fields.vec.empty() ? "" : "\n");
999
1000 std::string constructor_func = "constructor(";
1001 constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
1002
1003 const auto has_create =
1004 struct_def.fixed || CanCreateFactoryMethod(struct_def);
1005
1006 std::string pack_func_prototype =
1007 "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
1008
1009 std::string pack_func_offset_decl;
1010 std::string pack_func_create_call;
1011
Austin Schuh2dd86a92022-09-14 21:19:23 -07001012 const auto struct_name = AddImport(imports, struct_def, struct_def).name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001013
1014 if (has_create) {
1015 pack_func_create_call = " return " + struct_name + ".create" +
1016 GetPrefixedName(struct_def) + "(builder" +
1017 (struct_def.fields.vec.empty() ? "" : ",\n ");
1018 } else {
1019 pack_func_create_call = " " + struct_name + ".start" +
1020 GetPrefixedName(struct_def) + "(builder);\n";
1021 }
1022
1023 if (struct_def.fixed) {
1024 // when packing struct, nested struct's members instead of the struct's
1025 // offset are used
1026 pack_func_create_call +=
1027 GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n ";
1028 }
1029
1030 for (auto it = struct_def.fields.vec.begin();
1031 it != struct_def.fields.vec.end(); ++it) {
1032 auto &field = **it;
1033 if (field.deprecated) continue;
1034
Austin Schuh2dd86a92022-09-14 21:19:23 -07001035 const auto field_method = namer_.Method(field);
1036 const auto field_field = namer_.Field(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001037 const std::string field_binded_method =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001038 "this." + field_method + ".bind(this)";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001039
1040 std::string field_val;
1041 std::string field_type;
1042 // a string that declares a variable containing the
1043 // offset for things that can't be generated inline
1044 // empty otw
1045 std::string field_offset_decl;
1046 // a string that contains values for things that can be created inline or
1047 // the variable name from field_offset_decl
1048 std::string field_offset_val;
1049 const auto field_default_val = GenDefaultValue(field, imports);
1050
1051 // Emit a scalar field
1052 const auto is_string = IsString(field.value.type);
1053 if (IsScalar(field.value.type.base_type) || is_string) {
1054 const auto has_null_default = is_string || HasNullDefault(field);
1055
1056 field_type += GenTypeName(imports, field, field.value.type, false,
1057 has_null_default);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001058 field_val = "this." + namer_.Method(field) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001059
1060 if (field.value.type.base_type != BASE_TYPE_STRING) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001061 field_offset_val = "this." + namer_.Field(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001062 } else {
1063 field_offset_decl = GenNullCheckConditional(
Austin Schuh2dd86a92022-09-14 21:19:23 -07001064 "this." + namer_.Field(field),
1065 "builder.createString(this." + field_field + "!)", "0");
James Kuszmaul8e62b022022-03-22 09:33:25 -07001066 }
1067 }
1068
1069 // Emit an object field
1070 else {
1071 auto is_vector = false;
1072 switch (field.value.type.base_type) {
1073 case BASE_TYPE_STRUCT: {
1074 const auto &sd = *field.value.type.struct_def;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001075 field_type += AddImport(imports, struct_def, sd).object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001076
1077 const std::string field_accessor =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001078 "this." + namer_.Method(field) + "()";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001079 field_val = GenNullCheckConditional(field_accessor,
1080 field_accessor + "!.unpack()");
1081 auto packing = GenNullCheckConditional(
Austin Schuh2dd86a92022-09-14 21:19:23 -07001082 "this." + field_field,
1083 "this." + field_field + "!.pack(builder)", "0");
James Kuszmaul8e62b022022-03-22 09:33:25 -07001084
1085 if (sd.fixed) {
1086 field_offset_val = std::move(packing);
1087 } else {
1088 field_offset_decl = std::move(packing);
1089 }
1090
1091 break;
1092 }
1093
1094 case BASE_TYPE_VECTOR: {
1095 auto vectortype = field.value.type.VectorType();
1096 auto vectortypename =
1097 GenTypeName(imports, struct_def, vectortype, false);
1098 is_vector = true;
1099
1100 field_type = "(";
1101
1102 switch (vectortype.base_type) {
1103 case BASE_TYPE_STRUCT: {
1104 const auto &sd = *field.value.type.struct_def;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001105 field_type += GetTypeName(sd, /*object_api=*/true);
1106 ;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001107 field_type += ")[]";
1108
1109 field_val = GenBBAccess() + ".createObjList(" +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001110 field_binded_method + ", this." +
1111 namer_.Method(field, "Length") + "())";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001112
1113 if (sd.fixed) {
1114 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001115 "builder.createStructOffsetList(this." + field_field +
1116 ", " + AddImport(imports, struct_def, struct_def).name +
1117 "." + namer_.Method("start", field, "Vector") + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001118 } else {
1119 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001120 AddImport(imports, struct_def, struct_def).name + "." +
1121 namer_.Method("create", field, "Vector") +
1122 "(builder, builder.createObjectOffsetList(" + "this." +
1123 field_field + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001124 }
1125
1126 break;
1127 }
1128
1129 case BASE_TYPE_STRING: {
1130 field_type += "string)[]";
1131 field_val = GenBBAccess() + ".createScalarList(" +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001132 field_binded_method + ", this." +
1133 namer_.Field(field, "Length") + "())";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001134 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001135 AddImport(imports, struct_def, struct_def).name + "." +
1136 namer_.Method("create", field, "Vector") +
1137 "(builder, builder.createObjectOffsetList(" + "this." +
1138 namer_.Field(field) + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001139 break;
1140 }
1141
1142 case BASE_TYPE_UNION: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001143 field_type += GenObjApiUnionTypeTS(
1144 imports, struct_def, parser.opts, *(vectortype.enum_def));
James Kuszmaul8e62b022022-03-22 09:33:25 -07001145 field_type += ")[]";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001146 field_val = GenUnionValTS(imports, struct_def, field_method,
1147 vectortype, true);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001148
1149 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001150 AddImport(imports, struct_def, struct_def).name + "." +
1151 namer_.Method("create", field, "Vector") +
1152 "(builder, builder.createObjectOffsetList(" + "this." +
1153 namer_.Field(field) + "))";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001154
1155 break;
1156 }
1157 default: {
1158 if (vectortype.enum_def) {
1159 field_type += GenTypeName(imports, struct_def, vectortype,
1160 false, HasNullDefault(field));
1161 } else {
1162 field_type += vectortypename;
1163 }
1164 field_type += ")[]";
1165 field_val = GenBBAccess() + ".createScalarList(" +
Austin Schuh2dd86a92022-09-14 21:19:23 -07001166 field_binded_method + ", this." +
1167 namer_.Method(field, "Length") + "())";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001168
1169 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001170 AddImport(imports, struct_def, struct_def).name + "." +
1171 namer_.Method("create", field, "Vector") +
1172 "(builder, this." + field_field + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001173
1174 break;
1175 }
1176 }
1177
1178 break;
1179 }
1180
1181 case BASE_TYPE_UNION: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001182 field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
James Kuszmaul8e62b022022-03-22 09:33:25 -07001183 *(field.value.type.enum_def));
1184
Austin Schuh2dd86a92022-09-14 21:19:23 -07001185 field_val = GenUnionValTS(imports, struct_def, field_method,
1186 field.value.type);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001187 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001188 "builder.createObjectOffset(this." + field_field + ")";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001189 break;
1190 }
1191
1192 default: FLATBUFFERS_ASSERT(0); break;
1193 }
1194
1195 // length 0 vector is simply empty instead of null
1196 field_type += is_vector ? "" : "|null";
1197 }
1198
1199 if (!field_offset_decl.empty()) {
1200 field_offset_decl =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001201 " const " + field_field + " = " + field_offset_decl + ";";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001202 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001203 if (field_offset_val.empty()) { field_offset_val = field_field; }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001204
1205 unpack_func += " " + field_val;
Austin Schuh2dd86a92022-09-14 21:19:23 -07001206 unpack_to_func += " _o." + field_field + " = " + field_val + ";";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001207
Austin Schuh2dd86a92022-09-14 21:19:23 -07001208 // FIXME: if field_type and field_field are identical, then
James Kuszmaul8e62b022022-03-22 09:33:25 -07001209 // this generates invalid typescript.
Austin Schuh2dd86a92022-09-14 21:19:23 -07001210 constructor_func += " public " + field_field + ": " + field_type +
1211 " = " + field_default_val;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001212
1213 if (!struct_def.fixed) {
1214 if (!field_offset_decl.empty()) {
1215 pack_func_offset_decl += field_offset_decl + "\n";
1216 }
1217
1218 if (has_create) {
1219 pack_func_create_call += field_offset_val;
1220 } else {
1221 if (field.IsScalarOptional()) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001222 pack_func_create_call +=
1223 " if (" + field_offset_val + " !== null)\n ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001224 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001225 pack_func_create_call += " " + struct_name + "." +
1226 namer_.Method("add", field) + "(builder, " +
1227 field_offset_val + ");\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001228 }
1229 }
1230
1231 if (std::next(it) != struct_def.fields.vec.end()) {
1232 constructor_func += ",\n";
1233
1234 if (!struct_def.fixed && has_create) {
1235 pack_func_create_call += ",\n ";
1236 }
1237
1238 unpack_func += ",\n";
1239 unpack_to_func += "\n";
1240 } else {
1241 constructor_func += "\n";
1242 if (!struct_def.fixed) {
1243 pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
1244 pack_func_create_call += "\n ";
1245 }
1246
1247 unpack_func += "\n ";
1248 unpack_to_func += "\n";
1249 }
1250 }
1251
1252 constructor_func += "){}\n\n";
1253
1254 if (has_create) {
1255 pack_func_create_call += ");";
1256 } else {
1257 pack_func_create_call += "return " + struct_name + ".end" +
1258 GetPrefixedName(struct_def) + "(builder);";
1259 }
Austin Schuh2dd86a92022-09-14 21:19:23 -07001260 obj_api_class = "\n";
1261 obj_api_class += "export class ";
1262 obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
1263 obj_api_class += " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001264 obj_api_class += constructor_func;
1265 obj_api_class += pack_func_prototype + pack_func_offset_decl +
1266 pack_func_create_call + "\n}";
1267
1268 obj_api_class += "\n}\n";
1269
1270 unpack_func += ");\n}";
1271 unpack_to_func += "}\n";
1272
1273 obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
1274 }
1275
1276 static bool CanCreateFactoryMethod(const StructDef &struct_def) {
1277 // to preserve backwards compatibility, we allow the first field to be a
1278 // struct
1279 return struct_def.fields.vec.size() < 2 ||
1280 std::all_of(std::begin(struct_def.fields.vec) + 1,
1281 std::end(struct_def.fields.vec),
1282 [](const FieldDef *f) -> bool {
1283 FLATBUFFERS_ASSERT(f != nullptr);
1284 return f->value.type.base_type != BASE_TYPE_STRUCT;
1285 });
1286 }
1287
1288 // Generate an accessor struct with constructor for a flatbuffers struct.
1289 void GenStruct(const Parser &parser, StructDef &struct_def,
1290 std::string *code_ptr, import_set &imports) {
1291 if (struct_def.generated) return;
1292 std::string &code = *code_ptr;
1293
Austin Schuh2dd86a92022-09-14 21:19:23 -07001294 // Special case for the root struct, since no one will necessarily reference
1295 // it, we have to explicitly add it to the import list.
1296 if (&struct_def == parser_.root_struct_def_) {
1297 AddImport(imports, struct_def, struct_def);
1298 }
1299
1300 const std::string object_name = GetTypeName(struct_def);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001301
1302 // Emit constructor
James Kuszmaul8e62b022022-03-22 09:33:25 -07001303 GenDocComment(struct_def.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001304 code += "export class ";
1305 code += object_name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001306 code += " {\n";
1307 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
1308 code += " bb_pos = 0;\n";
1309
1310 // Generate the __init method that sets the field in a pre-existing
1311 // accessor object. This is to allow object reuse.
1312 code +=
Austin Schuh2dd86a92022-09-14 21:19:23 -07001313 " __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001314 code += " this.bb_pos = i;\n";
1315 code += " this.bb = bb;\n";
1316 code += " return this;\n";
1317 code += "}\n\n";
1318
1319 // Generate special accessors for the table that when used as the root of a
1320 // FlatBuffer
1321 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
1322 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
1323
1324 // Generate the identifier check method
1325 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
1326 !parser_.file_identifier_.empty()) {
1327 GenDocComment(code_ptr);
1328 code +=
1329 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
1330 "{\n";
1331 code += " return bb.__has_identifier('" + parser_.file_identifier_;
1332 code += "');\n}\n\n";
1333 }
1334
1335 // Emit field accessors
1336 for (auto it = struct_def.fields.vec.begin();
1337 it != struct_def.fields.vec.end(); ++it) {
1338 auto &field = **it;
1339 if (field.deprecated) continue;
1340 auto offset_prefix =
1341 " const offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1342 NumToString(field.value.offset) + ");\n return offset ? ";
1343
1344 // Emit a scalar field
1345 const auto is_string = IsString(field.value.type);
1346 if (IsScalar(field.value.type.base_type) || is_string) {
1347 const auto has_null_default = is_string || HasNullDefault(field);
1348
1349 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001350 std::string prefix = namer_.Method(field) + "(";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001351 if (is_string) {
1352 code += prefix + "):string|null\n";
1353 code +=
1354 prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
1355 GenTypeName(imports, struct_def, field.value.type, false, true) +
1356 "\n";
1357 code += prefix + "optionalEncoding?:any";
1358 } else {
1359 code += prefix;
1360 }
1361 if (field.value.type.enum_def) {
1362 code += "):" +
1363 GenTypeName(imports, struct_def, field.value.type, false,
1364 field.IsOptional()) +
1365 " {\n";
1366 } else {
1367 code += "):" +
1368 GenTypeName(imports, struct_def, field.value.type, false,
1369 has_null_default) +
1370 " {\n";
1371 }
1372
1373 if (struct_def.fixed) {
1374 code +=
1375 " return " +
1376 GenGetter(field.value.type,
1377 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
1378 ";\n";
1379 } else {
1380 std::string index = "this.bb_pos + offset";
1381 if (is_string) { index += ", optionalEncoding"; }
1382 code += offset_prefix +
1383 GenGetter(field.value.type, "(" + index + ")") + " : " +
1384 GenDefaultValue(field, imports);
1385 code += ";\n";
1386 }
1387 }
1388
1389 // Emit an object field
1390 else {
1391 switch (field.value.type.base_type) {
1392 case BASE_TYPE_STRUCT: {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001393 const auto type =
1394 AddImport(imports, struct_def, *field.value.type.struct_def)
1395 .name;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001396 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001397 code += namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001398 code += "(obj?:" + type + "):" + type + "|null {\n";
1399
1400 if (struct_def.fixed) {
1401 code += " return (obj || " + GenerateNewExpression(type);
1402 code += ").__init(this.bb_pos";
1403 code +=
1404 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
1405 } else {
1406 code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1407 ").__init(";
1408 code += field.value.type.struct_def->fixed
1409 ? "this.bb_pos + offset"
1410 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
1411 code += ", " + GenBBAccess() + ") : null;\n";
1412 }
1413
1414 break;
1415 }
1416
1417 case BASE_TYPE_VECTOR: {
1418 auto vectortype = field.value.type.VectorType();
1419 auto vectortypename =
1420 GenTypeName(imports, struct_def, vectortype, false);
1421 auto inline_size = InlineSize(vectortype);
1422 auto index = GenBBAccess() +
1423 ".__vector(this.bb_pos + offset) + index" +
1424 MaybeScale(inline_size);
1425 std::string ret_type;
1426 bool is_union = false;
1427 switch (vectortype.base_type) {
1428 case BASE_TYPE_STRUCT: ret_type = vectortypename; break;
1429 case BASE_TYPE_STRING: ret_type = vectortypename; break;
1430 case BASE_TYPE_UNION:
1431 ret_type = "?flatbuffers.Table";
1432 is_union = true;
1433 break;
1434 default: ret_type = vectortypename;
1435 }
1436 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001437 std::string prefix = namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001438 // TODO: make it work without any
1439 // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1440 if (is_union) { prefix += ""; }
1441 prefix += "(index: number";
1442 if (is_union) {
1443 const auto union_type =
1444 GenUnionGenericTypeTS(*(field.value.type.enum_def));
1445
1446 vectortypename = union_type;
1447 code += prefix + ", obj:" + union_type;
1448 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1449 code += prefix + ", obj?:" + vectortypename;
1450 } else if (IsString(vectortype)) {
1451 code += prefix + "):string\n";
1452 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1453 "):" + vectortypename + "\n";
1454 code += prefix + ",optionalEncoding?:any";
1455 } else {
1456 code += prefix;
1457 }
1458 code += "):" + vectortypename + "|null {\n";
1459
1460 if (vectortype.base_type == BASE_TYPE_STRUCT) {
1461 code += offset_prefix + "(obj || " +
1462 GenerateNewExpression(vectortypename);
1463 code += ").__init(";
1464 code += vectortype.struct_def->fixed
1465 ? index
1466 : GenBBAccess() + ".__indirect(" + index + ")";
1467 code += ", " + GenBBAccess() + ")";
1468 } else {
1469 if (is_union) {
1470 index = "obj, " + index;
1471 } else if (IsString(vectortype)) {
1472 index += ", optionalEncoding";
1473 }
1474 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1475 }
1476 code += " : ";
1477 if (field.value.type.element == BASE_TYPE_BOOL) {
1478 code += "false";
1479 } else if (field.value.type.element == BASE_TYPE_LONG ||
1480 field.value.type.element == BASE_TYPE_ULONG) {
1481 code += "BigInt(0)";
1482 } else if (IsScalar(field.value.type.element)) {
1483 if (field.value.type.enum_def) {
1484 code += field.value.constant;
1485 } else {
1486 code += "0";
1487 }
1488 } else {
1489 code += "null";
1490 }
1491 code += ";\n";
1492 break;
1493 }
1494
1495 case BASE_TYPE_UNION: {
1496 GenDocComment(field.doc_comment, code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001497 code += namer_.Method(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001498
1499 const auto &union_enum = *(field.value.type.enum_def);
1500 const auto union_type = GenUnionGenericTypeTS(union_enum);
1501 code += "<T extends flatbuffers.Table>(obj:" + union_type +
1502 "):" + union_type +
1503 "|null "
1504 "{\n";
1505
1506 code += offset_prefix +
1507 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1508 " : null;\n";
1509 break;
1510 }
1511 default: FLATBUFFERS_ASSERT(0);
1512 }
1513 }
1514 code += "}\n\n";
1515
1516 // Adds the mutable scalar value to the output
1517 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1518 !IsUnion(field.value.type)) {
1519 std::string type =
1520 GenTypeName(imports, struct_def, field.value.type, true);
1521
Austin Schuh2dd86a92022-09-14 21:19:23 -07001522 code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
1523 "):boolean {\n";
1524
1525 const std::string write_method =
1526 "." + namer_.Method("write", GenType(field.value.type));
James Kuszmaul8e62b022022-03-22 09:33:25 -07001527
1528 if (struct_def.fixed) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001529 code += " " + GenBBAccess() + write_method + "(this.bb_pos + " +
1530 NumToString(field.value.offset) + ", ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001531 } else {
1532 code += " const offset = " + GenBBAccess() +
1533 ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
1534 ");\n\n";
1535 code += " if (offset === 0) {\n";
1536 code += " return false;\n";
1537 code += " }\n\n";
1538
1539 // special case for bools, which are treated as uint8
Austin Schuh2dd86a92022-09-14 21:19:23 -07001540 code +=
1541 " " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001542 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1543 }
1544
1545 code += "value);\n";
1546 code += " return true;\n";
1547 code += "}\n\n";
1548 }
1549
1550 // Emit vector helpers
1551 if (IsVector(field.value.type)) {
1552 // Emit a length helper
1553 GenDocComment(code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001554 code += namer_.Method(field, "Length");
1555 code += "():number {\n" + offset_prefix;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001556
1557 code +=
1558 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
1559
1560 // For scalar types, emit a typed array helper
1561 auto vectorType = field.value.type.VectorType();
1562 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1563 GenDocComment(code_ptr);
1564
Austin Schuh2dd86a92022-09-14 21:19:23 -07001565 code += namer_.Method(field, "Array");
1566 code +=
1567 "():" + GenType(vectorType) + "Array|null {\n" + offset_prefix;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001568
1569 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1570 ".bytes().buffer, " + GenBBAccess() +
1571 ".bytes().byteOffset + " + GenBBAccess() +
1572 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1573 ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n";
1574 }
1575 }
1576 }
1577
1578 // Emit the fully qualified name
1579 if (parser_.opts.generate_name_strings) {
1580 GenDocComment(code_ptr);
1581 code += "static getFullyQualifiedName():string {\n";
1582 code += " return '" + WrapInNameSpace(struct_def) + "';\n";
1583 code += "}\n\n";
1584 }
1585
1586 // Emit the size of the struct.
1587 if (struct_def.fixed) {
1588 GenDocComment(code_ptr);
1589 code += "static sizeOf():number {\n";
1590 code += " return " + NumToString(struct_def.bytesize) + ";\n";
1591 code += "}\n\n";
1592 }
1593
1594 // Emit a factory constructor
1595 if (struct_def.fixed) {
1596 std::string arguments;
1597 GenStructArgs(imports, struct_def, &arguments, "");
1598 GenDocComment(code_ptr);
1599
1600 code += "static create" + GetPrefixedName(struct_def) +
1601 "(builder:flatbuffers.Builder";
1602 code += arguments + "):flatbuffers.Offset {\n";
1603
1604 GenStructBody(struct_def, &code, "");
1605 code += " return builder.offset();\n}\n\n";
1606 } else {
1607 // Generate a method to start building a new object
1608 GenDocComment(code_ptr);
1609
1610 code += "static start" + GetPrefixedName(struct_def) +
1611 "(builder:flatbuffers.Builder) {\n";
1612
1613 code += " builder.startObject(" +
1614 NumToString(struct_def.fields.vec.size()) + ");\n";
1615 code += "}\n\n";
1616
1617 // Generate a set of static methods that allow table construction
1618 for (auto it = struct_def.fields.vec.begin();
1619 it != struct_def.fields.vec.end(); ++it) {
1620 auto &field = **it;
1621 if (field.deprecated) continue;
1622 const auto argname = GetArgName(field);
1623
1624 // Generate the field insertion method
1625 GenDocComment(code_ptr);
Austin Schuh2dd86a92022-09-14 21:19:23 -07001626 code += "static " + namer_.Method("add", field);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001627 code += "(builder:flatbuffers.Builder, " + argname + ":" +
1628 GetArgType(imports, struct_def, field, false) + ") {\n";
1629 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1630 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1631 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1632 code += argname + ", ";
1633 if (!IsScalar(field.value.type.base_type)) {
1634 code += "0";
1635 } else if (HasNullDefault(field)) {
1636 if (IsLong(field.value.type.base_type)) {
1637 code += "BigInt(0)";
1638 } else {
1639 code += "0";
1640 }
1641 } else {
1642 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1643 code += GenDefaultValue(field, imports);
1644 }
1645 code += ");\n}\n\n";
1646
1647 if (IsVector(field.value.type)) {
1648 auto vector_type = field.value.type.VectorType();
1649 auto alignment = InlineAlignment(vector_type);
1650 auto elem_size = InlineSize(vector_type);
1651
1652 // Generate a method to create a vector from a JavaScript array
1653 if (!IsStruct(vector_type)) {
1654 GenDocComment(code_ptr);
1655
1656 const std::string sig_begin =
Austin Schuh2dd86a92022-09-14 21:19:23 -07001657 "static " + namer_.Method("create", field, "Vector") +
1658 "(builder:flatbuffers.Builder, data:";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001659 const std::string sig_end = "):flatbuffers.Offset";
1660 std::string type =
1661 GenTypeName(imports, struct_def, vector_type, true) + "[]";
1662 if (type == "number[]") {
1663 const auto &array_type = GenType(vector_type);
1664 // the old type should be deprecated in the future
1665 std::string type_old = "number[]|Uint8Array";
1666 std::string type_new = "number[]|" + array_type + "Array";
1667 if (type_old == type_new) {
1668 type = type_new;
1669 } else {
1670 // add function overloads
1671 code += sig_begin + type_new + sig_end + ";\n";
1672 code +=
1673 "/**\n * @deprecated This Uint8Array overload will "
1674 "be removed in the future.\n */\n";
1675 code += sig_begin + type_old + sig_end + ";\n";
1676 type = type_new + "|Uint8Array";
1677 }
1678 }
1679 code += sig_begin + type + sig_end + " {\n";
1680 code += " builder.startVector(" + NumToString(elem_size);
1681 code += ", data.length, " + NumToString(alignment) + ");\n";
1682 code += " for (let i = data.length - 1; i >= 0; i--) {\n";
1683 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1684 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1685 code += "data[i]!);\n";
1686 code += " }\n";
1687 code += " return builder.endVector();\n";
1688 code += "}\n\n";
1689 }
1690
1691 // Generate a method to start a vector, data to be added manually
1692 // after
1693 GenDocComment(code_ptr);
1694
Austin Schuh2dd86a92022-09-14 21:19:23 -07001695 code += "static ";
1696 code += namer_.Method("start", field, "Vector");
1697 code += "(builder:flatbuffers.Builder, numElems:number) {\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001698 code += " builder.startVector(" + NumToString(elem_size);
1699 code += ", numElems, " + NumToString(alignment) + ");\n";
1700 code += "}\n\n";
1701 }
1702 }
1703
1704 // Generate a method to stop building a new object
1705 GenDocComment(code_ptr);
1706
1707 code += "static end" + GetPrefixedName(struct_def);
1708 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1709
1710 code += " const offset = builder.endObject();\n";
1711 for (auto it = struct_def.fields.vec.begin();
1712 it != struct_def.fields.vec.end(); ++it) {
1713 auto &field = **it;
1714 if (!field.deprecated && field.IsRequired()) {
1715 code += " builder.requiredField(offset, ";
1716 code += NumToString(field.value.offset);
1717 code += ") // " + field.name + "\n";
1718 }
1719 }
1720 code += " return offset;\n";
1721 code += "}\n\n";
1722
1723 // Generate the methods to complete buffer construction
1724 GenerateFinisher(struct_def, code_ptr, code, false);
1725 GenerateFinisher(struct_def, code_ptr, code, true);
1726
1727 // Generate a convenient CreateX function
1728 if (CanCreateFactoryMethod(struct_def)) {
1729 code += "static create" + GetPrefixedName(struct_def);
1730 code += "(builder:flatbuffers.Builder";
1731 for (auto it = struct_def.fields.vec.begin();
1732 it != struct_def.fields.vec.end(); ++it) {
1733 const auto &field = **it;
1734 if (field.deprecated) continue;
1735 code += ", " + GetArgName(field) + ":" +
1736 GetArgType(imports, struct_def, field, true);
1737 }
1738
1739 code += "):flatbuffers.Offset {\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001740 code += " " + object_name + ".start" + GetPrefixedName(struct_def) +
1741 "(builder);\n";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001742
1743 std::string methodPrefix = object_name;
1744 for (auto it = struct_def.fields.vec.begin();
1745 it != struct_def.fields.vec.end(); ++it) {
1746 const auto &field = **it;
1747 if (field.deprecated) continue;
1748
1749 const auto arg_name = GetArgName(field);
1750
1751 if (field.IsScalarOptional()) {
1752 code += " if (" + arg_name + " !== null)\n ";
1753 }
1754
Austin Schuh2dd86a92022-09-14 21:19:23 -07001755 code += " " + methodPrefix + "." + namer_.Method("add", field) + "(";
James Kuszmaul8e62b022022-03-22 09:33:25 -07001756 code += "builder, " + arg_name + ");\n";
1757 }
1758
1759 code += " return " + methodPrefix + ".end" +
1760 GetPrefixedName(struct_def) + "(builder);\n";
1761 code += "}\n";
1762 }
1763 }
1764
1765 if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
1766 auto name = GetPrefixedName(struct_def, "");
1767 code += "\n";
1768 code += "serialize():Uint8Array {\n";
1769 code += " return this.bb!.bytes();\n";
1770 code += "}\n";
1771
1772 code += "\n";
Austin Schuh2dd86a92022-09-14 21:19:23 -07001773 code += "static deserialize(buffer: Uint8Array):" +
1774 namer_.EscapeKeyword(name) + " {\n";
1775 code += " return " + AddImport(imports, struct_def, struct_def).name +
James Kuszmaul8e62b022022-03-22 09:33:25 -07001776 ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
1777 code += "}\n";
1778 }
1779
1780 if (parser_.opts.generate_object_based_api) {
1781 std::string obj_api_class;
1782 std::string obj_api_unpack_func;
1783 GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
1784 imports);
1785
1786 code += obj_api_unpack_func + "}\n" + obj_api_class;
1787 } else {
1788 code += "}\n";
1789 }
1790 }
1791
1792 static bool HasNullDefault(const FieldDef &field) {
1793 return field.IsOptional() && field.value.constant == "null";
1794 }
1795
1796 std::string GetArgType(import_set &imports, const Definition &owner,
1797 const FieldDef &field, bool allowNull) {
1798 return GenTypeName(imports, owner, field.value.type, true,
1799 allowNull && field.IsOptional());
1800 }
1801
1802 std::string GetArgName(const FieldDef &field) {
Austin Schuh2dd86a92022-09-14 21:19:23 -07001803 auto argname = namer_.Variable(field);
1804 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001805 return argname;
1806 }
1807
1808 std::string GetPrefixedName(const StructDef &struct_def,
1809 const char *prefix = "") {
1810 return prefix + struct_def.name;
1811 }
1812}; // namespace ts
1813} // namespace ts
1814
1815bool GenerateTS(const Parser &parser, const std::string &path,
1816 const std::string &file_name) {
1817 ts::TsGenerator generator(parser, path, file_name);
1818 return generator.generate();
1819}
1820
1821std::string TSMakeRule(const Parser &parser, const std::string &path,
1822 const std::string &file_name) {
1823 std::string filebase =
1824 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1825 ts::TsGenerator generator(parser, path, file_name);
1826 std::string make_rule =
1827 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
1828
1829 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1830 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1831 make_rule += " " + *it;
1832 }
1833 return make_rule;
1834}
1835
1836} // namespace flatbuffers