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