From 0230a7173f3f281cc2a9f86aecec7532eadef5ec Mon Sep 17 00:00:00 2001 From: Michael Paulson Date: Fri, 22 Jul 2016 11:11:56 -0700 Subject: [PATCH 1/3] feat(mutable-js): The mutable Scalar generation. This is just the initial commit to start the conversation on adding mutation to javascript. --- src/idl_gen_js.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index d7c913e50..fec578029 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -79,7 +79,7 @@ class JsGenerator : public BaseGenerator { for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; - GenStruct(struct_def, decl_code_ptr, exports_code_ptr); + GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr); } } void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) { @@ -361,7 +361,7 @@ static void GenStructBody(const StructDef &struct_def, } // Generate an accessor struct with constructor for a flatbuffers struct. -void GenStruct(StructDef &struct_def, std::string *code_ptr, std::string *exports_ptr) { +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; @@ -550,6 +550,22 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr, std::string *export } code += "};\n\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}"); + + code += object_name + ".prototype.mutate_" + field.name + " = function(value) {\n"; + code += " var offset = this.bb.__offset(this.bb_pos, " + NumToString(field.value.offset) + ")\n\n"; + code += " if (offset === 0) {\n"; + code += " return false;\n"; + code += " }\n\n"; + code += " this.bb.write" + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + offset, value);\n"; + code += " return true;\n"; + code += "}\n\n"; + } + // Emit vector helpers if (field.value.type.base_type == BASE_TYPE_VECTOR) { // Emit a length helper From a351124cfd3b12ca46572a664c7884e5844908e3 Mon Sep 17 00:00:00 2001 From: Michael Paulson Date: Mon, 25 Jul 2016 14:33:46 -0700 Subject: [PATCH 2/3] chore(generate-code): Generate the JS code after mutation has been added. --- tests/monster_test_generated.js | 390 ++++++++++++++++++ .../namespace_test1_generated.js | 45 ++ .../namespace_test2_generated.js | 15 + 3 files changed, 450 insertions(+) diff --git a/tests/monster_test_generated.js b/tests/monster_test_generated.js index 7c4c28f82..774882d7c 100644 --- a/tests/monster_test_generated.js +++ b/tests/monster_test_generated.js @@ -127,6 +127,21 @@ MyGame.Example.Test.prototype.a = function() { return this.bb.readInt16(this.bb_pos); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Test.prototype.mutate_a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 0) + + if (offset === 0) { + return false; + } + + this.bb.writeInt16(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -134,6 +149,21 @@ MyGame.Example.Test.prototype.b = function() { return this.bb.readInt8(this.bb_pos + 2); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Test.prototype.mutate_b = function(value) { + var offset = this.bb.__offset(this.bb_pos, 2) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Builder} builder * @param {number} a @@ -191,6 +221,21 @@ MyGame.Example.TestSimpleTableWithEnum.prototype.color = function() { return offset ? /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + offset)) : MyGame.Example.Color.Green; }; +/** + * @param {MyGame.Example.Color} value + * @returns {boolean} + */ +MyGame.Example.TestSimpleTableWithEnum.prototype.mutate_color = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Builder} builder */ @@ -248,6 +293,21 @@ MyGame.Example.Vec3.prototype.x = function() { return this.bb.readFloat32(this.bb_pos); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Vec3.prototype.mutate_x = function(value) { + var offset = this.bb.__offset(this.bb_pos, 0) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -255,6 +315,21 @@ MyGame.Example.Vec3.prototype.y = function() { return this.bb.readFloat32(this.bb_pos + 4); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Vec3.prototype.mutate_y = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -262,6 +337,21 @@ MyGame.Example.Vec3.prototype.z = function() { return this.bb.readFloat32(this.bb_pos + 8); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Vec3.prototype.mutate_z = function(value) { + var offset = this.bb.__offset(this.bb_pos, 8) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -269,6 +359,21 @@ MyGame.Example.Vec3.prototype.test1 = function() { return this.bb.readFloat64(this.bb_pos + 16); }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Vec3.prototype.mutate_test1 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 16) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat64(this.bb_pos + offset, value); + return true; +} + /** * @returns {MyGame.Example.Color} */ @@ -276,6 +381,21 @@ MyGame.Example.Vec3.prototype.test2 = function() { return /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + 24)); }; +/** + * @param {MyGame.Example.Color} value + * @returns {boolean} + */ +MyGame.Example.Vec3.prototype.mutate_test2 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 24) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @param {MyGame.Example.Test=} obj * @returns {MyGame.Example.Test} @@ -364,6 +484,21 @@ MyGame.Example.Stat.prototype.val = function() { return offset ? this.bb.readInt64(this.bb_pos + offset) : this.bb.createLong(0, 0); }; +/** + * @param {flatbuffers.Long} value + * @returns {boolean} + */ +MyGame.Example.Stat.prototype.mutate_val = function(value) { + var offset = this.bb.__offset(this.bb_pos, 6) + + if (offset === 0) { + return false; + } + + this.bb.writeInt64(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -372,6 +507,21 @@ MyGame.Example.Stat.prototype.count = function() { return offset ? this.bb.readUint16(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Stat.prototype.mutate_count = function(value) { + var offset = this.bb.__offset(this.bb_pos, 8) + + if (offset === 0) { + return false; + } + + this.bb.writeUint16(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Builder} builder */ @@ -474,6 +624,21 @@ MyGame.Example.Monster.prototype.mana = function() { return offset ? this.bb.readInt16(this.bb_pos + offset) : 150; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_mana = function(value) { + var offset = this.bb.__offset(this.bb_pos, 6) + + if (offset === 0) { + return false; + } + + this.bb.writeInt16(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -482,6 +647,21 @@ MyGame.Example.Monster.prototype.hp = function() { return offset ? this.bb.readInt16(this.bb_pos + offset) : 100; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_hp = function(value) { + var offset = this.bb.__offset(this.bb_pos, 8) + + if (offset === 0) { + return false; + } + + this.bb.writeInt16(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Encoding=} optionalEncoding * @returns {string|Uint8Array} @@ -524,6 +704,21 @@ MyGame.Example.Monster.prototype.color = function() { return offset ? /** @type {MyGame.Example.Color} */ (this.bb.readInt8(this.bb_pos + offset)) : MyGame.Example.Color.Blue; }; +/** + * @param {MyGame.Example.Color} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_color = function(value) { + var offset = this.bb.__offset(this.bb_pos, 16) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @returns {MyGame.Example.Any} */ @@ -532,6 +727,21 @@ MyGame.Example.Monster.prototype.testType = function() { return offset ? /** @type {MyGame.Example.Any} */ (this.bb.readUint8(this.bb_pos + offset)) : MyGame.Example.Any.NONE; }; +/** + * @param {MyGame.Example.Any} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_test_type = function(value) { + var offset = this.bb.__offset(this.bb_pos, 18) + + if (offset === 0) { + return false; + } + + this.bb.writeUint8(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Table} obj * @returns {?flatbuffers.Table} @@ -649,6 +859,21 @@ MyGame.Example.Monster.prototype.testbool = function() { return offset ? !!this.bb.readInt8(this.bb_pos + offset) : false; }; +/** + * @param {boolean} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testbool = function(value) { + var offset = this.bb.__offset(this.bb_pos, 34) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -657,6 +882,21 @@ MyGame.Example.Monster.prototype.testhashs32Fnv1 = function() { return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashs32_fnv1 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 36) + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -665,6 +905,21 @@ MyGame.Example.Monster.prototype.testhashu32Fnv1 = function() { return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashu32_fnv1 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 38) + + if (offset === 0) { + return false; + } + + this.bb.writeUint32(this.bb_pos + offset, value); + return true; +} + /** * @returns {flatbuffers.Long} */ @@ -673,6 +928,21 @@ MyGame.Example.Monster.prototype.testhashs64Fnv1 = function() { return offset ? this.bb.readInt64(this.bb_pos + offset) : this.bb.createLong(0, 0); }; +/** + * @param {flatbuffers.Long} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashs64_fnv1 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 40) + + if (offset === 0) { + return false; + } + + this.bb.writeInt64(this.bb_pos + offset, value); + return true; +} + /** * @returns {flatbuffers.Long} */ @@ -681,6 +951,21 @@ MyGame.Example.Monster.prototype.testhashu64Fnv1 = function() { return offset ? this.bb.readUint64(this.bb_pos + offset) : this.bb.createLong(0, 0); }; +/** + * @param {flatbuffers.Long} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashu64_fnv1 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 42) + + if (offset === 0) { + return false; + } + + this.bb.writeUint64(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -689,6 +974,21 @@ MyGame.Example.Monster.prototype.testhashs32Fnv1a = function() { return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashs32_fnv1a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 44) + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -697,6 +997,21 @@ MyGame.Example.Monster.prototype.testhashu32Fnv1a = function() { return offset ? this.bb.readUint32(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashu32_fnv1a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 46) + + if (offset === 0) { + return false; + } + + this.bb.writeUint32(this.bb_pos + offset, value); + return true; +} + /** * @returns {flatbuffers.Long} */ @@ -705,6 +1020,21 @@ MyGame.Example.Monster.prototype.testhashs64Fnv1a = function() { return offset ? this.bb.readInt64(this.bb_pos + offset) : this.bb.createLong(0, 0); }; +/** + * @param {flatbuffers.Long} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashs64_fnv1a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 48) + + if (offset === 0) { + return false; + } + + this.bb.writeInt64(this.bb_pos + offset, value); + return true; +} + /** * @returns {flatbuffers.Long} */ @@ -713,6 +1043,21 @@ MyGame.Example.Monster.prototype.testhashu64Fnv1a = function() { return offset ? this.bb.readUint64(this.bb_pos + offset) : this.bb.createLong(0, 0); }; +/** + * @param {flatbuffers.Long} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testhashu64_fnv1a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 50) + + if (offset === 0) { + return false; + } + + this.bb.writeUint64(this.bb_pos + offset, value); + return true; +} + /** * @param {number} index * @returns {boolean} @@ -746,6 +1091,21 @@ MyGame.Example.Monster.prototype.testf = function() { return offset ? this.bb.readFloat32(this.bb_pos + offset) : 3.14159; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testf = function(value) { + var offset = this.bb.__offset(this.bb_pos, 54) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -754,6 +1114,21 @@ MyGame.Example.Monster.prototype.testf2 = function() { return offset ? this.bb.readFloat32(this.bb_pos + offset) : 3.0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testf2 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 56) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -762,6 +1137,21 @@ MyGame.Example.Monster.prototype.testf3 = function() { return offset ? this.bb.readFloat32(this.bb_pos + offset) : 0.0; }; +/** + * @param {number} value + * @returns {boolean} + */ +MyGame.Example.Monster.prototype.mutate_testf3 = function(value) { + var offset = this.bb.__offset(this.bb_pos, 58) + + if (offset === 0) { + return false; + } + + this.bb.writeFloat32(this.bb_pos + offset, value); + return true; +} + /** * @param {number} index * @param {flatbuffers.Encoding=} optionalEncoding diff --git a/tests/namespace_test/namespace_test1_generated.js b/tests/namespace_test/namespace_test1_generated.js index e6390567f..a8fea9ea6 100644 --- a/tests/namespace_test/namespace_test1_generated.js +++ b/tests/namespace_test/namespace_test1_generated.js @@ -64,6 +64,21 @@ NamespaceA.NamespaceB.TableInNestedNS.prototype.foo = function() { return offset ? this.bb.readInt32(this.bb_pos + offset) : 0; }; +/** + * @param {number} value + * @returns {boolean} + */ +NamespaceA.NamespaceB.TableInNestedNS.prototype.mutate_foo = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4) + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Builder} builder */ @@ -121,6 +136,21 @@ NamespaceA.NamespaceB.StructInNestedNS.prototype.a = function() { return this.bb.readInt32(this.bb_pos); }; +/** + * @param {number} value + * @returns {boolean} + */ +NamespaceA.NamespaceB.StructInNestedNS.prototype.mutate_a = function(value) { + var offset = this.bb.__offset(this.bb_pos, 0) + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +} + /** * @returns {number} */ @@ -128,6 +158,21 @@ NamespaceA.NamespaceB.StructInNestedNS.prototype.b = function() { return this.bb.readInt32(this.bb_pos + 4); }; +/** + * @param {number} value + * @returns {boolean} + */ +NamespaceA.NamespaceB.StructInNestedNS.prototype.mutate_b = function(value) { + var offset = this.bb.__offset(this.bb_pos, 4) + + if (offset === 0) { + return false; + } + + this.bb.writeInt32(this.bb_pos + offset, value); + return true; +} + /** * @param {flatbuffers.Builder} builder * @param {number} a diff --git a/tests/namespace_test/namespace_test2_generated.js b/tests/namespace_test/namespace_test2_generated.js index f76d5a95a..856bbff92 100644 --- a/tests/namespace_test/namespace_test2_generated.js +++ b/tests/namespace_test/namespace_test2_generated.js @@ -70,6 +70,21 @@ NamespaceA.TableInFirstNS.prototype.fooEnum = function() { return offset ? /** @type {NamespaceA.NamespaceB.EnumInNestedNS} */ (this.bb.readInt8(this.bb_pos + offset)) : NamespaceA.NamespaceB.EnumInNestedNS.A; }; +/** + * @param {NamespaceA.NamespaceB.EnumInNestedNS} value + * @returns {boolean} + */ +NamespaceA.TableInFirstNS.prototype.mutate_foo_enum = function(value) { + var offset = this.bb.__offset(this.bb_pos, 6) + + if (offset === 0) { + return false; + } + + this.bb.writeInt8(this.bb_pos + offset, value); + return true; +} + /** * @param {NamespaceA.NamespaceB.StructInNestedNS=} obj * @returns {NamespaceA.NamespaceB.StructInNestedNS} From d268d11ca29b95ce1b8186e6526b58b51f2c913a Mon Sep 17 00:00:00 2001 From: Michael Paulson Date: Wed, 27 Jul 2016 13:32:53 -0700 Subject: [PATCH 3/3] feat(test): Added mutation testing for scalar values. This is a port of the tests found in test.cpp --- tests/JavaScriptTest.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/JavaScriptTest.js b/tests/JavaScriptTest.js index c89ae85d7..c97ed2d08 100644 --- a/tests/JavaScriptTest.js +++ b/tests/JavaScriptTest.js @@ -64,7 +64,10 @@ function main() { fs.writeFileSync('monsterdata_javascript_wire.mon', new Buffer(fbb.asUint8Array())); - // Test it: + // Tests mutation first. This will verify that we did not trample any other + // part of the byte buffer. + testMutation(fbb.dataBuffer()); + testBuffer(fbb.dataBuffer()); test64bit(); @@ -74,6 +77,21 @@ function main() { console.log('FlatBuffers test: completed successfully'); } +function testMutation(bb) { + var monster = MyGame.Example.Monster.getRootAsMonster(bb); + + monster.mutate_hp(120); + assert.strictEqual(monster.hp(), 120); + + monster.mutate_hp(80); + assert.strictEqual(monster.hp(), 80); + + var manaRes = monster.mutate_mana(10); + assert.strictEqual(manaRes, false); // Field was NOT present, because default value. + + // TODO: There is not the availability to mutate structs or vectors. +} + function testBuffer(bb) { assert.ok(MyGame.Example.Monster.bufferHasIdentifier(bb));