diff --git a/.gitignore b/.gitignore index c8415516a..69e7586a1 100755 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ snapshot.sh tests/go_gen tests/monsterdata_java_wire.mon tests/monsterdata_go_wire.mon +tests/monsterdata_javascript_wire.mon CMakeLists.txt.user CMakeScripts/** CTestTestfile.cmake @@ -55,4 +56,4 @@ java/.idea java/*.iml java/target **/*.pyc -.idea \ No newline at end of file +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8cdee40..f0691dd90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_cpp.cpp src/idl_gen_general.cpp src/idl_gen_go.cpp + src/idl_gen_js.cpp src/idl_gen_python.cpp src/idl_gen_fbs.cpp src/flatc.cpp diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 4b0dd818c..82fb18bed 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -449,6 +449,7 @@ extern void GenComment(const std::vector &dc, // Container of options that may apply to any of the source/text generators. struct GeneratorOptions { bool strict_json; + bool skip_js_exports; bool output_default_scalars_in_json; int indent_step; bool output_enum_identifiers; @@ -464,6 +465,7 @@ struct GeneratorOptions { Language lang; GeneratorOptions() : strict_json(false), + skip_js_exports(false), output_default_scalars_in_json(false), indent_step(2), output_enum_identifiers(true), prefixed_enums(true), scoped_enums(false), @@ -506,6 +508,15 @@ extern bool GenerateCPP(const Parser &parser, const std::string &file_name, const GeneratorOptions &opts); +// Generate JavaScript code from the definitions in the Parser object. +// See idl_gen_js. +extern std::string GenerateJS(const Parser &parser, + const GeneratorOptions &opts); +extern bool GenerateJS(const Parser &parser, + const std::string &path, + const std::string &file_name, + const GeneratorOptions &opts); + // Generate Go files from the definitions in the Parser object. // See idl_gen_go.cpp. extern bool GenerateGo(const Parser &parser, @@ -551,6 +562,13 @@ extern bool GenerateFBS(const Parser &parser, const std::string &file_name, const GeneratorOptions &opts); +// Generate a make rule for the generated JavaScript code. +// See idl_gen_js.cpp. +extern std::string JSMakeRule(const Parser &parser, + const std::string &path, + const std::string &file_name, + const GeneratorOptions &opts); + // Generate a make rule for the generated C++ header. // See idl_gen_cpp.cpp. extern std::string CPPMakeRule(const Parser &parser, diff --git a/js/flatbuffers.js b/js/flatbuffers.js new file mode 100644 index 000000000..37995c9c8 --- /dev/null +++ b/js/flatbuffers.js @@ -0,0 +1,1021 @@ +var flatbuffers = {}; + +/** + * @typedef {number} + */ +flatbuffers.Offset; + +/** + * @typedef {{ + * bb: flatbuffers.ByteBuffer, + * bb_pos: number + * }} + */ +flatbuffers.Table; + +/** + * @type {number} + * @const + */ +flatbuffers.SIZEOF_SHORT = 2; + +/** + * @type {number} + * @const + */ +flatbuffers.SIZEOF_INT = 4; + +/** + * @type {number} + * @const + */ +flatbuffers.FILE_IDENTIFIER_LENGTH = 4; + +//////////////////////////////////////////////////////////////////////////////// + +/** + * @constructor + * @param {number} high + * @param {number} low + */ +flatbuffers.Long = function(low, high) { + /** + * @type {number} + * @const + */ + this.low = low | 0; + + /** + * @type {number} + * @const + */ + this.high = high | 0; +}; + +/** + * @returns {number} + */ +flatbuffers.Long.prototype.toFloat64 = function() { + return this.low + this.high * 0x100000000; +}; + +/** + * @param {flatbuffers.Long} other + * @returns {boolean} + */ +flatbuffers.Long.prototype.equals = function(other) { + return this.low == other.low && this.high == other.high; +}; + +/** + * @type {flatbuffers.Long} + * @const + */ +flatbuffers.Long.ZERO = new flatbuffers.Long(0, 0); + +//////////////////////////////////////////////////////////////////////////////// + +/** + * @constructor + * @param {number=} initial_size + */ +flatbuffers.Builder = function(initial_size) { + if (!initial_size) { + initial_size = 1024; + } + + /** + * @type {flatbuffers.ByteBuffer} + * @private + */ + this.bb = flatbuffers.ByteBuffer.allocate(initial_size); + + /** + * Remaining space in the ByteBuffer. + * + * @type {number} + * @private + */ + this.space = initial_size; + + /** + * Minimum alignment encountered so far. + * + * @type {number} + * @private + */ + this.minalign = 1; + + /** + * The vtable for the current table. + * + * @type {Array.} + * @private + */ + this.vtable = null; + + /** + * The amount of fields we're actually using. + * + * @type {number} + * @private + */ + this.vtable_in_use = 0; + + /** + * Whether we are currently serializing a table. + * + * @type {boolean} + * @private + */ + this.isNested = false; + + /** + * Starting offset of the current struct/table. + * + * @type {number} + * @private + */ + this.object_start = 0; + + /** + * List of offsets of all vtables. + * + * @type {Array.} + * @private + */ + this.vtables = []; + + /** + * For the current vector being built. + * + * @type {number} + * @private + */ + this.vector_num_elems = 0; + + /** + * False omits default values from the serialized data + * + * @type {boolean} + * @private + */ + this.force_defaults = false; +}; + +/** + * In order to save space, fields that are set to their default value + * don't get serialized into the buffer. Forcing defaults provides a + * way to manually disable this optimization. + * + * @param {boolean} forceDefaults true always serializes default values + */ +flatbuffers.Builder.prototype.forceDefaults = function(forceDefaults) { + this.force_defaults = forceDefaults; +}; + +/** + * Get the ByteBuffer representing the FlatBuffer. Only call this after you've + * called finish(). The actual data starts at the ByteBuffer's current position, + * not necessarily at 0. + * + * @returns {flatbuffers.ByteBuffer} + */ +flatbuffers.Builder.prototype.dataBuffer = function() { + return this.bb; +}; + +/** + * Get the ByteBuffer representing the FlatBuffer. Only call this after you've + * called finish(). The actual data starts at the ByteBuffer's current position, + * not necessarily at 0. + * + * @returns {Uint8Array} + */ +flatbuffers.Builder.prototype.asUint8Array = function() { + return this.bb.bytes().subarray(this.bb.position(), this.bb.position() + this.offset()); +}; + +/** + * Prepare to write an element of `size` after `additional_bytes` have been + * written, e.g. if you write a string, you need to align such the int length + * field is aligned to 4 bytes, and the string data follows it directly. If all + * you need to do is alignment, `additional_bytes` will be 0. + * + * @param {number} size This is the of the new element to write + * @param {number} additional_bytes The padding size + */ +flatbuffers.Builder.prototype.prep = function(size, additional_bytes) { + // Track the biggest thing we've ever aligned to. + if (size > this.minalign) { + this.minalign = size; + } + + // Find the amount of alignment needed such that `size` is properly + // aligned after `additional_bytes` + var align_size = ((~(this.bb.capacity() - this.space + additional_bytes)) + 1) & (size - 1); + + // Reallocate the buffer if needed. + while (this.space < align_size + size + additional_bytes) { + var old_buf_size = this.bb.capacity(); + this.bb = flatbuffers.Builder.growByteBuffer(this.bb); + this.space += this.bb.capacity() - old_buf_size; + } + + this.pad(align_size); +}; + +/** + * @param {number} byte_size + */ +flatbuffers.Builder.prototype.pad = function(byte_size) { + for (var i = 0; i < byte_size; i++) { + this.bb.writeInt8(--this.space, 0); + } +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.writeInt8 = function(value) { + this.bb.writeInt8(this.space -= 1, value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.writeInt16 = function(value) { + this.bb.writeInt16(this.space -= 2, value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.writeInt32 = function(value) { + this.bb.writeInt32(this.space -= 4, value); +}; + +/** + * @param {flatbuffers.Long} value + */ +flatbuffers.Builder.prototype.writeInt64 = function(value) { + this.bb.writeInt64(this.space -= 8, value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.writeFloat32 = function(value) { + this.bb.writeFloat32(this.space -= 4, value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.writeFloat64 = function(value) { + this.bb.writeFloat64(this.space -= 8, value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.addInt8 = function(value) { + this.prep(1, 0); + this.writeInt8(value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.addInt16 = function(value) { + this.prep(2, 0); + this.writeInt16(value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.addInt32 = function(value) { + this.prep(4, 0); + this.writeInt32(value); +}; + +/** + * @param {flatbuffers.Long} value + */ +flatbuffers.Builder.prototype.addInt64 = function(value) { + this.prep(8, 0); + this.writeInt64(value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.addFloat32 = function(value) { + this.prep(4, 0); + this.writeFloat32(value); +}; + +/** + * @param {number} value + */ +flatbuffers.Builder.prototype.addFloat64 = function(value) { + this.prep(8, 0); + this.writeFloat64(value); +}; + +/** + * @param {number} voffset + * @param {number} value + * @param {number} defaultValue + */ +flatbuffers.Builder.prototype.addFieldInt8 = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addInt8(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {number} value + * @param {number} defaultValue + */ +flatbuffers.Builder.prototype.addFieldInt16 = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addInt16(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {number} value + * @param {number} defaultValue + */ +flatbuffers.Builder.prototype.addFieldInt32 = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addInt32(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {flatbuffers.Long} value + * @param {flatbuffers.Long} defaultValue + */ +flatbuffers.Builder.prototype.addFieldInt64 = function(voffset, value, defaultValue) { + if (this.force_defaults || !value.equals(defaultValue)) { + this.addInt64(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {number} value + * @param {number} defaultValue + */ +flatbuffers.Builder.prototype.addFieldFloat32 = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addFloat32(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {number} value + * @param {number} defaultValue + */ +flatbuffers.Builder.prototype.addFieldFloat64 = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addFloat64(value); + this.slot(voffset); + } +}; + +/** + * @param {number} voffset + * @param {flatbuffers.Offset} value + * @param {flatbuffers.Offset} defaultValue + */ +flatbuffers.Builder.prototype.addFieldOffset = function(voffset, value, defaultValue) { + if (this.force_defaults || value != defaultValue) { + this.addOffset(value); + this.slot(voffset); + } +}; + +/** + * Structs are stored inline, so nothing additional is being added. `d` is always 0. + * + * @param {number} voffset + * @param {flatbuffers.Offset} value + * @param {flatbuffers.Offset} defaultValue + */ +flatbuffers.Builder.prototype.addFieldStruct = function(voffset, value, defaultValue) { + if (value != defaultValue) { + this.nested(value); + this.slot(voffset); + } +}; + +/** + * Structures are always stored inline, they need to be created right + * where they're used. You'll get this assertion failure if you + * created it elsewhere. + * + * @param {flatbuffers.Offset} obj The offset of the created object + */ +flatbuffers.Builder.prototype.nested = function(obj) { + if (obj != this.offset()) { + throw new Error('FlatBuffers: struct must be serialized inline.'); + } +}; + +/** + * Should not be creating any other object, string or vector + * while an object is being constructed + */ +flatbuffers.Builder.prototype.notNested = function() { + if (this.isNested) { + throw new Error('FlatBuffers: object serialization must not be nested.'); + } +}; + +/** + * Set the current vtable at `voffset` to the current location in the buffer. + * + * @param {number} voffset + */ +flatbuffers.Builder.prototype.slot = function(voffset) { + this.vtable[voffset] = this.offset(); +}; + +/** + * @returns {flatbuffers.Offset} Offset relative to the end of the buffer. + */ +flatbuffers.Builder.prototype.offset = function() { + return this.bb.capacity() - this.space; +}; + +/** + * Doubles the size of the backing ByteBuffer and copies the old data towards + * the end of the new buffer (since we build the buffer backwards). + * + * @param {flatbuffers.ByteBuffer} bb The current buffer with the existing data + * @returns {flatbuffers.ByteBuffer} A new byte buffer with the old data copied + * to it. The data is located at the end of the buffer. + */ +flatbuffers.Builder.growByteBuffer = function(bb) { + var old_buf_size = bb.capacity(); + + // Ensure we don't grow beyond what fits in an int. + if (old_buf_size & 0xC0000000) { + throw new Error('FlatBuffers: cannot grow buffer beyond 2 gigabytes.'); + } + + var new_buf_size = old_buf_size << 1; + var nbb = flatbuffers.ByteBuffer.allocate(new_buf_size); + nbb.setPosition(new_buf_size - old_buf_size); + nbb.bytes().set(bb.bytes(), new_buf_size - old_buf_size); + return nbb; +}; + +/** + * Adds on offset, relative to where it will be written. + * + * @param {flatbuffers.Offset} offset The offset to add + */ +flatbuffers.Builder.prototype.addOffset = function(offset) { + this.prep(flatbuffers.SIZEOF_INT, 0); // Ensure alignment is already done. + this.writeInt32(this.offset() - offset + flatbuffers.SIZEOF_INT); +}; + +/** + * Start encoding a new object in the buffer. Users will not usually need to + * call this directly. The FlatBuffers compiler will generate helper methods + * that call this method internally. + * + * @param {number} numfields + */ +flatbuffers.Builder.prototype.startObject = function(numfields) { + this.notNested(); + if (this.vtable == null) { + this.vtable = []; + } + this.vtable_in_use = numfields; + for (var i = 0; i < numfields; i++) { + this.vtable[i] = 0; // This will push additional elements as needed + } + this.isNested = true; + this.object_start = this.offset(); +}; + +/** + * Finish off writing the object that is under construction. + * + * @returns {flatbuffers.Offset} The offset to the object inside `dataBuffer` + */ +flatbuffers.Builder.prototype.endObject = function() { + if (this.vtable == null || !this.isNested) { + throw new Error('FlatBuffers: endObject called without startObject'); + } + + this.addInt32(0); + var vtableloc = this.offset(); + + // Write out the current vtable. + for (var i = this.vtable_in_use - 1; i >= 0; i--) { + // Offset relative to the start of the table. + this.addInt16(this.vtable[i] != 0 ? vtableloc - this.vtable[i] : 0); + } + + var standard_fields = 2; // The fields below: + this.addInt16(vtableloc - this.object_start); + this.addInt16((this.vtable_in_use + standard_fields) * flatbuffers.SIZEOF_SHORT); + + // Search for an existing vtable that matches the current one. + var existing_vtable = 0; +outer_loop: + for (var i = 0; i < this.vtables.length; i++) { + var vt1 = this.bb.capacity() - this.vtables[i]; + var vt2 = this.space; + var len = this.bb.readInt16(vt1); + if (len == this.bb.readInt16(vt2)) { + for (var j = flatbuffers.SIZEOF_SHORT; j < len; j += flatbuffers.SIZEOF_SHORT) { + if (this.bb.readInt16(vt1 + j) != this.bb.readInt16(vt2 + j)) { + continue outer_loop; + } + } + existing_vtable = this.vtables[i]; + break; + } + } + + if (existing_vtable) { + // Found a match: + // Remove the current vtable. + this.space = this.bb.capacity() - vtableloc; + + // Point table to existing vtable. + this.bb.writeInt32(this.space, existing_vtable - vtableloc); + } else { + // No match: + // Add the location of the current vtable to the list of vtables. + this.vtables.push(this.offset()); + + // Point table to current vtable. + this.bb.writeInt32(this.bb.capacity() - vtableloc, this.offset() - vtableloc); + } + + this.isNested = false; + return vtableloc; +}; + +/** + * @param {flatbuffers.Offset} root_table + * @param {string=} file_identifier + */ +flatbuffers.Builder.prototype.finish = function(root_table, file_identifier) { + if (file_identifier) { + this.prep(this.minalign, flatbuffers.SIZEOF_INT + + flatbuffers.FILE_IDENTIFIER_LENGTH); + if (file_identifier.length != flatbuffers.FILE_IDENTIFIER_LENGTH) { + throw new Error('FlatBuffers: file identifier must be length ' + + flatbuffers.FILE_IDENTIFIER_LENGTH); + } + for (var i = flatbuffers.FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) { + this.writeInt8(file_identifier.charCodeAt(i)); + } + } + this.prep(this.minalign, flatbuffers.SIZEOF_INT); + this.addOffset(root_table); + this.bb.setPosition(this.space); +}; + +/** + * This checks a required field has been set in a given table that has + * just been constructed. + * + * @param {flatbuffers.Offset} table + * @param {number} field + */ +flatbuffers.Builder.prototype.requiredField = function(table, field) { + var table_start = this.bb.capacity() - table; + var vtable_start = table_start - this.bb.readInt32(table_start); + var ok = this.bb.readInt16(vtable_start + field) != 0; + + // If this fails, the caller will show what field needs to be set. + if (!ok) { + throw new Error('FlatBuffers: field ' + field + ' must be set'); + } +}; + +/** + * Start a new array/vector of objects. Users usually will not call + * this directly. The FlatBuffers compiler will create a start/end + * method for vector types in generated code. + * + * @param {number} elem_size The size of each element in the array + * @param {number} num_elems The number of elements in the array + * @param {number} alignment The alignment of the array + */ +flatbuffers.Builder.prototype.startVector = function(elem_size, num_elems, alignment) { + this.notNested(); + this.vector_num_elems = num_elems; + this.prep(flatbuffers.SIZEOF_INT, elem_size * num_elems); + this.prep(alignment, elem_size * num_elems); // Just in case alignment > int. +}; + +/** + * Finish off the creation of an array and all its elements. The array must be + * created with `startVector`. + * + * @returns {flatbuffers.Offset} The offset at which the newly created array + * starts. + */ +flatbuffers.Builder.prototype.endVector = function() { + this.writeInt32(this.vector_num_elems); + return this.offset(); +}; + +/** + * Encode the string `s` in the buffer using UTF-8. + * + * @param {string} s The string to encode + * @return {flatbuffers.Offset} The offset in the buffer where the encoded string starts + */ +flatbuffers.Builder.prototype.createString = function(s) { + var utf8 = []; + var i = 0; + + while (i < s.length) { + var codePoint; + + // Decode UTF-16 + var a = s.charCodeAt(i++); + if (a < 0xD800 || a >= 0xDC00) { + codePoint = a; + } else { + var b = s.charCodeAt(i++); + codePoint = (a << 10) + b + (0x10000 - (0xD800 << 10) - 0xDC00); + } + + // Encode UTF-8 + if (codePoint < 0x80) { + utf8.push(codePoint); + } else { + if (codePoint < 0x800) { + utf8.push(((codePoint >> 6) & 0x1F) | 0xC0); + } else { + if (codePoint < 0x10000) { + utf8.push(((codePoint >> 12) & 0x0F) | 0xE0); + } else { + utf8.push( + ((codePoint >> 18) & 0x07) | 0xF0, + ((codePoint >> 12) & 0x3F) | 0x80); + } + utf8.push(((codePoint >> 6) & 0x3F) | 0x80); + } + utf8.push((codePoint & 0x3F) | 0x80); + } + } + + this.addInt8(0); + this.startVector(1, utf8.length, 1); + this.bb.setPosition(this.space -= utf8.length); + for (var i = 0, offset = this.space, bytes = this.bb.bytes(); i < utf8.length; i++) { + bytes[offset++] = utf8[i]; + } + return this.endVector(); +}; + +//////////////////////////////////////////////////////////////////////////////// + +/** + * @constructor + * @param {Uint8Array} bytes + */ +flatbuffers.ByteBuffer = function(bytes) { + /** + * @type {Uint8Array} + * @private + */ + this.bytes_ = bytes; + + /** + * @type {DataView} + * @private + */ + this.view_ = new DataView(bytes.buffer, bytes.byteOffset, bytes.length); + + /** + * @type {number} + * @private + */ + this.position_ = 0; +}; + +/** + * @param {number} byte_size + * @returns {flatbuffers.ByteBuffer} + */ +flatbuffers.ByteBuffer.allocate = function(byte_size) { + return new flatbuffers.ByteBuffer(new Uint8Array(byte_size)); +}; + +/** + * @returns {Uint8Array} + */ +flatbuffers.ByteBuffer.prototype.bytes = function() { + return this.bytes_; +}; + +/** + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.position = function() { + return this.position_; +}; + +/** + * @param {number} position + */ +flatbuffers.ByteBuffer.prototype.setPosition = function(position) { + this.position_ = position; +}; + +/** + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.capacity = function() { + return this.bytes_.length; +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readInt8 = function(offset) { + return this.view_.getInt8(offset); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readUint8 = function(offset) { + return this.view_.getUint8(offset); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readInt16 = function(offset) { + return this.view_.getInt16(offset, true); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readUint16 = function(offset) { + return this.view_.getUint16(offset, true); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readInt32 = function(offset) { + return this.view_.getInt32(offset, true); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readUint32 = function(offset) { + return this.view_.getUint32(offset, true); +}; + +/** + * @param {number} offset + * @returns {flatbuffers.Long} + */ +flatbuffers.ByteBuffer.prototype.readInt64 = function(offset) { + return new flatbuffers.Long(this.readInt32(offset), this.readInt32(offset + 4)); +}; + +/** + * @param {number} offset + * @returns {flatbuffers.Long} + */ +flatbuffers.ByteBuffer.prototype.readUint64 = function(offset) { + return new flatbuffers.Long(this.readInt32(offset), this.readInt32(offset + 4)); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readFloat32 = function(offset) { + return this.view_.getFloat32(offset, true); +}; + +/** + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.readFloat64 = function(offset) { + return this.view_.getFloat64(offset, true); +}; + +/** + * @param {number} offset + * @param {number} value + */ +flatbuffers.ByteBuffer.prototype.writeInt8 = function(offset, value) { + this.view_.setInt8(offset, value); +}; + +/** + * @param {number} offset + * @param {number} value + */ +flatbuffers.ByteBuffer.prototype.writeInt16 = function(offset, value) { + this.view_.setInt16(offset, value, true); +}; + +/** + * @param {number} offset + * @param {number} value + */ +flatbuffers.ByteBuffer.prototype.writeInt32 = function(offset, value) { + this.view_.setInt32(offset, value, true); +}; + +/** + * @param {number} offset + * @param {flatbuffers.Long} value + */ +flatbuffers.ByteBuffer.prototype.writeInt64 = function(offset, value) { + this.view_.setInt32(offset, value.low); + this.view_.setInt32(offset + 4, value.high); +}; + +/** + * @param {number} offset + * @param {number} value + */ +flatbuffers.ByteBuffer.prototype.writeFloat32 = function(offset, value) { + this.view_.setFloat32(offset, value, true); +}; + +/** + * @param {number} offset + * @param {number} value + */ +flatbuffers.ByteBuffer.prototype.writeFloat64 = function(offset, value) { + this.view_.setFloat64(offset, value, true); +}; + +/** + * Look up a field in the vtable, return an offset into the object, or 0 if the + * field is not present. + * + * @param {number} bb_pos + * @param {number} vtable_offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.__offset = function(bb_pos, vtable_offset) { + var vtable = bb_pos - this.readInt32(bb_pos); + return vtable_offset < this.readInt16(vtable) ? this.readInt16(vtable + vtable_offset) : 0; +}; + +/** + * Initialize any Table-derived type to point to the union at the given offset. + * + * @param {flatbuffers.Table} t + * @param {number} offset + * @returns {flatbuffers.Table} + */ +flatbuffers.ByteBuffer.prototype.__union = function(t, offset) { + t.bb_pos = offset + this.readInt32(offset); + t.bb = this; + return t; +}; + +/** + * Create a JavaScript string from UTF-8 data stored inside the flatbuffer. + * This allocates a new string and converts to wide chars upon each access. + * + * @param {number} offset + * @returns {string} + */ +flatbuffers.ByteBuffer.prototype.__string = function(offset) { + offset += this.readInt32(offset); + + var length = this.readInt32(offset); + var result = ''; + var i = 0; + + offset += flatbuffers.SIZEOF_INT; + + while (i < length) { + var codePoint; + + // Decode UTF-8 + var a = this.readUint8(offset + i++); + if (a < 0xC0) { + codePoint = a; + } else { + var b = this.readUint8(offset + i++); + if (a < 0xE0) { + codePoint = + ((a & 0x1F) << 6) | + (b & 0x3F); + } else { + var c = this.readUint8(offset + i++); + if (a < 0xF0) { + codePoint = + ((a & 0x0F) << 12) | + ((b & 0x3F) << 6) | + (c & 0x3F); + } else { + var d = this.readUint8(offset + i++); + codePoint = + ((a & 0x07) << 18) | + ((b & 0x3F) << 12) | + ((c & 0x3F) << 6) | + (d & 0x3F); + } + } + } + + // Encode UTF-16 + if (codePoint < 0x10000) { + result += String.fromCharCode(codePoint); + } else { + codePoint -= 0x10000; + result += String.fromCharCode( + (codePoint >> 10) + 0xD800, + (codePoint & ((1 << 10) - 1)) + 0xDC00); + } + } + + return result; +}; + +/** + * Retrieve the relative offset stored at "offset" + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.__indirect = function(offset) { + return offset + this.readInt32(offset); +}; + +/** + * Get the start of data of a vector whose offset is stored at "offset" in this object. + * + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.__vector = function(offset) { + return offset + this.readInt32(offset) + flatbuffers.SIZEOF_INT; // data starts after the length +}; + +/** + * Get the length of a vector whose offset is stored at "offset" in this object. + * + * @param {number} offset + * @returns {number} + */ +flatbuffers.ByteBuffer.prototype.__vector_len = function(offset) { + return this.readInt32(offset + this.readInt32(offset)); +}; + +/** + * @param {string} ident + * @returns {boolean} + */ +flatbuffers.ByteBuffer.prototype.__has_identifier = function(ident) { + if (ident.length != flatbuffers.FILE_IDENTIFIER_LENGTH) { + throw new Error('FlatBuffers: file identifier must be length ' + + flatbuffers.FILE_IDENTIFIER_LENGTH); + } + for (var i = 0; i < flatbuffers.FILE_IDENTIFIER_LENGTH; i++) { + if (ident.charCodeAt(i) != this.readInt8(this.position_ + flatbuffers.SIZEOF_INT + i)) { + return false; + } + } + return true; +}; + +// Exports for Node.js and RequireJS +this.flatbuffers = flatbuffers; diff --git a/src/flatc.cpp b/src/flatc.cpp index 7f654b0ba..e3c74d2e2 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -60,6 +60,10 @@ const Generator generators[] = { flatbuffers::GeneratorOptions::kJava, "Generate Java classes for tables/structs", flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateJS, "-s", "JavaScript", + flatbuffers::GeneratorOptions::kMAX, + "Generate JavaScript code for tables/structs", + flatbuffers::JSMakeRule }, { flatbuffers::GenerateGeneral, "-n", "C#", flatbuffers::GeneratorOptions::kCSharp, "Generate C# classes for tables/structs", @@ -140,6 +144,8 @@ int main(int argc, const char *argv[]) { include_directories.push_back(argv[argi]); } else if(arg == "--strict-json") { opts.strict_json = true; + } else if(arg == "--no-js-exports") { + opts.skip_js_exports = true; } else if(arg == "--defaults-json") { opts.output_default_scalars_in_json = true; } else if(arg == "--no-prefix") { diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp new file mode 100644 index 000000000..40726f1b3 --- /dev/null +++ b/src/idl_gen_js.cpp @@ -0,0 +1,712 @@ +/* + * 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 "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { +namespace js { + +static void GenNamespaces(const Parser &parser, std::string *code_ptr, + std::string *exports_ptr) { + std::set 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 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++) { + code += "/**\n * @const\n*/\n"; + if (it->find('.') == std::string::npos) { + code += "var "; + exports += "this." + *it + " = " + *it + ";\n"; + } + code += *it + " = " + *it + " || {};\n\n"; + } +} + +// Ensure that a type is prefixed with its namespace whenever it is used +// outside of its namespace. +static std::string WrapInNameSpace(const Namespace *ns, + const std::string &name) { + std::string qualified_name; + for (auto it = ns->components.begin(); + it != ns->components.end(); ++it) { + qualified_name += *it + "."; + } + return qualified_name + name; +} + +static std::string WrapInNameSpace(const Definition &def) { + return WrapInNameSpace(def.defined_namespace, def.name); +} + +// Generate a documentation comment, if available. +static void GenDocComment(const std::vector &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; + while (true) { + 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(), code_ptr, extra_lines); +} + +// Generate an enum declaration and an enum string lookup table. +static void GenEnum(EnumDef &enum_def, std::string *code_ptr, + std::string *exports_ptr) { + if (enum_def.generated) return; + std::string &code = *code_ptr; + std::string &exports = *exports_ptr; + GenDocComment(enum_def.doc_comment, code_ptr, "@enum"); + if (enum_def.defined_namespace->components.empty()) { + code += "var "; + 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 + ": " + NumToString(ev.value); + code += (it + 1) != enum_def.vals.vec.end() ? ",\n" : "\n"; + } + 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"; + } +} + +static std::string GenGetter(const Type &type, const std::string &arguments) { + switch (type.base_type) { + case BASE_TYPE_STRING: return "this.bb.__string" + arguments; + case BASE_TYPE_STRUCT: return "this.bb.__struct" + arguments; + case BASE_TYPE_UNION: return "this.bb.__union" + arguments; + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments); + default: { + auto getter = "this.bb.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; + } + } +} + +static std::string GenDefaultValue(const Value &value) { + if (value.type.enum_def) { + if (auto val = value.type.enum_def->ReverseLookup( + atoi(value.constant.c_str()), false)) { + return WrapInNameSpace(*value.type.enum_def) + "." + val->name; + } + } + + 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: + if (value.constant != "0") { + int64_t constant = std::atoll(value.constant.c_str()); + return "new flatbuffers.Long(" + NumToString((int32_t)constant) + + ", " + NumToString((int32_t)(constant >> 32)) + ")"; + } + return "flatbuffers.Long.ZERO"; + + default: + return value.constant; + } +} + +enum struct InOut { + IN, + OUT, +}; + +static std::string GenTypeName(const Type &type, InOut inOut) { + if (inOut == InOut::OUT) { + if (type.base_type == BASE_TYPE_STRING) { + return "?string"; + } + if (type.base_type == BASE_TYPE_STRUCT) { + return WrapInNameSpace(*type.struct_def); + } + } + + 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 +static std::string MaybeAdd(T value) { + return value != 0 ? " + " + NumToString(value) : ""; +} + +template +static std::string MaybeScale(T value) { + return value != 1 ? " * " + NumToString(value) : ""; +} + +static 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, InOut::IN); + *annotations += "} " + nameprefix + field.name + "\n"; + *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. +static void GenStruct(const Parser &parser, StructDef &struct_def, + std::string *code_ptr, std::string *exports_ptr) { + if (struct_def.generated) return; + std::string &code = *code_ptr; + std::string &exports = *exports_ptr; + + // Emit constructor + bool isStatement = struct_def.defined_namespace->components.empty(); + std::string object_name = WrapInNameSpace(struct_def); + GenDocComment(struct_def.doc_comment, code_ptr, "@constructor"); + if (isStatement) { + 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"; + 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 + "}"); + 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}"); + 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 = this.bb.__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, + "@returns {" + GenTypeName(field.value.type, InOut::OUT) + "}"); + code += object_name + ".prototype." + MakeCamel(field.name, false); + code += " = function() {\n"; + if (struct_def.fixed) { + code += " return " + GenGetter(field.value.type, "(this.bb_pos" + + MaybeAdd(field.value.offset) + ")") + ";\n"; + } else { + code += offset_prefix + GenGetter(field.value.type, + "(this.bb_pos + offset)") + " : " + GenDefaultValue(field.value); + 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 + "}"); + 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) + ", this.bb);\n"; + } else { + code += offset_prefix + "(obj || new " + type + ").__init("; + code += field.value.type.struct_def->fixed + ? "this.bb_pos + offset" + : "this.bb.__indirect(this.bb_pos + offset)"; + code += ", this.bb) : null;\n"; + } + break; + } + + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + auto vectortypename = GenTypeName(vectortype, InOut::OUT); + auto inline_size = InlineSize(vectortype); + auto index = "this.bb.__vector(this.bb_pos + offset) + index" + + MaybeScale(inline_size); + GenDocComment(field.doc_comment, code_ptr, + "@param {number} index\n" + + std::string(vectortype.base_type == BASE_TYPE_STRUCT ? + "@param {" + vectortypename + "=} obj\n" : + "") + + "@returns {" + vectortypename + "}"); + code += object_name + ".prototype." + MakeCamel(field.name, false); + code += " = function(index"; + if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += ", obj"; + } + code += ") {\n"; + if (vectortype.base_type == BASE_TYPE_STRUCT) { + code += offset_prefix + "(obj || new " + vectortypename; + code += ").__init("; + code += vectortype.struct_def->fixed + ? index + : "this.bb.__indirect(" + index + ")"; + code += ", this.bb)"; + } else { + 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 += "flatbuffers.Long.ZERO"; + } else if (IsScalar(field.value.type.element)) { + 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}"); + 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: + assert(0); + } + } + code += "};\n\n"; + + // Emit a length helper + if (field.value.type.base_type == BASE_TYPE_VECTOR) { + GenDocComment(code_ptr, "@returns {number}"); + code += object_name + ".prototype." + MakeCamel(field.name, false); + code += "Length = function() {\n" + offset_prefix; + code += "this.bb.__vector_len(this.bb_pos + offset) : 0;\n};\n\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}"); + 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"); + 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, InOut::IN) + "} " + + argname); + 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); + } + 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, InOut::IN) + + ">} data\n" + "@returns {flatbuffers.Offset}"); + 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); + 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"); + 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}"); + 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"); + code += object_name + ".finish" + struct_def.name + "Buffer"; + code += " = function(builder, offset) {\n"; + code += " return builder.finish(offset"; + if (!parser.file_identifier_.empty()) { + code += ", '" + parser.file_identifier_ + "'"; + } + code += ");\n"; + code += "};\n\n"; + } + } +} + +} // namespace js + +// Iterate through all definitions we haven't generate code for (enums, structs, +// and tables) and output them to a single file. +std::string GenerateJS(const Parser &parser, + const GeneratorOptions &opts) { + using namespace js; + + // Generate code for all the enum declarations. + std::string enum_code, exports_code; + for (auto it = parser.enums_.vec.begin(); + it != parser.enums_.vec.end(); ++it) { + GenEnum(**it, &enum_code, &exports_code); + } + + // Generate code for all structs, then all tables. + std::string decl_code; + for (auto it = parser.structs_.vec.begin(); + it != parser.structs_.vec.end(); ++it) { + GenStruct(parser, **it, &decl_code, &exports_code); + } + + // Only output file-level code if there were any declarations. + if (enum_code.length() || decl_code.length()) { + std::string code; + code = "// automatically generated by the FlatBuffers compiler," + " do not modify\n\n"; + + // Generate code for all the namespace declarations. + GenNamespaces(parser, &code, &exports_code); + + // Output the main declaration code from above. + code += enum_code; + code += decl_code; + + if (!exports_code.empty() && !opts.skip_js_exports) { + code += "// Exports for Node.js and RequireJS\n"; + code += exports_code; + } + + return code; + } + + return std::string(); +} + +static std::string GeneratedFileName(const std::string &path, + const std::string &file_name) { + return path + file_name + "_generated.js"; +} + +bool GenerateJS(const Parser &parser, + const std::string &path, + const std::string &file_name, + const GeneratorOptions &opts) { + auto code = GenerateJS(parser, opts); + return !code.length() || + SaveFile(GeneratedFileName(path, file_name).c_str(), code, false); +} + +std::string JSMakeRule(const Parser &parser, + const std::string &path, + const std::string &file_name, + const GeneratorOptions & /*opts*/) { + std::string filebase = flatbuffers::StripPath( + flatbuffers::StripExtension(file_name)); + std::string make_rule = GeneratedFileName(path, filebase) + ": "; + 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 diff --git a/tests/JavaScriptTest.js b/tests/JavaScriptTest.js new file mode 100644 index 000000000..af62a4ee6 --- /dev/null +++ b/tests/JavaScriptTest.js @@ -0,0 +1,116 @@ +var assert = require('assert'); +var fs = require('fs'); + +var flatbuffers = require('../js/flatbuffers').flatbuffers; +var MyGame = require('./monster_test_generated').MyGame; + +function main() { + + // First, let's test reading a FlatBuffer generated by C++ code: + // This file was generated from monsterdata_test.json + var data = new Uint8Array(fs.readFileSync('monsterdata_test.mon')); + + // Now test it: + + var bb = new flatbuffers.ByteBuffer(data); + testBuffer(bb); + + // Second, let's create a FlatBuffer from scratch in JavaScript, and test it also. + // We use an initial size of 1 to exercise the reallocation algorithm, + // normally a size larger than the typical FlatBuffer you generate would be + // better for performance. + var fbb = new flatbuffers.Builder(1); + + // We set up the same values as monsterdata.json: + + var str = fbb.createString('MyMonster'); + + var inv = MyGame.Example.Monster.createInventoryVector(fbb, [0, 1, 2, 3, 4]); + + var fred = fbb.createString('Fred'); + MyGame.Example.Monster.startMonster(fbb); + MyGame.Example.Monster.addName(fbb, fred); + var mon2 = MyGame.Example.Monster.endMonster(fbb); + + MyGame.Example.Monster.startTest4Vector(fbb, 2); + MyGame.Example.Test.createTest(fbb, 10, 20); + MyGame.Example.Test.createTest(fbb, 30, 40); + var test4 = fbb.endVector(); + + var testArrayOfString = MyGame.Example.Monster.createTestarrayofstringVector(fbb, [ + fbb.createString('test1'), + fbb.createString('test2') + ]); + + MyGame.Example.Monster.startMonster(fbb); + MyGame.Example.Monster.addPos(fbb, MyGame.Example.Vec3.createVec3(fbb, 1, 2, 3, 3, MyGame.Example.Color.Green, 5, 6)); + MyGame.Example.Monster.addHp(fbb, 80); + MyGame.Example.Monster.addName(fbb, str); + MyGame.Example.Monster.addInventory(fbb, inv); + MyGame.Example.Monster.addTestType(fbb, MyGame.Example.Any.Monster); + MyGame.Example.Monster.addTest(fbb, mon2); + MyGame.Example.Monster.addTest4(fbb, test4); + MyGame.Example.Monster.addTestarrayofstring(fbb, testArrayOfString); + MyGame.Example.Monster.addTestbool(fbb, false); + var mon = MyGame.Example.Monster.endMonster(fbb); + + MyGame.Example.Monster.finishMonsterBuffer(fbb, mon); + + // Write the result to a file for debugging purposes: + // Note that the binaries are not necessarily identical, since the JSON + // parser may serialize in a slightly different order than the above + // JavaScript code. They are functionally equivalent though. + + fs.writeFileSync('monsterdata_javascript_wire.mon', new Buffer(fbb.asUint8Array())); + + // Test it: + testBuffer(fbb.dataBuffer()); + + console.log('FlatBuffers test: completed successfully'); +} + +function testBuffer(bb) { + assert.ok(MyGame.Example.Monster.bufferHasIdentifier(bb)); + + var monster = MyGame.Example.Monster.getRootAsMonster(bb); + + assert.strictEqual(monster.hp(), 80); + assert.strictEqual(monster.mana(), 150); // default + + assert.strictEqual(monster.name(), 'MyMonster'); + + var pos = monster.pos(); + assert.strictEqual(pos.x(), 1); + assert.strictEqual(pos.y(), 2); + assert.strictEqual(pos.z(), 3); + assert.strictEqual(pos.test1(), 3); + assert.strictEqual(pos.test2(), MyGame.Example.Color.Green); + var t = pos.test3(); + assert.strictEqual(t.a(), 5); + assert.strictEqual(t.b(), 6); + + assert.strictEqual(monster.testType(), MyGame.Example.Any.Monster); + var monster2 = new MyGame.Example.Monster(); + assert.strictEqual(monster.test(monster2) != null, true); + assert.strictEqual(monster2.name(), 'Fred'); + + assert.strictEqual(monster.inventoryLength(), 5); + var invsum = 0; + for (var i = 0; i < monster.inventoryLength(); i++) { + invsum += monster.inventory(i); + } + assert.strictEqual(invsum, 10); + + var test_0 = monster.test4(0); + var test_1 = monster.test4(1); + assert.strictEqual(monster.test4Length(), 2); + assert.strictEqual(test_0.a() + test_0.b() + test_1.a() + test_1.b(), 100); + + assert.strictEqual(monster.testarrayofstringLength(), 2); + assert.strictEqual(monster.testarrayofstring(0), 'test1'); + assert.strictEqual(monster.testarrayofstring(1), 'test2'); + + assert.strictEqual(monster.testbool(), false); +} + +main(); diff --git a/tests/JavaScriptTest.sh b/tests/JavaScriptTest.sh new file mode 100755 index 000000000..40dba2be1 --- /dev/null +++ b/tests/JavaScriptTest.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +pushd "$(dirname $0)" >/dev/null +test_dir="$(pwd)" +node ${test_dir}/JavaScriptTest diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 5b768197f..3e96712f7 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -1,2 +1,2 @@ -../flatc -c -j -n -g -b -p --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +../flatc -c -j -n -g -b -p -s --gen-mutable --no-includes monster_test.fbs monsterdata_test.json ../flatc -b --schema monster_test.fbs diff --git a/tests/monster_test_generated.js b/tests/monster_test_generated.js new file mode 100644 index 000000000..25617cada --- /dev/null +++ b/tests/monster_test_generated.js @@ -0,0 +1,936 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/** + * @const +*/ +var MyGame = MyGame || {}; + +/** + * @const +*/ +MyGame.Example = MyGame.Example || {}; + +/** + * @const +*/ +MyGame.OtherNameSpace = MyGame.OtherNameSpace || {}; + +/** + * @enum + */ +MyGame.Example.Color = { + Red: 1, + Green: 2, + Blue: 8 +}; + +/** + * @enum + */ +MyGame.Example.Any = { + NONE: 0, + Monster: 1, + TestSimpleTableWithEnum: 2 +}; + +/** + * @constructor + */ +MyGame.Example.Test = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example.Test} + */ +MyGame.Example.Test.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +MyGame.Example.Test.prototype.a = function() { + return this.bb.readInt16(this.bb_pos); +}; + +/** + * @returns {number} + */ +MyGame.Example.Test.prototype.b = function() { + return this.bb.readInt8(this.bb_pos + 2); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} a + * @param {number} b + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Test.createTest = function(builder, a, b) { + builder.prep(2, 4); + builder.pad(1); + builder.writeInt8(b); + builder.writeInt16(a); + return builder.offset(); +}; + +/** + * @constructor + */ +MyGame.Example.TestSimpleTableWithEnum = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example.TestSimpleTableWithEnum} + */ +MyGame.Example.TestSimpleTableWithEnum.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {MyGame.Example.TestSimpleTableWithEnum=} obj + * @returns {MyGame.Example.TestSimpleTableWithEnum} + */ +MyGame.Example.TestSimpleTableWithEnum.getRootAsTestSimpleTableWithEnum = function(bb, obj) { + return (obj || new MyGame.Example.TestSimpleTableWithEnum).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @returns {MyGame.Example.Color} + */ +MyGame.Example.TestSimpleTableWithEnum.prototype.color = function() { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + offset)) : MyGame.Example.Color.Green; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +MyGame.Example.TestSimpleTableWithEnum.startTestSimpleTableWithEnum = function(builder) { + builder.startObject(1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {MyGame.Example.Color} color + */ +MyGame.Example.TestSimpleTableWithEnum.addColor = function(builder, color) { + builder.addFieldInt8(0, color, MyGame.Example.Color.Green); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +MyGame.Example.TestSimpleTableWithEnum.endTestSimpleTableWithEnum = function(builder) { + var offset = builder.endObject(); + return offset; +}; + +/** + * @constructor + */ +MyGame.Example.Vec3 = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example.Vec3} + */ +MyGame.Example.Vec3.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @returns {number} + */ +MyGame.Example.Vec3.prototype.x = function() { + return this.bb.readFloat32(this.bb_pos); +}; + +/** + * @returns {number} + */ +MyGame.Example.Vec3.prototype.y = function() { + return this.bb.readFloat32(this.bb_pos + 4); +}; + +/** + * @returns {number} + */ +MyGame.Example.Vec3.prototype.z = function() { + return this.bb.readFloat32(this.bb_pos + 8); +}; + +/** + * @returns {number} + */ +MyGame.Example.Vec3.prototype.test1 = function() { + return this.bb.readFloat64(this.bb_pos + 16); +}; + +/** + * @returns {MyGame.Example.Color} + */ +MyGame.Example.Vec3.prototype.test2 = function() { + return /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + 24)); +}; + +/** + * @param {MyGame.Example.Test=} obj + * @returns {MyGame.Example.Test} + */ +MyGame.Example.Vec3.prototype.test3 = function(obj) { + return (obj || new MyGame.Example.Test).__init(this.bb_pos + 26, this.bb); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} test1 + * @param {MyGame.Example.Color} test2 + * @param {number} test3_a + * @param {number} test3_b + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Vec3.createVec3 = function(builder, x, y, z, test1, test2, test3_a, test3_b) { + builder.prep(16, 32); + builder.pad(2); + builder.prep(2, 4); + builder.pad(1); + builder.writeInt8(test3_b); + builder.writeInt16(test3_a); + builder.pad(1); + builder.writeInt8(test2); + builder.writeFloat64(test1); + builder.pad(4); + builder.writeFloat32(z); + builder.writeFloat32(y); + builder.writeFloat32(x); + return builder.offset(); +}; + +/** + * @constructor + */ +MyGame.Example.Stat = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example.Stat} + */ +MyGame.Example.Stat.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {MyGame.Example.Stat=} obj + * @returns {MyGame.Example.Stat} + */ +MyGame.Example.Stat.getRootAsStat = function(bb, obj) { + return (obj || new MyGame.Example.Stat).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @returns {?string} + */ +MyGame.Example.Stat.prototype.id = function() { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? this.bb.__string(this.bb_pos + offset) : null; +}; + +/** + * @returns {flatbuffers.Long} + */ +MyGame.Example.Stat.prototype.val = function() { + var offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.readInt64(this.bb_pos + offset) : flatbuffers.Long.ZERO; +}; + +/** + * @returns {number} + */ +MyGame.Example.Stat.prototype.count = function() { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? this.bb.readUint16(this.bb_pos + offset) : 0; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +MyGame.Example.Stat.startStat = function(builder) { + builder.startObject(3); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} idOffset + */ +MyGame.Example.Stat.addId = function(builder, idOffset) { + builder.addFieldOffset(0, idOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Long} val + */ +MyGame.Example.Stat.addVal = function(builder, val) { + builder.addFieldInt64(1, val, flatbuffers.Long.ZERO); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} count + */ +MyGame.Example.Stat.addCount = function(builder, count) { + builder.addFieldInt16(2, count, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Stat.endStat = function(builder) { + var offset = builder.endObject(); + return offset; +}; + +/** + * @constructor + */ +MyGame.Example.Monster = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example.Monster} + */ +MyGame.Example.Monster.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {MyGame.Example.Monster=} obj + * @returns {MyGame.Example.Monster} + */ +MyGame.Example.Monster.getRootAsMonster = function(bb, obj) { + return (obj || new MyGame.Example.Monster).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @returns {boolean} + */ +MyGame.Example.Monster.bufferHasIdentifier = function(bb) { + return bb.__has_identifier('MONS'); +}; + +/** + * @param {MyGame.Example.Vec3=} obj + * @returns {MyGame.Example.Vec3} + */ +MyGame.Example.Monster.prototype.pos = function(obj) { + var offset = this.bb.__offset(this.bb_pos, 4); + return offset ? (obj || new MyGame.Example.Vec3).__init(this.bb_pos + offset, this.bb) : null; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.mana = function() { + var offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.readInt16(this.bb_pos + offset) : 150; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.hp = function() { + var offset = this.bb.__offset(this.bb_pos, 8); + return offset ? this.bb.readInt16(this.bb_pos + offset) : 100; +}; + +/** + * @returns {?string} + */ +MyGame.Example.Monster.prototype.name = function() { + var offset = this.bb.__offset(this.bb_pos, 10); + return offset ? this.bb.__string(this.bb_pos + offset) : null; +}; + +/** + * @param {number} index + * @returns {number} + */ +MyGame.Example.Monster.prototype.inventory = function(index) { + var offset = this.bb.__offset(this.bb_pos, 14); + return offset ? this.bb.readUint8(this.bb.__vector(this.bb_pos + offset) + index) : 0; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.inventoryLength = function() { + var offset = this.bb.__offset(this.bb_pos, 14); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @returns {MyGame.Example.Color} + */ +MyGame.Example.Monster.prototype.color = function() { + var offset = this.bb.__offset(this.bb_pos, 16); + return offset ? /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + offset)) : MyGame.Example.Color.Blue; +}; + +/** + * @returns {MyGame.Example.Any} + */ +MyGame.Example.Monster.prototype.testType = function() { + var offset = this.bb.__offset(this.bb_pos, 18); + return offset ? /** @type {MyGame.Example.Any} */ (this.bb.readUint8(this.bb_pos + offset)) : MyGame.Example.Any.NONE; +}; + +/** + * @param {flatbuffers.Table} obj + * @returns {?flatbuffers.Table} + */ +MyGame.Example.Monster.prototype.test = function(obj) { + var offset = this.bb.__offset(this.bb_pos, 20); + return offset ? this.bb.__union(obj, this.bb_pos + offset) : null; +}; + +/** + * @param {number} index + * @param {MyGame.Example.Test=} obj + * @returns {MyGame.Example.Test} + */ +MyGame.Example.Monster.prototype.test4 = function(index, obj) { + var offset = this.bb.__offset(this.bb_pos, 22); + return offset ? (obj || new MyGame.Example.Test).__init(this.bb.__vector(this.bb_pos + offset) + index * 4, this.bb) : null; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.test4Length = function() { + var offset = this.bb.__offset(this.bb_pos, 22); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @param {number} index + * @returns {?string} + */ +MyGame.Example.Monster.prototype.testarrayofstring = function(index) { + var offset = this.bb.__offset(this.bb_pos, 24); + return offset ? this.bb.__string(this.bb.__vector(this.bb_pos + offset) + index * 4) : null; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testarrayofstringLength = function() { + var offset = this.bb.__offset(this.bb_pos, 24); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * an example documentation comment: this will end up in the generated code + * multiline too + * + * @param {number} index + * @param {MyGame.Example.Monster=} obj + * @returns {MyGame.Example.Monster} + */ +MyGame.Example.Monster.prototype.testarrayoftables = function(index, obj) { + var offset = this.bb.__offset(this.bb_pos, 26); + return offset ? (obj || new MyGame.Example.Monster).__init(this.bb.__indirect(this.bb.__vector(this.bb_pos + offset) + index * 4), this.bb) : null; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testarrayoftablesLength = function() { + var offset = this.bb.__offset(this.bb_pos, 26); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @param {MyGame.Example.Monster=} obj + * @returns {MyGame.Example.Monster} + */ +MyGame.Example.Monster.prototype.enemy = function(obj) { + var offset = this.bb.__offset(this.bb_pos, 28); + return offset ? (obj || new MyGame.Example.Monster).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null; +}; + +/** + * @param {number} index + * @returns {number} + */ +MyGame.Example.Monster.prototype.testnestedflatbuffer = function(index) { + var offset = this.bb.__offset(this.bb_pos, 30); + return offset ? this.bb.readUint8(this.bb.__vector(this.bb_pos + offset) + index) : 0; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testnestedflatbufferLength = function() { + var offset = this.bb.__offset(this.bb_pos, 30); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** + * @param {MyGame.Example.Stat=} obj + * @returns {MyGame.Example.Stat} + */ +MyGame.Example.Monster.prototype.testempty = function(obj) { + var offset = this.bb.__offset(this.bb_pos, 32); + return offset ? (obj || new MyGame.Example.Stat).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null; +}; + +/** + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.testbool = function() { + var offset = this.bb.__offset(this.bb_pos, 34); + return offset ? !!this.bb.readInt8(this.bb_pos + offset) : false; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testhashs32Fnv1 = function() { + var offset = this.bb.__offset(this.bb_pos, 36); + return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testhashu32Fnv1 = function() { + var offset = this.bb.__offset(this.bb_pos, 38); + return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; +}; + +/** + * @returns {flatbuffers.Long} + */ +MyGame.Example.Monster.prototype.testhashs64Fnv1 = function() { + var offset = this.bb.__offset(this.bb_pos, 40); + return offset ? this.bb.readInt64(this.bb_pos + offset) : flatbuffers.Long.ZERO; +}; + +/** + * @returns {flatbuffers.Long} + */ +MyGame.Example.Monster.prototype.testhashu64Fnv1 = function() { + var offset = this.bb.__offset(this.bb_pos, 42); + return offset ? this.bb.readUint64(this.bb_pos + offset) : flatbuffers.Long.ZERO; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testhashs32Fnv1a = function() { + var offset = this.bb.__offset(this.bb_pos, 44); + return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testhashu32Fnv1a = function() { + var offset = this.bb.__offset(this.bb_pos, 46); + return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; +}; + +/** + * @returns {flatbuffers.Long} + */ +MyGame.Example.Monster.prototype.testhashs64Fnv1a = function() { + var offset = this.bb.__offset(this.bb_pos, 48); + return offset ? this.bb.readInt64(this.bb_pos + offset) : flatbuffers.Long.ZERO; +}; + +/** + * @returns {flatbuffers.Long} + */ +MyGame.Example.Monster.prototype.testhashu64Fnv1a = function() { + var offset = this.bb.__offset(this.bb_pos, 50); + return offset ? this.bb.readUint64(this.bb_pos + offset) : flatbuffers.Long.ZERO; +}; + +/** + * @param {flatbuffers.Builder} builder + */ +MyGame.Example.Monster.startMonster = function(builder) { + builder.startObject(24); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} posOffset + */ +MyGame.Example.Monster.addPos = function(builder, posOffset) { + builder.addFieldStruct(0, posOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} mana + */ +MyGame.Example.Monster.addMana = function(builder, mana) { + builder.addFieldInt16(1, mana, 150); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} hp + */ +MyGame.Example.Monster.addHp = function(builder, hp) { + builder.addFieldInt16(2, hp, 100); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} nameOffset + */ +MyGame.Example.Monster.addName = function(builder, nameOffset) { + builder.addFieldOffset(3, nameOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} inventoryOffset + */ +MyGame.Example.Monster.addInventory = function(builder, inventoryOffset) { + builder.addFieldOffset(5, inventoryOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.createInventoryVector = function(builder, data) { + builder.startVector(1, data.length, 1); + for (var i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startInventoryVector = function(builder, numElems) { + builder.startVector(1, numElems, 1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {MyGame.Example.Color} color + */ +MyGame.Example.Monster.addColor = function(builder, color) { + builder.addFieldInt8(6, color, MyGame.Example.Color.Blue); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {MyGame.Example.Any} testType + */ +MyGame.Example.Monster.addTestType = function(builder, testType) { + builder.addFieldInt8(7, testType, MyGame.Example.Any.NONE); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testOffset + */ +MyGame.Example.Monster.addTest = function(builder, testOffset) { + builder.addFieldOffset(8, testOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} test4Offset + */ +MyGame.Example.Monster.addTest4 = function(builder, test4Offset) { + builder.addFieldOffset(9, test4Offset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startTest4Vector = function(builder, numElems) { + builder.startVector(4, numElems, 2); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testarrayofstringOffset + */ +MyGame.Example.Monster.addTestarrayofstring = function(builder, testarrayofstringOffset) { + builder.addFieldOffset(10, testarrayofstringOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.createTestarrayofstringVector = function(builder, data) { + builder.startVector(4, data.length, 4); + for (var i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startTestarrayofstringVector = function(builder, numElems) { + builder.startVector(4, numElems, 4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testarrayoftablesOffset + */ +MyGame.Example.Monster.addTestarrayoftables = function(builder, testarrayoftablesOffset) { + builder.addFieldOffset(11, testarrayoftablesOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.createTestarrayoftablesVector = function(builder, data) { + builder.startVector(4, data.length, 4); + for (var i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startTestarrayoftablesVector = function(builder, numElems) { + builder.startVector(4, numElems, 4); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} enemyOffset + */ +MyGame.Example.Monster.addEnemy = function(builder, enemyOffset) { + builder.addFieldOffset(12, enemyOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testnestedflatbufferOffset + */ +MyGame.Example.Monster.addTestnestedflatbuffer = function(builder, testnestedflatbufferOffset) { + builder.addFieldOffset(13, testnestedflatbufferOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.} data + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.createTestnestedflatbufferVector = function(builder, data) { + builder.startVector(1, data.length, 1); + for (var i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startTestnestedflatbufferVector = function(builder, numElems) { + builder.startVector(1, numElems, 1); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testemptyOffset + */ +MyGame.Example.Monster.addTestempty = function(builder, testemptyOffset) { + builder.addFieldOffset(14, testemptyOffset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {boolean} testbool + */ +MyGame.Example.Monster.addTestbool = function(builder, testbool) { + builder.addFieldInt8(15, +testbool, +false); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} testhashs32Fnv1 + */ +MyGame.Example.Monster.addTesthashs32Fnv1 = function(builder, testhashs32Fnv1) { + builder.addFieldInt32(16, testhashs32Fnv1, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} testhashu32Fnv1 + */ +MyGame.Example.Monster.addTesthashu32Fnv1 = function(builder, testhashu32Fnv1) { + builder.addFieldInt32(17, testhashu32Fnv1, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Long} testhashs64Fnv1 + */ +MyGame.Example.Monster.addTesthashs64Fnv1 = function(builder, testhashs64Fnv1) { + builder.addFieldInt64(18, testhashs64Fnv1, flatbuffers.Long.ZERO); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Long} testhashu64Fnv1 + */ +MyGame.Example.Monster.addTesthashu64Fnv1 = function(builder, testhashu64Fnv1) { + builder.addFieldInt64(19, testhashu64Fnv1, flatbuffers.Long.ZERO); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} testhashs32Fnv1a + */ +MyGame.Example.Monster.addTesthashs32Fnv1a = function(builder, testhashs32Fnv1a) { + builder.addFieldInt32(20, testhashs32Fnv1a, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} testhashu32Fnv1a + */ +MyGame.Example.Monster.addTesthashu32Fnv1a = function(builder, testhashu32Fnv1a) { + builder.addFieldInt32(21, testhashu32Fnv1a, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Long} testhashs64Fnv1a + */ +MyGame.Example.Monster.addTesthashs64Fnv1a = function(builder, testhashs64Fnv1a) { + builder.addFieldInt64(22, testhashs64Fnv1a, flatbuffers.Long.ZERO); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Long} testhashu64Fnv1a + */ +MyGame.Example.Monster.addTesthashu64Fnv1a = function(builder, testhashu64Fnv1a) { + builder.addFieldInt64(23, testhashu64Fnv1a, flatbuffers.Long.ZERO); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.endMonster = function(builder) { + var offset = builder.endObject(); + builder.requiredField(offset, 10); // name + return offset; +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} offset + */ +MyGame.Example.Monster.finishMonsterBuffer = function(builder, offset) { + return builder.finish(offset, 'MONS'); +}; + +// Exports for Node.js and RequireJS +this.MyGame = MyGame;