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