blob: 0240f513059d52eb427cedc77410e96a911f5e0c [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// independent from idl_parser, since this code is not needed for most clients
Austin Schuh272c6132020-11-14 16:37:52 -080018#include <algorithm>
Austin Schuhe89fa2d2019-08-14 20:24:23 -070019#include <cassert>
20#include <unordered_map>
21#include <unordered_set>
22
23#include "flatbuffers/code_generators.h"
24#include "flatbuffers/flatbuffers.h"
25#include "flatbuffers/idl.h"
26#include "flatbuffers/util.h"
27
28namespace flatbuffers {
29
Austin Schuhe89fa2d2019-08-14 20:24:23 -070030struct JsTsLanguageParameters {
31 IDLOptions::Language language;
32 std::string file_extension;
33};
34
35struct ReexportDescription {
36 std::string symbol;
37 std::string source_namespace;
38 std::string target_namespace;
39};
40
41enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
42
43const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
44 static JsTsLanguageParameters js_language_parameters[] = {
45 {
46 IDLOptions::kJs,
47 ".js",
48 },
49 {
50 IDLOptions::kTs,
51 ".ts",
52 },
53 };
54
55 if (lang == IDLOptions::kJs) {
56 return js_language_parameters[0];
57 } else {
58 FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
59 return js_language_parameters[1];
60 }
61}
62
Austin Schuhe89fa2d2019-08-14 20:24:23 -070063namespace jsts {
64// Iterate through all definitions we haven't generate code for (enums, structs,
65// and tables) and output them to a single file.
66class JsTsGenerator : public BaseGenerator {
67 public:
68 typedef std::unordered_set<std::string> imported_fileset;
69 typedef std::unordered_multimap<std::string, ReexportDescription>
70 reexport_map;
71
72 JsTsGenerator(const Parser &parser, const std::string &path,
73 const std::string &file_name)
Austin Schuh272c6132020-11-14 16:37:52 -080074 : BaseGenerator(parser, path, file_name, "", ".",
75 parser.opts.lang == IDLOptions::kJs ? "js" : "ts"),
Austin Schuhe89fa2d2019-08-14 20:24:23 -070076 lang_(GetJsLangParams(parser_.opts.lang)) {}
77 // Iterate through all definitions we haven't generate code for (enums,
78 // structs, and tables) and output them to a single file.
79 bool generate() {
80 imported_fileset imported_files;
81 reexport_map reexports;
82
83 std::string enum_code, struct_code, import_code, exports_code, code;
Austin Schuh272c6132020-11-14 16:37:52 -080084 generateEnums(&enum_code, &exports_code, reexports, imported_files);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070085 generateStructs(&struct_code, &exports_code, imported_files);
86 generateImportDependencies(&import_code, imported_files);
87 generateReexports(&import_code, reexports, imported_files);
88
89 code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
90
91 // Generate code for all the namespace declarations.
92 GenNamespaces(&code, &exports_code);
93
94 // Output the main declaration code from above.
95 code += import_code;
96
97 code += enum_code;
98 code += struct_code;
99
100 if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
101 !parser_.opts.skip_js_exports) {
102 if (parser_.opts.use_ES6_js_export_format)
103 code += "// Exports for ECMAScript6 Modules\n";
104 else
105 code += "// Exports for Node.js and RequireJS\n";
106 code += exports_code;
107 }
108
Austin Schuh272c6132020-11-14 16:37:52 -0800109 return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
110 code, false);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700111 }
112
113 private:
114 JsTsLanguageParameters lang_;
115
116 // Generate code for imports
117 void generateImportDependencies(std::string *code_ptr,
118 const imported_fileset &imported_files) {
119 std::string &code = *code_ptr;
120 for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
121 const auto &file = *it;
122 const auto basename =
123 flatbuffers::StripPath(flatbuffers::StripExtension(file));
Austin Schuh272c6132020-11-14 16:37:52 -0800124 if (basename != file_name_) { code += GenPrefixedImport(file, basename); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700125 }
126 }
127
128 // Generate reexports, which might not have been explicitly imported using the
129 // "export import" trick
130 void generateReexports(std::string *code_ptr, const reexport_map &reexports,
131 imported_fileset imported_files) {
132 if (!parser_.opts.reexport_ts_modules ||
133 lang_.language != IDLOptions::kTs) {
134 return;
135 }
136
Austin Schuh272c6132020-11-14 16:37:52 -0800137 std::unordered_set<std::string> imported;
138
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700139 std::string &code = *code_ptr;
140 for (auto it = reexports.begin(); it != reexports.end(); ++it) {
141 const auto &file = *it;
142 const auto basename =
143 flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
Austin Schuh272c6132020-11-14 16:37:52 -0800144 if (basename != file_name_ &&
145 imported.find(file.second.symbol) == imported.end()) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700146 if (imported_files.find(file.first) == imported_files.end()) {
147 code += GenPrefixedImport(file.first, basename);
148 imported_files.emplace(file.first);
149 }
150
Austin Schuh272c6132020-11-14 16:37:52 -0800151 if (!file.second.target_namespace.empty()) {
152 code += "export namespace " + file.second.target_namespace + " { \n";
153 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700154 code += "export import " + file.second.symbol + " = ";
Austin Schuh272c6132020-11-14 16:37:52 -0800155 code += GenFileNamespacePrefix(file.first) + ".";
156 if (!file.second.source_namespace.empty()) {
157 code += file.second.source_namespace + ".";
158 }
159 code += file.second.symbol + ";\n";
160 if (!file.second.target_namespace.empty()) { code += "}\n"; }
161
162 imported.emplace(file.second.symbol);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700163 }
164 }
165 }
166
167 // Generate code for all enums.
168 void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
Austin Schuh272c6132020-11-14 16:37:52 -0800169 reexport_map &reexports,
170 imported_fileset &imported_files) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700171 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
172 ++it) {
173 auto &enum_def = **it;
Austin Schuh272c6132020-11-14 16:37:52 -0800174 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports,
175 imported_files, false);
176 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports,
177 imported_files, true);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700178 }
179 }
180
181 // Generate code for all structs.
182 void generateStructs(std::string *decl_code_ptr,
183 std::string *exports_code_ptr,
184 imported_fileset &imported_files) {
185 for (auto it = parser_.structs_.vec.begin();
186 it != parser_.structs_.vec.end(); ++it) {
187 auto &struct_def = **it;
188 GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
189 imported_files);
190 }
191 }
192 void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
193 if (lang_.language == IDLOptions::kTs &&
194 parser_.opts.skip_flatbuffers_import) {
195 return;
196 }
197
198 std::set<std::string> namespaces;
199
200 for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
201 ++it) {
202 std::string namespace_so_far;
203
204 // Gather all parent namespaces for this namespace
205 for (auto component = (*it)->components.begin();
206 component != (*it)->components.end(); ++component) {
207 if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
208 namespace_so_far += *component;
209 namespaces.insert(namespace_so_far);
210 }
211 }
212
213 // Make sure parent namespaces come before child namespaces
214 std::vector<std::string> sorted_namespaces(namespaces.begin(),
215 namespaces.end());
216 std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
217
218 // Emit namespaces in a form that Closure Compiler can optimize
219 std::string &code = *code_ptr;
220 std::string &exports = *exports_ptr;
Austin Schuh272c6132020-11-14 16:37:52 -0800221
222 if (lang_.language == IDLOptions::kTs) {
223 code += "import * as flatbuffers from 'flatbuffers';\n";
224 }
225
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700226 for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
227 ++it) {
228 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -0800229 if (it->find('.') == std::string::npos) { break; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700230 } else {
231 code += "/**\n * @const\n * @namespace\n */\n";
232 if (it->find('.') == std::string::npos) {
233 code += "var ";
234 if (parser_.opts.use_goog_js_export_format) {
235 exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
236 } else if (parser_.opts.use_ES6_js_export_format) {
237 exports += "export {" + *it + "};\n";
238 } else {
239 exports += "this." + *it + " = " + *it + ";\n";
240 }
241 }
242 code += *it + " = " + *it + " || {};\n\n";
243 }
244 }
245 }
246
247 // Generate a documentation comment, if available.
248 static void GenDocComment(const std::vector<std::string> &dc,
249 std::string *code_ptr,
250 const std::string &extra_lines,
251 const char *indent = nullptr) {
252 if (dc.empty() && extra_lines.empty()) {
253 // Don't output empty comment blocks with 0 lines of comment content.
254 return;
255 }
256
257 std::string &code = *code_ptr;
258 if (indent) code += indent;
259 code += "/**\n";
260 for (auto it = dc.begin(); it != dc.end(); ++it) {
261 if (indent) code += indent;
262 code += " *" + *it + "\n";
263 }
264 if (!extra_lines.empty()) {
265 if (!dc.empty()) {
266 if (indent) code += indent;
267 code += " *\n";
268 }
269 if (indent) code += indent;
270 std::string::size_type start = 0;
271 for (;;) {
272 auto end = extra_lines.find('\n', start);
273 if (end != std::string::npos) {
274 code += " * " + extra_lines.substr(start, end - start) + "\n";
275 start = end + 1;
276 } else {
277 code += " * " + extra_lines.substr(start) + "\n";
278 break;
279 }
280 }
281 }
282 if (indent) code += indent;
283 code += " */\n";
284 }
285
286 static void GenDocComment(std::string *code_ptr,
287 const std::string &extra_lines) {
288 GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
289 }
290
291 std::string GenTypeAnnotation(AnnotationType annotation_type,
292 const std::string &type_name,
293 const std::string &arg_name,
294 bool include_newline = true) {
295 std::string result = "";
296 switch (annotation_type) {
297 case kParam: {
298 result += "@param";
299 break;
300 }
301 case kType: {
302 if (lang_.language != IDLOptions::kTs) {
303 result += "@type";
304 } else {
305 return "";
306 }
307 break;
308 }
309 case kReturns: {
310 result += "@returns";
311 break;
312 }
313 }
314 switch (lang_.language) {
315 case IDLOptions::kTs: {
316 result += " " + type_name;
317 break;
318 }
Austin Schuh272c6132020-11-14 16:37:52 -0800319 default: {
320 result += " {" + type_name + "}";
321 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700322 }
Austin Schuh272c6132020-11-14 16:37:52 -0800323 if (!arg_name.empty()) { result += " " + arg_name; }
324 if (include_newline) { result += "\n"; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700325
326 return result;
327 }
328
329 // Generate an enum declaration and an enum string lookup table.
330 void GenEnum(EnumDef &enum_def, std::string *code_ptr,
331 std::string *exports_ptr, reexport_map &reexports,
Austin Schuh272c6132020-11-14 16:37:52 -0800332 imported_fileset &imported_files, bool reverse) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700333 if (enum_def.generated) return;
334 if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME.
335 std::string &code = *code_ptr;
336 std::string &exports = *exports_ptr;
337 GenDocComment(enum_def.doc_comment, code_ptr,
338 reverse ? "@enum {string}" : "@enum {number}");
339 std::string ns = GetNameSpace(enum_def);
340 std::string enum_def_name = enum_def.name + (reverse ? "Name" : "");
341 if (lang_.language == IDLOptions::kTs) {
342 if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
343 code += "export enum " + enum_def.name + "{\n";
344 } else {
345 if (enum_def.defined_namespace->components.empty()) {
346 code += "var ";
347 if (parser_.opts.use_goog_js_export_format) {
348 exports += "goog.exportSymbol('" + enum_def_name + "', " +
349 enum_def.name + ");\n";
350 } else if (parser_.opts.use_ES6_js_export_format) {
351 exports += "export {" + enum_def_name + "};\n";
352 } else {
353 exports += "this." + enum_def_name + " = " + enum_def_name + ";\n";
354 }
355 }
356 code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n";
357 }
358 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
359 auto &ev = **it;
360 if (!ev.doc_comment.empty()) {
361 if (it != enum_def.Vals().begin()) { code += '\n'; }
362 GenDocComment(ev.doc_comment, code_ptr, "", " ");
363 }
364
365 // Generate mapping between EnumName: EnumValue(int)
366 if (reverse) {
Austin Schuh272c6132020-11-14 16:37:52 -0800367 code += " '" + enum_def.ToString(ev) + "'";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700368 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
369 code += "'" + ev.name + "'";
370 } else {
371 code += " " + ev.name;
372 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
373 code += enum_def.ToString(ev);
374 }
375
376 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
377
378 if (ev.union_type.struct_def) {
Austin Schuh272c6132020-11-14 16:37:52 -0800379 ReexportDescription desc = { ev.union_type.struct_def->name,
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700380 GetNameSpace(*ev.union_type.struct_def),
381 GetNameSpace(enum_def) };
382 reexports.insert(
383 std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
384 }
385 }
Austin Schuh272c6132020-11-14 16:37:52 -0800386 code += "};";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700387
Austin Schuh272c6132020-11-14 16:37:52 -0800388 if (lang_.language == IDLOptions::kTs) {
389 if (enum_def.is_union) {
390 code += GenUnionConvFunc(enum_def.underlying_type, imported_files);
391 }
392 if (!ns.empty()) { code += "\n}"; }
393 }
394
395 code += "\n\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700396 }
397
398 static std::string GenType(const Type &type) {
399 switch (type.base_type) {
400 case BASE_TYPE_BOOL:
401 case BASE_TYPE_CHAR: return "Int8";
402 case BASE_TYPE_UTYPE:
403 case BASE_TYPE_UCHAR: return "Uint8";
404 case BASE_TYPE_SHORT: return "Int16";
405 case BASE_TYPE_USHORT: return "Uint16";
406 case BASE_TYPE_INT: return "Int32";
407 case BASE_TYPE_UINT: return "Uint32";
408 case BASE_TYPE_LONG: return "Int64";
409 case BASE_TYPE_ULONG: return "Uint64";
410 case BASE_TYPE_FLOAT: return "Float32";
411 case BASE_TYPE_DOUBLE: return "Float64";
412 case BASE_TYPE_STRING: return "String";
413 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
414 case BASE_TYPE_STRUCT: return type.struct_def->name;
415 default: return "Table";
416 }
417 }
418
419 std::string GenGetter(const Type &type, const std::string &arguments) {
420 switch (type.base_type) {
421 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
422 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
Austin Schuh272c6132020-11-14 16:37:52 -0800423 case BASE_TYPE_UNION:
424 if (!UnionHasStringType(*type.enum_def) ||
425 lang_.language == IDLOptions::kJs) {
426 return GenBBAccess() + ".__union" + arguments;
427 }
428 return GenBBAccess() + ".__union_with_string" + arguments;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700429 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
430 default: {
431 auto getter =
432 GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
433 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
434 if (type.enum_def) {
435 getter = "/** " +
436 GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "",
437 false) +
438 " */ (" + getter + ")";
439 }
440 return getter;
441 }
442 }
443 }
444
Austin Schuh272c6132020-11-14 16:37:52 -0800445 std::string GenBBAccess() const {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700446 return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
447 }
448
Austin Schuh272c6132020-11-14 16:37:52 -0800449 std::string GenDefaultValue(const FieldDef &field, const std::string &context) {
450 if (field.IsScalarOptional()) {
451 return "null";
452 }
453
454 const auto &value = field.value;
455 if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
456 value.type.base_type != BASE_TYPE_VECTOR) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700457 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
458 if (lang_.language == IDLOptions::kTs) {
459 return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
460 value.type.enum_def->file) +
461 "." + val->name;
462 } else {
463 return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
464 }
465 } else {
466 return "/** " +
467 GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def),
468 "", false) +
469 "} */ (" + value.constant + ")";
470 }
471 }
472
473 switch (value.type.base_type) {
474 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
475
Austin Schuh272c6132020-11-14 16:37:52 -0800476 case BASE_TYPE_STRING:
477 case BASE_TYPE_UNION:
478 case BASE_TYPE_STRUCT: {
479 return "null";
480 }
481
482 case BASE_TYPE_VECTOR: return "[]";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700483
484 case BASE_TYPE_LONG:
485 case BASE_TYPE_ULONG: {
486 int64_t constant = StringToInt(value.constant.c_str());
487 return context + ".createLong(" +
488 NumToString(static_cast<int32_t>(constant)) + ", " +
489 NumToString(static_cast<int32_t>(constant >> 32)) + ")";
490 }
491
492 default: return value.constant;
493 }
494 }
495
496 std::string GenTypeName(const Type &type, bool input,
497 bool allowNull = false) {
498 if (!input) {
Austin Schuh272c6132020-11-14 16:37:52 -0800499 if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700500 std::string name;
Austin Schuh272c6132020-11-14 16:37:52 -0800501 if (IsString(type)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700502 name = "string|Uint8Array";
503 } else {
504 name = WrapInNameSpace(*type.struct_def);
505 }
506 return (allowNull) ? (name + "|null") : (name);
507 }
508 }
509
510 switch (type.base_type) {
Austin Schuh272c6132020-11-14 16:37:52 -0800511 case BASE_TYPE_BOOL: return (allowNull) ? ("boolean|null") : ("boolean");
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700512 case BASE_TYPE_LONG:
Austin Schuh272c6132020-11-14 16:37:52 -0800513 case BASE_TYPE_ULONG: return (allowNull) ? ("flatbuffers.Long|null") : ("flatbuffers.Long");
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700514 default:
515 if (IsScalar(type.base_type)) {
Austin Schuh272c6132020-11-14 16:37:52 -0800516 if (type.enum_def) {
517 const auto enum_name = WrapInNameSpace(*type.enum_def);
518 return (allowNull) ? (enum_name + "|null") : (enum_name);
519 }
520
521 return (allowNull) ? ("number|null") : ("number");
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700522 }
523 return "flatbuffers.Offset";
524 }
525 }
526
527 // Returns the method name for use with add/put calls.
528 static std::string GenWriteMethod(const Type &type) {
529 // Forward to signed versions since unsigned versions don't exist
530 switch (type.base_type) {
531 case BASE_TYPE_UTYPE:
532 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
533 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
534 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
535 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
536 default: break;
537 }
538
539 return IsScalar(type.base_type) ? MakeCamel(GenType(type))
540 : (IsStruct(type) ? "Struct" : "Offset");
541 }
542
543 template<typename T> static std::string MaybeAdd(T value) {
544 return value != 0 ? " + " + NumToString(value) : "";
545 }
546
547 template<typename T> static std::string MaybeScale(T value) {
548 return value != 1 ? " * " + NumToString(value) : "";
549 }
550
551 static std::string GenFileNamespacePrefix(const std::string &file) {
552 return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str()));
553 }
554
555 std::string GenPrefixedImport(const std::string &full_file_name,
556 const std::string &base_name) {
557 // Either keep the include path as it was
558 // or use only the base_name + kGeneratedFileNamePostfix
559 std::string path;
560 if (parser_.opts.keep_include_path) {
561 auto it = parser_.included_files_.find(full_file_name);
562 FLATBUFFERS_ASSERT(it != parser_.included_files_.end());
Austin Schuh272c6132020-11-14 16:37:52 -0800563 path = flatbuffers::StripExtension(it->second) +
564 parser_.opts.filename_suffix;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700565 } else {
Austin Schuh272c6132020-11-14 16:37:52 -0800566 path = base_name + parser_.opts.filename_suffix;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700567 }
568
569 // Add the include prefix and make the path always relative
570 path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path);
571 path = std::string(".") + kPathSeparator + path;
572
573 return "import * as " + GenFileNamespacePrefix(full_file_name) +
574 " from \"" + path + "\";\n";
575 }
576
577 // Adds a source-dependent prefix, for of import * statements.
578 std::string GenPrefixedTypeName(const std::string &typeName,
579 const std::string &file) {
580 const auto basename =
581 flatbuffers::StripPath(flatbuffers::StripExtension(file));
582 if (basename == file_name_ || parser_.opts.generate_all) {
583 return typeName;
584 }
585 return GenFileNamespacePrefix(file) + "." + typeName;
586 }
587
588 void GenStructArgs(const StructDef &struct_def, std::string *annotations,
589 std::string *arguments, const std::string &nameprefix) {
590 for (auto it = struct_def.fields.vec.begin();
591 it != struct_def.fields.vec.end(); ++it) {
592 auto &field = **it;
593 if (IsStruct(field.value.type)) {
594 // Generate arguments for a struct inside a struct. To ensure names
595 // don't clash, and to make it obvious these arguments are constructing
596 // a nested struct, prefix the name with the field name.
597 GenStructArgs(*field.value.type.struct_def, annotations, arguments,
598 nameprefix + field.name + "_");
599 } else {
600 *annotations +=
Austin Schuh272c6132020-11-14 16:37:52 -0800601 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true, field.optional),
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700602 nameprefix + field.name);
603 if (lang_.language == IDLOptions::kTs) {
604 *arguments += ", " + nameprefix + field.name + ": " +
Austin Schuh272c6132020-11-14 16:37:52 -0800605 GenTypeName(field.value.type, true, field.optional);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700606 } else {
607 *arguments += ", " + nameprefix + field.name;
608 }
609 }
610 }
611 }
612
613 static void GenStructBody(const StructDef &struct_def, std::string *body,
614 const std::string &nameprefix) {
615 *body += " builder.prep(";
616 *body += NumToString(struct_def.minalign) + ", ";
617 *body += NumToString(struct_def.bytesize) + ");\n";
618
619 for (auto it = struct_def.fields.vec.rbegin();
620 it != struct_def.fields.vec.rend(); ++it) {
621 auto &field = **it;
622 if (field.padding) {
623 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
624 }
625 if (IsStruct(field.value.type)) {
626 // Generate arguments for a struct inside a struct. To ensure names
627 // don't clash, and to make it obvious these arguments are constructing
628 // a nested struct, prefix the name with the field name.
629 GenStructBody(*field.value.type.struct_def, body,
630 nameprefix + field.name + "_");
631 } else {
632 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
633 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
634 *body += nameprefix + field.name + ");\n";
635 }
636 }
637 }
638
Austin Schuh272c6132020-11-14 16:37:52 -0800639 std::string GenerateNewExpression(const std::string &object_name) {
640 return "new " + object_name +
641 (lang_.language == IDLOptions::kTs ? "()" : "");
642 }
643
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700644 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
Austin Schuh272c6132020-11-14 16:37:52 -0800645 std::string &code, std::string &object_name,
646 bool size_prefixed) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700647 if (!struct_def.fixed) {
648 GenDocComment(code_ptr,
649 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
650 GenTypeAnnotation(kParam, object_name + "=", "obj") +
651 GenTypeAnnotation(kReturns, object_name, "", false));
652 std::string sizePrefixed("SizePrefixed");
653 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -0800654 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
655 Verbose(struct_def, "As");
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700656 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
657 "):" + object_name + " {\n";
658 } else {
Austin Schuh272c6132020-11-14 16:37:52 -0800659 code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") +
660 "Root" + Verbose(struct_def, "As");
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700661 code += " = function(bb, obj) {\n";
662 }
Austin Schuh272c6132020-11-14 16:37:52 -0800663 if (size_prefixed) {
664 code +=
665 " bb.setPosition(bb.position() + "
666 "flatbuffers.SIZE_PREFIX_LENGTH);\n";
667 }
668 code += " return (obj || " + GenerateNewExpression(object_name);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700669 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
670 code += "};\n\n";
671 }
672 }
673
674 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
Austin Schuh272c6132020-11-14 16:37:52 -0800675 std::string &code, std::string &object_name,
676 bool size_prefixed) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700677 if (parser_.root_struct_def_ == &struct_def) {
678 std::string sizePrefixed("SizePrefixed");
679 GenDocComment(
680 code_ptr,
681 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
Austin Schuh272c6132020-11-14 16:37:52 -0800682 GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset", false));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700683
684 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -0800685 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
686 Verbose(struct_def) + "Buffer";
687 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700688 } else {
Austin Schuh272c6132020-11-14 16:37:52 -0800689 code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") +
690 Verbose(struct_def) + "Buffer";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700691 code += " = function(builder, offset) {\n";
692 }
693
694 code += " builder.finish(offset";
695 if (!parser_.file_identifier_.empty()) {
696 code += ", '" + parser_.file_identifier_ + "'";
697 }
698 if (size_prefixed) {
Austin Schuh272c6132020-11-14 16:37:52 -0800699 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700700 code += ", true";
701 }
702 code += ");\n";
703 code += "};\n\n";
704 }
705 }
706
Austin Schuh272c6132020-11-14 16:37:52 -0800707 static std::string GetObjApiClassName(const StructDef &sd,
708 const IDLOptions &opts) {
709 return GetObjApiClassName(sd.name, opts);
710 }
711
712 static std::string GetObjApiClassName(const std::string &name,
713 const IDLOptions &opts) {
714 return opts.object_prefix + name + opts.object_suffix;
715 }
716
717 bool UnionHasStringType(const EnumDef &union_enum) {
718 return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
719 [](const EnumVal *ev) {
720 return !(ev->IsZero()) &&
721 (IsString(ev->union_type));
722 });
723 }
724
725 std::string GenUnionGenericTypeTS(const EnumDef &union_enum) {
726 return std::string("T") + (UnionHasStringType(union_enum) ? "|string" : "");
727 }
728
729 std::string GenUnionTypeTS(const EnumDef &union_enum,
730 imported_fileset &imported_files) {
731 std::string ret;
732 std::set<std::string> type_list;
733
734 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
735 ++it) {
736 const auto &ev = **it;
737 if (ev.IsZero()) { continue; }
738
739 std::string type = "";
740 if (IsString(ev.union_type)) {
741 type = "string"; // no need to wrap string type in namespace
742 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
743 if (!parser_.opts.generate_all) {
744 imported_files.insert(ev.union_type.struct_def->file);
745 }
746
747 type = GenPrefixedTypeName(WrapInNameSpace(*(ev.union_type.struct_def)),
748 ev.union_type.struct_def->file);
749 } else {
750 FLATBUFFERS_ASSERT(false);
751 }
752 type_list.insert(type);
753 }
754
755 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
756 ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
757 }
758
759 return ret;
760 }
761
762 // Generate a TS union type based on a union's enum
763 std::string GenObjApiUnionTypeTS(const IDLOptions &opts,
764 const EnumDef &union_enum) {
765 std::string ret = "";
766 std::set<std::string> type_list;
767
768 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
769 ++it) {
770 const auto &ev = **it;
771 if (ev.IsZero()) { continue; }
772
773 std::string type = "";
774 if (IsString(ev.union_type)) {
775 type = "string"; // no need to wrap string type in namespace
776 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
777 type = GenPrefixedTypeName(
778 GetObjApiClassName(WrapInNameSpace(*(ev.union_type.struct_def)),
779 opts),
780 union_enum.file);
781 } else {
782 FLATBUFFERS_ASSERT(false);
783 }
784 type_list.insert(type);
785 }
786
787 size_t totalPrinted = 0;
788 for (auto it = type_list.begin(); it != type_list.end(); ++it) {
789 ++totalPrinted;
790 ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
791 }
792
793 return ret;
794 }
795
796 std::string GenUnionConvFuncName(const EnumDef &enum_def) {
797 return "unionTo" + enum_def.name;
798 }
799
800 std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
801 return "unionListTo" + enum_def.name;
802 }
803
804 std::string GenUnionConvFunc(const Type &union_type,
805 imported_fileset &imported_files) {
806 if (union_type.enum_def) {
807 const auto &enum_def = *union_type.enum_def;
808
809 const auto valid_union_type = GenUnionTypeTS(enum_def, imported_files);
810 const auto valid_union_type_with_null = valid_union_type + "|null";
811
812 auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
813 "(\n type: " + enum_def.name +
814 ",\n accessor: (obj:" + valid_union_type + ") => " +
815 valid_union_type_with_null +
816 "\n): " + valid_union_type_with_null + " {\n";
817
818 if (!parser_.opts.generate_all) {
819 imported_files.insert(union_type.enum_def->file);
820 }
821
822 const auto enum_type = GenPrefixedTypeName(
823 WrapInNameSpace(*(union_type.enum_def)), union_type.enum_def->file);
824 const auto &union_enum = *(union_type.enum_def);
825
826 const auto union_enum_loop = [&](const std::string &accessor_str) {
827 ret += " switch(" + enum_type + "[type]) {\n";
828 ret += " case 'NONE': return null; \n";
829
830 for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
831 ++it) {
832 const auto &ev = **it;
833 if (ev.IsZero()) { continue; }
834
835 ret += " case '" + ev.name + "': ";
836
837 if (IsString(ev.union_type)) {
838 ret += "return " + accessor_str + "'') as string;";
839 } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
840 const auto type = GenPrefixedTypeName(
841 WrapInNameSpace(*(ev.union_type.struct_def)),
842 ev.union_type.struct_def->file);
843 ret += "return " + accessor_str + "new " + type + "())! as " +
844 type + ";";
845 } else {
846 FLATBUFFERS_ASSERT(false);
847 }
848 ret += "\n";
849 }
850
851 ret += " default: return null;\n";
852 ret += " }\n";
853 };
854
855 union_enum_loop("accessor(");
856 ret += "}";
857
858 ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
859 "(\n type: " + enum_def.name +
860 ", \n accessor: (index: number, obj:" + valid_union_type +
861 ") => " + valid_union_type_with_null +
862 ", \n index: number\n): " + valid_union_type_with_null + " {\n";
863 union_enum_loop("accessor(index, ");
864 ret += "}";
865
866 return ret;
867 }
868 FLATBUFFERS_ASSERT(0);
869 return "";
870 }
871
872 // Used for generating a short function that returns the correct class
873 // based on union enum type. Assume the context is inside the non object api
874 // type
875 std::string GenUnionValTS(const std::string &field_name,
876 const Type &union_type,
877 const bool is_array = false) {
878 if (union_type.enum_def) {
879 const auto &enum_def = *union_type.enum_def;
880 const auto enum_type =
881 GenPrefixedTypeName(WrapInNameSpace(enum_def), enum_def.file);
882 const std::string union_accessor = "this." + field_name;
883
884 const auto union_has_string = UnionHasStringType(enum_def);
885 const auto field_binded_method = "this." + field_name + ".bind(this)";
886
887 std::string ret;
888
889 if (!is_array) {
890 const auto conversion_function =
891 GenPrefixedTypeName(WrapInNameSpace(enum_def.defined_namespace,
892 GenUnionConvFuncName(enum_def)),
893 enum_def.file);
894 const auto target_enum = "this." + field_name + "Type()";
895
896 ret = "(() => {\n";
897 ret += " let temp = " + conversion_function + "(" + target_enum +
898 ", " + field_binded_method + ");\n";
899 ret += " if(temp === null) { return null; }\n";
900 ret += union_has_string
901 ? " if(typeof temp === 'string') { return temp; }\n"
902 : "";
903 ret += " return temp.unpack()\n";
904 ret += " })()";
905 } else {
906 const auto conversion_function = GenPrefixedTypeName(
907 WrapInNameSpace(enum_def.defined_namespace,
908 GenUnionListConvFuncName(enum_def)),
909 enum_def.file);
910 const auto target_enum_accesor = "this." + field_name + "Type";
911 const auto target_enum_length = target_enum_accesor + "Length()";
912
913 ret = "(() => {\n";
914 ret += " let ret = [];\n";
915 ret += " for(let targetEnumIndex = 0; targetEnumIndex < " +
916 target_enum_length +
917 "; "
918 "++targetEnumIndex) {\n";
919 ret += " let targetEnum = " + target_enum_accesor +
920 "(targetEnumIndex);\n";
921 ret += " if(targetEnum === null || " + enum_type +
922 "[targetEnum!] === 'NONE') { "
923 "continue; }\n\n";
924 ret += " let temp = " + conversion_function + "(targetEnum, " +
925 field_binded_method + ", targetEnumIndex);\n";
926 ret += " if(temp === null) { continue; }\n";
927 ret += union_has_string ? " if(typeof temp === 'string') { "
928 "ret.push(temp); continue; }\n"
929 : "";
930 ret += " ret.push(temp.unpack());\n";
931 ret += " }\n";
932 ret += " return ret;\n";
933 ret += " })()";
934 }
935
936 return ret;
937 }
938
939 FLATBUFFERS_ASSERT(0);
940 return "";
941 }
942
943 static std::string GenNullCheckConditional(const std::string &nullCheckVar,
944 const std::string &trueVal,
945 const std::string &falseVal = "null") {
946 return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal +
947 ")";
948 }
949
950 std::string GenStructMemberValueTS(const StructDef &struct_def,
951 const std::string &prefix,
952 const std::string &delimiter,
953 const bool nullCheck = true) {
954 std::string ret;
955 for (auto it = struct_def.fields.vec.begin();
956 it != struct_def.fields.vec.end(); ++it) {
957 auto &field = **it;
958
959 const auto curr_member_accessor =
960 prefix + "." + MakeCamel(field.name, false);
961 if (IsStruct(field.value.type)) {
962 ret += GenStructMemberValueTS(*field.value.type.struct_def,
963 curr_member_accessor, delimiter);
964 } else {
965 if (nullCheck) {
966 ret +=
967 "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)";
968 } else {
969 ret += curr_member_accessor;
970 }
971 }
972
973 if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; }
974 }
975
976 return ret;
977 }
978
979 void GenObjApi(const Parser &parser, StructDef &struct_def,
980 std::string &obj_api_unpack_func, std::string &obj_api_class,
981 imported_fileset &imported_files) {
982 const auto class_name = GetObjApiClassName(struct_def, parser.opts);
983
984 std::string unpack_func =
985 "\n/**\n * " + GenTypeAnnotation(kReturns, class_name, "") +
986 " */\nunpack(): " + class_name + " {\n return new " + class_name +
987 "(" + (struct_def.fields.vec.empty() ? "" : "\n");
988 std::string unpack_to_func =
989 "/**\n * " + GenTypeAnnotation(kParam, class_name, "_o") +
990 " */\nunpackTo(_o: " + class_name + "): void {" +
991 +(struct_def.fields.vec.empty() ? "" : "\n");
992
993 std::string constructor_annotation = "/**\n * @constructor";
994 constructor_annotation += (struct_def.fields.vec.empty() ? "" : "\n");
995 std::string constructor_func = "constructor(";
996 constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
997
998 const auto has_create =
999 struct_def.fixed || CanCreateFactoryMethod(struct_def);
1000
1001 std::string pack_func_prototype =
1002 "/**\n * " +
1003 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") + " * " +
1004 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "") +
1005 " */\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
1006
1007 std::string pack_func_offset_decl;
1008 std::string pack_func_create_call;
1009
1010 const auto struct_name =
1011 GenPrefixedTypeName(WrapInNameSpace(struct_def), struct_def.file);
1012
1013 if (has_create) {
1014 pack_func_create_call = " return " + struct_name + ".create" +
1015 Verbose(struct_def) + "(builder" +
1016 (struct_def.fields.vec.empty() ? "" : ",\n ");
1017 } else {
1018 pack_func_create_call = " " + struct_name + ".start(builder);\n";
1019 }
1020
1021 if (struct_def.fixed) {
1022 // when packing struct, nested struct's members instead of the struct's
1023 // offset are used
1024 pack_func_create_call +=
1025 GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n ";
1026 }
1027
1028 for (auto it = struct_def.fields.vec.begin();
1029 it != struct_def.fields.vec.end(); ++it) {
1030 auto &field = **it;
1031 if (field.deprecated) continue;
1032
1033 const auto field_name = MakeCamel(field.name, false);
1034 const std::string field_binded_method =
1035 "this." + field_name + ".bind(this)";
1036
1037 std::string field_val;
1038 std::string field_type;
1039 // a string that declares a variable containing the
1040 // offset for things that can't be generated inline
1041 // empty otw
1042 std::string field_offset_decl;
1043 // a string that contains values for things that can be created inline or
1044 // the variable name from field_offset_decl
1045 std::string field_offset_val;
1046 const auto field_default_val =
1047 GenDefaultValue(field, "flatbuffers");
1048
1049 // Emit a scalar field
1050 const auto is_string = IsString(field.value.type);
1051 if (IsScalar(field.value.type.base_type) || is_string) {
1052 const auto has_null_default = is_string || HasNullDefault(field);
1053
1054 if (field.value.type.enum_def) {
1055 if (!parser_.opts.generate_all) {
1056 imported_files.insert(field.value.type.enum_def->file);
1057 }
1058
1059 field_type +=
1060 GenPrefixedTypeName(GenTypeName(field.value.type, false, has_null_default),
1061 field.value.type.enum_def->file);
1062 } else {
1063 field_type += GenTypeName(field.value.type, false, has_null_default);
1064 }
1065 field_val = "this." + field_name + "()";
1066
1067 if (field.value.type.base_type != BASE_TYPE_STRING) {
1068 field_offset_val = "this." + field_name;
1069 } else {
1070 field_offset_decl = GenNullCheckConditional(
1071 "this." + field_name,
1072 "builder.createString(this." + field_name + "!)", "0");
1073 }
1074 }
1075
1076 // Emit an object field
1077 else {
1078 auto is_vector = false;
1079 switch (field.value.type.base_type) {
1080 case BASE_TYPE_STRUCT: {
1081 const auto &sd = *field.value.type.struct_def;
1082 field_type += GenPrefixedTypeName(
1083 WrapInNameSpace(sd.defined_namespace,
1084 GetObjApiClassName(sd, parser.opts)),
1085 field.value.type.struct_def->file);
1086
1087 const std::string field_accessor = "this." + field_name + "()";
1088 field_val = GenNullCheckConditional(field_accessor,
1089 field_accessor + "!.unpack()");
1090 field_offset_val = GenNullCheckConditional(
1091 "this." + field_name, "this." + field_name + "!.pack(builder)",
1092 "0");
1093
1094 break;
1095 }
1096
1097 case BASE_TYPE_VECTOR: {
1098 auto vectortype = field.value.type.VectorType();
1099 auto vectortypename = GenTypeName(vectortype, false);
1100 is_vector = true;
1101
1102 field_type = "(";
1103
1104 switch (vectortype.base_type) {
1105 case BASE_TYPE_STRUCT: {
1106 const auto &sd = *field.value.type.struct_def;
1107 field_type += GenPrefixedTypeName(
1108 WrapInNameSpace(sd.defined_namespace,
1109 GetObjApiClassName(sd, parser.opts)),
1110 field.value.type.struct_def->file);
1111 field_type += ")[]";
1112
1113 field_val = GenBBAccess() + ".createObjList(" +
1114 field_binded_method + ", this." + field_name +
1115 "Length())";
1116
1117 if (sd.fixed) {
1118 field_offset_decl =
1119 "builder.createStructOffsetList(this." + field_name +
1120 ", " +
1121 GenPrefixedTypeName(WrapInNameSpace(struct_def),
1122 struct_def.file) +
1123 ".start" + MakeCamel(field_name) + "Vector)";
1124 } else {
1125 field_offset_decl =
1126 GenPrefixedTypeName(WrapInNameSpace(struct_def),
1127 struct_def.file) +
1128 ".create" + MakeCamel(field_name) +
1129 "Vector(builder, builder.createObjectOffsetList(" +
1130 "this." + field_name + "))";
1131 }
1132
1133 break;
1134 }
1135
1136 case BASE_TYPE_STRING: {
1137 field_type += "string)[]";
1138 field_val = GenBBAccess() + ".createStringList(" +
1139 field_binded_method + ", this." + field_name +
1140 "Length())";
1141 field_offset_decl =
1142 GenPrefixedTypeName(WrapInNameSpace(struct_def),
1143 struct_def.file) +
1144 ".create" + MakeCamel(field_name) +
1145 "Vector(builder, builder.createObjectOffsetList(" +
1146 "this." + field_name + "))";
1147 break;
1148 }
1149
1150 case BASE_TYPE_UNION: {
1151 field_type +=
1152 GenObjApiUnionTypeTS(parser.opts, *(vectortype.enum_def));
1153 field_type += ")[]";
1154 field_val = GenUnionValTS(field_name, vectortype, true);
1155
1156 field_offset_decl =
1157 GenPrefixedTypeName(WrapInNameSpace(struct_def),
1158 struct_def.file) +
1159 ".create" + MakeCamel(field_name) +
1160 "Vector(builder, builder.createObjectOffsetList(" +
1161 "this." + field_name + "))";
1162
1163 break;
1164 }
1165 default: {
1166 if (vectortype.enum_def) {
1167 if (!parser_.opts.generate_all) {
1168 imported_files.insert(vectortype.enum_def->file);
1169 }
1170
1171 field_type +=
1172 GenPrefixedTypeName(GenTypeName(vectortype, false, HasNullDefault(field)),
1173 vectortype.enum_def->file);
1174 } else {
1175 field_type += vectortypename;
1176 }
1177 field_type += ")[]";
1178 field_val = GenBBAccess() + ".createScalarList(" +
1179 field_binded_method + ", this." + field_name +
1180 "Length())";
1181
1182 field_offset_decl =
1183 GenPrefixedTypeName(WrapInNameSpace(struct_def),
1184 struct_def.file) +
1185 ".create" + MakeCamel(field_name) +
1186 "Vector(builder, this." + field_name + ")";
1187
1188 break;
1189 }
1190 }
1191
1192 break;
1193 }
1194
1195 case BASE_TYPE_UNION: {
1196 if (!parser_.opts.generate_all) {
1197 imported_files.insert(field.value.type.enum_def->file);
1198 }
1199
1200 field_type +=
1201 GenObjApiUnionTypeTS(parser.opts, *(field.value.type.enum_def));
1202
1203 field_val = GenUnionValTS(field_name, field.value.type);
1204 field_offset_decl =
1205 "builder.createObjectOffset(this." + field_name + ")";
1206 break;
1207 }
1208
1209 default: FLATBUFFERS_ASSERT(0); break;
1210 }
1211
1212 // length 0 vector is simply empty instead of null
1213 field_type += is_vector ? "" : "|null";
1214 }
1215
1216 if (!field_offset_decl.empty()) {
1217 field_offset_decl =
1218 " const " + field_name + " = " + field_offset_decl + ";";
1219 }
1220 if (field_offset_val.empty()) { field_offset_val = field_name; }
1221
1222 unpack_func += " " + field_val;
1223 unpack_to_func += " _o." + field_name + " = " + field_val + ";";
1224
1225 constructor_annotation +=
1226 " * " + GenTypeAnnotation(kParam, field_type, field_name, false);
1227 constructor_func += " public " + field_name + ": " + field_type + " = " +
1228 field_default_val;
1229
1230 if (!struct_def.fixed) {
1231 if (!field_offset_decl.empty()) {
1232 pack_func_offset_decl += field_offset_decl + "\n";
1233 }
1234
1235 if (has_create) {
1236 pack_func_create_call += field_offset_val;
1237 } else {
1238 pack_func_create_call += " " + struct_name + ".add" +
1239 MakeCamel(field.name) + "(builder, " +
1240 field_offset_val + ");\n";
1241 }
1242 }
1243
1244 if (std::next(it) != struct_def.fields.vec.end()) {
1245 constructor_annotation += "\n";
1246 constructor_func += ",\n";
1247
1248 if (!struct_def.fixed && has_create) {
1249 pack_func_create_call += ",\n ";
1250 }
1251
1252 unpack_func += ",\n";
1253 unpack_to_func += "\n";
1254 } else {
1255 constructor_func += "\n";
1256 if (!struct_def.fixed) {
1257 pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
1258 pack_func_create_call += "\n ";
1259 }
1260
1261 unpack_func += "\n ";
1262 unpack_to_func += "\n";
1263 }
1264 }
1265
1266 constructor_annotation += "\n */\n";
1267 constructor_func += "){};\n\n";
1268
1269 if (has_create) {
1270 pack_func_create_call += ");";
1271 } else {
1272 pack_func_create_call += "return " + struct_name + ".end(builder);";
1273 }
1274
1275 obj_api_class = "\nexport class " +
1276 GetObjApiClassName(struct_def, parser.opts) + " {\n";
1277
1278 obj_api_class += constructor_annotation + constructor_func;
1279
1280 obj_api_class += pack_func_prototype + pack_func_offset_decl +
1281 pack_func_create_call + "\n};";
1282
1283 obj_api_class += "\n}\n";
1284
1285 unpack_func += ");\n};";
1286 unpack_to_func += "};\n";
1287
1288 obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
1289 }
1290
1291 static bool CanCreateFactoryMethod(const StructDef &struct_def) {
1292 // to preserve backwards compatibility, we allow the first field to be a
1293 // struct
1294 return struct_def.fields.vec.size() < 2 ||
1295 std::all_of(std::begin(struct_def.fields.vec) + 1,
1296 std::end(struct_def.fields.vec),
1297 [](const FieldDef *f) -> bool {
1298 FLATBUFFERS_ASSERT(f != nullptr);
1299 return f->value.type.base_type != BASE_TYPE_STRUCT;
1300 });
1301 }
1302
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001303 // Generate an accessor struct with constructor for a flatbuffers struct.
1304 void GenStruct(const Parser &parser, StructDef &struct_def,
1305 std::string *code_ptr, std::string *exports_ptr,
1306 imported_fileset &imported_files) {
1307 if (struct_def.generated) return;
1308 std::string &code = *code_ptr;
1309 std::string &exports = *exports_ptr;
1310
1311 std::string object_name;
1312 std::string object_namespace = GetNameSpace(struct_def);
1313
1314 // Emit constructor
1315 if (lang_.language == IDLOptions::kTs) {
1316 object_name = struct_def.name;
1317 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
1318 if (!object_namespace.empty()) {
1319 code += "export namespace " + object_namespace + "{\n";
1320 }
1321 code += "export class " + struct_def.name;
1322 code += " {\n";
1323 if (lang_.language != IDLOptions::kTs) {
1324 code += " /**\n";
Austin Schuh272c6132020-11-14 16:37:52 -08001325 code +=
1326 " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001327 code += " */\n";
1328 }
1329 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
1330 code += "\n";
1331 if (lang_.language != IDLOptions::kTs) {
1332 code += " /**\n";
1333 code += " * " + GenTypeAnnotation(kType, "number", "");
1334 code += " */\n";
1335 }
1336 code += " bb_pos:number = 0;\n";
1337 } else {
1338 bool isStatement = struct_def.defined_namespace->components.empty();
1339 object_name = WrapInNameSpace(struct_def);
1340 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
1341 if (isStatement) {
1342 if (parser_.opts.use_goog_js_export_format) {
1343 exports += "goog.exportSymbol('" + struct_def.name + "', " +
1344 struct_def.name + ");\n";
1345 } else if (parser_.opts.use_ES6_js_export_format) {
1346 exports += "export {" + struct_def.name + "};\n";
1347 } else {
1348 exports +=
1349 "this." + struct_def.name + " = " + struct_def.name + ";\n";
1350 }
1351 code += "function " + object_name;
1352 } else {
1353 code += object_name + " = function";
1354 }
1355 code += "() {\n";
1356 code += " /**\n";
1357 code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
1358 code += " */\n";
1359 code += " this.bb = null;\n";
1360 code += "\n";
1361 code += " /**\n";
1362 code += " * " + GenTypeAnnotation(kType, "number", "");
1363 code += " */\n";
1364 code += " this.bb_pos = 0;\n";
1365 code += isStatement ? "}\n\n" : "};\n\n";
1366 }
1367
1368 // Generate the __init method that sets the field in a pre-existing
1369 // accessor object. This is to allow object reuse.
1370 code += "/**\n";
1371 code += " * " + GenTypeAnnotation(kParam, "number", "i");
1372 code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
1373 code += " * " + GenTypeAnnotation(kReturns, object_name, "");
1374 code += " */\n";
1375
1376 if (lang_.language == IDLOptions::kTs) {
1377 code +=
1378 "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
1379 } else {
1380 code += object_name + ".prototype.__init = function(i, bb) {\n";
1381 }
1382
1383 code += " this.bb_pos = i;\n";
1384 code += " this.bb = bb;\n";
1385 code += " return this;\n";
1386 code += "};\n\n";
1387
1388 // Generate special accessors for the table that when used as the root of a
1389 // FlatBuffer
1390 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
1391 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
1392
1393 // Generate the identifier check method
1394 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
1395 !parser_.file_identifier_.empty()) {
Austin Schuh272c6132020-11-14 16:37:52 -08001396 GenDocComment(code_ptr,
1397 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
1398 GenTypeAnnotation(kReturns, "boolean", "", false));
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001399 if (lang_.language == IDLOptions::kTs) {
1400 code +=
1401 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
1402 "{\n";
1403 } else {
1404 code += object_name + ".bufferHasIdentifier = function(bb) {\n";
1405 }
1406
1407 code += " return bb.__has_identifier('" + parser_.file_identifier_;
1408 code += "');\n};\n\n";
1409 }
1410
1411 // Emit field accessors
1412 for (auto it = struct_def.fields.vec.begin();
1413 it != struct_def.fields.vec.end(); ++it) {
1414 auto &field = **it;
1415 if (field.deprecated) continue;
1416 auto offset_prefix =
1417 " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1418 NumToString(field.value.offset) + ");\n return offset ? ";
1419
1420 // Emit a scalar field
Austin Schuh272c6132020-11-14 16:37:52 -08001421 const auto is_string = IsString(field.value.type);
1422 if (IsScalar(field.value.type.base_type) || is_string) {
1423 const auto has_null_default = is_string || HasNullDefault(field);
1424
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001425 GenDocComment(
1426 field.doc_comment, code_ptr,
Austin Schuh272c6132020-11-14 16:37:52 -08001427 std::string(is_string
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001428 ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
1429 "optionalEncoding")
1430 : "") +
1431 GenTypeAnnotation(kReturns,
Austin Schuh272c6132020-11-14 16:37:52 -08001432 GenTypeName(field.value.type, false, has_null_default),
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001433 "", false));
1434 if (lang_.language == IDLOptions::kTs) {
1435 std::string prefix = MakeCamel(field.name, false) + "(";
Austin Schuh272c6132020-11-14 16:37:52 -08001436 if (is_string) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001437 code += prefix + "):string|null\n";
1438 code += prefix + "optionalEncoding:flatbuffers.Encoding" +
1439 "):" + GenTypeName(field.value.type, false, true) + "\n";
1440 code += prefix + "optionalEncoding?:any";
1441 } else {
1442 code += prefix;
1443 }
1444 if (field.value.type.enum_def) {
1445 code +=
1446 "):" +
Austin Schuh272c6132020-11-14 16:37:52 -08001447 GenPrefixedTypeName(GenTypeName(field.value.type, false, field.optional),
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001448 field.value.type.enum_def->file) +
1449 " {\n";
1450
1451 if (!parser_.opts.generate_all) {
1452 imported_files.insert(field.value.type.enum_def->file);
1453 }
1454 } else {
Austin Schuh272c6132020-11-14 16:37:52 -08001455 code += "):" + GenTypeName(field.value.type, false, has_null_default) + " {\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001456 }
1457 } else {
1458 code += object_name + ".prototype." + MakeCamel(field.name, false);
1459 code += " = function(";
Austin Schuh272c6132020-11-14 16:37:52 -08001460 if (is_string) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001461 code += "optionalEncoding";
1462 }
1463 code += ") {\n";
1464 }
1465
1466 if (struct_def.fixed) {
1467 code +=
1468 " return " +
1469 GenGetter(field.value.type,
1470 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
1471 ";\n";
1472 } else {
1473 std::string index = "this.bb_pos + offset";
Austin Schuh272c6132020-11-14 16:37:52 -08001474 if (is_string) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001475 index += ", optionalEncoding";
1476 }
1477 code += offset_prefix +
1478 GenGetter(field.value.type, "(" + index + ")") + " : " +
Austin Schuh272c6132020-11-14 16:37:52 -08001479 GenDefaultValue(field, GenBBAccess());
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001480 code += ";\n";
1481 }
1482 }
1483
1484 // Emit an object field
1485 else {
1486 switch (field.value.type.base_type) {
1487 case BASE_TYPE_STRUCT: {
1488 auto type = WrapInNameSpace(*field.value.type.struct_def);
1489 GenDocComment(
1490 field.doc_comment, code_ptr,
1491 GenTypeAnnotation(kParam, type + "=", "obj") +
1492 GenTypeAnnotation(kReturns, type + "|null", "", false));
1493 if (lang_.language == IDLOptions::kTs) {
1494 type =
1495 GenPrefixedTypeName(type, field.value.type.struct_def->file);
1496 code += MakeCamel(field.name, false);
1497 code += "(obj?:" + type + "):" + type + "|null {\n";
1498 } else {
1499 code +=
1500 object_name + ".prototype." + MakeCamel(field.name, false);
1501 code += " = function(obj) {\n";
1502 }
1503
1504 if (struct_def.fixed) {
Austin Schuh272c6132020-11-14 16:37:52 -08001505 code += " return (obj || " + GenerateNewExpression(type);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001506 code += ").__init(this.bb_pos";
1507 code +=
1508 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
1509 } else {
Austin Schuh272c6132020-11-14 16:37:52 -08001510 code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1511 ").__init(";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001512 code += field.value.type.struct_def->fixed
1513 ? "this.bb_pos + offset"
1514 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
1515 code += ", " + GenBBAccess() + ") : null;\n";
1516 }
1517
Austin Schuh272c6132020-11-14 16:37:52 -08001518 if (lang_.language == IDLOptions::kTs &&
1519 !parser_.opts.generate_all) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001520 imported_files.insert(field.value.type.struct_def->file);
1521 }
1522
1523 break;
1524 }
1525
1526 case BASE_TYPE_VECTOR: {
1527 auto vectortype = field.value.type.VectorType();
1528 auto vectortypename = GenTypeName(vectortype, false);
Austin Schuh272c6132020-11-14 16:37:52 -08001529
1530 if (vectortype.enum_def) {
1531 vectortypename = GenPrefixedTypeName(vectortypename,
1532 vectortype.enum_def->file);
1533 }
1534
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001535 auto inline_size = InlineSize(vectortype);
1536 auto index = GenBBAccess() +
1537 ".__vector(this.bb_pos + offset) + index" +
1538 MaybeScale(inline_size);
1539 std::string args = GenTypeAnnotation(kParam, "number", "index");
1540 std::string ret_type;
1541 bool is_union = false;
1542 switch (vectortype.base_type) {
1543 case BASE_TYPE_STRUCT:
1544 args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
1545 ret_type = vectortypename;
1546 break;
1547 case BASE_TYPE_STRING:
1548 args += GenTypeAnnotation(
1549 kParam, "flatbuffers.Encoding=", "optionalEncoding");
1550 ret_type = vectortypename;
1551 break;
1552 case BASE_TYPE_UNION:
1553 args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
1554 ret_type = "?flatbuffers.Table";
1555 is_union = true;
1556 break;
1557 default: ret_type = vectortypename;
1558 }
1559 GenDocComment(
1560 field.doc_comment, code_ptr,
1561 args + GenTypeAnnotation(kReturns, ret_type, "", false));
1562 if (lang_.language == IDLOptions::kTs) {
1563 std::string prefix = MakeCamel(field.name, false);
1564 if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
1565 prefix += "(index: number";
1566 if (is_union) {
Austin Schuh272c6132020-11-14 16:37:52 -08001567 const auto union_type =
1568 GenUnionGenericTypeTS(*(field.value.type.enum_def));
1569
1570 vectortypename = union_type;
1571 code += prefix + ", obj:" + union_type;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001572 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
1573 vectortypename = GenPrefixedTypeName(
1574 vectortypename, vectortype.struct_def->file);
1575 code += prefix + ", obj?:" + vectortypename;
1576
1577 if (!parser_.opts.generate_all) {
1578 imported_files.insert(vectortype.struct_def->file);
1579 }
Austin Schuh272c6132020-11-14 16:37:52 -08001580 } else if (IsString(vectortype)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001581 code += prefix + "):string\n";
1582 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
1583 "):" + vectortypename + "\n";
1584 code += prefix + ",optionalEncoding?:any";
1585 } else {
1586 code += prefix;
Austin Schuh272c6132020-11-14 16:37:52 -08001587
1588 if (vectortype.enum_def && !parser_.opts.generate_all) {
1589 imported_files.insert(vectortype.enum_def->file);
1590 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001591 }
1592 code += "):" + vectortypename + "|null {\n";
1593 } else {
1594 code +=
1595 object_name + ".prototype." + MakeCamel(field.name, false);
1596 code += " = function(index";
1597 if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
1598 code += ", obj";
Austin Schuh272c6132020-11-14 16:37:52 -08001599 } else if (IsString(vectortype)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001600 code += ", optionalEncoding";
1601 }
1602 code += ") {\n";
1603 }
1604
1605 if (vectortype.base_type == BASE_TYPE_STRUCT) {
Austin Schuh272c6132020-11-14 16:37:52 -08001606 code += offset_prefix + "(obj || " +
1607 GenerateNewExpression(vectortypename);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001608 code += ").__init(";
1609 code += vectortype.struct_def->fixed
1610 ? index
1611 : GenBBAccess() + ".__indirect(" + index + ")";
1612 code += ", " + GenBBAccess() + ")";
1613 } else {
1614 if (is_union) {
1615 index = "obj, " + index;
Austin Schuh272c6132020-11-14 16:37:52 -08001616 } else if (IsString(vectortype)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001617 index += ", optionalEncoding";
1618 }
1619 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1620 }
1621 code += " : ";
1622 if (field.value.type.element == BASE_TYPE_BOOL) {
1623 code += "false";
1624 } else if (field.value.type.element == BASE_TYPE_LONG ||
1625 field.value.type.element == BASE_TYPE_ULONG) {
1626 code += GenBBAccess() + ".createLong(0, 0)";
1627 } else if (IsScalar(field.value.type.element)) {
1628 if (field.value.type.enum_def) {
1629 code += "/** " +
1630 GenTypeAnnotation(
1631 kType, WrapInNameSpace(*field.value.type.enum_def),
1632 "", false) +
1633 " */ (" + field.value.constant + ")";
1634 } else {
1635 code += "0";
1636 }
1637 } else {
1638 code += "null";
1639 }
1640 code += ";\n";
1641 break;
1642 }
1643
1644 case BASE_TYPE_UNION:
1645 GenDocComment(
1646 field.doc_comment, code_ptr,
1647 GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
1648 GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
1649 false));
1650 if (lang_.language == IDLOptions::kTs) {
1651 code += MakeCamel(field.name, false);
Austin Schuh272c6132020-11-14 16:37:52 -08001652
1653 const auto &union_enum = *(field.value.type.enum_def);
1654 const auto union_type = GenUnionGenericTypeTS(union_enum);
1655 code += "<T extends flatbuffers.Table>(obj:" + union_type +
1656 "):" + union_type +
1657 "|null "
1658 "{\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001659 } else {
1660 code +=
1661 object_name + ".prototype." + MakeCamel(field.name, false);
1662 code += " = function(obj) {\n";
1663 }
1664
1665 code += offset_prefix +
1666 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1667 " : null;\n";
1668 break;
1669
1670 default: FLATBUFFERS_ASSERT(0);
1671 }
1672 }
1673 code += "};\n\n";
1674
1675 if (parser_.opts.use_goog_js_export_format) {
1676 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1677 MakeCamel(field.name, false) + "', " + object_name +
1678 ".prototype." + MakeCamel(field.name, false) + ");\n";
1679 }
1680
1681 // Adds the mutable scalar value to the output
Austin Schuh272c6132020-11-14 16:37:52 -08001682 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1683 !IsUnion(field.value.type)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001684 std::string annotations = GenTypeAnnotation(
1685 kParam, GenTypeName(field.value.type, true), "value");
1686 GenDocComment(
1687 code_ptr,
1688 annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
1689
1690 if (lang_.language == IDLOptions::kTs) {
1691 std::string type;
1692 if (field.value.type.enum_def) {
Austin Schuh272c6132020-11-14 16:37:52 -08001693 if (!parser_.opts.generate_all) {
1694 imported_files.insert(field.value.type.enum_def->file);
1695 }
1696
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001697 type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
1698 field.value.type.enum_def->file);
1699 } else {
1700 type = GenTypeName(field.value.type, true);
1701 }
1702
1703 code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1704 } else {
1705 code += object_name + ".prototype.mutate_" + field.name +
1706 " = function(value) {\n";
1707 }
1708
Austin Schuh272c6132020-11-14 16:37:52 -08001709 if (struct_def.fixed) {
1710 code += " " + GenBBAccess() + ".write" +
1711 MakeCamel(GenType(field.value.type)) + "(this.bb_pos + " +
1712 NumToString(field.value.offset) + ", ";
1713 } else {
1714 code += " var offset = " + GenBBAccess() +
1715 ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
1716 ");\n\n";
1717 code += " if (offset === 0) {\n";
1718 code += " return false;\n";
1719 code += " }\n\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001720
Austin Schuh272c6132020-11-14 16:37:52 -08001721 // special case for bools, which are treated as uint8
1722 code += " " + GenBBAccess() + ".write" +
1723 MakeCamel(GenType(field.value.type)) +
1724 "(this.bb_pos + offset, ";
1725 if (field.value.type.base_type == BASE_TYPE_BOOL &&
1726 lang_.language == IDLOptions::kTs) {
1727 code += "+";
1728 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001729 }
1730
1731 code += "value);\n";
1732 code += " return true;\n";
1733 code += "};\n\n";
1734
1735 if (parser_.opts.use_goog_js_export_format) {
1736 exports += "goog.exportProperty(" + object_name +
1737 ".prototype, 'mutate_" + field.name + "', " + object_name +
1738 ".prototype.mutate_" + field.name + ");\n";
1739 }
1740 }
1741
1742 // Emit vector helpers
Austin Schuh272c6132020-11-14 16:37:52 -08001743 if (IsVector(field.value.type)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001744 // Emit a length helper
1745 GenDocComment(code_ptr,
1746 GenTypeAnnotation(kReturns, "number", "", false));
1747 if (lang_.language == IDLOptions::kTs) {
1748 code += MakeCamel(field.name, false);
1749 code += "Length():number {\n" + offset_prefix;
1750 } else {
1751 code += object_name + ".prototype." + MakeCamel(field.name, false);
1752 code += "Length = function() {\n" + offset_prefix;
1753 }
1754
1755 code +=
1756 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
1757
1758 if (parser_.opts.use_goog_js_export_format) {
1759 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1760 MakeCamel(field.name, false) + "Length', " + object_name +
1761 ".prototype." + MakeCamel(field.name, false) +
1762 "Length);\n";
1763 }
1764
1765 // For scalar types, emit a typed array helper
1766 auto vectorType = field.value.type.VectorType();
1767 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1768 GenDocComment(code_ptr, GenTypeAnnotation(
1769 kReturns, GenType(vectorType) + "Array",
1770 "", false));
1771
1772 if (lang_.language == IDLOptions::kTs) {
1773 code += MakeCamel(field.name, false);
1774 code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1775 offset_prefix;
1776 } else {
1777 code += object_name + ".prototype." + MakeCamel(field.name, false);
1778 code += "Array = function() {\n" + offset_prefix;
1779 }
1780
1781 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1782 ".bytes().buffer, " + GenBBAccess() +
1783 ".bytes().byteOffset + " + GenBBAccess() +
1784 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1785 ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
1786
1787 if (parser_.opts.use_goog_js_export_format) {
1788 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1789 MakeCamel(field.name, false) + "Array', " + object_name +
1790 ".prototype." + MakeCamel(field.name, false) +
1791 "Array);\n";
1792 }
1793 }
1794 }
1795 }
1796
Austin Schuh272c6132020-11-14 16:37:52 -08001797 // Emit the fully qualified name
1798 if (parser_.opts.generate_name_strings) {
1799 GenDocComment(code_ptr, GenTypeAnnotation(kReturns, "string", "", false));
1800 if (lang_.language == IDLOptions::kTs) {
1801 code += "static getFullyQualifiedName():string {\n";
1802 } else {
1803 code += object_name + ".getFullyQualifiedName = function() {\n";
1804 }
1805 code += " return '" + WrapInNameSpace(struct_def) + "';\n";
1806 code += "}\n\n";
1807 }
1808
1809 // Emit the size of the struct.
1810 if (struct_def.fixed) {
1811 GenDocComment(code_ptr, GenTypeAnnotation(kReturns, "number", "", false));
1812 if (lang_.language == IDLOptions::kTs) {
1813 code += "static sizeOf():number {\n";
1814 } else {
1815 code += object_name + ".sizeOf = function() {\n";
1816 }
1817 code += " return " + NumToString(struct_def.bytesize) + ";\n";
1818 code += "}\n\n";
1819 }
1820
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001821 // Emit a factory constructor
1822 if (struct_def.fixed) {
1823 std::string annotations =
1824 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1825 std::string arguments;
1826 GenStructArgs(struct_def, &annotations, &arguments, "");
1827 GenDocComment(code_ptr, annotations + GenTypeAnnotation(
1828 kReturns, "flatbuffers.Offset",
1829 "", false));
1830
1831 if (lang_.language == IDLOptions::kTs) {
1832 code += "static create" + Verbose(struct_def) +
1833 "(builder:flatbuffers.Builder";
1834 code += arguments + "):flatbuffers.Offset {\n";
1835 } else {
1836 code += object_name + ".create" + Verbose(struct_def);
1837 code += " = function(builder";
1838 code += arguments + ") {\n";
1839 }
1840
1841 GenStructBody(struct_def, &code, "");
1842 code += " return builder.offset();\n};\n\n";
1843 } else {
1844 // Generate a method to start building a new object
1845 GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
1846 "builder", false));
1847
1848 if (lang_.language == IDLOptions::kTs) {
1849 code += "static start" + Verbose(struct_def) +
1850 "(builder:flatbuffers.Builder) {\n";
1851 } else {
1852 code += object_name + ".start" + Verbose(struct_def);
1853 code += " = function(builder) {\n";
1854 }
1855
1856 code += " builder.startObject(" +
1857 NumToString(struct_def.fields.vec.size()) + ");\n";
1858 code += "};\n\n";
1859
1860 // Generate a set of static methods that allow table construction
1861 for (auto it = struct_def.fields.vec.begin();
1862 it != struct_def.fields.vec.end(); ++it) {
1863 auto &field = **it;
1864 if (field.deprecated) continue;
1865 const auto argname = GetArgName(field);
1866
1867 // Generate the field insertion method
1868 GenDocComment(
1869 code_ptr,
1870 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1871 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
1872 argname, false));
1873
1874 if (lang_.language == IDLOptions::kTs) {
1875 code += "static add" + MakeCamel(field.name);
1876 code += "(builder:flatbuffers.Builder, " + argname + ":" +
Austin Schuh272c6132020-11-14 16:37:52 -08001877 GetArgType(field, false) + ") {\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001878 } else {
1879 code += object_name + ".add" + MakeCamel(field.name);
1880 code += " = function(builder, " + argname + ") {\n";
1881 }
1882
1883 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1884 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1885 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1886 code += argname + ", ";
1887 if (!IsScalar(field.value.type.base_type)) {
1888 code += "0";
Austin Schuh272c6132020-11-14 16:37:52 -08001889 } else if (HasNullDefault(field)) {
1890 if (IsLong(field.value.type.base_type)) {
1891 code += "builder.createLong(0, 0)";
1892 } else {
1893 code += "0";
1894 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001895 } else {
1896 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
Austin Schuh272c6132020-11-14 16:37:52 -08001897 code += GenDefaultValue(field, "builder");
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001898 }
1899 code += ");\n};\n\n";
1900
Austin Schuh272c6132020-11-14 16:37:52 -08001901 if (IsVector(field.value.type)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001902 auto vector_type = field.value.type.VectorType();
1903 auto alignment = InlineAlignment(vector_type);
1904 auto elem_size = InlineSize(vector_type);
1905
1906 // Generate a method to create a vector from a JavaScript array
1907 if (!IsStruct(vector_type)) {
1908 GenDocComment(
1909 code_ptr,
1910 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1911 GenTypeAnnotation(
1912 kParam,
1913 "Array.<" + GenTypeName(vector_type, true) + ">",
1914 "data") +
1915 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
1916 false));
1917
1918 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -08001919 const std::string sig_begin =
1920 "static create" + MakeCamel(field.name) +
1921 "Vector(builder:flatbuffers.Builder, data:";
1922 const std::string sig_end = "):flatbuffers.Offset";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001923 std::string type = GenTypeName(vector_type, true) + "[]";
Austin Schuh272c6132020-11-14 16:37:52 -08001924 if (type == "number[]") {
1925 const auto &array_type = GenType(vector_type);
1926 // the old type should be deprecated in the future
1927 std::string type_old = "number[]|Uint8Array";
1928 std::string type_new = "number[]|" + array_type + "Array";
1929 if (type_old == type_new) {
1930 type = type_new;
1931 } else {
1932 // add function overloads
1933 code += sig_begin + type_new + sig_end + ";\n";
1934 code +=
1935 "/**\n * @deprecated This Uint8Array overload will "
1936 "be removed in the future.\n */\n";
1937 code += sig_begin + type_old + sig_end + ";\n";
1938 type = type_new + "|Uint8Array";
1939 }
1940 } else {
1941 if (vector_type.enum_def) {
1942 if (!parser_.opts.generate_all) {
1943 imported_files.insert(vector_type.enum_def->file);
1944 }
1945
1946 type = GenPrefixedTypeName(type, vector_type.enum_def->file);
1947 }
1948 }
1949 code += sig_begin + type + sig_end + " {\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001950 } else {
1951 code += object_name + ".create" + MakeCamel(field.name);
1952 code += "Vector = function(builder, data) {\n";
1953 }
1954
1955 code += " builder.startVector(" + NumToString(elem_size);
1956 code += ", data.length, " + NumToString(alignment) + ");\n";
1957 code += " for (var i = data.length - 1; i >= 0; i--) {\n";
1958 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1959 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1960 code += "data[i]);\n";
1961 code += " }\n";
1962 code += " return builder.endVector();\n";
1963 code += "};\n\n";
1964 }
1965
1966 // Generate a method to start a vector, data to be added manually
1967 // after
1968 GenDocComment(
1969 code_ptr,
1970 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1971 GenTypeAnnotation(kParam, "number", "numElems", false));
1972
1973 if (lang_.language == IDLOptions::kTs) {
1974 code += "static start" + MakeCamel(field.name);
1975 code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1976 } else {
1977 code += object_name + ".start" + MakeCamel(field.name);
1978 code += "Vector = function(builder, numElems) {\n";
1979 }
1980
1981 code += " builder.startVector(" + NumToString(elem_size);
1982 code += ", numElems, " + NumToString(alignment) + ");\n";
1983 code += "};\n\n";
1984 }
1985 }
1986
1987 // Generate a method to stop building a new object
1988 GenDocComment(
1989 code_ptr,
1990 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1991 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
1992
1993 if (lang_.language == IDLOptions::kTs) {
1994 code += "static end" + Verbose(struct_def);
1995 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1996 } else {
1997 code += object_name + ".end" + Verbose(struct_def);
1998 code += " = function(builder) {\n";
1999 }
2000
2001 code += " var offset = builder.endObject();\n";
2002 for (auto it = struct_def.fields.vec.begin();
2003 it != struct_def.fields.vec.end(); ++it) {
2004 auto &field = **it;
2005 if (!field.deprecated && field.required) {
2006 code += " builder.requiredField(offset, ";
2007 code += NumToString(field.value.offset);
2008 code += "); // " + field.name + "\n";
2009 }
2010 }
2011 code += " return offset;\n";
2012 code += "};\n\n";
2013
2014 // Generate the methods to complete buffer construction
2015 GenerateFinisher(struct_def, code_ptr, code, object_name, false);
2016 GenerateFinisher(struct_def, code_ptr, code, object_name, true);
2017
2018 // Generate a convenient CreateX function
Austin Schuh272c6132020-11-14 16:37:52 -08002019 if (CanCreateFactoryMethod(struct_def)) {
2020 if (lang_.language == IDLOptions::kJs) {
2021 std::string paramDoc =
2022 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
2023 for (auto it = struct_def.fields.vec.begin();
2024 it != struct_def.fields.vec.end(); ++it) {
2025 const auto &field = **it;
2026 if (field.deprecated) continue;
2027 paramDoc +=
2028 GenTypeAnnotation(kParam, GetArgType(field, true), GetArgName(field));
2029 }
2030 paramDoc +=
2031 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false);
2032
2033 GenDocComment(code_ptr, paramDoc);
2034 }
2035
2036 if (lang_.language == IDLOptions::kTs) {
2037 code += "static create" + Verbose(struct_def);
2038 code += "(builder:flatbuffers.Builder";
2039 } else {
2040 code += object_name + ".create" + Verbose(struct_def);
2041 code += " = function(builder";
2042 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002043 for (auto it = struct_def.fields.vec.begin();
2044 it != struct_def.fields.vec.end(); ++it) {
2045 const auto &field = **it;
Austin Schuh272c6132020-11-14 16:37:52 -08002046 if (field.deprecated) continue;
2047
2048 if (lang_.language == IDLOptions::kTs) {
2049 code += ", " + GetArgName(field) + ":" + GetArgType(field, true);
2050 } else {
2051 code += ", " + GetArgName(field);
2052 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002053 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002054
2055 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -08002056 code += "):flatbuffers.Offset {\n";
2057 code += " " + struct_def.name + ".start" + Verbose(struct_def) +
2058 "(builder);\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002059 } else {
Austin Schuh272c6132020-11-14 16:37:52 -08002060 code += ") {\n";
2061 code += " " + object_name + ".start" + Verbose(struct_def) +
2062 "(builder);\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002063 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002064
Austin Schuh272c6132020-11-14 16:37:52 -08002065 std::string methodPrefix =
2066 lang_.language == IDLOptions::kTs ? struct_def.name : object_name;
2067 for (auto it = struct_def.fields.vec.begin();
2068 it != struct_def.fields.vec.end(); ++it) {
2069 const auto &field = **it;
2070 if (field.deprecated) continue;
2071
2072 const auto arg_name = GetArgName(field);
2073
2074 if (field.IsScalarOptional()) {
2075 code += " if (" + arg_name + " !== null)\n ";
2076 }
2077
2078 code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "(";
2079 code += "builder, " + arg_name + ");\n";
2080 }
2081
2082 code += " return " + methodPrefix + ".end" + Verbose(struct_def) +
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002083 "(builder);\n";
Austin Schuh272c6132020-11-14 16:37:52 -08002084 code += "}\n";
2085 if (lang_.language == IDLOptions::kJs) code += "\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002086 }
Austin Schuh272c6132020-11-14 16:37:52 -08002087 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002088
Austin Schuh272c6132020-11-14 16:37:52 -08002089 if (!struct_def.fixed && parser_.services_.vec.size() != 0 &&
2090 lang_.language == IDLOptions::kTs) {
2091 auto name = Verbose(struct_def, "");
2092 code += "\n";
2093 code += "serialize():Uint8Array {\n";
2094 code += " return this.bb!.bytes();\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002095 code += "}\n";
Austin Schuh272c6132020-11-14 16:37:52 -08002096
2097 code += "\n";
2098 code += "static deserialize(buffer: Uint8Array):"+ name +" {\n";
2099 code += " return " + name + ".getRootAs" + name +
2100 "(new flatbuffers.ByteBuffer(buffer))\n";
2101 code += "}\n";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002102 }
2103
2104 if (lang_.language == IDLOptions::kTs) {
Austin Schuh272c6132020-11-14 16:37:52 -08002105 if (parser_.opts.generate_object_based_api) {
2106 std::string obj_api_class;
2107 std::string obj_api_unpack_func;
2108 GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
2109 imported_files);
2110
2111 code += obj_api_unpack_func + "}\n" + obj_api_class;
2112 } else {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002113 code += "}\n";
2114 }
Austin Schuh272c6132020-11-14 16:37:52 -08002115 if (!object_namespace.empty()) { code += "}\n"; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002116 }
2117 }
Austin Schuh272c6132020-11-14 16:37:52 -08002118
2119 static bool HasNullDefault(const FieldDef &field) {
2120 return field.optional && field.value.constant == "null";
2121 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002122
Austin Schuh272c6132020-11-14 16:37:52 -08002123 std::string GetArgType(const FieldDef &field, bool allowNull) {
2124 auto type_name = GenTypeName(field.value.type, true, allowNull && field.optional);
2125
2126 if (field.value.type.enum_def) {
2127 if (IsScalar(field.value.type.base_type)) {
2128 return GenPrefixedTypeName(type_name, field.value.type.enum_def->file);
2129 }
2130 }
2131
2132 return type_name;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002133 }
2134
2135 static std::string GetArgName(const FieldDef &field) {
2136 auto argname = MakeCamel(field.name, false);
2137 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
2138
2139 return argname;
2140 }
2141
Austin Schuh272c6132020-11-14 16:37:52 -08002142 std::string Verbose(const StructDef &struct_def, const char *prefix = "") {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002143 return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name;
2144 }
Austin Schuh272c6132020-11-14 16:37:52 -08002145}; // namespace jsts
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002146} // namespace jsts
2147
2148bool GenerateJSTS(const Parser &parser, const std::string &path,
2149 const std::string &file_name) {
2150 jsts::JsTsGenerator generator(parser, path, file_name);
2151 return generator.generate();
2152}
2153
2154std::string JSTSMakeRule(const Parser &parser, const std::string &path,
2155 const std::string &file_name) {
2156 FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002157
2158 std::string filebase =
2159 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
Austin Schuh272c6132020-11-14 16:37:52 -08002160 jsts::JsTsGenerator generator(parser, path, file_name);
2161 std::string make_rule =
2162 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
Austin Schuhe89fa2d2019-08-14 20:24:23 -07002163
2164 auto included_files = parser.GetIncludedFilesRecursive(file_name);
2165 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
2166 make_rule += " " + *it;
2167 }
2168 return make_rule;
2169}
2170
2171} // namespace flatbuffers