mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 12:05:50 +00:00
* Add suppport for ES6 style exports Adds support for ECMAScript 6 module exports during Javascript generation. This is useful as many development projects are switching to this new standard and away from custom module solutions. By integrating support into flatbuffers, users do not need to perform additional post-processing of generated files in order to use flatbuffers output directly in their codebases. Reference to ECMAScript 6 modules: https://www.ecma-international.org/ecma-262/6.0/#sec-exports Changes: * Added `--es6-js-export` option to cli parser tool * Added conditional code to generate a ES6 style export line, replacing the normal NodeJS/RequireJS line. * Fixed missing export statements Added exports for definition and struct names that were not inside namespaces * Updated Compiler.md with new generator option Added entry to Compiler.md in docs for the `--es6-js-export` flag, including a brief description of the effects and usefulness.
1208 lines
46 KiB
C++
1208 lines
46 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.
|
|
*/
|
|
|
|
// independent from idl_parser, since this code is not needed for most clients
|
|
#include <cassert>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "flatbuffers/code_generators.h"
|
|
#include "flatbuffers/flatbuffers.h"
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/util.h"
|
|
|
|
namespace flatbuffers {
|
|
|
|
const std::string kGeneratedFileNamePostfix = "_generated";
|
|
|
|
struct JsLanguageParameters {
|
|
IDLOptions::Language language;
|
|
std::string file_extension;
|
|
};
|
|
|
|
struct ReexportDescription {
|
|
std::string symbol;
|
|
std::string source_namespace;
|
|
std::string target_namespace;
|
|
};
|
|
|
|
const JsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
|
|
static JsLanguageParameters js_language_parameters[] = {
|
|
{
|
|
IDLOptions::kJs,
|
|
".js",
|
|
},
|
|
{
|
|
IDLOptions::kTs,
|
|
".ts",
|
|
},
|
|
};
|
|
|
|
if (lang == IDLOptions::kJs) {
|
|
return js_language_parameters[0];
|
|
} else {
|
|
FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
|
|
return js_language_parameters[1];
|
|
}
|
|
}
|
|
|
|
static std::string GeneratedFileName(const std::string &path,
|
|
const std::string &file_name,
|
|
const JsLanguageParameters &lang) {
|
|
return path + file_name + kGeneratedFileNamePostfix + lang.file_extension;
|
|
}
|
|
|
|
namespace js {
|
|
// Iterate through all definitions we haven't generate code for (enums, structs,
|
|
// and tables) and output them to a single file.
|
|
class JsGenerator : public BaseGenerator {
|
|
public:
|
|
typedef std::unordered_set<std::string> imported_fileset;
|
|
typedef std::unordered_multimap<std::string, ReexportDescription>
|
|
reexport_map;
|
|
|
|
JsGenerator(const Parser &parser, const std::string &path,
|
|
const std::string &file_name)
|
|
: BaseGenerator(parser, path, file_name, "", "."),
|
|
lang_(GetJsLangParams(parser_.opts.lang)){};
|
|
// Iterate through all definitions we haven't generate code for (enums,
|
|
// structs, and tables) and output them to a single file.
|
|
bool generate() {
|
|
imported_fileset imported_files;
|
|
reexport_map reexports;
|
|
|
|
std::string enum_code, struct_code, import_code, exports_code, code;
|
|
generateEnums(&enum_code, &exports_code, reexports);
|
|
generateStructs(&struct_code, &exports_code, imported_files);
|
|
generateImportDependencies(&import_code, imported_files);
|
|
generateReexports(&import_code, reexports, imported_files);
|
|
|
|
code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
|
|
|
|
// Generate code for all the namespace declarations.
|
|
GenNamespaces(&code, &exports_code);
|
|
|
|
// Output the main declaration code from above.
|
|
code += import_code;
|
|
|
|
code += enum_code;
|
|
code += struct_code;
|
|
|
|
if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
|
|
!parser_.opts.skip_js_exports) {
|
|
if( parser_.opts.use_ES6_js_export_format )
|
|
code += "// Exports for ECMAScript6 Modules\n";
|
|
else
|
|
code += "// Exports for Node.js and RequireJS\n";
|
|
code += exports_code;
|
|
}
|
|
|
|
return SaveFile(GeneratedFileName(path_, file_name_, lang_).c_str(), code,
|
|
false);
|
|
}
|
|
|
|
private:
|
|
JsLanguageParameters lang_;
|
|
|
|
// Generate code for imports
|
|
void generateImportDependencies(std::string *code_ptr,
|
|
const imported_fileset &imported_files) {
|
|
std::string &code = *code_ptr;
|
|
for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
|
|
const auto &file = *it;
|
|
const auto basename =
|
|
flatbuffers::StripPath(flatbuffers::StripExtension(file));
|
|
if (basename != file_name_) {
|
|
const auto file_name = basename + kGeneratedFileNamePostfix;
|
|
code += GenPrefixedImport(file, file_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate reexports, which might not have been explicitly imported using the
|
|
// "export import" trick
|
|
void generateReexports(std::string *code_ptr, const reexport_map &reexports,
|
|
imported_fileset imported_files) {
|
|
if (!parser_.opts.reexport_ts_modules ||
|
|
lang_.language != IDLOptions::kTs) {
|
|
return;
|
|
}
|
|
|
|
std::string &code = *code_ptr;
|
|
for (auto it = reexports.begin(); it != reexports.end(); ++it) {
|
|
const auto &file = *it;
|
|
const auto basename =
|
|
flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
|
|
if (basename != file_name_) {
|
|
const auto file_name = basename + kGeneratedFileNamePostfix;
|
|
|
|
if (imported_files.find(file.first) == imported_files.end()) {
|
|
code += GenPrefixedImport(file.first, file_name);
|
|
imported_files.emplace(file.first);
|
|
}
|
|
|
|
code += "export namespace " + file.second.target_namespace + " { \n";
|
|
code += "export import " + file.second.symbol + " = ";
|
|
code += GenFileNamespacePrefix(file.first) + "." +
|
|
file.second.source_namespace + "." + file.second.symbol +
|
|
"; }\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate code for all enums.
|
|
void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
|
|
reexport_map &reexports) {
|
|
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
|
|
++it) {
|
|
auto &enum_def = **it;
|
|
GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports);
|
|
}
|
|
}
|
|
|
|
// Generate code for all structs.
|
|
void generateStructs(std::string *decl_code_ptr,
|
|
std::string *exports_code_ptr,
|
|
imported_fileset &imported_files) {
|
|
for (auto it = parser_.structs_.vec.begin();
|
|
it != parser_.structs_.vec.end(); ++it) {
|
|
auto &struct_def = **it;
|
|
GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
|
|
imported_files);
|
|
}
|
|
}
|
|
void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
|
|
if (lang_.language == IDLOptions::kTs &&
|
|
parser_.opts.skip_flatbuffers_import) {
|
|
return;
|
|
}
|
|
|
|
std::set<std::string> namespaces;
|
|
|
|
for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
|
|
++it) {
|
|
std::string namespace_so_far;
|
|
|
|
// Gather all parent namespaces for this namespace
|
|
for (auto component = (*it)->components.begin();
|
|
component != (*it)->components.end(); ++component) {
|
|
if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
|
|
namespace_so_far += *component;
|
|
namespaces.insert(namespace_so_far);
|
|
}
|
|
}
|
|
|
|
// Make sure parent namespaces come before child namespaces
|
|
std::vector<std::string> sorted_namespaces(namespaces.begin(),
|
|
namespaces.end());
|
|
std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
|
|
|
|
// Emit namespaces in a form that Closure Compiler can optimize
|
|
std::string &code = *code_ptr;
|
|
std::string &exports = *exports_ptr;
|
|
for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
|
|
it++) {
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
if (it->find('.') == std::string::npos) {
|
|
code += "import { flatbuffers } from \"./flatbuffers\"\n";
|
|
break;
|
|
}
|
|
} else {
|
|
code += "/**\n * @const\n * @namespace\n */\n";
|
|
if (it->find('.') == std::string::npos) {
|
|
code += "var ";
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
|
|
} else if( parser_.opts.use_ES6_js_export_format){
|
|
exports += "export {" + *it + "};\n";
|
|
} else {
|
|
exports += "this." + *it + " = " + *it + ";\n";
|
|
}
|
|
}
|
|
code += *it + " = " + *it + " || {};\n\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate a documentation comment, if available.
|
|
static void GenDocComment(const std::vector<std::string> &dc,
|
|
std::string *code_ptr,
|
|
const std::string &extra_lines,
|
|
const char *indent = nullptr) {
|
|
if (dc.empty() && extra_lines.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;
|
|
code += " *" + *it + "\n";
|
|
}
|
|
if (!extra_lines.empty()) {
|
|
if (!dc.empty()) {
|
|
if (indent) code += indent;
|
|
code += " *\n";
|
|
}
|
|
if (indent) code += indent;
|
|
std::string::size_type start = 0;
|
|
for (;;) {
|
|
auto end = extra_lines.find('\n', start);
|
|
if (end != std::string::npos) {
|
|
code += " * " + extra_lines.substr(start, end - start) + "\n";
|
|
start = end + 1;
|
|
} else {
|
|
code += " * " + extra_lines.substr(start) + "\n";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (indent) code += indent;
|
|
code += " */\n";
|
|
}
|
|
|
|
static void GenDocComment(std::string *code_ptr,
|
|
const std::string &extra_lines) {
|
|
GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
|
|
}
|
|
|
|
// Generate an enum declaration and an enum string lookup table.
|
|
void GenEnum(EnumDef &enum_def, std::string *code_ptr,
|
|
std::string *exports_ptr, reexport_map &reexports) {
|
|
if (enum_def.generated) return;
|
|
std::string &code = *code_ptr;
|
|
std::string &exports = *exports_ptr;
|
|
GenDocComment(enum_def.doc_comment, code_ptr, "@enum");
|
|
std::string ns = GetNameSpace(enum_def);
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
|
|
code += "export enum " + enum_def.name + "{\n";
|
|
} else {
|
|
if (enum_def.defined_namespace->components.empty()) {
|
|
code += "var ";
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportSymbol('" + enum_def.name + "', " +
|
|
enum_def.name + ");\n";
|
|
} else if (parser_.opts.use_ES6_js_export_format) {
|
|
exports += "export {" + enum_def.name + "};\n";
|
|
} else {
|
|
exports += "this." + enum_def.name + " = " + enum_def.name + ";\n";
|
|
}
|
|
}
|
|
code += WrapInNameSpace(enum_def) + " = {\n";
|
|
}
|
|
for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
|
|
++it) {
|
|
auto &ev = **it;
|
|
if (!ev.doc_comment.empty()) {
|
|
if (it != enum_def.vals.vec.begin()) { code += '\n'; }
|
|
GenDocComment(ev.doc_comment, code_ptr, "", " ");
|
|
}
|
|
code += " " + ev.name;
|
|
code += lang_.language == IDLOptions::kTs ? "= " : ": ";
|
|
code += NumToString(ev.value);
|
|
code += (it + 1) != enum_def.vals.vec.end() ? ",\n" : "\n";
|
|
|
|
if (ev.union_type.struct_def) {
|
|
ReexportDescription desc = { ev.name,
|
|
GetNameSpace(*ev.union_type.struct_def),
|
|
GetNameSpace(enum_def) };
|
|
reexports.insert(
|
|
std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
|
|
}
|
|
}
|
|
|
|
if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; }
|
|
code += "};\n\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:
|
|
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 "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: return GenBBAccess() + ".__union" + arguments;
|
|
case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
|
|
default: {
|
|
auto getter =
|
|
GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
|
|
if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
|
|
if (type.enum_def) {
|
|
getter = "/** @type {" + WrapInNameSpace(*type.enum_def) + "} */ (" +
|
|
getter + ")";
|
|
}
|
|
return getter;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string GenBBAccess() {
|
|
return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
|
|
}
|
|
|
|
std::string GenDefaultValue(const Value &value, const std::string &context) {
|
|
if (value.type.enum_def) {
|
|
if (auto val = value.type.enum_def->ReverseLookup(
|
|
StringToInt(value.constant.c_str()), false)) {
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
|
|
value.type.enum_def->file) +
|
|
"." + val->name;
|
|
} else {
|
|
return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
|
|
}
|
|
} else {
|
|
return "/** @type {" + WrapInNameSpace(*value.type.enum_def) +
|
|
"} */ (" + value.constant + ")";
|
|
}
|
|
}
|
|
|
|
switch (value.type.base_type) {
|
|
case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
|
|
|
|
case BASE_TYPE_STRING: return "null";
|
|
|
|
case BASE_TYPE_LONG:
|
|
case BASE_TYPE_ULONG: {
|
|
int64_t constant = StringToInt(value.constant.c_str());
|
|
return context + ".createLong(" + NumToString((int32_t)constant) +
|
|
", " + NumToString((int32_t)(constant >> 32)) + ")";
|
|
}
|
|
|
|
default: return value.constant;
|
|
}
|
|
}
|
|
|
|
std::string GenTypeName(const Type &type, bool input,
|
|
bool allowNull = false) {
|
|
if (!input) {
|
|
if (type.base_type == BASE_TYPE_STRING ||
|
|
type.base_type == BASE_TYPE_STRUCT) {
|
|
std::string name;
|
|
if (type.base_type == BASE_TYPE_STRING) {
|
|
name = "string|Uint8Array";
|
|
} else {
|
|
name = WrapInNameSpace(*type.struct_def);
|
|
}
|
|
return (allowNull) ? (name + "|null") : (name);
|
|
}
|
|
}
|
|
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_BOOL: return "boolean";
|
|
case BASE_TYPE_LONG:
|
|
case BASE_TYPE_ULONG: return "flatbuffers.Long";
|
|
default:
|
|
if (IsScalar(type.base_type)) {
|
|
if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
|
|
return "number";
|
|
}
|
|
return "flatbuffers.Offset";
|
|
}
|
|
}
|
|
|
|
// Returns the method name for use with add/put calls.
|
|
static std::string GenWriteMethod(const Type &type) {
|
|
// Forward to signed versions since unsigned versions don't exist
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_UTYPE:
|
|
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) ? MakeCamel(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) : "";
|
|
}
|
|
|
|
static std::string GenFileNamespacePrefix(const std::string &file) {
|
|
return "NS" + std::to_string(static_cast<unsigned long long>(
|
|
std::hash<std::string>()(file)));
|
|
}
|
|
|
|
static std::string GenPrefixedImport(const std::string &full_file_name,
|
|
const std::string &base_file_name) {
|
|
return "import * as " + GenFileNamespacePrefix(full_file_name) +
|
|
" from \"./" + base_file_name + "\";\n";
|
|
}
|
|
|
|
// Adds a source-dependent prefix, for of import * statements.
|
|
std::string GenPrefixedTypeName(const std::string &typeName,
|
|
const std::string &file) {
|
|
const auto basename =
|
|
flatbuffers::StripPath(flatbuffers::StripExtension(file));
|
|
if (basename == file_name_ || parser_.opts.generate_all) { return typeName; }
|
|
return GenFileNamespacePrefix(file) + "." + typeName;
|
|
}
|
|
|
|
void GenStructArgs(const StructDef &struct_def, std::string *annotations,
|
|
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(*field.value.type.struct_def, annotations, arguments,
|
|
nameprefix + field.name + "_");
|
|
} else {
|
|
*annotations += "@param {" + GenTypeName(field.value.type, true);
|
|
*annotations += "} " + nameprefix + field.name + "\n";
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
*arguments += ", " + nameprefix + field.name + ": " +
|
|
GenTypeName(field.value.type, true);
|
|
} else {
|
|
*arguments += ", " + nameprefix + field.name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static 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 + field.name + "_");
|
|
} else {
|
|
*body += " builder.write" + GenWriteMethod(field.value.type) + "(";
|
|
if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
|
|
*body += nameprefix + field.name + ");\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate an accessor struct with constructor for a flatbuffers struct.
|
|
void GenStruct(const Parser &parser, StructDef &struct_def,
|
|
std::string *code_ptr, std::string *exports_ptr,
|
|
imported_fileset &imported_files) {
|
|
if (struct_def.generated) return;
|
|
std::string &code = *code_ptr;
|
|
std::string &exports = *exports_ptr;
|
|
|
|
std::string object_name;
|
|
std::string object_namespace = GetNameSpace(struct_def);
|
|
|
|
// Emit constructor
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
object_name = struct_def.name;
|
|
GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
|
|
if (!object_namespace.empty()) {
|
|
code += "export namespace " + object_namespace + "{\n";
|
|
}
|
|
code += "export class " + struct_def.name;
|
|
code += " {\n";
|
|
code += " /**\n";
|
|
code += " * @type {flatbuffers.ByteBuffer}\n";
|
|
code += " */\n";
|
|
code += " bb: flatbuffers.ByteBuffer|null = null;\n";
|
|
code += "\n";
|
|
code += " /**\n";
|
|
code += " * @type {number}\n";
|
|
code += " */\n";
|
|
code += " bb_pos:number = 0;\n";
|
|
} else {
|
|
bool isStatement = struct_def.defined_namespace->components.empty();
|
|
object_name = WrapInNameSpace(struct_def);
|
|
GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
|
|
if (isStatement) {
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportSymbol('" + struct_def.name + "', " +
|
|
struct_def.name + ");\n";
|
|
} else if (parser_.opts.use_ES6_js_export_format) {
|
|
exports += "export {" + struct_def.name + "};\n";
|
|
} else {
|
|
exports +=
|
|
"this." + struct_def.name + " = " + struct_def.name + ";\n";
|
|
}
|
|
code += "function " + object_name;
|
|
} else {
|
|
code += object_name + " = function";
|
|
}
|
|
code += "() {\n";
|
|
code += " /**\n";
|
|
code += " * @type {flatbuffers.ByteBuffer}\n";
|
|
code += " */\n";
|
|
code += " this.bb = null;\n";
|
|
code += "\n";
|
|
code += " /**\n";
|
|
code += " * @type {number}\n";
|
|
code += " */\n";
|
|
code += " this.bb_pos = 0;\n";
|
|
code += isStatement ? "}\n\n" : "};\n\n";
|
|
}
|
|
|
|
// Generate the __init method that sets the field in a pre-existing
|
|
// accessor object. This is to allow object reuse.
|
|
code += "/**\n";
|
|
code += " * @param {number} i\n";
|
|
code += " * @param {flatbuffers.ByteBuffer} bb\n";
|
|
code += " * @returns {" + object_name + "}\n";
|
|
code += " */\n";
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code +=
|
|
"__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
|
|
} else {
|
|
code += object_name + ".prototype.__init = function(i, bb) {\n";
|
|
}
|
|
|
|
code += " this.bb_pos = i;\n";
|
|
code += " this.bb = bb;\n";
|
|
code += " return this;\n";
|
|
code += "};\n\n";
|
|
|
|
// Generate a special accessor for the table that when used as the root of a
|
|
// FlatBuffer
|
|
if (!struct_def.fixed) {
|
|
GenDocComment(code_ptr,
|
|
"@param {flatbuffers.ByteBuffer} bb\n"
|
|
"@param {" +
|
|
object_name +
|
|
"=} obj\n"
|
|
"@returns {" +
|
|
object_name + "}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static getRootAs" + struct_def.name;
|
|
code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
|
|
"):" + object_name + " {\n";
|
|
} else {
|
|
code += object_name + ".getRootAs" + struct_def.name;
|
|
code += " = function(bb, obj) {\n";
|
|
}
|
|
code += " return (obj || new " + object_name;
|
|
code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
|
|
code += "};\n\n";
|
|
|
|
// Generate the identifier check method
|
|
if (parser_.root_struct_def_ == &struct_def &&
|
|
!parser_.file_identifier_.empty()) {
|
|
GenDocComment(code_ptr,
|
|
"@param {flatbuffers.ByteBuffer} bb\n"
|
|
"@returns {boolean}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code +=
|
|
"static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
|
|
"{\n";
|
|
} else {
|
|
code += object_name + ".bufferHasIdentifier = function(bb) {\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;
|
|
auto offset_prefix =
|
|
" var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
|
|
NumToString(field.value.offset) + ");\n return offset ? ";
|
|
|
|
// Emit a scalar field
|
|
if (IsScalar(field.value.type.base_type) ||
|
|
field.value.type.base_type == BASE_TYPE_STRING) {
|
|
GenDocComment(
|
|
field.doc_comment, code_ptr,
|
|
std::string(
|
|
field.value.type.base_type == BASE_TYPE_STRING
|
|
? "@param {flatbuffers.Encoding=} optionalEncoding\n"
|
|
: "") +
|
|
"@returns {" + GenTypeName(field.value.type, false, true) +
|
|
"}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
std::string prefix = MakeCamel(field.name, false) + "(";
|
|
if (field.value.type.base_type == BASE_TYPE_STRING) {
|
|
code += prefix + "):string|null\n";
|
|
code += prefix + "optionalEncoding:flatbuffers.Encoding" +
|
|
"):" + GenTypeName(field.value.type, false, true) + "\n";
|
|
code += prefix + "optionalEncoding?:any";
|
|
} else {
|
|
code += prefix;
|
|
}
|
|
if (field.value.type.enum_def) {
|
|
code +=
|
|
"):" +
|
|
GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
|
|
field.value.type.enum_def->file) +
|
|
" {\n";
|
|
|
|
if (!parser_.opts.generate_all) {
|
|
imported_files.insert(field.value.type.enum_def->file);
|
|
}
|
|
} else {
|
|
code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
|
|
}
|
|
} else {
|
|
code += object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += " = function(";
|
|
if (field.value.type.base_type == BASE_TYPE_STRING) {
|
|
code += "optionalEncoding";
|
|
}
|
|
code += ") {\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 (field.value.type.base_type == BASE_TYPE_STRING) {
|
|
index += ", optionalEncoding";
|
|
}
|
|
code += offset_prefix +
|
|
GenGetter(field.value.type, "(" + index + ")") + " : " +
|
|
GenDefaultValue(field.value, GenBBAccess());
|
|
code += ";\n";
|
|
}
|
|
}
|
|
|
|
// Emit an object field
|
|
else {
|
|
switch (field.value.type.base_type) {
|
|
case BASE_TYPE_STRUCT: {
|
|
auto type = WrapInNameSpace(*field.value.type.struct_def);
|
|
GenDocComment(
|
|
field.doc_comment, code_ptr,
|
|
"@param {" + type + "=} obj\n@returns {" + type + "|null}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
type =
|
|
GenPrefixedTypeName(type, field.value.type.struct_def->file);
|
|
code += MakeCamel(field.name, false);
|
|
code += "(obj?:" + type + "):" + type + "|null {\n";
|
|
} else {
|
|
code +=
|
|
object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += " = function(obj) {\n";
|
|
}
|
|
|
|
if (struct_def.fixed) {
|
|
code += " return (obj || new " + type;
|
|
code += ").__init(this.bb_pos";
|
|
code +=
|
|
MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
|
|
} else {
|
|
code += offset_prefix + "(obj || new " + type + ").__init(";
|
|
code += field.value.type.struct_def->fixed
|
|
? "this.bb_pos + offset"
|
|
: GenBBAccess() + ".__indirect(this.bb_pos + offset)";
|
|
code += ", " + GenBBAccess() + ") : null;\n";
|
|
}
|
|
|
|
if (lang_.language == IDLOptions::kTs && !parser_.opts.generate_all) {
|
|
imported_files.insert(field.value.type.struct_def->file);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case BASE_TYPE_VECTOR: {
|
|
auto vectortype = field.value.type.VectorType();
|
|
auto vectortypename = GenTypeName(vectortype, false);
|
|
auto inline_size = InlineSize(vectortype);
|
|
auto index = GenBBAccess() +
|
|
".__vector(this.bb_pos + offset) + index" +
|
|
MaybeScale(inline_size);
|
|
std::string args = "@param {number} index\n";
|
|
std::string ret_type;
|
|
bool is_union = false;
|
|
switch (vectortype.base_type) {
|
|
case BASE_TYPE_STRUCT:
|
|
args += "@param {" + vectortypename + "=} obj\n";
|
|
ret_type = vectortypename;
|
|
break;
|
|
case BASE_TYPE_STRING:
|
|
args += "@param {flatbuffers.Encoding=} optionalEncoding\n";
|
|
ret_type = vectortypename;
|
|
break;
|
|
case BASE_TYPE_UNION:
|
|
args += "@param {flatbuffers.Table=} obj\n";
|
|
ret_type = "?flatbuffers.Table";
|
|
is_union = true;
|
|
break;
|
|
default: ret_type = vectortypename;
|
|
}
|
|
GenDocComment(field.doc_comment, code_ptr,
|
|
args + "@returns {" + ret_type + "}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
std::string prefix = MakeCamel(field.name, false);
|
|
if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
|
|
prefix += "(index: number";
|
|
if (is_union) {
|
|
vectortypename = "T";
|
|
code += prefix + ", obj:T";
|
|
} else if (vectortype.base_type == BASE_TYPE_STRUCT) {
|
|
vectortypename = GenPrefixedTypeName(
|
|
vectortypename, vectortype.struct_def->file);
|
|
code += prefix + ", obj?:" + vectortypename;
|
|
|
|
if (!parser_.opts.generate_all) {
|
|
imported_files.insert(vectortype.struct_def->file);
|
|
}
|
|
} else if (vectortype.base_type == BASE_TYPE_STRING) {
|
|
code += prefix + "):string\n";
|
|
code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
|
|
"):" + vectortypename + "\n";
|
|
code += prefix + ",optionalEncoding?:any";
|
|
} else {
|
|
code += prefix;
|
|
}
|
|
code += "):" + vectortypename + "|null {\n";
|
|
} else {
|
|
code +=
|
|
object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += " = function(index";
|
|
if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
|
|
code += ", obj";
|
|
} else if (vectortype.base_type == BASE_TYPE_STRING) {
|
|
code += ", optionalEncoding";
|
|
}
|
|
code += ") {\n";
|
|
}
|
|
|
|
if (vectortype.base_type == BASE_TYPE_STRUCT) {
|
|
code += offset_prefix + "(obj || new " + vectortypename;
|
|
code += ").__init(";
|
|
code += vectortype.struct_def->fixed
|
|
? index
|
|
: GenBBAccess() + ".__indirect(" + index + ")";
|
|
code += ", " + GenBBAccess() + ")";
|
|
} else {
|
|
if (is_union) {
|
|
index = "obj, " + index;
|
|
} else if (vectortype.base_type == BASE_TYPE_STRING) {
|
|
index += ", optionalEncoding";
|
|
}
|
|
code += offset_prefix + 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 += GenBBAccess() + ".createLong(0, 0)";
|
|
} else if (IsScalar(field.value.type.element)) {
|
|
if (field.value.type.enum_def) {
|
|
code += "/** @type {" +
|
|
WrapInNameSpace(*field.value.type.enum_def) + "} */ (" +
|
|
field.value.constant + ")";
|
|
} else {
|
|
code += "0";
|
|
}
|
|
} else {
|
|
code += "null";
|
|
}
|
|
code += ";\n";
|
|
break;
|
|
}
|
|
|
|
case BASE_TYPE_UNION:
|
|
GenDocComment(field.doc_comment, code_ptr,
|
|
"@param {flatbuffers.Table} obj\n"
|
|
"@returns {?flatbuffers.Table}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += MakeCamel(field.name, false);
|
|
code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
|
|
} else {
|
|
code +=
|
|
object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += " = function(obj) {\n";
|
|
}
|
|
|
|
code += offset_prefix +
|
|
GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
|
|
" : null;\n";
|
|
break;
|
|
|
|
default: FLATBUFFERS_ASSERT(0);
|
|
}
|
|
}
|
|
code += "};\n\n";
|
|
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportProperty(" + object_name + ".prototype, '" +
|
|
MakeCamel(field.name, false) + "', " + object_name +
|
|
".prototype." + MakeCamel(field.name, false) + ");\n";
|
|
}
|
|
|
|
// Adds the mutable scalar value to the output
|
|
if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) {
|
|
std::string annotations =
|
|
"@param {" + GenTypeName(field.value.type, true) + "} value\n";
|
|
GenDocComment(code_ptr, annotations + "@returns {boolean}");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
std::string type;
|
|
if (field.value.type.enum_def) {
|
|
type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
|
|
field.value.type.enum_def->file);
|
|
} else {
|
|
type = GenTypeName(field.value.type, true);
|
|
}
|
|
|
|
code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
|
|
} else {
|
|
code += object_name + ".prototype.mutate_" + field.name +
|
|
" = function(value) {\n";
|
|
}
|
|
|
|
code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
|
|
NumToString(field.value.offset) + ");\n\n";
|
|
code += " if (offset === 0) {\n";
|
|
code += " return false;\n";
|
|
code += " }\n\n";
|
|
|
|
// special case for bools, which are treated as uint8
|
|
code += " " + GenBBAccess() + ".write" +
|
|
MakeCamel(GenType(field.value.type)) +
|
|
"(this.bb_pos + offset, ";
|
|
if (field.value.type.base_type == BASE_TYPE_BOOL &&
|
|
lang_.language == IDLOptions::kTs) {
|
|
code += "+";
|
|
}
|
|
|
|
code += "value);\n";
|
|
code += " return true;\n";
|
|
code += "};\n\n";
|
|
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportProperty(" + object_name +
|
|
".prototype, 'mutate_" + field.name + "', " + object_name +
|
|
".prototype.mutate_" + field.name + ");\n";
|
|
}
|
|
}
|
|
|
|
// Emit vector helpers
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
|
|
// Emit a length helper
|
|
GenDocComment(code_ptr, "@returns {number}");
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += MakeCamel(field.name, false);
|
|
code += "Length():number {\n" + offset_prefix;
|
|
} else {
|
|
code += object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += "Length = function() {\n" + offset_prefix;
|
|
}
|
|
|
|
code +=
|
|
GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
|
|
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportProperty(" + object_name + ".prototype, '" +
|
|
MakeCamel(field.name, false) + "Length', " + object_name +
|
|
".prototype." + MakeCamel(field.name, false) +
|
|
"Length);\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,
|
|
"@returns {" + GenType(vectorType) + "Array}");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += MakeCamel(field.name, false);
|
|
code += "Array():" + GenType(vectorType) + "Array|null {\n" +
|
|
offset_prefix;
|
|
} else {
|
|
code += object_name + ".prototype." + MakeCamel(field.name, false);
|
|
code += "Array = function() {\n" + offset_prefix;
|
|
}
|
|
|
|
code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
|
|
".bytes().buffer, " + GenBBAccess() +
|
|
".bytes().byteOffset + " + GenBBAccess() +
|
|
".__vector(this.bb_pos + offset), " + GenBBAccess() +
|
|
".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
|
|
|
|
if (parser_.opts.use_goog_js_export_format) {
|
|
exports += "goog.exportProperty(" + object_name + ".prototype, '" +
|
|
MakeCamel(field.name, false) + "Array', " + object_name +
|
|
".prototype." + MakeCamel(field.name, false) +
|
|
"Array);\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit a factory constructor
|
|
if (struct_def.fixed) {
|
|
std::string annotations = "@param {flatbuffers.Builder} builder\n";
|
|
std::string arguments;
|
|
GenStructArgs(struct_def, &annotations, &arguments, "");
|
|
GenDocComment(code_ptr, annotations + "@returns {flatbuffers.Offset}");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code +=
|
|
"static create" + struct_def.name + "(builder:flatbuffers.Builder";
|
|
code += arguments + "):flatbuffers.Offset {\n";
|
|
} else {
|
|
code +=
|
|
object_name + ".create" + struct_def.name + " = function(builder";
|
|
code += arguments + ") {\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, "@param {flatbuffers.Builder} builder");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static start" + struct_def.name;
|
|
code += "(builder:flatbuffers.Builder) {\n";
|
|
} else {
|
|
code += object_name + ".start" + struct_def.name;
|
|
code += " = function(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;
|
|
auto argname = MakeCamel(field.name, false);
|
|
if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
|
|
|
|
// Generate the field insertion method
|
|
GenDocComment(code_ptr,
|
|
"@param {flatbuffers.Builder} builder\n"
|
|
"@param {" +
|
|
GenTypeName(field.value.type, true) + "} " + argname);
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
std::string argType;
|
|
if (field.value.type.enum_def) {
|
|
argType = GenPrefixedTypeName(GenTypeName(field.value.type, true),
|
|
field.value.type.enum_def->file);
|
|
} else {
|
|
argType = GenTypeName(field.value.type, true);
|
|
}
|
|
|
|
code += "static add" + MakeCamel(field.name);
|
|
code += "(builder:flatbuffers.Builder, " + argname + ":" + argType +
|
|
") {\n";
|
|
} else {
|
|
code += object_name + ".add" + MakeCamel(field.name);
|
|
code += " = function(builder, " + argname + ") {\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 (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
|
|
code += GenDefaultValue(field.value, "builder");
|
|
}
|
|
code += ");\n};\n\n";
|
|
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
|
|
auto vector_type = field.value.type.VectorType();
|
|
auto alignment = InlineAlignment(vector_type);
|
|
auto elem_size = InlineSize(vector_type);
|
|
|
|
// Generate a method to create a vector from a JavaScript array
|
|
if (!IsStruct(vector_type)) {
|
|
GenDocComment(code_ptr,
|
|
"@param {flatbuffers.Builder} builder\n"
|
|
"@param {Array.<" +
|
|
GenTypeName(vector_type, true) +
|
|
">} data\n"
|
|
"@returns {flatbuffers.Offset}");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static create" + MakeCamel(field.name);
|
|
std::string type = GenTypeName(vector_type, true) + "[]";
|
|
if (type == "number[]") { type += " | Uint8Array"; }
|
|
code += "Vector(builder:flatbuffers.Builder, data:" + type +
|
|
"):flatbuffers.Offset {\n";
|
|
} else {
|
|
code += object_name + ".create" + MakeCamel(field.name);
|
|
code += "Vector = function(builder, data) {\n";
|
|
}
|
|
|
|
code += " builder.startVector(" + NumToString(elem_size);
|
|
code += ", data.length, " + NumToString(alignment) + ");\n";
|
|
code += " for (var 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,
|
|
"@param {flatbuffers.Builder} builder\n"
|
|
"@param {number} numElems");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static start" + MakeCamel(field.name);
|
|
code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
|
|
} else {
|
|
code += object_name + ".start" + MakeCamel(field.name);
|
|
code += "Vector = function(builder, numElems) {\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,
|
|
"@param {flatbuffers.Builder} builder\n"
|
|
"@returns {flatbuffers.Offset}");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static end" + struct_def.name;
|
|
code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
|
|
} else {
|
|
code += object_name + ".end" + struct_def.name;
|
|
code += " = function(builder) {\n";
|
|
}
|
|
|
|
code += " var 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.required) {
|
|
code += " builder.requiredField(offset, ";
|
|
code += NumToString(field.value.offset);
|
|
code += "); // " + field.name + "\n";
|
|
}
|
|
}
|
|
code += " return offset;\n";
|
|
code += "};\n\n";
|
|
|
|
// Generate the method to complete buffer construction
|
|
if (parser_.root_struct_def_ == &struct_def) {
|
|
GenDocComment(code_ptr,
|
|
"@param {flatbuffers.Builder} builder\n"
|
|
"@param {flatbuffers.Offset} offset");
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
code += "static finish" + struct_def.name + "Buffer";
|
|
code +=
|
|
"(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
|
|
} else {
|
|
code += object_name + ".finish" + struct_def.name + "Buffer";
|
|
code += " = function(builder, offset) {\n";
|
|
}
|
|
|
|
code += " builder.finish(offset";
|
|
if (!parser_.file_identifier_.empty()) {
|
|
code += ", '" + parser_.file_identifier_ + "'";
|
|
}
|
|
code += ");\n";
|
|
code += "};\n\n";
|
|
}
|
|
}
|
|
|
|
if (lang_.language == IDLOptions::kTs) {
|
|
if (!object_namespace.empty()) { code += "}\n"; }
|
|
code += "}\n";
|
|
}
|
|
}
|
|
};
|
|
} // namespace js
|
|
|
|
bool GenerateJS(const Parser &parser, const std::string &path,
|
|
const std::string &file_name) {
|
|
js::JsGenerator generator(parser, path, file_name);
|
|
return generator.generate();
|
|
}
|
|
|
|
std::string JSMakeRule(const Parser &parser, const std::string &path,
|
|
const std::string &file_name) {
|
|
FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
|
|
const auto &lang = GetJsLangParams(parser.opts.lang);
|
|
|
|
std::string filebase =
|
|
flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
|
|
std::string make_rule = GeneratedFileName(path, filebase, lang) + ": ";
|
|
|
|
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 flatbuffers
|