Files
flatbuffers/src/idl_gen_ts.cpp
Kevin Zhao 8afb68f074 codegen: escape string default values to prevent code injection (#8964)
String default values parsed from .fbs schemas are un-escaped by the IDL
parser (e.g., \x22 becomes a raw " byte), but code generators embed these
raw values directly into generated source code string literals. This allows
specially crafted .fbs files to break out of string literals and inject
arbitrary code into generated C++, Rust, TypeScript, and Swift source.

Fix by adding EscapeCodeGenString() helper that re-escapes string content
before embedding, and applying it to all 7 affected injection points across
5 code generators (C++, Rust, TypeScript, Swift, FBS).

Resolves the TODO comments in idl_gen_cpp.cpp and idl_gen_rust.cpp.
2026-03-18 22:01:23 -04:00

2470 lines
89 KiB
C++

/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "idl_gen_ts.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include "flatbuffers/code_generators.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/flatc.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#include "idl_namer.h"
namespace flatbuffers {
namespace {
struct ImportDefinition {
std::string name;
std::string import_statement;
std::string export_statement;
std::string bare_file_path;
std::string rel_file_path;
std::string object_name;
const Definition* dependent = nullptr;
const Definition* dependency = nullptr;
};
struct NsDefinition {
std::string path;
std::string filepath;
std::string symbolic_name;
const Namespace* ns = nullptr;
std::map<std::string, const Definition*> definitions;
};
Namer::Config TypeScriptDefaultConfig() {
return {/*types=*/Case::kKeep,
/*constants=*/Case::kUnknown,
/*methods=*/Case::kLowerCamel,
/*functions=*/Case::kLowerCamel,
/*fields=*/Case::kLowerCamel,
/*variables=*/Case::kLowerCamel,
/*variants=*/Case::kKeep,
/*enum_variant_seperator=*/"::",
/*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
/*namespaces=*/Case::kKeep,
/*namespace_seperator=*/"_",
/*object_prefix=*/"",
/*object_suffix=*/"T",
/*keyword_prefix=*/"",
/*keyword_suffix=*/"_",
/*keywords_casing=*/Namer::Config::KeywordsCasing::CaseSensitive,
/*filenames=*/Case::kDasher,
/*directories=*/Case::kDasher,
/*output_path=*/"",
/*filename_suffix=*/"_generated",
/*filename_extension=*/".ts"};
}
std::set<std::string> TypescriptKeywords() {
// List of keywords retrieved from here:
// https://github.com/microsoft/TypeScript/issues/2536
return {
"arguments", "break", "case", "catch", "class", "const",
"continue", "debugger", "default", "delete", "do", "else",
"enum", "export", "extends", "false", "finally", "for",
"function", "if", "import", "in", "instanceof", "new",
"null", "Object", "return", "super", "switch", "this",
"throw", "true", "try", "typeof", "var", "void",
"while", "with", "as", "implements", "interface", "let",
"package", "private", "protected", "public", "static", "yield",
"undefined" // Used with --ts-undefined-for-optionals
};
}
enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
template <typename T>
struct SupportsObjectAPI : std::false_type {};
template <>
struct SupportsObjectAPI<StructDef> : std::true_type {};
} // namespace
namespace ts {
// Iterate through all definitions we haven't generate code for (enums, structs,
// and tables) and output them to a single file.
class TsGenerator : public BaseGenerator {
public:
typedef std::map<std::string, ImportDefinition> import_set;
TsGenerator(const Parser& parser, const std::string& path,
const std::string& file_name)
: BaseGenerator(parser, path, file_name, "", "_", "ts"),
namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
TypescriptKeywords()),
null_keyword_(parser_.opts.ts_undefined_for_optionals ? "undefined"
: "null") {}
bool generate() {
generateEnums();
generateStructs();
if (!parser_.opts.ts_omit_entrypoint) {
generateEntry();
}
return true;
}
std::string GetTypeName(const EnumDef& def, const bool = false,
const bool force_ns_wrap = false) {
if (force_ns_wrap) {
return namer_.NamespacedType(def);
}
return namer_.Type(def);
}
std::string GetTypeName(const StructDef& def, const bool object_api = false,
const bool force_ns_wrap = false) {
if (object_api && parser_.opts.generate_object_based_api) {
if (force_ns_wrap) {
return namer_.NamespacedObjectType(def);
} else {
return namer_.ObjectType(def);
}
} else {
if (force_ns_wrap) {
return namer_.NamespacedType(def);
} else {
return namer_.Type(def);
}
}
}
// Save out the generated code for a single class while adding
// declaration boilerplate.
bool SaveType(const Definition& definition, const std::string& class_code,
import_set& imports, import_set& bare_imports) {
if (!class_code.length()) return true;
std::string code;
code += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n" +
"/* eslint-disable @typescript-eslint/no-unused-vars, "
"@typescript-eslint/no-explicit-any, "
"@typescript-eslint/no-non-null-assertion */\n\n";
for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) {
code += it->second.import_statement + "\n";
}
if (!bare_imports.empty()) code += "\n";
for (auto it = imports.begin(); it != imports.end(); it++) {
if (it->second.dependency != &definition) {
code += it->second.import_statement + "\n";
}
}
if (!imports.empty()) code += "\n\n";
code += class_code;
auto dirs = namer_.Directories(*definition.defined_namespace);
EnsureDirExists(dirs);
auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
return parser_.opts.file_saver->SaveFile(basename.c_str(), code, false);
}
void TrackNsDef(const Definition& definition, std::string type_name) {
std::string path;
std::string filepath;
std::string symbolic_name;
if (definition.defined_namespace->components.size() > 0) {
path = namer_.Directories(*definition.defined_namespace,
SkipDir::TrailingPathSeperator);
filepath = path + ".ts";
path = namer_.Directories(*definition.defined_namespace,
SkipDir::OutputPathAndTrailingPathSeparator);
symbolic_name = definition.defined_namespace->components.back();
} else {
auto def_mod_name = namer_.File(definition, SkipFile::SuffixAndExtension);
symbolic_name = file_name_;
filepath = path_ + symbolic_name + ".ts";
}
if (ns_defs_.count(path) == 0) {
NsDefinition nsDef;
nsDef.path = path;
nsDef.filepath = filepath;
nsDef.ns = definition.defined_namespace;
nsDef.definitions.insert(std::make_pair(type_name, &definition));
nsDef.symbolic_name = symbolic_name;
ns_defs_[path] = nsDef;
} else {
ns_defs_[path].definitions.insert(std::make_pair(type_name, &definition));
}
}
private:
IdlNamer namer_;
std::map<std::string, NsDefinition> ns_defs_;
std::string null_keyword_;
// Generate code for all enums.
void generateEnums() {
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
++it) {
import_set bare_imports;
import_set imports;
std::string enumcode;
auto& enum_def = **it;
GenEnum(enum_def, &enumcode, imports, false);
GenEnum(enum_def, &enumcode, imports, true);
std::string type_name = GetTypeName(enum_def);
TrackNsDef(enum_def, type_name);
SaveType(enum_def, enumcode, imports, bare_imports);
}
}
// Generate code for all structs.
void generateStructs() {
for (auto it = parser_.structs_.vec.begin();
it != parser_.structs_.vec.end(); ++it) {
import_set bare_imports;
import_set imports;
AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
auto& struct_def = **it;
std::string declcode;
GenStruct(parser_, struct_def, &declcode, imports);
std::string type_name = GetTypeName(struct_def);
TrackNsDef(struct_def, type_name);
SaveType(struct_def, declcode, imports, bare_imports);
}
}
// Generate code for a single entry point module.
void generateEntry() {
std::string code;
// add root namespace def if not already existing from defs tracking
std::string root;
if (ns_defs_.count(root) == 0) {
NsDefinition nsDef;
nsDef.path = root;
nsDef.symbolic_name = file_name_;
nsDef.filepath = path_ + file_name_ + ".ts";
nsDef.ns = parser_.empty_namespace_;
ns_defs_[nsDef.path] = nsDef;
}
for (const auto& it : ns_defs_) {
code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n" +
"/* eslint-disable @typescript-eslint/no-unused-vars, "
"@typescript-eslint/no-explicit-any, "
"@typescript-eslint/no-non-null-assertion */\n\n";
// export all definitions in ns entry point module
int export_counter = 0;
for (const auto& def : it.second.definitions) {
std::vector<std::string> rel_components;
// build path for root level vs child level
if (it.second.ns->components.size() > 0) {
rel_components.push_back(it.second.ns->components.back());
}
auto base_file_name =
namer_.File(*(def.second), SkipFile::SuffixAndExtension);
auto base_name =
namer_.Directories(it.second.ns->components, SkipDir::OutputPath) +
base_file_name;
auto ts_file_path = base_name + ".ts";
auto base_name_rel = std::string("./");
base_name_rel +=
namer_.Directories(rel_components, SkipDir::OutputPath);
base_name_rel += base_file_name;
auto ts_file_path_rel = base_name_rel + ".ts";
auto type_name = def.first;
auto fully_qualified_type_name =
it.second.ns->GetFullyQualifiedName(type_name);
auto is_struct = parser_.structs_.Lookup(fully_qualified_type_name);
code += "export { " + type_name;
if (parser_.opts.generate_object_based_api && is_struct) {
code += ", " + type_name + parser_.opts.object_suffix;
}
code += " } from '";
std::string import_extension =
parser_.opts.ts_no_import_ext ? "" : ".js";
code += base_name_rel + import_extension + "';\n";
export_counter++;
}
// re-export child namespace(s) in parent
const auto child_ns_level = it.second.ns->components.size() + 1;
for (const auto& it2 : ns_defs_) {
if (it2.second.ns->components.size() != child_ns_level) continue;
auto ts_file_path = it2.second.path + ".ts";
code += "export * as " + it2.second.symbolic_name + " from './";
int count = it2.second.ns->components.size() > 1 ? 2 : 1;
std::vector<std::string> rel_path;
std::copy(it2.second.ns->components.end() - count,
it2.second.ns->components.end(),
std::back_inserter(rel_path));
code += namer_.Directories(
rel_path, SkipDir::OutputPathAndTrailingPathSeparator) +
".js';\n";
export_counter++;
}
if (export_counter > 0) {
parser_.opts.file_saver->SaveFile(it.second.filepath.c_str(), code,
false);
}
}
}
// Generate a documentation comment, if available.
static void GenDocComment(const std::vector<std::string>& dc,
std::string* code_ptr,
const char* indent = nullptr) {
if (dc.empty()) {
// Don't output empty comment blocks with 0 lines of comment content.
return;
}
std::string& code = *code_ptr;
if (indent) code += indent;
code += "/**\n";
for (auto it = dc.begin(); it != dc.end(); ++it) {
if (indent) code += indent;
std::string safe = *it;
for (size_t pos = 0; (pos = safe.find("*/", pos)) != std::string::npos;) {
safe.replace(pos, 2, "*\\/");
pos += 3;
}
code += " *" + safe + "\n";
}
if (indent) code += indent;
code += " */\n";
}
static void GenDocComment(std::string* code_ptr) {
GenDocComment(std::vector<std::string>(), code_ptr);
}
// Generate an enum declaration and an enum string lookup table.
void GenEnum(EnumDef& enum_def, std::string* code_ptr, import_set& imports,
bool reverse) {
if (enum_def.generated) return;
if (reverse) return; // FIXME.
std::string& code = *code_ptr;
GenDocComment(enum_def.doc_comment, code_ptr);
code += "export enum ";
code += GetTypeName(enum_def);
code += " {\n";
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
auto& ev = **it;
if (!ev.doc_comment.empty()) {
if (it != enum_def.Vals().begin()) {
code += '\n';
}
GenDocComment(ev.doc_comment, code_ptr, " ");
}
// Generate mapping between EnumName: EnumValue(int)
if (reverse) {
code += " '" + enum_def.ToString(ev) + "'";
code += " = ";
code += "'" + namer_.Variant(ev) + "'";
} else {
code += " " + namer_.Variant(ev);
code += " = ";
// Unfortunately, because typescript does not support bigint enums,
// for 64-bit enums, we instead map the enum names to strings.
switch (enum_def.underlying_type.base_type) {
case BASE_TYPE_LONG:
case BASE_TYPE_ULONG: {
code += "'" + enum_def.ToString(ev) + "'";
break;
}
default:
code += enum_def.ToString(ev);
}
}
code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
}
code += "}";
if (enum_def.is_union) {
code += GenUnionConvFunc(enum_def.underlying_type, imports);
}
code += "\n";
}
static std::string GenType(const Type& type) {
switch (type.base_type) {
case BASE_TYPE_BOOL:
case BASE_TYPE_CHAR:
return "Int8";
case BASE_TYPE_UTYPE:
return GenType(GetUnionUnderlyingType(type));
case BASE_TYPE_UCHAR:
return "Uint8";
case BASE_TYPE_SHORT:
return "Int16";
case BASE_TYPE_USHORT:
return "Uint16";
case BASE_TYPE_INT:
return "Int32";
case BASE_TYPE_UINT:
return "Uint32";
case BASE_TYPE_LONG:
return "Int64";
case BASE_TYPE_ULONG:
return "Uint64";
case BASE_TYPE_FLOAT:
return "Float32";
case BASE_TYPE_DOUBLE:
return "Float64";
case BASE_TYPE_STRING:
return "String";
case BASE_TYPE_VECTOR:
return GenType(type.VectorType());
case BASE_TYPE_STRUCT:
return type.struct_def->name;
default:
return "flatbuffers.Table";
}
}
std::string GenGetter(const Type& type, const std::string& arguments) {
switch (type.base_type) {
case BASE_TYPE_STRING:
return GenBBAccess() + ".__string" + arguments;
case BASE_TYPE_STRUCT:
return GenBBAccess() + ".__struct" + arguments;
case BASE_TYPE_UNION:
if (!UnionHasStringType(*type.enum_def)) {
return GenBBAccess() + ".__union" + arguments;
}
return GenBBAccess() + ".__union_with_string" + arguments;
case BASE_TYPE_VECTOR:
return GenGetter(type.VectorType(), arguments);
default: {
auto getter = GenBBAccess() + "." + "read" + GenType(type) + arguments;
if (type.base_type == BASE_TYPE_BOOL) {
getter = "!!" + getter;
}
return getter;
}
}
}
std::string GenBBAccess() const { return "this.bb!"; }
std::string GenDefaultValue(const FieldDef& field, import_set& imports) {
if (field.IsScalarOptional()) {
return null_keyword_;
}
const auto& value = field.value;
if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION &&
value.type.base_type != BASE_TYPE_VECTOR) {
switch (value.type.base_type) {
case BASE_TYPE_ARRAY: {
std::string ret = "[";
for (auto i = 0; i < value.type.fixed_length; ++i) {
std::string enum_name =
AddImport(imports, *value.type.enum_def, *value.type.enum_def)
.name;
const EnumVal* val =
value.type.enum_def->FindByValue(value.constant);
if (val == nullptr) {
val = value.type.enum_def->MinValue();
}
std::string enum_value = namer_.Variant(*val);
ret += enum_name + "." + enum_value +
(i < value.type.fixed_length - 1 ? ", " : "");
}
ret += "]";
return ret;
}
case BASE_TYPE_LONG:
case BASE_TYPE_ULONG: {
// If the value is an enum with a 64-bit base type, we have to just
// return the bigint value directly since typescript does not support
// enums with bigint backing types.
return "BigInt('" + value.constant + "')";
}
default: {
const EnumVal* val = value.type.enum_def->FindByValue(value.constant);
if (val == nullptr) {
val = value.type.enum_def->MinValue();
}
return AddImport(imports, *value.type.enum_def, *value.type.enum_def)
.name +
"." + namer_.Variant(*val);
}
}
}
switch (value.type.base_type) {
case BASE_TYPE_BOOL:
return value.constant == "0" ? "false" : "true";
case BASE_TYPE_STRING: {
// NOTE: Strings without a default value are parsed as "0" by
// the parser, so therefore we have to treat "0" as a null-signifying
// value.
if (value.constant == "0" || value.constant == "null") {
return "null";
} else {
std::string escaped;
flatbuffers::EscapeString(value.constant.c_str(),
value.constant.length(), &escaped,
true, false);
return escaped;
}
}
case BASE_TYPE_UNION:
case BASE_TYPE_STRUCT: {
return null_keyword_;
}
case BASE_TYPE_ARRAY:
case BASE_TYPE_VECTOR:
return "[]";
case BASE_TYPE_LONG:
case BASE_TYPE_ULONG: {
return "BigInt('" + value.constant + "')";
}
default: {
if (StringIsFlatbufferNan(value.constant)) {
return "NaN";
} else if (StringIsFlatbufferPositiveInfinity(value.constant)) {
return "Infinity";
} else if (StringIsFlatbufferNegativeInfinity(value.constant)) {
return "-Infinity";
}
return value.constant;
}
}
}
std::string GenTypeName(import_set& imports, const Definition& owner,
const Type& type, bool input,
bool allowNull = false) {
if (!input) {
if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) {
std::string name;
if (IsString(type)) {
name = "string|Uint8Array";
} else {
name = AddImport(imports, owner, *type.struct_def).name;
}
return allowNull ? (name + "|" + null_keyword_) : name;
}
}
switch (type.base_type) {
case BASE_TYPE_BOOL:
return allowNull ? ("boolean|" + null_keyword_) : "boolean";
case BASE_TYPE_LONG:
case BASE_TYPE_ULONG:
return allowNull ? ("bigint|" + null_keyword_) : "bigint";
case BASE_TYPE_ARRAY: {
std::string name;
if (type.element == BASE_TYPE_LONG || type.element == BASE_TYPE_ULONG) {
name = "bigint[]";
} else if (type.element != BASE_TYPE_STRUCT) {
name = "number[]";
} else {
name = "any[]";
if (parser_.opts.generate_object_based_api) {
name = "(any|" +
GetTypeName(*type.struct_def, /*object_api =*/true) + ")[]";
}
}
return name + (allowNull ? ("|" + null_keyword_) : "");
}
default:
if (IsScalar(type.base_type)) {
if (type.enum_def) {
const auto enum_name =
AddImport(imports, owner, *type.enum_def).name;
return allowNull ? (enum_name + "|" + null_keyword_) : enum_name;
}
return allowNull ? ("number|" + null_keyword_) : "number";
}
return "flatbuffers.Offset";
}
}
static Type GetUnionUnderlyingType(const Type& type) {
if (type.enum_def != nullptr &&
type.enum_def->underlying_type.base_type != type.base_type) {
return type.enum_def->underlying_type;
} else {
return Type(BASE_TYPE_UCHAR);
}
}
static Type GetUnderlyingVectorType(const Type& vector_type) {
return (vector_type.base_type == BASE_TYPE_UTYPE)
? GetUnionUnderlyingType(vector_type)
: vector_type;
}
// Returns the method name for use with add/put calls.
std::string GenWriteMethod(const Type& type) {
// Forward to signed versions since unsigned versions don't exist
switch (type.base_type) {
case BASE_TYPE_UTYPE:
return GenWriteMethod(GetUnionUnderlyingType(type));
case BASE_TYPE_UCHAR:
return GenWriteMethod(Type(BASE_TYPE_CHAR));
case BASE_TYPE_USHORT:
return GenWriteMethod(Type(BASE_TYPE_SHORT));
case BASE_TYPE_UINT:
return GenWriteMethod(Type(BASE_TYPE_INT));
case BASE_TYPE_ULONG:
return GenWriteMethod(Type(BASE_TYPE_LONG));
default:
break;
}
return IsScalar(type.base_type) ? namer_.Type(GenType(type))
: (IsStruct(type) ? "Struct" : "Offset");
}
template <typename T>
static std::string MaybeAdd(T value) {
return value != 0 ? " + " + NumToString(value) : "";
}
template <typename T>
static std::string MaybeScale(T value) {
return value != 1 ? " * " + NumToString(value) : "";
}
void GenStructArgs(import_set& imports, const StructDef& struct_def,
const Definition& owner, std::string* arguments,
const std::string& nameprefix) {
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (IsStruct(field.value.type)) {
// Generate arguments for a struct inside a struct. To ensure names
// don't clash, and to make it obvious these arguments are constructing
// a nested struct, prefix the name with the field name.
GenStructArgs(imports, *field.value.type.struct_def, owner, arguments,
nameprefix + field.name + "_");
} else {
*arguments += ", " + nameprefix + field.name + ": " +
GenTypeName(imports, owner, field.value.type, true,
field.IsOptional());
}
}
}
void GenStructBody(const StructDef& struct_def, std::string* body,
const std::string& nameprefix) {
*body += " builder.prep(";
*body += NumToString(struct_def.minalign) + ", ";
*body += NumToString(struct_def.bytesize) + ");\n";
for (auto it = struct_def.fields.vec.rbegin();
it != struct_def.fields.vec.rend(); ++it) {
auto& field = **it;
if (field.padding) {
*body += " builder.pad(" + NumToString(field.padding) + ");\n";
}
if (IsStruct(field.value.type)) {
// Generate arguments for a struct inside a struct. To ensure names
// don't clash, and to make it obvious these arguments are constructing
// a nested struct, prefix the name with the field name.
GenStructBody(
*field.value.type.struct_def, body,
nameprefix.length() ? nameprefix + "_" + field.name : field.name);
} else {
auto element_type = field.value.type.element;
if (field.value.type.base_type == BASE_TYPE_ARRAY) {
switch (field.value.type.element) {
case BASE_TYPE_STRUCT: {
std::string str_last_item_idx =
NumToString(field.value.type.fixed_length - 1);
*body += "\n for (let i = " + str_last_item_idx +
"; i >= 0; --i" + ") {\n";
std::string fname = nameprefix.length()
? nameprefix + "_" + field.name
: field.name;
*body += " const item = " + fname + "?.[i];\n\n";
if (parser_.opts.generate_object_based_api) {
*body += " if (item instanceof " +
GetTypeName(*field.value.type.struct_def,
/*object_api =*/true) +
") {\n";
*body += " item.pack(builder);\n";
*body += " continue;\n";
*body += " }\n\n";
}
std::string class_name =
GetPrefixedName(*field.value.type.struct_def);
std::string pack_func_create_call =
class_name + ".create" + class_name + "(builder,\n";
pack_func_create_call +=
" " +
GenStructMemberValueTS(*field.value.type.struct_def, "item",
",\n ", false) +
"\n ";
*body += " " + pack_func_create_call;
*body += " );\n }\n\n";
break;
}
default: {
std::string str_last_item_idx =
NumToString(field.value.type.fixed_length - 1);
std::string fname = nameprefix.length()
? nameprefix + "_" + field.name
: field.name;
*body += "\n for (let i = " + str_last_item_idx +
"; i >= 0; --i) {\n";
*body += " builder.write";
*body += GenWriteMethod(
static_cast<flatbuffers::Type>(field.value.type.element));
*body += "(";
*body += element_type == BASE_TYPE_BOOL ? "+" : "";
if (element_type == BASE_TYPE_LONG ||
element_type == BASE_TYPE_ULONG) {
*body += "BigInt(" + fname + "?.[i] ?? 0));\n";
} else {
*body += "(" + fname + "?.[i] ?? 0));\n\n";
}
*body += " }\n\n";
break;
}
}
} else {
std::string fname =
nameprefix.length() ? nameprefix + "_" + field.name : field.name;
*body += " builder.write" + GenWriteMethod(field.value.type) + "(";
if (field.value.type.base_type == BASE_TYPE_BOOL) {
*body += "Number(Boolean(" + fname + ")));\n";
continue;
} else if (field.value.type.base_type == BASE_TYPE_LONG ||
field.value.type.base_type == BASE_TYPE_ULONG) {
*body += "BigInt(" + fname + " ?? 0));\n";
continue;
}
*body += fname + ");\n";
}
}
}
}
std::string GenerateNewExpression(const std::string& object_name) {
return "new " + namer_.Type(object_name) + "()";
}
void GenerateRootAccessor(StructDef& struct_def, std::string* code_ptr,
std::string& code, const std::string& object_name,
bool size_prefixed) {
if (!struct_def.fixed) {
GenDocComment(code_ptr);
std::string sizePrefixed("SizePrefixed");
code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" +
GetPrefixedName(struct_def, "As");
code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
"):" + object_name + " {\n";
if (size_prefixed) {
code +=
" bb.setPosition(bb.position() + "
"flatbuffers.SIZE_PREFIX_LENGTH);\n";
}
code += " return (obj || " + GenerateNewExpression(object_name);
code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
code += "}\n\n";
}
}
void GenerateFinisher(StructDef& struct_def, std::string* code_ptr,
std::string& code, bool size_prefixed) {
if (parser_.root_struct_def_ == &struct_def) {
std::string sizePrefixed("SizePrefixed");
GenDocComment(code_ptr);
code += "static finish" + (size_prefixed ? sizePrefixed : "") +
GetPrefixedName(struct_def) + "Buffer";
code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
code += " builder.finish(offset";
if (!parser_.file_identifier_.empty()) {
code += ", '" + parser_.file_identifier_ + "'";
}
if (size_prefixed) {
if (parser_.file_identifier_.empty()) {
code += ", undefined";
}
code += ", true";
}
code += ");\n";
code += "}\n\n";
}
}
bool UnionHasStringType(const EnumDef& union_enum) {
return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
[](const EnumVal* ev) {
return !ev->IsZero() && IsString(ev->union_type);
});
}
std::string GenUnionGenericTypeTS(const EnumDef& union_enum) {
// TODO: make it work without any
// return std::string("T") + (UnionHasStringType(union_enum) ? "|string" :
// "");
return std::string("any") +
(UnionHasStringType(union_enum) ? "|string" : "");
}
std::string GenUnionTypeTS(const EnumDef& union_enum, import_set& imports) {
std::string ret;
std::set<std::string> type_list;
for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
++it) {
const auto& ev = **it;
if (ev.IsZero()) {
continue;
}
std::string type = "";
if (IsString(ev.union_type)) {
type = "string"; // no need to wrap string type in namespace
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
} else {
FLATBUFFERS_ASSERT(false);
}
type_list.insert(type);
}
for (auto it = type_list.begin(); it != type_list.end(); ++it) {
ret += *it + ((std::next(it) == type_list.end()) ? "" : "|");
}
return ret;
}
static bool CheckIfNameClashes(const import_set& imports,
const std::string& name) {
// TODO: this would be better as a hashset.
for (auto it = imports.begin(); it != imports.end(); it++) {
if (it->second.name == name) {
return true;
}
}
return false;
}
std::string GenSymbolExpression(const StructDef& struct_def,
const bool has_name_clash,
const std::string& import_name,
const std::string& name,
const std::string& object_name) {
std::string symbols_expression;
if (has_name_clash) {
// We have a name clash
symbols_expression += import_name + " as " + name;
if (parser_.opts.generate_object_based_api) {
symbols_expression += ", " +
GetTypeName(struct_def, /*object_api =*/true) +
" as " + object_name;
}
} else {
// No name clash, use the provided name
symbols_expression += name;
if (parser_.opts.generate_object_based_api) {
symbols_expression += ", " + object_name;
}
}
return symbols_expression;
}
std::string GenSymbolExpression(const EnumDef& enum_def,
const bool has_name_clash,
const std::string& import_name,
const std::string& name, const std::string&) {
std::string symbols_expression;
if (has_name_clash) {
symbols_expression += import_name + " as " + name;
} else {
symbols_expression += name;
}
if (enum_def.is_union) {
symbols_expression += (", " + namer_.Function("unionTo" + name));
symbols_expression += (", " + namer_.Function("unionListTo" + name));
}
return symbols_expression;
}
std::vector<std::string> PathComponents(const std::string& path) const {
std::vector<std::string> components;
size_t start = 0;
while (start < path.size()) {
auto end = path.find(kPathSeparator, start);
if (end == std::string::npos) end = path.size();
if (end > start) {
components.emplace_back(path.substr(start, end - start));
}
if (end == path.size()) break;
start = end + 1;
}
return components;
}
std::string RelativeDirectory(const std::vector<std::string>& from,
const std::vector<std::string>& to) const {
size_t common = 0;
while (common < from.size() && common < to.size() &&
from[common] == to[common]) {
++common;
}
std::string rel;
const size_t ups = from.size() - common;
if (ups == 0) {
rel = ".";
} else {
for (size_t i = 0; i < ups; ++i) {
if (!rel.empty()) rel += kPathSeparator;
rel += "..";
}
}
for (size_t i = common; i < to.size(); ++i) {
if (!rel.empty()) rel += kPathSeparator;
rel += to[i];
}
return rel;
}
template <typename DefinitionT>
ImportDefinition AddImport(import_set& imports, const Definition& dependent,
const DefinitionT& dependency) {
// The unique name of the dependency, fully qualified in its namespace.
const std::string unique_name = GetTypeName(
dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
// Look if we have already added this import and return its name if found.
const auto import_pair = imports.find(unique_name);
if (import_pair != imports.end()) {
return import_pair->second;
}
// Check if this name would have a name clash with another type. Just use
// the "base" name (properly escaped) without any namespacing applied.
const std::string import_name = GetTypeName(dependency);
const bool has_name_clash = CheckIfNameClashes(imports, import_name);
// If we have a name clash, use the unique name, otherwise use simple name.
std::string name = has_name_clash ? unique_name : import_name;
const std::string object_name =
GetTypeName(dependency, /*object_api=*/true, has_name_clash);
const std::string symbols_expression = GenSymbolExpression(
dependency, has_name_clash, import_name, name, object_name);
const Namespace* dependent_ns = dependent.defined_namespace
? dependent.defined_namespace
: parser_.empty_namespace_;
const Namespace* dependency_ns = dependency.defined_namespace
? dependency.defined_namespace
: parser_.empty_namespace_;
const std::string dependent_dirs =
namer_.Directories(*dependent_ns, SkipDir::OutputPath);
const std::string dependency_dirs =
namer_.Directories(*dependency_ns, SkipDir::OutputPath);
const auto dependent_components = PathComponents(dependent_dirs);
const auto dependency_components = PathComponents(dependency_dirs);
std::string rel_dir =
RelativeDirectory(dependent_components, dependency_components);
if (rel_dir.empty()) rel_dir = ".";
if (!rel_dir.empty()) rel_dir += kPathSeparator;
std::string rel_file_path =
rel_dir + namer_.File(dependency, SkipFile::SuffixAndExtension);
std::string bare_file_path =
kPathSeparator + dependency_dirs +
namer_.File(dependency, SkipFile::SuffixAndExtension);
ImportDefinition import;
import.name = name;
import.object_name = object_name;
import.bare_file_path = bare_file_path;
import.rel_file_path = rel_file_path;
std::string import_extension = parser_.opts.ts_no_import_ext ? "" : ".js";
import.import_statement = "import { " + symbols_expression + " } from '" +
rel_file_path + import_extension + "';";
import.export_statement = "export { " + symbols_expression + " } from '." +
bare_file_path + import_extension + "';";
import.dependency = &dependency;
import.dependent = &dependent;
imports.insert(std::make_pair(unique_name, import));
return import;
}
void AddImport(import_set& imports, std::string import_name,
std::string fileName) {
ImportDefinition import;
import.name = import_name;
import.import_statement =
"import " + import_name + " from '" + fileName + "';";
imports.insert(std::make_pair(import_name, import));
}
// Generate a TS union type based on a union's enum
std::string GenObjApiUnionTypeTS(import_set& imports,
const StructDef& dependent,
const IDLOptions&,
const EnumDef& union_enum) {
std::string ret = "";
std::set<std::string> type_list;
for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end();
++it) {
const auto& ev = **it;
if (ev.IsZero()) {
continue;
}
std::string type = "";
if (IsString(ev.union_type)) {
type = "string"; // no need to wrap string type in namespace
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
type = AddImport(imports, dependent, *ev.union_type.struct_def)
.object_name;
} else {
FLATBUFFERS_ASSERT(false);
}
type_list.insert(type);
}
size_t totalPrinted = 0;
for (auto it = type_list.begin(); it != type_list.end(); ++it) {
++totalPrinted;
ret += *it + ((totalPrinted == type_list.size()) ? "" : "|");
}
return ret;
}
std::string GenUnionConvFuncName(const EnumDef& enum_def) {
return namer_.Function("unionTo", enum_def);
}
std::string GenUnionListConvFuncName(const EnumDef& enum_def) {
return namer_.Function("unionListTo", enum_def);
}
std::string GenUnionConvFunc(const Type& union_type, import_set& imports) {
if (union_type.enum_def) {
const auto& enum_def = *union_type.enum_def;
const auto valid_union_type = GenUnionTypeTS(enum_def, imports);
const auto valid_union_type_with_null =
valid_union_type + "|" + null_keyword_;
auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
"(\n type: " + GetTypeName(enum_def) +
",\n accessor: (obj:" + valid_union_type + ") => " +
valid_union_type_with_null +
"\n): " + valid_union_type_with_null + " {\n";
const auto enum_type = AddImport(imports, enum_def, enum_def).name;
const auto union_enum_loop = [&](const std::string& accessor_str) {
ret += " switch(" + enum_type + "[type]) {\n";
ret += " case 'NONE': return " + null_keyword_ + "; \n";
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
const auto& ev = **it;
if (ev.IsZero()) {
continue;
}
ret += " case '" + namer_.Variant(ev) + "': ";
if (IsString(ev.union_type)) {
ret += "return " + accessor_str + "'') as string;";
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
const auto type =
AddImport(imports, enum_def, *ev.union_type.struct_def).name;
ret += "return " + accessor_str + "new " + type + "())! as " +
type + ";";
} else {
FLATBUFFERS_ASSERT(false);
}
ret += "\n";
}
ret += " default: return " + null_keyword_ + ";\n";
ret += " }\n";
};
union_enum_loop("accessor(");
ret += "}";
ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
"(\n type: " + GetTypeName(enum_def) +
", \n accessor: (index: number, obj:" + valid_union_type +
") => " + valid_union_type_with_null +
", \n index: number\n): " + valid_union_type_with_null + " {\n";
union_enum_loop("accessor(index, ");
ret += "}";
return ret;
}
FLATBUFFERS_ASSERT(0);
return "";
}
// Used for generating a short function that returns the correct class
// based on union enum type. Assume the context is inside the non object api
// type
std::string GenUnionValTS(import_set& imports, const StructDef& dependent,
const std::string& field_name,
const Type& union_type,
const bool is_array = false) {
if (union_type.enum_def) {
const auto& enum_def = *union_type.enum_def;
const auto enum_type = AddImport(imports, dependent, enum_def).name;
const std::string union_accessor = "this." + field_name;
const auto union_has_string = UnionHasStringType(enum_def);
const auto field_binded_method = "this." + field_name + ".bind(this)";
std::string ret;
if (!is_array) {
const auto conversion_function = GenUnionConvFuncName(enum_def);
ret = "(() => {\n";
ret += " const temp = " + conversion_function + "(this." +
namer_.Method(field_name, "Type") + "(), " +
field_binded_method + ");\n";
ret += " if(temp === " + null_keyword_ + ") { return " +
null_keyword_ + "; }\n";
ret += union_has_string
? " if(typeof temp === 'string') { return temp; }\n"
: "";
ret += " return temp.unpack()\n";
ret += " })()";
} else {
const auto conversion_function = GenUnionListConvFuncName(enum_def);
ret = "(() => {\n";
ret += " const ret: (" +
GenObjApiUnionTypeTS(imports, *union_type.struct_def,
parser_.opts, *union_type.enum_def) +
")[] = [];\n";
ret += " for(let targetEnumIndex = 0; targetEnumIndex < this." +
namer_.Method(field_name, "TypeLength") + "()" +
"; "
"++targetEnumIndex) {\n";
ret += " const targetEnum = this." +
namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
ret += " if(targetEnum === " + null_keyword_ + " || " + enum_type +
"[targetEnum!] === 'NONE') { "
"continue; }\n\n";
ret += " const temp = " + conversion_function + "(targetEnum, " +
field_binded_method + ", targetEnumIndex);\n";
ret += " if(temp === " + null_keyword_ + ") { continue; }\n";
ret += union_has_string ? " if(typeof temp === 'string') { "
"ret.push(temp); continue; }\n"
: "";
ret += " ret.push(temp.unpack());\n";
ret += " }\n";
ret += " return ret;\n";
ret += " })()";
}
return ret;
}
FLATBUFFERS_ASSERT(0);
return "";
}
std::string GenNullCheckConditional(const std::string& nullCheckVar,
const std::string& trueVal,
const std::string& falseVal = "") {
std::string false_val = falseVal.empty() ? null_keyword_ : falseVal;
return "(" + nullCheckVar + " !== " + null_keyword_ + " ? " + trueVal +
" : " + false_val + ")";
}
std::string GenStructMemberValueTS(const StructDef& struct_def,
const std::string& prefix,
const std::string& delimiter,
const bool nullCheck = true) {
std::string ret;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
auto curr_member_accessor = prefix + "." + namer_.Method(field);
if (prefix != "this") {
curr_member_accessor = prefix + "?." + namer_.Method(field);
}
if (IsStruct(field.value.type)) {
ret += GenStructMemberValueTS(*field.value.type.struct_def,
curr_member_accessor, delimiter);
} else {
if (nullCheck) {
std::string nullValue = "0";
if (field.value.type.base_type == BASE_TYPE_BOOL) {
nullValue = "false";
} else if (field.value.type.base_type == BASE_TYPE_LONG ||
field.value.type.base_type == BASE_TYPE_ULONG) {
nullValue = "BigInt(0)";
} else if (field.value.type.base_type == BASE_TYPE_ARRAY) {
nullValue = "[]";
}
ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
} else {
ret += curr_member_accessor;
}
}
if (std::next(it) != struct_def.fields.vec.end()) {
ret += delimiter;
}
}
return ret;
}
void GenObjApi(const Parser& parser, StructDef& struct_def,
std::string& obj_api_unpack_func, std::string& obj_api_class,
import_set& imports) {
const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
std::string unpack_func = "\nunpack(): " + class_name +
" {\n return new " + class_name + "(" +
(struct_def.fields.vec.empty() ? "" : "\n");
std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" +
+(struct_def.fields.vec.empty() ? "" : "\n");
std::string constructor_func = "constructor(";
constructor_func += (struct_def.fields.vec.empty() ? "" : "\n");
const auto has_create =
struct_def.fixed || CanCreateFactoryMethod(struct_def);
std::string pack_func_prototype =
"\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n";
std::string pack_func_offset_decl;
std::string pack_func_create_call;
const auto struct_name = AddImport(imports, struct_def, struct_def).name;
if (has_create) {
pack_func_create_call = " return " + struct_name + ".create" +
GetPrefixedName(struct_def) + "(builder" +
(struct_def.fields.vec.empty() ? "" : ",\n ");
} else {
pack_func_create_call = " " + struct_name + ".start" +
GetPrefixedName(struct_def) + "(builder);\n";
}
if (struct_def.fixed) {
// when packing struct, nested struct's members instead of the struct's
// offset are used
pack_func_create_call +=
GenStructMemberValueTS(struct_def, "this", ",\n ", false) + "\n ";
}
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (field.deprecated) continue;
const auto field_method = namer_.Method(field);
const auto field_field = namer_.Field(field);
const std::string field_binded_method =
"this." + field_method + ".bind(this)";
std::string field_val;
std::string field_type;
// a string that declares a variable containing the
// offset for things that can't be generated inline
// empty otw
std::string field_offset_decl;
// a string that contains values for things that can be created inline or
// the variable name from field_offset_decl
std::string field_offset_val;
const auto field_default_val = GenDefaultValue(field, imports);
// Emit a scalar field
const auto is_string = IsString(field.value.type);
if (IsScalar(field.value.type.base_type) || is_string) {
field_type +=
GenTypeName(imports, field, field.value.type, false,
!HasDefaultValue(field) || HasNullDefault(field));
field_val = "this." + namer_.Method(field) + "()";
if (field.value.type.base_type != BASE_TYPE_STRING) {
field_offset_val = "this." + namer_.Field(field);
} else {
field_offset_decl = GenNullCheckConditional(
"this." + namer_.Field(field),
"builder.createString(this." + field_field + "!)", "0");
}
}
// Emit an object field
else {
auto is_vector = false;
switch (field.value.type.base_type) {
case BASE_TYPE_STRUCT: {
const auto& sd = *field.value.type.struct_def;
field_type += AddImport(imports, struct_def, sd).object_name;
const std::string field_accessor =
"this." + namer_.Method(field) + "()";
field_val = GenNullCheckConditional(
field_accessor, field_accessor + "!.unpack()", null_keyword_);
auto packing = GenNullCheckConditional(
"this." + field_field,
"this." + field_field + "!.pack(builder)", "0");
if (sd.fixed) {
field_offset_val = std::move(packing);
} else {
field_offset_decl = std::move(packing);
}
break;
}
case BASE_TYPE_ARRAY: {
auto vectortype = field.value.type.VectorType();
auto vectortypename =
GenTypeName(imports, struct_def, vectortype, false);
is_vector = true;
field_type = "(";
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT: {
const auto& sd = *field.value.type.struct_def;
const auto field_type_name =
GetTypeName(sd, /*object_api=*/true);
field_type += field_type_name;
field_type += ")[]";
field_val = GenBBAccess() + ".createObjList<" + vectortypename +
", " + field_type_name + ">(" +
field_binded_method + ", " +
NumToString(field.value.type.fixed_length) + ")";
if (sd.fixed) {
field_offset_decl =
"builder.createStructOffsetList(this." + field_field +
", " + AddImport(imports, struct_def, struct_def).name +
"." + namer_.Method("start", field, "Vector") + ")";
} else {
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
field_field + "))";
}
break;
}
case BASE_TYPE_STRING: {
field_type += "string)[]";
field_val = GenBBAccess() + ".createScalarList<string>(" +
field_binded_method + ", this." +
namer_.Field(field, "Length") + "())";
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
namer_.Field(field) + "))";
break;
}
case BASE_TYPE_UNION: {
field_type += GenObjApiUnionTypeTS(
imports, struct_def, parser.opts, *(vectortype.enum_def));
field_type += ")[]";
field_val = GenUnionValTS(imports, struct_def, field_method,
vectortype, true);
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
namer_.Field(field) + "))";
break;
}
default: {
if (vectortype.enum_def) {
field_type += GenTypeName(imports, struct_def, vectortype,
false, HasNullDefault(field));
} else {
field_type += vectortypename;
}
field_type += ")[]";
field_val = GenBBAccess() + ".createScalarList<" +
vectortypename + ">(" + field_binded_method + ", " +
NumToString(field.value.type.fixed_length) + ")";
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, this." + field_field + ")";
break;
}
}
break;
}
case BASE_TYPE_VECTOR: {
auto vectortype = field.value.type.VectorType();
auto vectortypename =
GenTypeName(imports, struct_def, vectortype, false);
is_vector = true;
field_type = "(";
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT: {
const auto& sd = *field.value.type.struct_def;
const auto field_type_name =
GetTypeName(sd, /*object_api=*/true);
field_type += field_type_name;
field_type += ")[]";
field_val = GenBBAccess() + ".createObjList<" + vectortypename +
", " + field_type_name + ">(" +
field_binded_method + ", this." +
namer_.Method(field, "Length") + "())";
if (sd.fixed) {
field_offset_decl =
"builder.createStructOffsetList(this." + field_field +
", " + AddImport(imports, struct_def, struct_def).name +
"." + namer_.Method("start", field, "Vector") + ")";
} else {
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
field_field + "))";
}
break;
}
case BASE_TYPE_STRING: {
field_type += "string)[]";
field_val = GenBBAccess() + ".createScalarList<string>(" +
field_binded_method + ", this." +
namer_.Field(field, "Length") + "())";
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
namer_.Field(field) + "))";
break;
}
case BASE_TYPE_UNION: {
field_type += GenObjApiUnionTypeTS(
imports, struct_def, parser.opts, *(vectortype.enum_def));
field_type += ")[]";
field_val = GenUnionValTS(imports, struct_def, field_method,
vectortype, true);
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, builder.createObjectOffsetList(" + "this." +
namer_.Field(field) + "))";
break;
}
default: {
if (vectortype.enum_def) {
field_type += GenTypeName(imports, struct_def, vectortype,
false, HasNullDefault(field));
} else {
field_type += vectortypename;
}
field_type += ")[]";
field_val = GenBBAccess() + ".createScalarList<" +
vectortypename + ">(" + field_binded_method +
", this." + namer_.Method(field, "Length") + "())";
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
namer_.Method("create", field, "Vector") +
"(builder, this." + field_field + ")";
break;
}
}
break;
}
case BASE_TYPE_UNION: {
field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
*(field.value.type.enum_def));
field_val = GenUnionValTS(imports, struct_def, field_method,
field.value.type);
field_offset_decl =
"builder.createObjectOffset(this." + field_field + ")";
break;
}
default:
FLATBUFFERS_ASSERT(0);
break;
}
// length 0 vector is simply empty instead of null/undefined.
field_type += is_vector ? "" : ("|" + null_keyword_);
}
if (!field_offset_decl.empty()) {
field_offset_decl =
" const " + field_field + " = " + field_offset_decl + ";";
}
if (field_offset_val.empty()) {
field_offset_val = field_field;
}
unpack_func += " " + field_val;
unpack_to_func += " _o." + field_field + " = " + field_val + ";";
// FIXME: if field_type and field_field are identical, then
// this generates invalid typescript.
constructor_func += " public " + field_field + ": " + field_type +
" = " + field_default_val;
if (!struct_def.fixed) {
if (!field_offset_decl.empty()) {
pack_func_offset_decl += field_offset_decl + "\n";
}
if (has_create) {
pack_func_create_call += field_offset_val;
} else {
if (field.IsScalarOptional()) {
pack_func_create_call +=
" if (" + field_offset_val + " !== " + null_keyword_ + ")\n ";
}
pack_func_create_call += " " + struct_name + "." +
namer_.Method("add", field) + "(builder, " +
field_offset_val + ");\n";
}
}
if (std::next(it) != struct_def.fields.vec.end()) {
constructor_func += ",\n";
if (!struct_def.fixed && has_create) {
pack_func_create_call += ",\n ";
}
unpack_func += ",\n";
unpack_to_func += "\n";
} else {
constructor_func += "\n";
if (!struct_def.fixed) {
pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n");
pack_func_create_call += "\n ";
}
unpack_func += "\n ";
unpack_to_func += "\n";
}
}
constructor_func += "){}\n\n";
if (has_create) {
pack_func_create_call += ");";
} else {
pack_func_create_call += "return " + struct_name + ".end" +
GetPrefixedName(struct_def) + "(builder);";
}
obj_api_class = "\n";
obj_api_class += "export class ";
obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
obj_api_class += " implements flatbuffers.IGeneratedObject {\n";
obj_api_class += constructor_func;
obj_api_class += pack_func_prototype + pack_func_offset_decl +
pack_func_create_call + "\n}";
obj_api_class += "\n}\n";
unpack_func += ");\n}";
unpack_to_func += "}\n";
obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func;
}
static bool CanCreateFactoryMethod(const StructDef& struct_def) {
// to preserve backwards compatibility, we allow the first field to be a
// struct
return struct_def.fields.vec.size() < 2 ||
std::all_of(
std::begin(struct_def.fields.vec) + 1,
std::end(struct_def.fields.vec),
[](const FieldDef* f) -> bool {
FLATBUFFERS_ASSERT(f != nullptr);
return f->value.type.base_type != BASE_TYPE_STRUCT;
});
}
// Generate an accessor struct with constructor for a flatbuffers struct.
void GenStruct(const Parser& parser, StructDef& struct_def,
std::string* code_ptr, import_set& imports) {
if (struct_def.generated) return;
std::string& code = *code_ptr;
// Special case for the root struct, since no one will necessarily reference
// it, we have to explicitly add it to the import list.
if (&struct_def == parser_.root_struct_def_) {
AddImport(imports, struct_def, struct_def);
}
const std::string object_name = GetTypeName(struct_def);
const std::string object_api_name = GetTypeName(struct_def, true);
// Emit constructor
GenDocComment(struct_def.doc_comment, code_ptr);
code += "export class ";
code += object_name;
if (parser.opts.generate_object_based_api) {
code += " implements flatbuffers.IUnpackableObject<" + object_api_name +
"> {\n";
} else {
code += " {\n";
}
code += " bb: flatbuffers.ByteBuffer|" + null_keyword_ + " = " +
null_keyword_ + ";\n";
code += " bb_pos = 0;\n";
// Generate the __init method that sets the field in a pre-existing
// accessor object. This is to allow object reuse.
code +=
" __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
code += " this.bb_pos = i;\n";
code += " this.bb = bb;\n";
code += " return this;\n";
code += "}\n\n";
// Generate special accessors for the table that when used as the root of a
// FlatBuffer
GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
// Generate the identifier check method
if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
!parser_.file_identifier_.empty()) {
GenDocComment(code_ptr);
code +=
"static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
"{\n";
code += " return bb.__has_identifier('" + parser_.file_identifier_;
code += "');\n}\n\n";
}
// Emit field accessors
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (field.deprecated) continue;
std::string offset_prefix = "";
if (field.value.type.base_type == BASE_TYPE_ARRAY) {
offset_prefix = " return ";
} else {
offset_prefix = " const offset = " + GenBBAccess() +
".__offset(this.bb_pos, " +
NumToString(field.value.offset) + ");\n";
offset_prefix += " return ";
}
// Emit a scalar field
const auto is_string = IsString(field.value.type);
if (IsScalar(field.value.type.base_type) || is_string) {
const auto has_null_default =
!field.IsRequired() && !HasDefaultValue(field);
GenDocComment(field.doc_comment, code_ptr);
std::string prefix = namer_.Method(field) + "(";
if (is_string) {
code += prefix + "):" + (has_null_default ? "string|" + null_keyword_ : "string") + "\n";
code +=
prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
GenTypeName(imports, struct_def, field.value.type, false, has_null_default) +
"\n";
code += prefix + "optionalEncoding?:any";
} else {
code += prefix;
}
if (field.value.type.enum_def) {
code += "):" +
GenTypeName(imports, struct_def, field.value.type, false,
field.IsOptional()) +
" {\n";
} else {
code += "):" +
GenTypeName(imports, struct_def, field.value.type, false,
has_null_default) +
" {\n";
}
if (struct_def.fixed) {
code +=
" return " +
GenGetter(field.value.type,
"(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
";\n";
} else {
std::string index = "this.bb_pos + offset";
if (is_string) {
index += ", optionalEncoding";
}
if (field.IsRequired()) {
code +=
offset_prefix + GenGetter(field.value.type, "(" + index + ")");
;
} else {
code += offset_prefix + "offset ? " +
GenGetter(field.value.type, "(" + index + ")");
}
if (field.value.type.base_type != BASE_TYPE_ARRAY &&
!field.IsRequired()) {
code += " : " + GenDefaultValue(field, imports);
}
code += ";\n";
}
}
// Emit an object field
else {
switch (field.value.type.base_type) {
case BASE_TYPE_STRUCT: {
const auto type =
AddImport(imports, struct_def, *field.value.type.struct_def)
.name;
GenDocComment(field.doc_comment, code_ptr);
code += namer_.Method(field);
code +=
"(obj?:" + type + "):" + type + "|" + null_keyword_ + " {\n";
if (struct_def.fixed) {
code += " return (obj || " + GenerateNewExpression(type);
code += ").__init(this.bb_pos";
code +=
MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
} else {
code += offset_prefix + "offset ? (obj || " +
GenerateNewExpression(type) + ").__init(";
code += field.value.type.struct_def->fixed
? "this.bb_pos + offset"
: GenBBAccess() + ".__indirect(this.bb_pos + offset)";
code += ", " + GenBBAccess() + ") : " + null_keyword_ + ";\n";
}
break;
}
case BASE_TYPE_ARRAY: {
auto vectortype = field.value.type.VectorType();
auto vectortypename =
GenTypeName(imports, struct_def, vectortype, false);
auto inline_size = InlineSize(vectortype);
auto index = "this.bb_pos + " + NumToString(field.value.offset) +
" + index" + MaybeScale(inline_size);
std::string ret_type;
bool is_union = false;
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT:
ret_type = vectortypename;
break;
case BASE_TYPE_STRING:
ret_type = vectortypename;
break;
case BASE_TYPE_UNION:
ret_type = "?flatbuffers.Table";
is_union = true;
break;
default:
ret_type = vectortypename;
}
GenDocComment(field.doc_comment, code_ptr);
std::string prefix = namer_.Method(field);
// TODO: make it work without any
// if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
if (is_union) {
prefix += "";
}
prefix += "(index: number";
if (is_union) {
const auto union_type =
GenUnionGenericTypeTS(*(field.value.type.enum_def));
vectortypename = union_type;
code += prefix + ", obj:" + union_type;
} else if (vectortype.base_type == BASE_TYPE_STRUCT) {
code += prefix + ", obj?:" + vectortypename;
} else if (IsString(vectortype)) {
code += prefix + "):string\n";
code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
"):" + vectortypename + "\n";
code += prefix + ",optionalEncoding?:any";
} else {
code += prefix;
}
code += "):" + vectortypename + "|" + null_keyword_ + " {\n";
if (vectortype.base_type == BASE_TYPE_STRUCT) {
code += offset_prefix + "(obj || " +
GenerateNewExpression(vectortypename);
code += ").__init(";
code += vectortype.struct_def->fixed
? index
: GenBBAccess() + ".__indirect(" + index + ")";
code += ", " + GenBBAccess() + ")";
} else {
if (is_union) {
index = "obj, " + index;
} else if (IsString(vectortype)) {
index += ", optionalEncoding";
}
code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
}
switch (field.value.type.base_type) {
case BASE_TYPE_ARRAY: {
break;
}
case BASE_TYPE_BOOL: {
code += " : false";
break;
}
case BASE_TYPE_LONG:
case BASE_TYPE_ULONG: {
code += " : BigInt(0)";
break;
}
default: {
if (IsScalar(field.value.type.element)) {
if (field.value.type.enum_def) {
code += field.value.constant;
} else {
code += " : 0";
}
} else {
code += ": " + null_keyword_;
}
break;
}
}
code += ";\n";
break;
}
case BASE_TYPE_VECTOR: {
auto vectortype = field.value.type.VectorType();
auto vectortypename =
GenTypeName(imports, struct_def, vectortype, false);
auto type = GetUnderlyingVectorType(vectortype);
auto inline_size = InlineSize(type);
auto index = GenBBAccess() +
".__vector(this.bb_pos + offset) + index" +
MaybeScale(inline_size);
std::string ret_type;
bool is_union = false;
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT:
ret_type = vectortypename;
break;
case BASE_TYPE_STRING:
ret_type = vectortypename;
break;
case BASE_TYPE_UNION:
ret_type = "?flatbuffers.Table";
is_union = true;
break;
default:
ret_type = vectortypename;
}
GenDocComment(field.doc_comment, code_ptr);
std::string prefix = namer_.Method(field);
// TODO: make it work without any
// if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
if (is_union) {
prefix += "";
}
prefix += "(index: number";
if (is_union) {
const auto union_type =
GenUnionGenericTypeTS(*(field.value.type.enum_def));
vectortypename = union_type;
code += prefix + ", obj:" + union_type;
} else if (vectortype.base_type == BASE_TYPE_STRUCT) {
code += prefix + ", obj?:" + vectortypename;
} else if (IsString(vectortype)) {
code += prefix + "):string\n";
code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
"):" + vectortypename + "\n";
code += prefix + ",optionalEncoding?:any";
} else {
code += prefix;
}
code += "):" + vectortypename + "|" + null_keyword_ + " {\n";
if (vectortype.base_type == BASE_TYPE_STRUCT) {
code += offset_prefix + "offset ? (obj || " +
GenerateNewExpression(vectortypename);
code += ").__init(";
code += vectortype.struct_def->fixed
? index
: GenBBAccess() + ".__indirect(" + index + ")";
code += ", " + GenBBAccess() + ")";
} else {
if (is_union) {
index = "obj, " + index;
} else if (IsString(vectortype)) {
index += ", optionalEncoding";
}
code += offset_prefix + "offset ? " +
GenGetter(vectortype, "(" + index + ")");
}
code += " : ";
if (field.value.type.element == BASE_TYPE_BOOL) {
code += "false";
} else if (field.value.type.element == BASE_TYPE_LONG ||
field.value.type.element == BASE_TYPE_ULONG) {
code += "BigInt(0)";
} else if (IsScalar(field.value.type.element)) {
if (field.value.type.enum_def) {
code += null_keyword_;
} else {
code += "0";
}
} else {
code += null_keyword_;
}
code += ";\n";
break;
}
case BASE_TYPE_UNION: {
GenDocComment(field.doc_comment, code_ptr);
code += namer_.Method(field);
const auto& union_enum = *(field.value.type.enum_def);
const auto union_type = GenUnionGenericTypeTS(union_enum);
code += "<T extends flatbuffers.Table>(obj:" + union_type +
"):" + union_type + "|" + null_keyword_ +
" "
"{\n";
code += offset_prefix + "offset ? " +
GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
" : " + null_keyword_ + ";\n";
break;
}
default:
FLATBUFFERS_ASSERT(0);
}
}
code += "}\n\n";
// Adds the mutable scalar value to the output
if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer &&
!IsUnion(field.value.type)) {
std::string type =
GenTypeName(imports, struct_def, field.value.type, true);
code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
"):boolean {\n";
const std::string write_method =
"." + namer_.Method("write", GenType(field.value.type));
if (struct_def.fixed) {
code += " " + GenBBAccess() + write_method + "(this.bb_pos + " +
NumToString(field.value.offset) + ", ";
} else {
code += " const offset = " + GenBBAccess() +
".__offset(this.bb_pos, " + NumToString(field.value.offset) +
");\n\n";
code += " if (offset === 0) {\n";
code += " return false;\n";
code += " }\n\n";
code +=
" " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
}
// special case for bools, which are treated as uint8
if (field.value.type.base_type == BASE_TYPE_BOOL) {
code += "+";
}
code += "value);\n";
code += " return true;\n";
code += "}\n\n";
}
// Emit vector helpers
if (IsVector(field.value.type)) {
// Emit a length helper
GenDocComment(code_ptr);
code += namer_.Method(field, "Length");
code += "():number {\n" + offset_prefix + "offset ? ";
code +=
GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
// For scalar types, emit a typed array helper
auto vectorType = field.value.type.VectorType();
if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
GenDocComment(code_ptr);
code += namer_.Method(field, "Array");
std::string return_type =
(field.IsRequired() || HasDefaultValue(field))
? "Array"
: ("Array|" + null_keyword_);
if (field.IsRequired()) {
code += "():" + GenType(vectorType) + return_type + " {\n" +
offset_prefix + "new " + GenType(vectorType) + "Array(" +
GenBBAccess() + ".bytes().buffer, " + GenBBAccess() +
".bytes().byteOffset + " + GenBBAccess() +
".__vector(this.bb_pos + offset), " + GenBBAccess() +
".__vector_len(this.bb_pos + offset));\n}\n\n";
} else {
std::string value = HasDefaultValue(field)
? "new " + GenType(vectorType) + "Array()"
: "null";
code += "():" + GenType(vectorType) + return_type + " {\n" +
offset_prefix + "offset ? new " + GenType(vectorType) +
"Array(" + GenBBAccess() + ".bytes().buffer, " +
GenBBAccess() + ".bytes().byteOffset + " + GenBBAccess() +
".__vector(this.bb_pos + offset), " + GenBBAccess() +
".__vector_len(this.bb_pos + offset)) : " + value +
";\n}\n\n";
}
}
}
}
// Emit the fully qualified name
if (parser_.opts.generate_name_strings) {
const std::string fullyQualifiedName =
struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name);
GenDocComment(code_ptr);
code += "static getFullyQualifiedName(): \"";
code += fullyQualifiedName;
code += "\" {\n";
code += " return '" + fullyQualifiedName + "';\n";
code += "}\n\n";
}
// Emit the size of the struct.
if (struct_def.fixed) {
GenDocComment(code_ptr);
code += "static sizeOf():number {\n";
code += " return " + NumToString(struct_def.bytesize) + ";\n";
code += "}\n\n";
}
// Emit a factory constructor
if (struct_def.fixed) {
std::string arguments;
GenStructArgs(imports, struct_def, struct_def, &arguments, "");
GenDocComment(code_ptr);
code += "static create" + GetPrefixedName(struct_def) +
"(builder:flatbuffers.Builder";
code += arguments + "):flatbuffers.Offset {\n";
GenStructBody(struct_def, &code, "");
code += " return builder.offset();\n}\n\n";
} else {
// Generate a method to start building a new object
GenDocComment(code_ptr);
code += "static start" + GetPrefixedName(struct_def) +
"(builder:flatbuffers.Builder) {\n";
code += " builder.startObject(" +
NumToString(struct_def.fields.vec.size()) + ");\n";
code += "}\n\n";
// Generate a set of static methods that allow table construction
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (field.deprecated) continue;
const auto argname = GetArgName(field);
// Generate the field insertion method
GenDocComment(code_ptr);
code += "static " + namer_.Method("add", field);
code += "(builder:flatbuffers.Builder, " + argname + ":" +
GetArgType(imports, struct_def, field, false) + ") {\n";
code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
if (field.value.type.base_type == BASE_TYPE_BOOL) {
code += "+";
}
code += argname + ", ";
if (!IsScalar(field.value.type.base_type)) {
code += "0";
} else if (HasNullDefault(field)) {
code += null_keyword_;
} else {
if (field.value.type.base_type == BASE_TYPE_BOOL) {
code += "+";
}
code += GenDefaultValue(field, imports);
}
code += ");\n}\n\n";
if (IsVector(field.value.type)) {
auto vector_type = field.value.type.VectorType();
auto type = GetUnderlyingVectorType(vector_type);
auto alignment = InlineAlignment(type);
auto elem_size = InlineSize(type);
// Generate a method to create a vector from a JavaScript array
if (!IsStruct(vector_type)) {
GenDocComment(code_ptr);
const std::string sig_begin =
"static " + namer_.Method("create", field, "Vector") +
"(builder:flatbuffers.Builder, data:";
const std::string sig_end = "):flatbuffers.Offset";
std::string type =
GenTypeName(imports, struct_def, vector_type, true) + "[]";
if (type == "number[]") {
const auto& array_type = GenType(vector_type);
// the old type should be deprecated in the future
std::string type_old = "number[]|Uint8Array";
std::string type_new = "number[]|" + array_type + "Array";
if (type_old == type_new) {
type = type_new;
} else {
// add function overloads
code += sig_begin + type_new + sig_end + ";\n";
code +=
"/**\n * @deprecated This Uint8Array overload will "
"be removed in the future.\n */\n";
code += sig_begin + type_old + sig_end + ";\n";
type = type_new + "|Uint8Array";
}
}
code += sig_begin + type + sig_end + " {\n";
code += " builder.startVector(" + NumToString(elem_size);
code += ", data.length, " + NumToString(alignment) + ");\n";
code += " for (let i = data.length - 1; i >= 0; i--) {\n";
code += " builder.add" + GenWriteMethod(vector_type) + "(";
if (vector_type.base_type == BASE_TYPE_BOOL) {
code += "+";
}
code += "data[i]!);\n";
code += " }\n";
code += " return builder.endVector();\n";
code += "}\n\n";
}
// Generate a method to start a vector, data to be added manually
// after
GenDocComment(code_ptr);
code += "static ";
code += namer_.Method("start", field, "Vector");
code += "(builder:flatbuffers.Builder, numElems:number) {\n";
code += " builder.startVector(" + NumToString(elem_size);
code += ", numElems, " + NumToString(alignment) + ");\n";
code += "}\n\n";
}
}
// Generate a method to stop building a new object
GenDocComment(code_ptr);
code += "static end" + GetPrefixedName(struct_def);
code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
code += " const offset = builder.endObject();\n";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (!field.deprecated && field.IsRequired()) {
code += " builder.requiredField(offset, ";
code += NumToString(field.value.offset);
code += ") // " + field.name + "\n";
}
}
code += " return offset;\n";
code += "}\n\n";
// Generate the methods to complete buffer construction
GenerateFinisher(struct_def, code_ptr, code, false);
GenerateFinisher(struct_def, code_ptr, code, true);
// Generate a convenient CreateX function
if (CanCreateFactoryMethod(struct_def)) {
code += "static create" + GetPrefixedName(struct_def);
code += "(builder:flatbuffers.Builder";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto& field = **it;
if (field.deprecated) continue;
code += ", " + GetArgName(field) + ":" +
GetArgType(imports, struct_def, field, true);
}
code += "):flatbuffers.Offset {\n";
code += " " + object_name + ".start" + GetPrefixedName(struct_def) +
"(builder);\n";
std::string methodPrefix = object_name;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto& field = **it;
if (field.deprecated) continue;
const auto arg_name = GetArgName(field);
if (field.IsScalarOptional()) {
code += " if (" + arg_name + " !== " + null_keyword_ + ")\n ";
}
code += " " + methodPrefix + "." + namer_.Method("add", field) + "(";
code += "builder, " + arg_name + ");\n";
}
code += " return " + methodPrefix + ".end" +
GetPrefixedName(struct_def) + "(builder);\n";
code += "}\n";
}
}
if (!struct_def.fixed && parser_.services_.vec.size() != 0) {
auto name = GetPrefixedName(struct_def, "");
code += "\n";
code += "serialize():Uint8Array {\n";
code += " return this.bb!.bytes();\n";
code += "}\n";
code += "\n";
code += "static deserialize(buffer: Uint8Array):" +
namer_.EscapeKeyword(name) + " {\n";
code += " return " + AddImport(imports, struct_def, struct_def).name +
".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
code += "}\n";
}
if (parser_.opts.generate_object_based_api) {
std::string obj_api_class;
std::string obj_api_unpack_func;
GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class,
imports);
code += obj_api_unpack_func + "}\n" + obj_api_class;
} else {
code += "}\n";
}
}
bool HasNullDefault(const FieldDef& field) {
return field.IsOptional() && field.value.constant == "null";
}
static bool HasDefaultValue(const FieldDef& field) {
switch (field.value.type.base_type) {
// These types can't have defaults
case BASE_TYPE_UNION:
case BASE_TYPE_STRUCT: {
return false;
}
// Arrays always have a default (empty array)
case BASE_TYPE_ARRAY:
return true;
// The only supported default for vectors is []
case BASE_TYPE_VECTOR:
return field.value.constant == "[]";
// Even strings are presumed to be null-default if the default value is
// "0", this is intended behavior.
case BASE_TYPE_STRING: {
return field.value.constant != "0" && field.value.constant != "null";
}
default: {
return field.value.constant != "null";
}
}
}
std::string GetArgType(import_set& imports, const Definition& owner,
const FieldDef& field, bool allowNull) {
return GenTypeName(imports, owner, field.value.type, true,
allowNull && field.IsOptional());
}
std::string GetArgName(const FieldDef& field) {
auto argname = namer_.Variable(field);
if (!IsScalar(field.value.type.base_type)) {
argname += "Offset";
}
return argname;
}
std::string GetPrefixedName(const StructDef& struct_def,
const char* prefix = "") {
return prefix + struct_def.name;
}
}; // namespace ts
} // namespace ts
static bool GenerateTS(const Parser& parser, const std::string& path,
const std::string& file_name) {
ts::TsGenerator generator(parser, path, file_name);
return generator.generate();
}
static std::string TSMakeRule(const Parser& parser, const std::string& path,
const std::string& file_name) {
std::string filebase =
flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
ts::TsGenerator generator(parser, path, file_name);
std::string make_rule =
generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
auto included_files = parser.GetIncludedFilesRecursive(file_name);
for (auto it = included_files.begin(); it != included_files.end(); ++it) {
make_rule += " " + *it;
}
return make_rule;
}
namespace {
class TsCodeGenerator : public CodeGenerator {
public:
Status GenerateCode(const Parser& parser, const std::string& path,
const std::string& filename) override {
if (!GenerateTS(parser, path, filename)) {
return Status::ERROR;
}
return Status::OK;
}
Status GenerateCode(const uint8_t*, int64_t, const CodeGenOptions&) override {
return Status::NOT_IMPLEMENTED;
}
Status GenerateMakeRule(const Parser& parser, const std::string& path,
const std::string& filename,
std::string& output) override {
output = TSMakeRule(parser, path, filename);
return Status::OK;
}
Status GenerateGrpcCode(const Parser& parser, const std::string& path,
const std::string& filename) override {
if (!GenerateTSGRPC(parser, path, filename)) {
return Status::ERROR;
}
return Status::OK;
}
Status GenerateRootFile(const Parser& parser,
const std::string& path) override {
(void)parser;
(void)path;
return Status::NOT_IMPLEMENTED;
}
bool IsSchemaOnly() const override { return true; }
bool SupportsBfbsGeneration() const override { return false; }
bool SupportsRootFileGeneration() const override { return false; }
IDLOptions::Language Language() const override { return IDLOptions::kTs; }
std::string LanguageName() const override { return "TS"; }
};
} // namespace
std::unique_ptr<CodeGenerator> NewTsCodeGenerator() {
return std::unique_ptr<TsCodeGenerator>(new TsCodeGenerator());
}
} // namespace flatbuffers