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; |