| Index: test/tests/typedObjects.js | 
| =================================================================== | 
| --- a/test/tests/typedObjects.js | 
| +++ b/test/tests/typedObjects.js | 
| @@ -58,17 +58,16 @@ | 
| mtd: function() { | 
| return this.foo * 2; | 
| } | 
| }, {bufferSize: 8}); | 
| ok(type, "Type created"); | 
| equal(typeof type.typeId, "number"); | 
| equal(typeof type.byteLength, "number"); | 
| - equal(type.byteLength, 8); | 
| // Create an object and check default properties | 
| let objects = []; | 
| objects.push(type()); | 
| ok(objects[0], "Object created"); | 
| equal(typeof objects[0].typeId, "number"); | 
| equal(objects[0].typeId, type.typeId); | 
| @@ -79,17 +78,17 @@ | 
| equal(typeof objects[0].byteOffset, "number"); | 
| equal(objects[0].byteOffset, 0); | 
| // The first 8 objects should go into the same buffer | 
| for (let i = 1; i < 8; i++) | 
| { | 
| objects.push(type()); | 
| equal(objects[i].bufferIndex, 0); | 
| - equal(objects[i].byteOffset, 8 * i); | 
| + equal(objects[i].byteOffset, type.byteLength * i); | 
| } | 
| // Properties should persist and methods should be able to access them | 
| for (let i = 0; i < objects.length; i++) | 
| { | 
| objects[i].foo = i; | 
| objects[i].bar = 8.5 - objects[i].foo; | 
| } | 
| @@ -104,17 +103,17 @@ | 
| // Next objects should go into a new buffer | 
| let obj = type(); | 
| equal(obj.bufferIndex, 1); | 
| equal(obj.byteOffset, 0); | 
| obj = type(); | 
| equal(obj.bufferIndex, 1); | 
| - equal(obj.byteOffset, 8); | 
| + equal(obj.byteOffset, type.byteLength); | 
| }); | 
| test("Object constructors", function() | 
| { | 
| let {ObjectType, uint8, float32} = require("typedObjects"); | 
| let type = new ObjectType({ | 
| foo: uint8, | 
| bar: float32 | 
| @@ -294,9 +293,108 @@ | 
| type3.extend({foo: uint8}); | 
| }, "Property masks method"); | 
| throws(function() | 
| { | 
| type3.extend({x: function() {}}); | 
| }, "Method masks property"); | 
| }); | 
| + | 
| + test("Garbage collection", function() | 
| + { | 
| + let {ObjectType, uint8, float32} = require("typedObjects"); | 
| + | 
| + let destroyed; | 
| + | 
| + let type1 = new ObjectType({ | 
| + foo: uint8 | 
| + }, { | 
| + constructor: function(foo) | 
| + { | 
| + this.foo = foo; | 
| + }, | 
| + destructor: function() | 
| + { | 
| + destroyed.push(["type1", this.foo]); | 
| + } | 
| + }); | 
| + | 
| + // Single release() call | 
| + destroyed = []; | 
| + type1(1).release(); | 
| + deepEqual(destroyed, [["type1", 1]], "Destructor called after release()"); | 
| + | 
| + // retain() and multiple release() calls | 
| + destroyed = []; | 
| + let obj2 = type1(2); | 
| + equal(obj2.bufferIndex, 0, "New object replaces the destroyed one"); | 
| + equal(obj2.byteOffset, 0, "New object replaces the destroyed one"); | 
| + | 
| + obj2.retain(); | 
| + obj2.release(); | 
| + deepEqual(destroyed, [], "Destructor not called after release() if retain() was called"); | 
| + obj2.release(); | 
| + deepEqual(destroyed, [["type1", 2]], "Destructor called after second release()"); | 
| + | 
| + // References holding object | 
| + let type2 = type1.extend({ | 
| + bar: type1 | 
| + }, { | 
| + destructor: function(super_) | 
| + { | 
| + super_(); | 
| + destroyed.push(["type2", this.foo]); | 
| + } | 
| + }); | 
| + | 
| + destroyed = []; | 
| + let obj3 = type1(3); | 
| + let obj4 = type2(4); | 
| + obj4.bar = obj3; | 
| + obj3.release(); | 
| + deepEqual(destroyed, [], "Destructor not called if references to object exist"); | 
| + obj4.bar = null; | 
| + deepEqual(destroyed, [["type1", 3]], "Destructor called after reference is cleared"); | 
| + | 
| + // Recursive destruction | 
| + destroyed = []; | 
| + let obj5 = type1(5); | 
| + obj4.bar = obj5; | 
| + obj5.release(); | 
| + deepEqual(destroyed, [], "Destructor not called if references to object exist"); | 
| + obj4.release(); | 
| + deepEqual(destroyed, [["type1", 4], ["type2", 4], ["type1", 5]], "Destroying an object released its references"); | 
| + | 
| + // Misbehaving destructors | 
| + let type3 = type1.extend({}, { | 
| + destructor: function(super_) | 
| + { | 
| + this.retain(); | 
| + } | 
| + }); | 
| + throws(function() | 
| + { | 
| + type3(0).release(); | 
| + }, "Retaining reference in destructor is prohibited"); | 
| + | 
| + let type4 = type1.extend({}, { | 
| + destructor: function(super_) | 
| + { | 
| + this.release(); | 
| + } | 
| + }); | 
| + throws(function() | 
| + { | 
| + type4(0).release(); | 
| + }, "Releasing reference in destructor is prohibited"); | 
| + | 
| + let type5 = type1.extend({}, { | 
| + destructor: function(super_) | 
| + { | 
| + this.retain(); | 
| + this.release(); | 
| + } | 
| + }); | 
| + type5(0).release(); | 
| + ok(true, "Temporarily retaining reference in destructor is allowed"); | 
| + }); | 
| })(); |