2 * Copyright 2014 Google Inc. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 // independent from idl_parser, since this code is not needed for most clients
19 #include <unordered_map>
20 #include <unordered_set>
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
27 namespace flatbuffers {
29 const std::string kGeneratedFileNamePostfix = "_generated";
31 struct JsTsLanguageParameters {
32 IDLOptions::Language language;
33 std::string file_extension;
36 struct ReexportDescription {
38 std::string source_namespace;
39 std::string target_namespace;
42 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
44 const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
45 static JsTsLanguageParameters js_language_parameters[] = {
56 if (lang == IDLOptions::kJs) {
57 return js_language_parameters[0];
59 FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
60 return js_language_parameters[1];
64 static std::string GeneratedFileName(const std::string &path,
65 const std::string &file_name,
66 const JsTsLanguageParameters &lang) {
67 return path + file_name + kGeneratedFileNamePostfix + lang.file_extension;
71 // Iterate through all definitions we haven't generate code for (enums, structs,
72 // and tables) and output them to a single file.
73 class JsTsGenerator : public BaseGenerator {
75 typedef std::unordered_set<std::string> imported_fileset;
76 typedef std::unordered_multimap<std::string, ReexportDescription>
79 JsTsGenerator(const Parser &parser, const std::string &path,
80 const std::string &file_name)
81 : BaseGenerator(parser, path, file_name, "", "."),
82 lang_(GetJsLangParams(parser_.opts.lang)) {}
83 // Iterate through all definitions we haven't generate code for (enums,
84 // structs, and tables) and output them to a single file.
86 imported_fileset imported_files;
87 reexport_map reexports;
89 std::string enum_code, struct_code, import_code, exports_code, code;
90 generateEnums(&enum_code, &exports_code, reexports);
91 generateStructs(&struct_code, &exports_code, imported_files);
92 generateImportDependencies(&import_code, imported_files);
93 generateReexports(&import_code, reexports, imported_files);
95 code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
97 // Generate code for all the namespace declarations.
98 GenNamespaces(&code, &exports_code);
100 // Output the main declaration code from above.
106 if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
107 !parser_.opts.skip_js_exports) {
108 if (parser_.opts.use_ES6_js_export_format)
109 code += "// Exports for ECMAScript6 Modules\n";
111 code += "// Exports for Node.js and RequireJS\n";
112 code += exports_code;
115 return SaveFile(GeneratedFileName(path_, file_name_, lang_).c_str(), code,
120 JsTsLanguageParameters lang_;
122 // Generate code for imports
123 void generateImportDependencies(std::string *code_ptr,
124 const imported_fileset &imported_files) {
125 std::string &code = *code_ptr;
126 for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
127 const auto &file = *it;
128 const auto basename =
129 flatbuffers::StripPath(flatbuffers::StripExtension(file));
130 if (basename != file_name_) { code += GenPrefixedImport(file, basename); }
134 // Generate reexports, which might not have been explicitly imported using the
135 // "export import" trick
136 void generateReexports(std::string *code_ptr, const reexport_map &reexports,
137 imported_fileset imported_files) {
138 if (!parser_.opts.reexport_ts_modules ||
139 lang_.language != IDLOptions::kTs) {
143 std::string &code = *code_ptr;
144 for (auto it = reexports.begin(); it != reexports.end(); ++it) {
145 const auto &file = *it;
146 const auto basename =
147 flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
148 if (basename != file_name_) {
149 if (imported_files.find(file.first) == imported_files.end()) {
150 code += GenPrefixedImport(file.first, basename);
151 imported_files.emplace(file.first);
154 code += "export namespace " + file.second.target_namespace + " { \n";
155 code += "export import " + file.second.symbol + " = ";
156 code += GenFileNamespacePrefix(file.first) + "." +
157 file.second.source_namespace + "." + file.second.symbol +
163 // Generate code for all enums.
164 void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
165 reexport_map &reexports) {
166 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
168 auto &enum_def = **it;
169 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, false);
170 GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true);
174 // Generate code for all structs.
175 void generateStructs(std::string *decl_code_ptr,
176 std::string *exports_code_ptr,
177 imported_fileset &imported_files) {
178 for (auto it = parser_.structs_.vec.begin();
179 it != parser_.structs_.vec.end(); ++it) {
180 auto &struct_def = **it;
181 GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
185 void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
186 if (lang_.language == IDLOptions::kTs &&
187 parser_.opts.skip_flatbuffers_import) {
191 std::set<std::string> namespaces;
193 for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
195 std::string namespace_so_far;
197 // Gather all parent namespaces for this namespace
198 for (auto component = (*it)->components.begin();
199 component != (*it)->components.end(); ++component) {
200 if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
201 namespace_so_far += *component;
202 namespaces.insert(namespace_so_far);
206 // Make sure parent namespaces come before child namespaces
207 std::vector<std::string> sorted_namespaces(namespaces.begin(),
209 std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
211 // Emit namespaces in a form that Closure Compiler can optimize
212 std::string &code = *code_ptr;
213 std::string &exports = *exports_ptr;
214 for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
216 if (lang_.language == IDLOptions::kTs) {
217 if (it->find('.') == std::string::npos) {
218 code += "import { flatbuffers } from \"./flatbuffers\"\n";
222 code += "/**\n * @const\n * @namespace\n */\n";
223 if (it->find('.') == std::string::npos) {
225 if (parser_.opts.use_goog_js_export_format) {
226 exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
227 } else if (parser_.opts.use_ES6_js_export_format) {
228 exports += "export {" + *it + "};\n";
230 exports += "this." + *it + " = " + *it + ";\n";
233 code += *it + " = " + *it + " || {};\n\n";
238 // Generate a documentation comment, if available.
239 static void GenDocComment(const std::vector<std::string> &dc,
240 std::string *code_ptr,
241 const std::string &extra_lines,
242 const char *indent = nullptr) {
243 if (dc.empty() && extra_lines.empty()) {
244 // Don't output empty comment blocks with 0 lines of comment content.
248 std::string &code = *code_ptr;
249 if (indent) code += indent;
251 for (auto it = dc.begin(); it != dc.end(); ++it) {
252 if (indent) code += indent;
253 code += " *" + *it + "\n";
255 if (!extra_lines.empty()) {
257 if (indent) code += indent;
260 if (indent) code += indent;
261 std::string::size_type start = 0;
263 auto end = extra_lines.find('\n', start);
264 if (end != std::string::npos) {
265 code += " * " + extra_lines.substr(start, end - start) + "\n";
268 code += " * " + extra_lines.substr(start) + "\n";
273 if (indent) code += indent;
277 static void GenDocComment(std::string *code_ptr,
278 const std::string &extra_lines) {
279 GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
282 std::string GenTypeAnnotation(AnnotationType annotation_type,
283 const std::string &type_name,
284 const std::string &arg_name,
285 bool include_newline = true) {
286 std::string result = "";
287 switch (annotation_type) {
293 if (lang_.language != IDLOptions::kTs) {
301 result += "@returns";
305 switch (lang_.language) {
306 case IDLOptions::kTs: {
307 result += " " + type_name;
311 result += " {" + type_name + "}";
314 if (!arg_name.empty()) { result += " " + arg_name; }
315 if (include_newline) { result += "\n"; }
320 // Generate an enum declaration and an enum string lookup table.
321 void GenEnum(EnumDef &enum_def, std::string *code_ptr,
322 std::string *exports_ptr, reexport_map &reexports,
324 if (enum_def.generated) return;
325 if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME.
326 std::string &code = *code_ptr;
327 std::string &exports = *exports_ptr;
328 GenDocComment(enum_def.doc_comment, code_ptr,
329 reverse ? "@enum {string}" : "@enum {number}");
330 std::string ns = GetNameSpace(enum_def);
331 std::string enum_def_name = enum_def.name + (reverse ? "Name" : "");
332 if (lang_.language == IDLOptions::kTs) {
333 if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
334 code += "export enum " + enum_def.name + "{\n";
336 if (enum_def.defined_namespace->components.empty()) {
338 if (parser_.opts.use_goog_js_export_format) {
339 exports += "goog.exportSymbol('" + enum_def_name + "', " +
340 enum_def.name + ");\n";
341 } else if (parser_.opts.use_ES6_js_export_format) {
342 exports += "export {" + enum_def_name + "};\n";
344 exports += "this." + enum_def_name + " = " + enum_def_name + ";\n";
347 code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n";
349 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
351 if (!ev.doc_comment.empty()) {
352 if (it != enum_def.Vals().begin()) { code += '\n'; }
353 GenDocComment(ev.doc_comment, code_ptr, "", " ");
356 // Generate mapping between EnumName: EnumValue(int)
358 code += " '" + enum_def.ToString(ev) + "'";
359 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
360 code += "'" + ev.name + "'";
362 code += " " + ev.name;
363 code += lang_.language == IDLOptions::kTs ? "= " : ": ";
364 code += enum_def.ToString(ev);
367 code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
369 if (ev.union_type.struct_def) {
370 ReexportDescription desc = { ev.name,
371 GetNameSpace(*ev.union_type.struct_def),
372 GetNameSpace(enum_def) };
374 std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
378 if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; }
382 static std::string GenType(const Type &type) {
383 switch (type.base_type) {
385 case BASE_TYPE_CHAR: return "Int8";
386 case BASE_TYPE_UTYPE:
387 case BASE_TYPE_UCHAR: return "Uint8";
388 case BASE_TYPE_SHORT: return "Int16";
389 case BASE_TYPE_USHORT: return "Uint16";
390 case BASE_TYPE_INT: return "Int32";
391 case BASE_TYPE_UINT: return "Uint32";
392 case BASE_TYPE_LONG: return "Int64";
393 case BASE_TYPE_ULONG: return "Uint64";
394 case BASE_TYPE_FLOAT: return "Float32";
395 case BASE_TYPE_DOUBLE: return "Float64";
396 case BASE_TYPE_STRING: return "String";
397 case BASE_TYPE_VECTOR: return GenType(type.VectorType());
398 case BASE_TYPE_STRUCT: return type.struct_def->name;
399 default: return "Table";
403 std::string GenGetter(const Type &type, const std::string &arguments) {
404 switch (type.base_type) {
405 case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
406 case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
407 case BASE_TYPE_UNION: return GenBBAccess() + ".__union" + arguments;
408 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
411 GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
412 if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
415 GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "",
417 " */ (" + getter + ")";
424 std::string GenBBAccess() {
425 return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
428 std::string GenDefaultValue(const Value &value, const std::string &context) {
429 if (value.type.enum_def) {
430 if (auto val = value.type.enum_def->FindByValue(value.constant)) {
431 if (lang_.language == IDLOptions::kTs) {
432 return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
433 value.type.enum_def->file) +
436 return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
440 GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def),
442 "} */ (" + value.constant + ")";
446 switch (value.type.base_type) {
447 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
449 case BASE_TYPE_STRING: return "null";
452 case BASE_TYPE_ULONG: {
453 int64_t constant = StringToInt(value.constant.c_str());
454 return context + ".createLong(" +
455 NumToString(static_cast<int32_t>(constant)) + ", " +
456 NumToString(static_cast<int32_t>(constant >> 32)) + ")";
459 default: return value.constant;
463 std::string GenTypeName(const Type &type, bool input,
464 bool allowNull = false) {
466 if (type.base_type == BASE_TYPE_STRING ||
467 type.base_type == BASE_TYPE_STRUCT) {
469 if (type.base_type == BASE_TYPE_STRING) {
470 name = "string|Uint8Array";
472 name = WrapInNameSpace(*type.struct_def);
474 return (allowNull) ? (name + "|null") : (name);
478 switch (type.base_type) {
479 case BASE_TYPE_BOOL: return "boolean";
481 case BASE_TYPE_ULONG: return "flatbuffers.Long";
483 if (IsScalar(type.base_type)) {
484 if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
487 return "flatbuffers.Offset";
491 // Returns the method name for use with add/put calls.
492 static std::string GenWriteMethod(const Type &type) {
493 // Forward to signed versions since unsigned versions don't exist
494 switch (type.base_type) {
495 case BASE_TYPE_UTYPE:
496 case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
497 case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
498 case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
499 case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
503 return IsScalar(type.base_type) ? MakeCamel(GenType(type))
504 : (IsStruct(type) ? "Struct" : "Offset");
507 template<typename T> static std::string MaybeAdd(T value) {
508 return value != 0 ? " + " + NumToString(value) : "";
511 template<typename T> static std::string MaybeScale(T value) {
512 return value != 1 ? " * " + NumToString(value) : "";
515 static std::string GenFileNamespacePrefix(const std::string &file) {
516 return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str()));
519 std::string GenPrefixedImport(const std::string &full_file_name,
520 const std::string &base_name) {
521 // Either keep the include path as it was
522 // or use only the base_name + kGeneratedFileNamePostfix
524 if (parser_.opts.keep_include_path) {
525 auto it = parser_.included_files_.find(full_file_name);
526 FLATBUFFERS_ASSERT(it != parser_.included_files_.end());
528 flatbuffers::StripExtension(it->second) + kGeneratedFileNamePostfix;
530 path = base_name + kGeneratedFileNamePostfix;
533 // Add the include prefix and make the path always relative
534 path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path);
535 path = std::string(".") + kPathSeparator + path;
537 return "import * as " + GenFileNamespacePrefix(full_file_name) +
538 " from \"" + path + "\";\n";
541 // Adds a source-dependent prefix, for of import * statements.
542 std::string GenPrefixedTypeName(const std::string &typeName,
543 const std::string &file) {
544 const auto basename =
545 flatbuffers::StripPath(flatbuffers::StripExtension(file));
546 if (basename == file_name_ || parser_.opts.generate_all) {
549 return GenFileNamespacePrefix(file) + "." + typeName;
552 void GenStructArgs(const StructDef &struct_def, std::string *annotations,
553 std::string *arguments, const std::string &nameprefix) {
554 for (auto it = struct_def.fields.vec.begin();
555 it != struct_def.fields.vec.end(); ++it) {
557 if (IsStruct(field.value.type)) {
558 // Generate arguments for a struct inside a struct. To ensure names
559 // don't clash, and to make it obvious these arguments are constructing
560 // a nested struct, prefix the name with the field name.
561 GenStructArgs(*field.value.type.struct_def, annotations, arguments,
562 nameprefix + field.name + "_");
565 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
566 nameprefix + field.name);
567 if (lang_.language == IDLOptions::kTs) {
568 *arguments += ", " + nameprefix + field.name + ": " +
569 GenTypeName(field.value.type, true);
571 *arguments += ", " + nameprefix + field.name;
577 static void GenStructBody(const StructDef &struct_def, std::string *body,
578 const std::string &nameprefix) {
579 *body += " builder.prep(";
580 *body += NumToString(struct_def.minalign) + ", ";
581 *body += NumToString(struct_def.bytesize) + ");\n";
583 for (auto it = struct_def.fields.vec.rbegin();
584 it != struct_def.fields.vec.rend(); ++it) {
587 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
589 if (IsStruct(field.value.type)) {
590 // Generate arguments for a struct inside a struct. To ensure names
591 // don't clash, and to make it obvious these arguments are constructing
592 // a nested struct, prefix the name with the field name.
593 GenStructBody(*field.value.type.struct_def, body,
594 nameprefix + field.name + "_");
596 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
597 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
598 *body += nameprefix + field.name + ");\n";
603 void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
604 std::string &code, std::string &object_name,
605 bool size_prefixed) {
606 if (!struct_def.fixed) {
607 GenDocComment(code_ptr,
608 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
609 GenTypeAnnotation(kParam, object_name + "=", "obj") +
610 GenTypeAnnotation(kReturns, object_name, "", false));
611 std::string sizePrefixed("SizePrefixed");
612 if (lang_.language == IDLOptions::kTs) {
613 code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
614 Verbose(struct_def, "As");
615 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
616 "):" + object_name + " {\n";
618 code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") +
619 "Root" + Verbose(struct_def, "As");
620 code += " = function(bb, obj) {\n";
622 code += " return (obj || new " + object_name;
623 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
628 void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
629 std::string &code, std::string &object_name,
630 bool size_prefixed) {
631 if (parser_.root_struct_def_ == &struct_def) {
632 std::string sizePrefixed("SizePrefixed");
635 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
636 GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset", false));
638 if (lang_.language == IDLOptions::kTs) {
639 code += "static finish" + (size_prefixed ? sizePrefixed : "") +
640 Verbose(struct_def) + "Buffer";
641 code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
643 code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") +
644 Verbose(struct_def) + "Buffer";
645 code += " = function(builder, offset) {\n";
648 code += " builder.finish(offset";
649 if (!parser_.file_identifier_.empty()) {
650 code += ", '" + parser_.file_identifier_ + "'";
653 if (parser_.file_identifier_.empty()) { code += ", undefined"; }
661 // Generate an accessor struct with constructor for a flatbuffers struct.
662 void GenStruct(const Parser &parser, StructDef &struct_def,
663 std::string *code_ptr, std::string *exports_ptr,
664 imported_fileset &imported_files) {
665 if (struct_def.generated) return;
666 std::string &code = *code_ptr;
667 std::string &exports = *exports_ptr;
669 std::string object_name;
670 std::string object_namespace = GetNameSpace(struct_def);
673 if (lang_.language == IDLOptions::kTs) {
674 object_name = struct_def.name;
675 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
676 if (!object_namespace.empty()) {
677 code += "export namespace " + object_namespace + "{\n";
679 code += "export class " + struct_def.name;
681 if (lang_.language != IDLOptions::kTs) {
684 " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
687 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
689 if (lang_.language != IDLOptions::kTs) {
691 code += " * " + GenTypeAnnotation(kType, "number", "");
694 code += " bb_pos:number = 0;\n";
696 bool isStatement = struct_def.defined_namespace->components.empty();
697 object_name = WrapInNameSpace(struct_def);
698 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
700 if (parser_.opts.use_goog_js_export_format) {
701 exports += "goog.exportSymbol('" + struct_def.name + "', " +
702 struct_def.name + ");\n";
703 } else if (parser_.opts.use_ES6_js_export_format) {
704 exports += "export {" + struct_def.name + "};\n";
707 "this." + struct_def.name + " = " + struct_def.name + ";\n";
709 code += "function " + object_name;
711 code += object_name + " = function";
715 code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
717 code += " this.bb = null;\n";
720 code += " * " + GenTypeAnnotation(kType, "number", "");
722 code += " this.bb_pos = 0;\n";
723 code += isStatement ? "}\n\n" : "};\n\n";
726 // Generate the __init method that sets the field in a pre-existing
727 // accessor object. This is to allow object reuse.
729 code += " * " + GenTypeAnnotation(kParam, "number", "i");
730 code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
731 code += " * " + GenTypeAnnotation(kReturns, object_name, "");
734 if (lang_.language == IDLOptions::kTs) {
736 "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
738 code += object_name + ".prototype.__init = function(i, bb) {\n";
741 code += " this.bb_pos = i;\n";
742 code += " this.bb = bb;\n";
743 code += " return this;\n";
746 // Generate special accessors for the table that when used as the root of a
748 GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
749 GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
751 // Generate the identifier check method
752 if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
753 !parser_.file_identifier_.empty()) {
754 GenDocComment(code_ptr,
755 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
756 GenTypeAnnotation(kReturns, "boolean", "", false));
757 if (lang_.language == IDLOptions::kTs) {
759 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
762 code += object_name + ".bufferHasIdentifier = function(bb) {\n";
765 code += " return bb.__has_identifier('" + parser_.file_identifier_;
766 code += "');\n};\n\n";
769 // Emit field accessors
770 for (auto it = struct_def.fields.vec.begin();
771 it != struct_def.fields.vec.end(); ++it) {
773 if (field.deprecated) continue;
775 " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
776 NumToString(field.value.offset) + ");\n return offset ? ";
778 // Emit a scalar field
779 if (IsScalar(field.value.type.base_type) ||
780 field.value.type.base_type == BASE_TYPE_STRING) {
782 field.doc_comment, code_ptr,
783 std::string(field.value.type.base_type == BASE_TYPE_STRING
784 ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
787 GenTypeAnnotation(kReturns,
788 GenTypeName(field.value.type, false, true),
790 if (lang_.language == IDLOptions::kTs) {
791 std::string prefix = MakeCamel(field.name, false) + "(";
792 if (field.value.type.base_type == BASE_TYPE_STRING) {
793 code += prefix + "):string|null\n";
794 code += prefix + "optionalEncoding:flatbuffers.Encoding" +
795 "):" + GenTypeName(field.value.type, false, true) + "\n";
796 code += prefix + "optionalEncoding?:any";
800 if (field.value.type.enum_def) {
803 GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
804 field.value.type.enum_def->file) +
807 if (!parser_.opts.generate_all) {
808 imported_files.insert(field.value.type.enum_def->file);
811 code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
814 code += object_name + ".prototype." + MakeCamel(field.name, false);
815 code += " = function(";
816 if (field.value.type.base_type == BASE_TYPE_STRING) {
817 code += "optionalEncoding";
822 if (struct_def.fixed) {
825 GenGetter(field.value.type,
826 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
829 std::string index = "this.bb_pos + offset";
830 if (field.value.type.base_type == BASE_TYPE_STRING) {
831 index += ", optionalEncoding";
833 code += offset_prefix +
834 GenGetter(field.value.type, "(" + index + ")") + " : " +
835 GenDefaultValue(field.value, GenBBAccess());
840 // Emit an object field
842 switch (field.value.type.base_type) {
843 case BASE_TYPE_STRUCT: {
844 auto type = WrapInNameSpace(*field.value.type.struct_def);
846 field.doc_comment, code_ptr,
847 GenTypeAnnotation(kParam, type + "=", "obj") +
848 GenTypeAnnotation(kReturns, type + "|null", "", false));
849 if (lang_.language == IDLOptions::kTs) {
851 GenPrefixedTypeName(type, field.value.type.struct_def->file);
852 code += MakeCamel(field.name, false);
853 code += "(obj?:" + type + "):" + type + "|null {\n";
856 object_name + ".prototype." + MakeCamel(field.name, false);
857 code += " = function(obj) {\n";
860 if (struct_def.fixed) {
861 code += " return (obj || new " + type;
862 code += ").__init(this.bb_pos";
864 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
866 code += offset_prefix + "(obj || new " + type + ").__init(";
867 code += field.value.type.struct_def->fixed
868 ? "this.bb_pos + offset"
869 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
870 code += ", " + GenBBAccess() + ") : null;\n";
873 if (lang_.language == IDLOptions::kTs &&
874 !parser_.opts.generate_all) {
875 imported_files.insert(field.value.type.struct_def->file);
881 case BASE_TYPE_VECTOR: {
882 auto vectortype = field.value.type.VectorType();
883 auto vectortypename = GenTypeName(vectortype, false);
884 auto inline_size = InlineSize(vectortype);
885 auto index = GenBBAccess() +
886 ".__vector(this.bb_pos + offset) + index" +
887 MaybeScale(inline_size);
888 std::string args = GenTypeAnnotation(kParam, "number", "index");
889 std::string ret_type;
890 bool is_union = false;
891 switch (vectortype.base_type) {
892 case BASE_TYPE_STRUCT:
893 args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
894 ret_type = vectortypename;
896 case BASE_TYPE_STRING:
897 args += GenTypeAnnotation(
898 kParam, "flatbuffers.Encoding=", "optionalEncoding");
899 ret_type = vectortypename;
901 case BASE_TYPE_UNION:
902 args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
903 ret_type = "?flatbuffers.Table";
906 default: ret_type = vectortypename;
909 field.doc_comment, code_ptr,
910 args + GenTypeAnnotation(kReturns, ret_type, "", false));
911 if (lang_.language == IDLOptions::kTs) {
912 std::string prefix = MakeCamel(field.name, false);
913 if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
914 prefix += "(index: number";
916 vectortypename = "T";
917 code += prefix + ", obj:T";
918 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
919 vectortypename = GenPrefixedTypeName(
920 vectortypename, vectortype.struct_def->file);
921 code += prefix + ", obj?:" + vectortypename;
923 if (!parser_.opts.generate_all) {
924 imported_files.insert(vectortype.struct_def->file);
926 } else if (vectortype.base_type == BASE_TYPE_STRING) {
927 code += prefix + "):string\n";
928 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
929 "):" + vectortypename + "\n";
930 code += prefix + ",optionalEncoding?:any";
934 code += "):" + vectortypename + "|null {\n";
937 object_name + ".prototype." + MakeCamel(field.name, false);
938 code += " = function(index";
939 if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
941 } else if (vectortype.base_type == BASE_TYPE_STRING) {
942 code += ", optionalEncoding";
947 if (vectortype.base_type == BASE_TYPE_STRUCT) {
948 code += offset_prefix + "(obj || new " + vectortypename;
950 code += vectortype.struct_def->fixed
952 : GenBBAccess() + ".__indirect(" + index + ")";
953 code += ", " + GenBBAccess() + ")";
956 index = "obj, " + index;
957 } else if (vectortype.base_type == BASE_TYPE_STRING) {
958 index += ", optionalEncoding";
960 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
963 if (field.value.type.element == BASE_TYPE_BOOL) {
965 } else if (field.value.type.element == BASE_TYPE_LONG ||
966 field.value.type.element == BASE_TYPE_ULONG) {
967 code += GenBBAccess() + ".createLong(0, 0)";
968 } else if (IsScalar(field.value.type.element)) {
969 if (field.value.type.enum_def) {
972 kType, WrapInNameSpace(*field.value.type.enum_def),
974 " */ (" + field.value.constant + ")";
985 case BASE_TYPE_UNION:
987 field.doc_comment, code_ptr,
988 GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
989 GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
991 if (lang_.language == IDLOptions::kTs) {
992 code += MakeCamel(field.name, false);
993 code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
996 object_name + ".prototype." + MakeCamel(field.name, false);
997 code += " = function(obj) {\n";
1000 code += offset_prefix +
1001 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1005 default: FLATBUFFERS_ASSERT(0);
1010 if (parser_.opts.use_goog_js_export_format) {
1011 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1012 MakeCamel(field.name, false) + "', " + object_name +
1013 ".prototype." + MakeCamel(field.name, false) + ");\n";
1016 // Adds the mutable scalar value to the output
1017 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer && !IsUnion(field.value.type)) {
1018 std::string annotations = GenTypeAnnotation(
1019 kParam, GenTypeName(field.value.type, true), "value");
1022 annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
1024 if (lang_.language == IDLOptions::kTs) {
1026 if (field.value.type.enum_def) {
1027 type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
1028 field.value.type.enum_def->file);
1030 type = GenTypeName(field.value.type, true);
1033 code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1035 code += object_name + ".prototype.mutate_" + field.name +
1036 " = function(value) {\n";
1039 code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1040 NumToString(field.value.offset) + ");\n\n";
1041 code += " if (offset === 0) {\n";
1042 code += " return false;\n";
1045 // special case for bools, which are treated as uint8
1046 code += " " + GenBBAccess() + ".write" +
1047 MakeCamel(GenType(field.value.type)) +
1048 "(this.bb_pos + offset, ";
1049 if (field.value.type.base_type == BASE_TYPE_BOOL &&
1050 lang_.language == IDLOptions::kTs) {
1054 code += "value);\n";
1055 code += " return true;\n";
1058 if (parser_.opts.use_goog_js_export_format) {
1059 exports += "goog.exportProperty(" + object_name +
1060 ".prototype, 'mutate_" + field.name + "', " + object_name +
1061 ".prototype.mutate_" + field.name + ");\n";
1065 // Emit vector helpers
1066 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1067 // Emit a length helper
1068 GenDocComment(code_ptr,
1069 GenTypeAnnotation(kReturns, "number", "", false));
1070 if (lang_.language == IDLOptions::kTs) {
1071 code += MakeCamel(field.name, false);
1072 code += "Length():number {\n" + offset_prefix;
1074 code += object_name + ".prototype." + MakeCamel(field.name, false);
1075 code += "Length = function() {\n" + offset_prefix;
1079 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
1081 if (parser_.opts.use_goog_js_export_format) {
1082 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1083 MakeCamel(field.name, false) + "Length', " + object_name +
1084 ".prototype." + MakeCamel(field.name, false) +
1088 // For scalar types, emit a typed array helper
1089 auto vectorType = field.value.type.VectorType();
1090 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1091 GenDocComment(code_ptr, GenTypeAnnotation(
1092 kReturns, GenType(vectorType) + "Array",
1095 if (lang_.language == IDLOptions::kTs) {
1096 code += MakeCamel(field.name, false);
1097 code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1100 code += object_name + ".prototype." + MakeCamel(field.name, false);
1101 code += "Array = function() {\n" + offset_prefix;
1104 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1105 ".bytes().buffer, " + GenBBAccess() +
1106 ".bytes().byteOffset + " + GenBBAccess() +
1107 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1108 ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
1110 if (parser_.opts.use_goog_js_export_format) {
1111 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1112 MakeCamel(field.name, false) + "Array', " + object_name +
1113 ".prototype." + MakeCamel(field.name, false) +
1120 // Emit a factory constructor
1121 if (struct_def.fixed) {
1122 std::string annotations =
1123 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1124 std::string arguments;
1125 GenStructArgs(struct_def, &annotations, &arguments, "");
1126 GenDocComment(code_ptr, annotations + GenTypeAnnotation(
1127 kReturns, "flatbuffers.Offset",
1130 if (lang_.language == IDLOptions::kTs) {
1131 code += "static create" + Verbose(struct_def) +
1132 "(builder:flatbuffers.Builder";
1133 code += arguments + "):flatbuffers.Offset {\n";
1135 code += object_name + ".create" + Verbose(struct_def);
1136 code += " = function(builder";
1137 code += arguments + ") {\n";
1140 GenStructBody(struct_def, &code, "");
1141 code += " return builder.offset();\n};\n\n";
1143 // Generate a method to start building a new object
1144 GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
1147 if (lang_.language == IDLOptions::kTs) {
1148 code += "static start" + Verbose(struct_def) +
1149 "(builder:flatbuffers.Builder) {\n";
1151 code += object_name + ".start" + Verbose(struct_def);
1152 code += " = function(builder) {\n";
1155 code += " builder.startObject(" +
1156 NumToString(struct_def.fields.vec.size()) + ");\n";
1159 // Generate a set of static methods that allow table construction
1160 for (auto it = struct_def.fields.vec.begin();
1161 it != struct_def.fields.vec.end(); ++it) {
1163 if (field.deprecated) continue;
1164 const auto argname = GetArgName(field);
1166 // Generate the field insertion method
1169 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1170 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
1173 if (lang_.language == IDLOptions::kTs) {
1174 code += "static add" + MakeCamel(field.name);
1175 code += "(builder:flatbuffers.Builder, " + argname + ":" +
1176 GetArgType(field) + ") {\n";
1178 code += object_name + ".add" + MakeCamel(field.name);
1179 code += " = function(builder, " + argname + ") {\n";
1182 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1183 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1184 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1185 code += argname + ", ";
1186 if (!IsScalar(field.value.type.base_type)) {
1189 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1190 code += GenDefaultValue(field.value, "builder");
1192 code += ");\n};\n\n";
1194 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1195 auto vector_type = field.value.type.VectorType();
1196 auto alignment = InlineAlignment(vector_type);
1197 auto elem_size = InlineSize(vector_type);
1199 // Generate a method to create a vector from a JavaScript array
1200 if (!IsStruct(vector_type)) {
1203 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1206 "Array.<" + GenTypeName(vector_type, true) + ">",
1208 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
1211 if (lang_.language == IDLOptions::kTs) {
1212 code += "static create" + MakeCamel(field.name);
1213 std::string type = GenTypeName(vector_type, true) + "[]";
1214 if (type == "number[]") { type += " | Uint8Array"; }
1215 code += "Vector(builder:flatbuffers.Builder, data:" + type +
1216 "):flatbuffers.Offset {\n";
1218 code += object_name + ".create" + MakeCamel(field.name);
1219 code += "Vector = function(builder, data) {\n";
1222 code += " builder.startVector(" + NumToString(elem_size);
1223 code += ", data.length, " + NumToString(alignment) + ");\n";
1224 code += " for (var i = data.length - 1; i >= 0; i--) {\n";
1225 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1226 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1227 code += "data[i]);\n";
1229 code += " return builder.endVector();\n";
1233 // Generate a method to start a vector, data to be added manually
1237 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1238 GenTypeAnnotation(kParam, "number", "numElems", false));
1240 if (lang_.language == IDLOptions::kTs) {
1241 code += "static start" + MakeCamel(field.name);
1242 code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1244 code += object_name + ".start" + MakeCamel(field.name);
1245 code += "Vector = function(builder, numElems) {\n";
1248 code += " builder.startVector(" + NumToString(elem_size);
1249 code += ", numElems, " + NumToString(alignment) + ");\n";
1254 // Generate a method to stop building a new object
1257 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1258 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
1260 if (lang_.language == IDLOptions::kTs) {
1261 code += "static end" + Verbose(struct_def);
1262 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1264 code += object_name + ".end" + Verbose(struct_def);
1265 code += " = function(builder) {\n";
1268 code += " var offset = builder.endObject();\n";
1269 for (auto it = struct_def.fields.vec.begin();
1270 it != struct_def.fields.vec.end(); ++it) {
1272 if (!field.deprecated && field.required) {
1273 code += " builder.requiredField(offset, ";
1274 code += NumToString(field.value.offset);
1275 code += "); // " + field.name + "\n";
1278 code += " return offset;\n";
1281 // Generate the methods to complete buffer construction
1282 GenerateFinisher(struct_def, code_ptr, code, object_name, false);
1283 GenerateFinisher(struct_def, code_ptr, code, object_name, true);
1285 // Generate a convenient CreateX function
1286 if (lang_.language == IDLOptions::kJs) {
1287 std::string paramDoc =
1288 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1289 for (auto it = struct_def.fields.vec.begin();
1290 it != struct_def.fields.vec.end(); ++it) {
1291 const auto &field = **it;
1292 if (field.deprecated) continue;
1294 GenTypeAnnotation(kParam, GetArgType(field), GetArgName(field));
1297 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false);
1299 GenDocComment(code_ptr, paramDoc);
1302 if (lang_.language == IDLOptions::kTs) {
1303 code += "static create" + Verbose(struct_def);
1304 code += "(builder:flatbuffers.Builder";
1306 code += object_name + ".create" + Verbose(struct_def);
1307 code += " = function(builder";
1309 for (auto it = struct_def.fields.vec.begin();
1310 it != struct_def.fields.vec.end(); ++it) {
1311 const auto &field = **it;
1312 if (field.deprecated) continue;
1314 if (lang_.language == IDLOptions::kTs) {
1315 code += ", " + GetArgName(field) + ":" + GetArgType(field);
1317 code += ", " + GetArgName(field);
1321 if (lang_.language == IDLOptions::kTs) {
1322 code += "):flatbuffers.Offset {\n";
1323 code += " " + struct_def.name + ".start" + Verbose(struct_def) +
1327 code += " " + object_name + ".start" + Verbose(struct_def) +
1331 std::string methodPrefix =
1332 lang_.language == IDLOptions::kTs ? struct_def.name : object_name;
1333 for (auto it = struct_def.fields.vec.begin();
1334 it != struct_def.fields.vec.end(); ++it) {
1335 const auto &field = **it;
1336 if (field.deprecated) continue;
1338 code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "(";
1339 code += "builder, " + GetArgName(field) + ");\n";
1342 code += " return " + methodPrefix + ".end" + Verbose(struct_def) +
1345 if (lang_.language == IDLOptions::kJs) code += "\n";
1348 if (lang_.language == IDLOptions::kTs) {
1349 if (!object_namespace.empty()) { code += "}\n"; }
1354 std::string GetArgType(const FieldDef &field) {
1355 if (field.value.type.enum_def)
1356 return GenPrefixedTypeName(GenTypeName(field.value.type, true),
1357 field.value.type.enum_def->file);
1358 return GenTypeName(field.value.type, true);
1361 static std::string GetArgName(const FieldDef &field) {
1362 auto argname = MakeCamel(field.name, false);
1363 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
1368 std::string Verbose(const StructDef &struct_def, const char *prefix = "") {
1369 return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name;
1374 bool GenerateJSTS(const Parser &parser, const std::string &path,
1375 const std::string &file_name) {
1376 jsts::JsTsGenerator generator(parser, path, file_name);
1377 return generator.generate();
1380 std::string JSTSMakeRule(const Parser &parser, const std::string &path,
1381 const std::string &file_name) {
1382 FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
1383 const auto &lang = GetJsLangParams(parser.opts.lang);
1385 std::string filebase =
1386 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1387 std::string make_rule = GeneratedFileName(path, filebase, lang) + ": ";
1389 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1390 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1391 make_rule += " " + *it;
1396 } // namespace flatbuffers