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 // independent from idl_parser, since this code is not needed for most clients
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
29 struct JsTsLanguageParameters {
30 IDLOptions::Language language;
31 std::string file_extension;
32 };
33
34 struct ReexportDescription {
35 std::string symbol;
36 std::string source_namespace;
37 std::string target_namespace;
38 };
39
40 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
41
GetJsLangParams(IDLOptions::Language lang)42 const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
43 static JsTsLanguageParameters js_language_parameters[] = {
44 {
45 IDLOptions::kJs,
46 ".js",
47 },
48 {
49 IDLOptions::kTs,
50 ".ts",
51 },
52 };
53
54 if (lang == IDLOptions::kJs) {
55 return js_language_parameters[0];
56 } else {
57 FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
58 return js_language_parameters[1];
59 }
60 }
61
62 namespace jsts {
63 // Iterate through all definitions we haven't generate code for (enums, structs,
64 // and tables) and output them to a single file.
65 class JsTsGenerator : public BaseGenerator {
66 public:
67 typedef std::unordered_set<std::string> imported_fileset;
68 typedef std::unordered_multimap<std::string, ReexportDescription>
69 reexport_map;
70
JsTsGenerator(const Parser & parser,const std::string & path,const std::string & file_name)71 JsTsGenerator(const Parser &parser, const std::string &path,
72 const std::string &file_name)
73 : BaseGenerator(parser, path, file_name, "", ".",
74 parser.opts.lang == IDLOptions::kJs ? "js" : "ts"),
75 lang_(GetJsLangParams(parser_.opts.lang)) {}
76 // Iterate through all definitions we haven't generate code for (enums,
77 // structs, and tables) and output them to a single file.
generate()78 bool generate() {
79 imported_fileset imported_files;
80 reexport_map reexports;
81
82 std::string enum_code, struct_code, import_code, exports_code, code;
83 generateEnums(&enum_code, &exports_code, reexports);
84 generateStructs(&struct_code, &exports_code, imported_files);
85 generateImportDependencies(&import_code, imported_files);
86 generateReexports(&import_code, reexports, imported_files);
87
88 code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
89
90 // Generate code for all the namespace declarations.
91 GenNamespaces(&code, &exports_code);
92
93 // Output the main declaration code from above.
94 code += import_code;
95
96 code += enum_code;
97 code += struct_code;
98
99 if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
100 !parser_.opts.skip_js_exports) {
101 if (parser_.opts.use_ES6_js_export_format)
102 code += "// Exports for ECMAScript6 Modules\n";
103 else
104 code += "// Exports for Node.js and RequireJS\n";
105 code += exports_code;
106 }
107
108 return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
109 code, false);
110 }
111
112 private:
113 JsTsLanguageParameters lang_;
114
115 // Generate code for imports
generateImportDependencies(std::string * code_ptr,const imported_fileset & imported_files)116 void generateImportDependencies(std::string *code_ptr,
117 const imported_fileset &imported_files) {
118 std::string &code = *code_ptr;
119 for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
120 const auto &file = *it;
121 const auto basename =
122 flatbuffers::StripPath(flatbuffers::StripExtension(file));
123 if (basename != file_name_) { code += GenPrefixedImport(file, basename); }
124 }
125 }
126
127 // Generate reexports, which might not have been explicitly imported using the
128 // "export import" trick
generateReexports(std::string * code_ptr,const reexport_map & reexports,imported_fileset imported_files)129 void generateReexports(std::string *code_ptr, const reexport_map &reexports,
130 imported_fileset imported_files) {
131 if (!parser_.opts.reexport_ts_modules ||
132 lang_.language != IDLOptions::kTs) {
133 return;
134 }
135
136 std::string &code = *code_ptr;
137 for (auto it = reexports.begin(); it != reexports.end(); ++it) {
138 const auto &file = *it;
139 const auto basename =
140 flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
141 if (basename != file_name_) {
142 if (imported_files.find(file.first) == imported_files.end()) {
143 code += GenPrefixedImport(file.first, basename);
144 imported_files.emplace(file.first);
145 }
146
147 code += "export namespace " + file.second.target_namespace + " { \n";
148 code += "export import " + file.second.symbol + " = ";
149 code += GenFileNamespacePrefix(file.first) + "." +
150 file.second.source_namespace + "." + file.second.symbol +
151 "; }\n";
152 }
153 }
154 }
155
156 // Generate code for all enums.
generateEnums(std::string * enum_code_ptr,std::string * exports_code_ptr,reexport_map & reexports)157 void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
158 reexport_map &reexports) {
159 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
160 ++it) {
161 auto &enum_def = **it;
162 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, false);
163 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true);
164 }
165 }
166
167 // Generate code for all structs.
generateStructs(std::string * decl_code_ptr,std::string * exports_code_ptr,imported_fileset & imported_files)168 void generateStructs(std::string *decl_code_ptr,
169 std::string *exports_code_ptr,
170 imported_fileset &imported_files) {
171 for (auto it = parser_.structs_.vec.begin();
172 it != parser_.structs_.vec.end(); ++it) {
173 auto &struct_def = **it;
174 GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
175 imported_files);
176 }
177 }
GenNamespaces(std::string * code_ptr,std::string * exports_ptr)178 void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
179 if (lang_.language == IDLOptions::kTs &&
180 parser_.opts.skip_flatbuffers_import) {
181 return;
182 }
183
184 std::set<std::string> namespaces;
185
186 for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
187 ++it) {
188 std::string namespace_so_far;
189
190 // Gather all parent namespaces for this namespace
191 for (auto component = (*it)->components.begin();
192 component != (*it)->components.end(); ++component) {
193 if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
194 namespace_so_far += *component;
195 namespaces.insert(namespace_so_far);
196 }
197 }
198
199 // Make sure parent namespaces come before child namespaces
200 std::vector<std::string> sorted_namespaces(namespaces.begin(),
201 namespaces.end());
202 std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
203
204 // Emit namespaces in a form that Closure Compiler can optimize
205 std::string &code = *code_ptr;
206 std::string &exports = *exports_ptr;
207 for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
208 ++it) {
209 if (lang_.language == IDLOptions::kTs) {
210 if (it->find('.') == std::string::npos) {
211 code += "import { flatbuffers } from \"./flatbuffers\"\n";
212 break;
213 }
214 } else {
215 code += "/**\n * @const\n * @namespace\n */\n";
216 if (it->find('.') == std::string::npos) {
217 code += "var ";
218 if (parser_.opts.use_goog_js_export_format) {
219 exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
220 } else if (parser_.opts.use_ES6_js_export_format) {
221 exports += "export {" + *it + "};\n";
222 } else {
223 exports += "this." + *it + " = " + *it + ";\n";
224 }
225 }
226 code += *it + " = " + *it + " || {};\n\n";
227 }
228 }
229 }
230
231 // Generate a documentation comment, if available.
GenDocComment(const std::vector<std::string> & dc,std::string * code_ptr,const std::string & extra_lines,const char * indent=nullptr)232 static void GenDocComment(const std::vector<std::string> &dc,
233 std::string *code_ptr,
234 const std::string &extra_lines,
235 const char *indent = nullptr) {
236 if (dc.empty() && extra_lines.empty()) {
237 // Don't output empty comment blocks with 0 lines of comment content.
238 return;
239 }
240
241 std::string &code = *code_ptr;
242 if (indent) code += indent;
243 code += "/**\n";
244 for (auto it = dc.begin(); it != dc.end(); ++it) {
245 if (indent) code += indent;
246 code += " *" + *it + "\n";
247 }
248 if (!extra_lines.empty()) {
249 if (!dc.empty()) {
250 if (indent) code += indent;
251 code += " *\n";
252 }
253 if (indent) code += indent;
254 std::string::size_type start = 0;
255 for (;;) {
256 auto end = extra_lines.find('\n', start);
257 if (end != std::string::npos) {
258 code += " * " + extra_lines.substr(start, end - start) + "\n";
259 start = end + 1;
260 } else {
261 code += " * " + extra_lines.substr(start) + "\n";
262 break;
263 }
264 }
265 }
266 if (indent) code += indent;
267 code += " */\n";
268 }
269
GenDocComment(std::string * code_ptr,const std::string & extra_lines)270 static void GenDocComment(std::string *code_ptr,
271 const std::string &extra_lines) {
272 GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
273 }
274
GenTypeAnnotation(AnnotationType annotation_type,const std::string & type_name,const std::string & arg_name,bool include_newline=true)275 std::string GenTypeAnnotation(AnnotationType annotation_type,
276 const std::string &type_name,
277 const std::string &arg_name,
278 bool include_newline = true) {
279 std::string result = "";
280 switch (annotation_type) {
281 case kParam: {
282 result += "@param";
283 break;
284 }
285 case kType: {
286 if (lang_.language != IDLOptions::kTs) {
287 result += "@type";
288 } else {
289 return "";
290 }
291 break;
292 }
293 case kReturns: {
294 result += "@returns";
295 break;
296 }
297 }
298 switch (lang_.language) {
299 case IDLOptions::kTs: {
300 result += " " + type_name;
301 break;
302 }
303 default: {
304 result += " {" + type_name + "}";
305 }
306 }
307 if (!arg_name.empty()) { result += " " + arg_name; }
308 if (include_newline) { result += "\n"; }
309
310 return result;
311 }
312
313 // Generate an enum declaration and an enum string lookup table.
GenEnum(EnumDef & enum_def,std::string * code_ptr,std::string * exports_ptr,reexport_map & reexports,bool reverse)314 void GenEnum(EnumDef &enum_def, std::string *code_ptr,
315 std::string *exports_ptr, reexport_map &reexports,
316 bool reverse) {
317 if (enum_def.generated) return;
318 if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME.
319 std::string &code = *code_ptr;
320 std::string &exports = *exports_ptr;
321 GenDocComment(enum_def.doc_comment, code_ptr,
322 reverse ? "@enum {string}" : "@enum {number}");
323 std::string ns = GetNameSpace(enum_def);
324 std::string enum_def_name = enum_def.name + (reverse ? "Name" : "");
325 if (lang_.language == IDLOptions::kTs) {
326 if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
327 code += "export enum " + enum_def.name + "{\n";
328 } else {
329 if (enum_def.defined_namespace->components.empty()) {
330 code += "var ";
331 if (parser_.opts.use_goog_js_export_format) {
332 exports += "goog.exportSymbol('" + enum_def_name + "', " +
333 enum_def.name + ");\n";
334 } else if (parser_.opts.use_ES6_js_export_format) {
335 exports += "export {" + enum_def_name + "};\n";
336 } else {
337 exports += "this." + enum_def_name + " = " + enum_def_name + ";\n";
338 }
339 }
340 code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n";
341 }
342 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
343 auto &ev = **it;
344 if (!ev.doc_comment.empty()) {
345 if (it != enum_def.Vals().begin()) { code += '\n'; }
346 GenDocComment(ev.doc_comment, code_ptr, "", " ");
347 }
348
349 // Generate mapping between EnumName: EnumValue(int)
350 if (reverse) {
351 code += " '" + enum_def.ToString(ev) + "'";
352 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
353 code += "'" + ev.name + "'";
354 } else {
355 code += " " + ev.name;
356 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
357 code += enum_def.ToString(ev);
358 }
359
360 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
361
362 if (ev.union_type.struct_def) {
363 ReexportDescription desc = { ev.name,
364 GetNameSpace(*ev.union_type.struct_def),
365 GetNameSpace(enum_def) };
366 reexports.insert(
367 std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
368 }
369 }
370
371 if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; }
372 code += "};\n\n";
373 }
374
GenType(const Type & type)375 static std::string GenType(const Type &type) {
376 switch (type.base_type) {
377 case BASE_TYPE_BOOL:
378 case BASE_TYPE_CHAR: return "Int8";
379 case BASE_TYPE_UTYPE:
380 case BASE_TYPE_UCHAR: return "Uint8";
381 case BASE_TYPE_SHORT: return "Int16";
382 case BASE_TYPE_USHORT: return "Uint16";
383 case BASE_TYPE_INT: return "Int32";
384 case BASE_TYPE_UINT: return "Uint32";
385 case BASE_TYPE_LONG: return "Int64";
386 case BASE_TYPE_ULONG: return "Uint64";
387 case BASE_TYPE_FLOAT: return "Float32";
388 case BASE_TYPE_DOUBLE: return "Float64";
389 case BASE_TYPE_STRING: return "String";
390 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
391 case BASE_TYPE_STRUCT: return type.struct_def->name;
392 default: return "Table";
393 }
394 }
395
GenGetter(const Type & type,const std::string & arguments)396 std::string GenGetter(const Type &type, const std::string &arguments) {
397 switch (type.base_type) {
398 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
399 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
400 case BASE_TYPE_UNION: return GenBBAccess() + ".__union" + arguments;
401 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
402 default: {
403 auto getter =
404 GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
405 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
406 if (type.enum_def) {
407 getter = "/** " +
408 GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "",
409 false) +
410 " */ (" + getter + ")";
411 }
412 return getter;
413 }
414 }
415 }
416
GenBBAccess()417 std::string GenBBAccess() {
418 return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
419 }
420
GenDefaultValue(const Value & value,const std::string & context)421 std::string GenDefaultValue(const Value &value, const std::string &context) {
422 if (value.type.enum_def) {
423 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
424 if (lang_.language == IDLOptions::kTs) {
425 return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
426 value.type.enum_def->file) +
427 "." + val->name;
428 } else {
429 return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
430 }
431 } else {
432 return "/** " +
433 GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def),
434 "", false) +
435 "} */ (" + value.constant + ")";
436 }
437 }
438
439 switch (value.type.base_type) {
440 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
441
442 case BASE_TYPE_STRING: return "null";
443
444 case BASE_TYPE_LONG:
445 case BASE_TYPE_ULONG: {
446 int64_t constant = StringToInt(value.constant.c_str());
447 return context + ".createLong(" +
448 NumToString(static_cast<int32_t>(constant)) + ", " +
449 NumToString(static_cast<int32_t>(constant >> 32)) + ")";
450 }
451
452 default: return value.constant;
453 }
454 }
455
GenTypeName(const Type & type,bool input,bool allowNull=false)456 std::string GenTypeName(const Type &type, bool input,
457 bool allowNull = false) {
458 if (!input) {
459 if (type.base_type == BASE_TYPE_STRING ||
460 type.base_type == BASE_TYPE_STRUCT) {
461 std::string name;
462 if (type.base_type == BASE_TYPE_STRING) {
463 name = "string|Uint8Array";
464 } else {
465 name = WrapInNameSpace(*type.struct_def);
466 }
467 return (allowNull) ? (name + "|null") : (name);
468 }
469 }
470
471 switch (type.base_type) {
472 case BASE_TYPE_BOOL: return "boolean";
473 case BASE_TYPE_LONG:
474 case BASE_TYPE_ULONG: return "flatbuffers.Long";
475 default:
476 if (IsScalar(type.base_type)) {
477 if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
478 return "number";
479 }
480 return "flatbuffers.Offset";
481 }
482 }
483
484 // Returns the method name for use with add/put calls.
GenWriteMethod(const Type & type)485 static std::string GenWriteMethod(const Type &type) {
486 // Forward to signed versions since unsigned versions don't exist
487 switch (type.base_type) {
488 case BASE_TYPE_UTYPE:
489 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
490 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
491 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
492 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
493 default: break;
494 }
495
496 return IsScalar(type.base_type) ? MakeCamel(GenType(type))
497 : (IsStruct(type) ? "Struct" : "Offset");
498 }
499
MaybeAdd(T value)500 template<typename T> static std::string MaybeAdd(T value) {
501 return value != 0 ? " + " + NumToString(value) : "";
502 }
503
MaybeScale(T value)504 template<typename T> static std::string MaybeScale(T value) {
505 return value != 1 ? " * " + NumToString(value) : "";
506 }
507
GenFileNamespacePrefix(const std::string & file)508 static std::string GenFileNamespacePrefix(const std::string &file) {
509 return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str()));
510 }
511
GenPrefixedImport(const std::string & full_file_name,const std::string & base_name)512 std::string GenPrefixedImport(const std::string &full_file_name,
513 const std::string &base_name) {
514 // Either keep the include path as it was
515 // or use only the base_name + kGeneratedFileNamePostfix
516 std::string path;
517 if (parser_.opts.keep_include_path) {
518 auto it = parser_.included_files_.find(full_file_name);
519 FLATBUFFERS_ASSERT(it != parser_.included_files_.end());
520 path = flatbuffers::StripExtension(it->second) +
521 parser_.opts.filename_suffix;
522 } else {
523 path = base_name + parser_.opts.filename_suffix;
524 }
525
526 // Add the include prefix and make the path always relative
527 path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path);
528 path = std::string(".") + kPathSeparator + path;
529
530 return "import * as " + GenFileNamespacePrefix(full_file_name) +
531 " from \"" + path + "\";\n";
532 }
533
534 // Adds a source-dependent prefix, for of import * statements.
GenPrefixedTypeName(const std::string & typeName,const std::string & file)535 std::string GenPrefixedTypeName(const std::string &typeName,
536 const std::string &file) {
537 const auto basename =
538 flatbuffers::StripPath(flatbuffers::StripExtension(file));
539 if (basename == file_name_ || parser_.opts.generate_all) {
540 return typeName;
541 }
542 return GenFileNamespacePrefix(file) + "." + typeName;
543 }
544
GenStructArgs(const StructDef & struct_def,std::string * annotations,std::string * arguments,const std::string & nameprefix)545 void GenStructArgs(const StructDef &struct_def, std::string *annotations,
546 std::string *arguments, const std::string &nameprefix) {
547 for (auto it = struct_def.fields.vec.begin();
548 it != struct_def.fields.vec.end(); ++it) {
549 auto &field = **it;
550 if (IsStruct(field.value.type)) {
551 // Generate arguments for a struct inside a struct. To ensure names
552 // don't clash, and to make it obvious these arguments are constructing
553 // a nested struct, prefix the name with the field name.
554 GenStructArgs(*field.value.type.struct_def, annotations, arguments,
555 nameprefix + field.name + "_");
556 } else {
557 *annotations +=
558 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
559 nameprefix + field.name);
560 if (lang_.language == IDLOptions::kTs) {
561 *arguments += ", " + nameprefix + field.name + ": " +
562 GenTypeName(field.value.type, true);
563 } else {
564 *arguments += ", " + nameprefix + field.name;
565 }
566 }
567 }
568 }
569
GenStructBody(const StructDef & struct_def,std::string * body,const std::string & nameprefix)570 static void GenStructBody(const StructDef &struct_def, std::string *body,
571 const std::string &nameprefix) {
572 *body += " builder.prep(";
573 *body += NumToString(struct_def.minalign) + ", ";
574 *body += NumToString(struct_def.bytesize) + ");\n";
575
576 for (auto it = struct_def.fields.vec.rbegin();
577 it != struct_def.fields.vec.rend(); ++it) {
578 auto &field = **it;
579 if (field.padding) {
580 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
581 }
582 if (IsStruct(field.value.type)) {
583 // Generate arguments for a struct inside a struct. To ensure names
584 // don't clash, and to make it obvious these arguments are constructing
585 // a nested struct, prefix the name with the field name.
586 GenStructBody(*field.value.type.struct_def, body,
587 nameprefix + field.name + "_");
588 } else {
589 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
590 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
591 *body += nameprefix + field.name + ");\n";
592 }
593 }
594 }
595
GenerateNewExpression(const std::string & object_name)596 std::string GenerateNewExpression(const std::string &object_name) {
597 return "new " + object_name +
598 (lang_.language == IDLOptions::kTs ? "()" : "");
599 }
600
GenerateRootAccessor(StructDef & struct_def,std::string * code_ptr,std::string & code,std::string & object_name,bool size_prefixed)601 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
602 std::string &code, std::string &object_name,
603 bool size_prefixed) {
604 if (!struct_def.fixed) {
605 GenDocComment(code_ptr,
606 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
607 GenTypeAnnotation(kParam, object_name + "=", "obj") +
608 GenTypeAnnotation(kReturns, object_name, "", false));
609 std::string sizePrefixed("SizePrefixed");
610 if (lang_.language == IDLOptions::kTs) {
611 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
612 Verbose(struct_def, "As");
613 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
614 "):" + object_name + " {\n";
615 } else {
616 code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") +
617 "Root" + Verbose(struct_def, "As");
618 code += " = function(bb, obj) {\n";
619 }
620 if (size_prefixed) {
621 code +=
622 " bb.setPosition(bb.position() + "
623 "flatbuffers.SIZE_PREFIX_LENGTH);\n";
624 }
625 code += " return (obj || " + GenerateNewExpression(object_name);
626 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
627 code += "};\n\n";
628 }
629 }
630
GenerateFinisher(StructDef & struct_def,std::string * code_ptr,std::string & code,std::string & object_name,bool size_prefixed)631 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
632 std::string &code, std::string &object_name,
633 bool size_prefixed) {
634 if (parser_.root_struct_def_ == &struct_def) {
635 std::string sizePrefixed("SizePrefixed");
636 GenDocComment(
637 code_ptr,
638 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
639 GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset", false));
640
641 if (lang_.language == IDLOptions::kTs) {
642 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
643 Verbose(struct_def) + "Buffer";
644 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
645 } else {
646 code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") +
647 Verbose(struct_def) + "Buffer";
648 code += " = function(builder, offset) {\n";
649 }
650
651 code += " builder.finish(offset";
652 if (!parser_.file_identifier_.empty()) {
653 code += ", '" + parser_.file_identifier_ + "'";
654 }
655 if (size_prefixed) {
656 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
657 code += ", true";
658 }
659 code += ");\n";
660 code += "};\n\n";
661 }
662 }
663
664 // Generate an accessor struct with constructor for a flatbuffers struct.
GenStruct(const Parser & parser,StructDef & struct_def,std::string * code_ptr,std::string * exports_ptr,imported_fileset & imported_files)665 void GenStruct(const Parser &parser, StructDef &struct_def,
666 std::string *code_ptr, std::string *exports_ptr,
667 imported_fileset &imported_files) {
668 if (struct_def.generated) return;
669 std::string &code = *code_ptr;
670 std::string &exports = *exports_ptr;
671
672 std::string object_name;
673 std::string object_namespace = GetNameSpace(struct_def);
674
675 // Emit constructor
676 if (lang_.language == IDLOptions::kTs) {
677 object_name = struct_def.name;
678 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
679 if (!object_namespace.empty()) {
680 code += "export namespace " + object_namespace + "{\n";
681 }
682 code += "export class " + struct_def.name;
683 code += " {\n";
684 if (lang_.language != IDLOptions::kTs) {
685 code += " /**\n";
686 code +=
687 " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
688 code += " */\n";
689 }
690 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
691 code += "\n";
692 if (lang_.language != IDLOptions::kTs) {
693 code += " /**\n";
694 code += " * " + GenTypeAnnotation(kType, "number", "");
695 code += " */\n";
696 }
697 code += " bb_pos:number = 0;\n";
698 } else {
699 bool isStatement = struct_def.defined_namespace->components.empty();
700 object_name = WrapInNameSpace(struct_def);
701 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
702 if (isStatement) {
703 if (parser_.opts.use_goog_js_export_format) {
704 exports += "goog.exportSymbol('" + struct_def.name + "', " +
705 struct_def.name + ");\n";
706 } else if (parser_.opts.use_ES6_js_export_format) {
707 exports += "export {" + struct_def.name + "};\n";
708 } else {
709 exports +=
710 "this." + struct_def.name + " = " + struct_def.name + ";\n";
711 }
712 code += "function " + object_name;
713 } else {
714 code += object_name + " = function";
715 }
716 code += "() {\n";
717 code += " /**\n";
718 code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
719 code += " */\n";
720 code += " this.bb = null;\n";
721 code += "\n";
722 code += " /**\n";
723 code += " * " + GenTypeAnnotation(kType, "number", "");
724 code += " */\n";
725 code += " this.bb_pos = 0;\n";
726 code += isStatement ? "}\n\n" : "};\n\n";
727 }
728
729 // Generate the __init method that sets the field in a pre-existing
730 // accessor object. This is to allow object reuse.
731 code += "/**\n";
732 code += " * " + GenTypeAnnotation(kParam, "number", "i");
733 code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
734 code += " * " + GenTypeAnnotation(kReturns, object_name, "");
735 code += " */\n";
736
737 if (lang_.language == IDLOptions::kTs) {
738 code +=
739 "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
740 } else {
741 code += object_name + ".prototype.__init = function(i, bb) {\n";
742 }
743
744 code += " this.bb_pos = i;\n";
745 code += " this.bb = bb;\n";
746 code += " return this;\n";
747 code += "};\n\n";
748
749 // Generate special accessors for the table that when used as the root of a
750 // FlatBuffer
751 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
752 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
753
754 // Generate the identifier check method
755 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
756 !parser_.file_identifier_.empty()) {
757 GenDocComment(code_ptr,
758 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
759 GenTypeAnnotation(kReturns, "boolean", "", false));
760 if (lang_.language == IDLOptions::kTs) {
761 code +=
762 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
763 "{\n";
764 } else {
765 code += object_name + ".bufferHasIdentifier = function(bb) {\n";
766 }
767
768 code += " return bb.__has_identifier('" + parser_.file_identifier_;
769 code += "');\n};\n\n";
770 }
771
772 // Emit field accessors
773 for (auto it = struct_def.fields.vec.begin();
774 it != struct_def.fields.vec.end(); ++it) {
775 auto &field = **it;
776 if (field.deprecated) continue;
777 auto offset_prefix =
778 " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
779 NumToString(field.value.offset) + ");\n return offset ? ";
780
781 // Emit a scalar field
782 if (IsScalar(field.value.type.base_type) ||
783 field.value.type.base_type == BASE_TYPE_STRING) {
784 GenDocComment(
785 field.doc_comment, code_ptr,
786 std::string(field.value.type.base_type == BASE_TYPE_STRING
787 ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
788 "optionalEncoding")
789 : "") +
790 GenTypeAnnotation(kReturns,
791 GenTypeName(field.value.type, false, true),
792 "", false));
793 if (lang_.language == IDLOptions::kTs) {
794 std::string prefix = MakeCamel(field.name, false) + "(";
795 if (field.value.type.base_type == BASE_TYPE_STRING) {
796 code += prefix + "):string|null\n";
797 code += prefix + "optionalEncoding:flatbuffers.Encoding" +
798 "):" + GenTypeName(field.value.type, false, true) + "\n";
799 code += prefix + "optionalEncoding?:any";
800 } else {
801 code += prefix;
802 }
803 if (field.value.type.enum_def) {
804 code +=
805 "):" +
806 GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
807 field.value.type.enum_def->file) +
808 " {\n";
809
810 if (!parser_.opts.generate_all) {
811 imported_files.insert(field.value.type.enum_def->file);
812 }
813 } else {
814 code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
815 }
816 } else {
817 code += object_name + ".prototype." + MakeCamel(field.name, false);
818 code += " = function(";
819 if (field.value.type.base_type == BASE_TYPE_STRING) {
820 code += "optionalEncoding";
821 }
822 code += ") {\n";
823 }
824
825 if (struct_def.fixed) {
826 code +=
827 " return " +
828 GenGetter(field.value.type,
829 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
830 ";\n";
831 } else {
832 std::string index = "this.bb_pos + offset";
833 if (field.value.type.base_type == BASE_TYPE_STRING) {
834 index += ", optionalEncoding";
835 }
836 code += offset_prefix +
837 GenGetter(field.value.type, "(" + index + ")") + " : " +
838 GenDefaultValue(field.value, GenBBAccess());
839 code += ";\n";
840 }
841 }
842
843 // Emit an object field
844 else {
845 switch (field.value.type.base_type) {
846 case BASE_TYPE_STRUCT: {
847 auto type = WrapInNameSpace(*field.value.type.struct_def);
848 GenDocComment(
849 field.doc_comment, code_ptr,
850 GenTypeAnnotation(kParam, type + "=", "obj") +
851 GenTypeAnnotation(kReturns, type + "|null", "", false));
852 if (lang_.language == IDLOptions::kTs) {
853 type =
854 GenPrefixedTypeName(type, field.value.type.struct_def->file);
855 code += MakeCamel(field.name, false);
856 code += "(obj?:" + type + "):" + type + "|null {\n";
857 } else {
858 code +=
859 object_name + ".prototype." + MakeCamel(field.name, false);
860 code += " = function(obj) {\n";
861 }
862
863 if (struct_def.fixed) {
864 code += " return (obj || " + GenerateNewExpression(type);
865 code += ").__init(this.bb_pos";
866 code +=
867 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
868 } else {
869 code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
870 ").__init(";
871 code += field.value.type.struct_def->fixed
872 ? "this.bb_pos + offset"
873 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
874 code += ", " + GenBBAccess() + ") : null;\n";
875 }
876
877 if (lang_.language == IDLOptions::kTs &&
878 !parser_.opts.generate_all) {
879 imported_files.insert(field.value.type.struct_def->file);
880 }
881
882 break;
883 }
884
885 case BASE_TYPE_VECTOR: {
886 auto vectortype = field.value.type.VectorType();
887 auto vectortypename = GenTypeName(vectortype, false);
888 auto inline_size = InlineSize(vectortype);
889 auto index = GenBBAccess() +
890 ".__vector(this.bb_pos + offset) + index" +
891 MaybeScale(inline_size);
892 std::string args = GenTypeAnnotation(kParam, "number", "index");
893 std::string ret_type;
894 bool is_union = false;
895 switch (vectortype.base_type) {
896 case BASE_TYPE_STRUCT:
897 args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
898 ret_type = vectortypename;
899 break;
900 case BASE_TYPE_STRING:
901 args += GenTypeAnnotation(
902 kParam, "flatbuffers.Encoding=", "optionalEncoding");
903 ret_type = vectortypename;
904 break;
905 case BASE_TYPE_UNION:
906 args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
907 ret_type = "?flatbuffers.Table";
908 is_union = true;
909 break;
910 default: ret_type = vectortypename;
911 }
912 GenDocComment(
913 field.doc_comment, code_ptr,
914 args + GenTypeAnnotation(kReturns, ret_type, "", false));
915 if (lang_.language == IDLOptions::kTs) {
916 std::string prefix = MakeCamel(field.name, false);
917 if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
918 prefix += "(index: number";
919 if (is_union) {
920 vectortypename = "T";
921 code += prefix + ", obj:T";
922 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
923 vectortypename = GenPrefixedTypeName(
924 vectortypename, vectortype.struct_def->file);
925 code += prefix + ", obj?:" + vectortypename;
926
927 if (!parser_.opts.generate_all) {
928 imported_files.insert(vectortype.struct_def->file);
929 }
930 } else if (vectortype.base_type == BASE_TYPE_STRING) {
931 code += prefix + "):string\n";
932 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
933 "):" + vectortypename + "\n";
934 code += prefix + ",optionalEncoding?:any";
935 } else {
936 code += prefix;
937 }
938 code += "):" + vectortypename + "|null {\n";
939 } else {
940 code +=
941 object_name + ".prototype." + MakeCamel(field.name, false);
942 code += " = function(index";
943 if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
944 code += ", obj";
945 } else if (vectortype.base_type == BASE_TYPE_STRING) {
946 code += ", optionalEncoding";
947 }
948 code += ") {\n";
949 }
950
951 if (vectortype.base_type == BASE_TYPE_STRUCT) {
952 code += offset_prefix + "(obj || " +
953 GenerateNewExpression(vectortypename);
954 code += ").__init(";
955 code += vectortype.struct_def->fixed
956 ? index
957 : GenBBAccess() + ".__indirect(" + index + ")";
958 code += ", " + GenBBAccess() + ")";
959 } else {
960 if (is_union) {
961 index = "obj, " + index;
962 } else if (vectortype.base_type == BASE_TYPE_STRING) {
963 index += ", optionalEncoding";
964 }
965 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
966 }
967 code += " : ";
968 if (field.value.type.element == BASE_TYPE_BOOL) {
969 code += "false";
970 } else if (field.value.type.element == BASE_TYPE_LONG ||
971 field.value.type.element == BASE_TYPE_ULONG) {
972 code += GenBBAccess() + ".createLong(0, 0)";
973 } else if (IsScalar(field.value.type.element)) {
974 if (field.value.type.enum_def) {
975 code += "/** " +
976 GenTypeAnnotation(
977 kType, WrapInNameSpace(*field.value.type.enum_def),
978 "", false) +
979 " */ (" + field.value.constant + ")";
980 } else {
981 code += "0";
982 }
983 } else {
984 code += "null";
985 }
986 code += ";\n";
987 break;
988 }
989
990 case BASE_TYPE_UNION:
991 GenDocComment(
992 field.doc_comment, code_ptr,
993 GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
994 GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
995 false));
996 if (lang_.language == IDLOptions::kTs) {
997 code += MakeCamel(field.name, false);
998 code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
999 } else {
1000 code +=
1001 object_name + ".prototype." + MakeCamel(field.name, false);
1002 code += " = function(obj) {\n";
1003 }
1004
1005 code += offset_prefix +
1006 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1007 " : null;\n";
1008 break;
1009
1010 default: FLATBUFFERS_ASSERT(0);
1011 }
1012 }
1013 code += "};\n\n";
1014
1015 if (parser_.opts.use_goog_js_export_format) {
1016 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1017 MakeCamel(field.name, false) + "', " + object_name +
1018 ".prototype." + MakeCamel(field.name, false) + ");\n";
1019 }
1020
1021 // Adds the mutable scalar value to the output
1022 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
1023 !IsUnion(field.value.type)) {
1024 std::string annotations = GenTypeAnnotation(
1025 kParam, GenTypeName(field.value.type, true), "value");
1026 GenDocComment(
1027 code_ptr,
1028 annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
1029
1030 if (lang_.language == IDLOptions::kTs) {
1031 std::string type;
1032 if (field.value.type.enum_def) {
1033 type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
1034 field.value.type.enum_def->file);
1035 } else {
1036 type = GenTypeName(field.value.type, true);
1037 }
1038
1039 code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1040 } else {
1041 code += object_name + ".prototype.mutate_" + field.name +
1042 " = function(value) {\n";
1043 }
1044
1045 code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1046 NumToString(field.value.offset) + ");\n\n";
1047 code += " if (offset === 0) {\n";
1048 code += " return false;\n";
1049 code += " }\n\n";
1050
1051 // special case for bools, which are treated as uint8
1052 code += " " + GenBBAccess() + ".write" +
1053 MakeCamel(GenType(field.value.type)) +
1054 "(this.bb_pos + offset, ";
1055 if (field.value.type.base_type == BASE_TYPE_BOOL &&
1056 lang_.language == IDLOptions::kTs) {
1057 code += "+";
1058 }
1059
1060 code += "value);\n";
1061 code += " return true;\n";
1062 code += "};\n\n";
1063
1064 if (parser_.opts.use_goog_js_export_format) {
1065 exports += "goog.exportProperty(" + object_name +
1066 ".prototype, 'mutate_" + field.name + "', " + object_name +
1067 ".prototype.mutate_" + field.name + ");\n";
1068 }
1069 }
1070
1071 // Emit vector helpers
1072 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1073 // Emit a length helper
1074 GenDocComment(code_ptr,
1075 GenTypeAnnotation(kReturns, "number", "", false));
1076 if (lang_.language == IDLOptions::kTs) {
1077 code += MakeCamel(field.name, false);
1078 code += "Length():number {\n" + offset_prefix;
1079 } else {
1080 code += object_name + ".prototype." + MakeCamel(field.name, false);
1081 code += "Length = function() {\n" + offset_prefix;
1082 }
1083
1084 code +=
1085 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
1086
1087 if (parser_.opts.use_goog_js_export_format) {
1088 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1089 MakeCamel(field.name, false) + "Length', " + object_name +
1090 ".prototype." + MakeCamel(field.name, false) +
1091 "Length);\n";
1092 }
1093
1094 // For scalar types, emit a typed array helper
1095 auto vectorType = field.value.type.VectorType();
1096 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1097 GenDocComment(code_ptr, GenTypeAnnotation(
1098 kReturns, GenType(vectorType) + "Array",
1099 "", false));
1100
1101 if (lang_.language == IDLOptions::kTs) {
1102 code += MakeCamel(field.name, false);
1103 code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1104 offset_prefix;
1105 } else {
1106 code += object_name + ".prototype." + MakeCamel(field.name, false);
1107 code += "Array = function() {\n" + offset_prefix;
1108 }
1109
1110 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1111 ".bytes().buffer, " + GenBBAccess() +
1112 ".bytes().byteOffset + " + GenBBAccess() +
1113 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1114 ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
1115
1116 if (parser_.opts.use_goog_js_export_format) {
1117 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1118 MakeCamel(field.name, false) + "Array', " + object_name +
1119 ".prototype." + MakeCamel(field.name, false) +
1120 "Array);\n";
1121 }
1122 }
1123 }
1124 }
1125
1126 // Emit a factory constructor
1127 if (struct_def.fixed) {
1128 std::string annotations =
1129 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1130 std::string arguments;
1131 GenStructArgs(struct_def, &annotations, &arguments, "");
1132 GenDocComment(code_ptr, annotations + GenTypeAnnotation(
1133 kReturns, "flatbuffers.Offset",
1134 "", false));
1135
1136 if (lang_.language == IDLOptions::kTs) {
1137 code += "static create" + Verbose(struct_def) +
1138 "(builder:flatbuffers.Builder";
1139 code += arguments + "):flatbuffers.Offset {\n";
1140 } else {
1141 code += object_name + ".create" + Verbose(struct_def);
1142 code += " = function(builder";
1143 code += arguments + ") {\n";
1144 }
1145
1146 GenStructBody(struct_def, &code, "");
1147 code += " return builder.offset();\n};\n\n";
1148 } else {
1149 // Generate a method to start building a new object
1150 GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
1151 "builder", false));
1152
1153 if (lang_.language == IDLOptions::kTs) {
1154 code += "static start" + Verbose(struct_def) +
1155 "(builder:flatbuffers.Builder) {\n";
1156 } else {
1157 code += object_name + ".start" + Verbose(struct_def);
1158 code += " = function(builder) {\n";
1159 }
1160
1161 code += " builder.startObject(" +
1162 NumToString(struct_def.fields.vec.size()) + ");\n";
1163 code += "};\n\n";
1164
1165 // Generate a set of static methods that allow table construction
1166 for (auto it = struct_def.fields.vec.begin();
1167 it != struct_def.fields.vec.end(); ++it) {
1168 auto &field = **it;
1169 if (field.deprecated) continue;
1170 const auto argname = GetArgName(field);
1171
1172 // Generate the field insertion method
1173 GenDocComment(
1174 code_ptr,
1175 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1176 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
1177 argname, false));
1178
1179 if (lang_.language == IDLOptions::kTs) {
1180 code += "static add" + MakeCamel(field.name);
1181 code += "(builder:flatbuffers.Builder, " + argname + ":" +
1182 GetArgType(field) + ") {\n";
1183 } else {
1184 code += object_name + ".add" + MakeCamel(field.name);
1185 code += " = function(builder, " + argname + ") {\n";
1186 }
1187
1188 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1189 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1190 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1191 code += argname + ", ";
1192 if (!IsScalar(field.value.type.base_type)) {
1193 code += "0";
1194 } else {
1195 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1196 code += GenDefaultValue(field.value, "builder");
1197 }
1198 code += ");\n};\n\n";
1199
1200 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1201 auto vector_type = field.value.type.VectorType();
1202 auto alignment = InlineAlignment(vector_type);
1203 auto elem_size = InlineSize(vector_type);
1204
1205 // Generate a method to create a vector from a JavaScript array
1206 if (!IsStruct(vector_type)) {
1207 GenDocComment(
1208 code_ptr,
1209 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1210 GenTypeAnnotation(
1211 kParam,
1212 "Array.<" + GenTypeName(vector_type, true) + ">",
1213 "data") +
1214 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
1215 false));
1216
1217 if (lang_.language == IDLOptions::kTs) {
1218 code += "static create" + MakeCamel(field.name);
1219 std::string type = GenTypeName(vector_type, true) + "[]";
1220 if (type == "number[]") { type += " | Uint8Array"; }
1221 code += "Vector(builder:flatbuffers.Builder, data:" + type +
1222 "):flatbuffers.Offset {\n";
1223 } else {
1224 code += object_name + ".create" + MakeCamel(field.name);
1225 code += "Vector = function(builder, data) {\n";
1226 }
1227
1228 code += " builder.startVector(" + NumToString(elem_size);
1229 code += ", data.length, " + NumToString(alignment) + ");\n";
1230 code += " for (var i = data.length - 1; i >= 0; i--) {\n";
1231 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1232 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1233 code += "data[i]);\n";
1234 code += " }\n";
1235 code += " return builder.endVector();\n";
1236 code += "};\n\n";
1237 }
1238
1239 // Generate a method to start a vector, data to be added manually
1240 // after
1241 GenDocComment(
1242 code_ptr,
1243 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1244 GenTypeAnnotation(kParam, "number", "numElems", false));
1245
1246 if (lang_.language == IDLOptions::kTs) {
1247 code += "static start" + MakeCamel(field.name);
1248 code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1249 } else {
1250 code += object_name + ".start" + MakeCamel(field.name);
1251 code += "Vector = function(builder, numElems) {\n";
1252 }
1253
1254 code += " builder.startVector(" + NumToString(elem_size);
1255 code += ", numElems, " + NumToString(alignment) + ");\n";
1256 code += "};\n\n";
1257 }
1258 }
1259
1260 // Generate a method to stop building a new object
1261 GenDocComment(
1262 code_ptr,
1263 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1264 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
1265
1266 if (lang_.language == IDLOptions::kTs) {
1267 code += "static end" + Verbose(struct_def);
1268 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1269 } else {
1270 code += object_name + ".end" + Verbose(struct_def);
1271 code += " = function(builder) {\n";
1272 }
1273
1274 code += " var offset = builder.endObject();\n";
1275 for (auto it = struct_def.fields.vec.begin();
1276 it != struct_def.fields.vec.end(); ++it) {
1277 auto &field = **it;
1278 if (!field.deprecated && field.required) {
1279 code += " builder.requiredField(offset, ";
1280 code += NumToString(field.value.offset);
1281 code += "); // " + field.name + "\n";
1282 }
1283 }
1284 code += " return offset;\n";
1285 code += "};\n\n";
1286
1287 // Generate the methods to complete buffer construction
1288 GenerateFinisher(struct_def, code_ptr, code, object_name, false);
1289 GenerateFinisher(struct_def, code_ptr, code, object_name, true);
1290
1291 // Generate a convenient CreateX function
1292 if (lang_.language == IDLOptions::kJs) {
1293 std::string paramDoc =
1294 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1295 for (auto it = struct_def.fields.vec.begin();
1296 it != struct_def.fields.vec.end(); ++it) {
1297 const auto &field = **it;
1298 if (field.deprecated) continue;
1299 paramDoc +=
1300 GenTypeAnnotation(kParam, GetArgType(field), GetArgName(field));
1301 }
1302 paramDoc +=
1303 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false);
1304
1305 GenDocComment(code_ptr, paramDoc);
1306 }
1307
1308 if (lang_.language == IDLOptions::kTs) {
1309 code += "static create" + Verbose(struct_def);
1310 code += "(builder:flatbuffers.Builder";
1311 } else {
1312 code += object_name + ".create" + Verbose(struct_def);
1313 code += " = function(builder";
1314 }
1315 for (auto it = struct_def.fields.vec.begin();
1316 it != struct_def.fields.vec.end(); ++it) {
1317 const auto &field = **it;
1318 if (field.deprecated) continue;
1319
1320 if (lang_.language == IDLOptions::kTs) {
1321 code += ", " + GetArgName(field) + ":" + GetArgType(field);
1322 } else {
1323 code += ", " + GetArgName(field);
1324 }
1325 }
1326
1327 if (lang_.language == IDLOptions::kTs) {
1328 code += "):flatbuffers.Offset {\n";
1329 code += " " + struct_def.name + ".start" + Verbose(struct_def) +
1330 "(builder);\n";
1331 } else {
1332 code += ") {\n";
1333 code += " " + object_name + ".start" + Verbose(struct_def) +
1334 "(builder);\n";
1335 }
1336
1337 std::string methodPrefix =
1338 lang_.language == IDLOptions::kTs ? struct_def.name : object_name;
1339 for (auto it = struct_def.fields.vec.begin();
1340 it != struct_def.fields.vec.end(); ++it) {
1341 const auto &field = **it;
1342 if (field.deprecated) continue;
1343
1344 code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "(";
1345 code += "builder, " + GetArgName(field) + ");\n";
1346 }
1347
1348 code += " return " + methodPrefix + ".end" + Verbose(struct_def) +
1349 "(builder);\n";
1350 code += "}\n";
1351 if (lang_.language == IDLOptions::kJs) code += "\n";
1352 }
1353
1354 if (lang_.language == IDLOptions::kTs) {
1355 if (!object_namespace.empty()) { code += "}\n"; }
1356 code += "}\n";
1357 }
1358 }
1359
GetArgType(const FieldDef & field)1360 std::string GetArgType(const FieldDef &field) {
1361 if (field.value.type.enum_def)
1362 return GenPrefixedTypeName(GenTypeName(field.value.type, true),
1363 field.value.type.enum_def->file);
1364 return GenTypeName(field.value.type, true);
1365 }
1366
GetArgName(const FieldDef & field)1367 static std::string GetArgName(const FieldDef &field) {
1368 auto argname = MakeCamel(field.name, false);
1369 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
1370
1371 return argname;
1372 }
1373
Verbose(const StructDef & struct_def,const char * prefix="")1374 std::string Verbose(const StructDef &struct_def, const char *prefix = "") {
1375 return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name;
1376 }
1377 };
1378 } // namespace jsts
1379
GenerateJSTS(const Parser & parser,const std::string & path,const std::string & file_name)1380 bool GenerateJSTS(const Parser &parser, const std::string &path,
1381 const std::string &file_name) {
1382 jsts::JsTsGenerator generator(parser, path, file_name);
1383 return generator.generate();
1384 }
1385
JSTSMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1386 std::string JSTSMakeRule(const Parser &parser, const std::string &path,
1387 const std::string &file_name) {
1388 FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
1389
1390 std::string filebase =
1391 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1392 jsts::JsTsGenerator generator(parser, path, file_name);
1393 std::string make_rule =
1394 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
1395
1396 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1397 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1398 make_rule += " " + *it;
1399 }
1400 return make_rule;
1401 }
1402
1403 } // namespace flatbuffers
1404