| Index: lib/typedObjects/objectTypes.js | 
| =================================================================== | 
| --- a/lib/typedObjects/objectTypes.js | 
| +++ b/lib/typedObjects/objectTypes.js | 
| @@ -14,16 +14,21 @@ | 
| * You should have received a copy of the GNU General Public License | 
| * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
|  | 
| "use strict"; | 
|  | 
| let {fixedPropertyDescriptor, getViewsForType, defineProperties} = require("typedObjects/utils"); | 
| let {Reference, TypedReference} = require("typedObjects/references"); | 
| +let {uint8, uint32} = require("typedObjects/primitiveTypes"); | 
| + | 
| +const STATE_UNINITIALIZED = 0; | 
| +const STATE_CREATED = 1; | 
| +const STATE_RELEASING = 2; | 
|  | 
| /** | 
| * List of registered types (typeId is the index in that array). | 
| */ | 
| let types = []; | 
|  | 
| function fromReference(reference) | 
| { | 
| @@ -78,21 +83,54 @@ function create() | 
| } | 
| } | 
|  | 
| let result = Object.create(this.proto, { | 
| typeId: fixedPropertyDescriptor(this.typeId), | 
| bufferIndex: fixedPropertyDescriptor(bufferIndex), | 
| byteOffset: fixedPropertyDescriptor(byteOffset) | 
| }); | 
| + | 
| +  result._state = STATE_UNINITIALIZED; | 
| +  for (let [prop, value] of this.cleanupValues) | 
| +    result[prop] = value; | 
| +  result._state = STATE_CREATED; | 
| +  result._refCount = 1; | 
| + | 
| if (this.constructor) | 
| this.constructor.apply(result, arguments); | 
| return result; | 
| } | 
|  | 
| +function free(obj) | 
| +{ | 
| +  try | 
| +  { | 
| +    if (this.destructor) | 
| +    { | 
| +      this.destructor.call(obj); | 
| +      if (obj._refCount | 0) | 
| +        throw new Error("Reference count is no longer zero after calling the destructor"); | 
| +    } | 
| +  } | 
| +  finally | 
| +  { | 
| +    for (let [prop, value] of this.cleanupValues) | 
| +      obj[prop] = value; | 
| + | 
| +    // Mark object as first free spot | 
| +    let oldFreeBufferIndex = this.firstFree.bufferIndex; | 
| +    let oldFreeByteOffset = this.firstFree.byteOffset; | 
| +    this.firstFree.bufferIndex = obj.bufferIndex; | 
| +    this.firstFree.byteOffset = obj.byteOffset; | 
| +    this.firstFree.targetBufferIndex = oldFreeBufferIndex; | 
| +    this.firstFree.targetByteOffset = oldFreeByteOffset; | 
| +  } | 
| +} | 
| + | 
| function createGetter(offset) | 
| { | 
| offset = offset | 0; | 
|  | 
| let views = Array.prototype.slice.call(arguments, 1); | 
| let reference = new Reference(types, views); | 
| return function() | 
| { | 
| @@ -111,21 +149,30 @@ function createSetter(typeId, offset) | 
| let reference = new Reference(types, views); | 
| return function(value) | 
| { | 
| if (value && !isinstance(typeId, value)) | 
| throw new Error("Incompatible type"); | 
|  | 
| reference.bufferIndex = this.bufferIndex | 0; | 
| reference.byteOffset = (this.byteOffset | 0) + offset; | 
| + | 
| +    if ((this._state | 0) > STATE_UNINITIALIZED) | 
| +    { | 
| +      let oldValue = fromReference(reference); | 
| +      if (oldValue) | 
| +        oldValue.release(); | 
| +    } | 
| + | 
| if (value) | 
| { | 
| reference.typeId = value.typeId; | 
| reference.targetBufferIndex = value.bufferIndex; | 
| reference.targetByteOffset = value.byteOffset; | 
| +      value.retain(); | 
| } | 
| else | 
| reference.typeId = -1; | 
| }; | 
| } | 
|  | 
| /** | 
| * Overridden methods get the respective method of the superclass as the first | 
| @@ -193,58 +240,72 @@ function extend(parentTypeInfo, typeDesc | 
| else | 
| throw new Error("Unrecognized type " + type + " given for property " + name); | 
| } | 
|  | 
| let proto = {}; | 
| let buffers = []; | 
| let viewTypes = []; | 
| let views = []; | 
| -  let byteLength = defineProperties(proto, properties, viewTypes, views, 0); | 
| +  let cleanupValues = []; | 
| +  let byteLength = defineProperties(proto, properties, viewTypes, views, 0, cleanupValues); | 
| Object.defineProperties(proto, methods); | 
|  | 
| // Round up to be a multiple of the maximal property size | 
| byteLength = ((byteLength - 1) | (maxReferenceLength - 1)) + 1; | 
|  | 
| // We need to be able to store a typed reference in the object's buffer | 
| byteLength = Math.max(byteLength, TypedReference.byteLength) | 0; | 
| let typedReferenceViews = getViewsForType(TypedReference, viewTypes, views); | 
|  | 
| -  // Take constructor from meta parameter, allow calling superclass constructor. | 
| +  // Take constructor and destructor from meta parameters, allow calling | 
| +  // superclass constructor/destructor. | 
| let constructor = parentTypeInfo && parentTypeInfo.constructor; | 
| if (meta.hasOwnProperty("constructor") && typeof meta.constructor == "function") | 
| { | 
| if (constructor) | 
| constructor = createSubclassMethod(meta.constructor, constructor); | 
| else | 
| constructor = meta.constructor; | 
| } | 
|  | 
| +  let destructor = parentTypeInfo && parentTypeInfo.destructor; | 
| +  if (meta.hasOwnProperty("destructor") && typeof meta.destructor == "function") | 
| +  { | 
| +    if (destructor) | 
| +      destructor = createSubclassMethod(meta.destructor, destructor); | 
| +    else | 
| +      destructor = meta.destructor; | 
| +  } | 
| + | 
| let typeId = types.length | 0; | 
| let typeInfo = { | 
| byteLength: byteLength, | 
| bufferSize: "bufferSize" in meta ? Math.max(meta.bufferSize | 0, 2) : 128, | 
| firstFree: new TypedReference(typeId, typedReferenceViews), | 
| proto: proto, | 
| properties: properties, | 
| methods: methods, | 
| buffers: buffers, | 
| viewTypes: viewTypes, | 
| views: views, | 
| +    cleanupValues: cleanupValues, | 
| typeId: typeId, | 
| parentTypeInfo: parentTypeInfo, | 
| -    constructor: constructor | 
| +    constructor: constructor, | 
| +    destructor: destructor | 
| }; | 
|  | 
| let result = create.bind(typeInfo); | 
| Object.defineProperties(result, { | 
| byteLength: fixedPropertyDescriptor(byteLength), | 
|  | 
| referenceLength: fixedPropertyDescriptor(Reference.byteLength), | 
| viewTypes: fixedPropertyDescriptor(Reference.viewTypes), | 
| +    cleanupValue: fixedPropertyDescriptor(null), | 
|  | 
| typeId: fixedPropertyDescriptor(typeId), | 
| extend: fixedPropertyDescriptor(extend.bind(null, typeInfo)), | 
| isinstance: fixedPropertyDescriptor(isinstance.bind(null, typeId)), | 
|  | 
| createGetter: fixedPropertyDescriptor(createGetter), | 
| createSetter: fixedPropertyDescriptor(createSetter.bind(null, typeId)) | 
| }); | 
| @@ -264,17 +325,35 @@ function isinstance(typeId, obj) | 
| if ((typeInfo.typeId | 0) == typeId) | 
| return true; | 
| typeInfo = typeInfo.parentTypeInfo; | 
| } | 
| return false; | 
| } | 
|  | 
| let ObjectBase = exports.ObjectBase = extend(null, { | 
| +  _state: uint8, | 
| +  _refCount: uint32, | 
| + | 
| equals: function(obj) | 
| { | 
| if (!obj) | 
| return false; | 
| return this.typeId == obj.typeId && this.bufferIndex == obj.bufferIndex && this.byteOffset == obj.byteOffset; | 
| +  }, | 
| + | 
| +  retain: function() | 
| +  { | 
| +    this._refCount++; | 
| +  }, | 
| + | 
| +  release: function() | 
| +  { | 
| +    this._refCount--; | 
| +    if (this._refCount == 0 && this._state < STATE_RELEASING) | 
| +    { | 
| +      this._state = STATE_RELEASING; | 
| +      free.call(types[this.typeId | 0], this); | 
| +    } | 
| } | 
| }, null); | 
|  | 
| exports.ObjectType = ObjectBase.extend; | 
|  |