| Index: lib/typedObjects/objectTypes.js | 
| =================================================================== | 
| --- a/lib/typedObjects/objectTypes.js | 
| +++ b/lib/typedObjects/objectTypes.js | 
| @@ -12,23 +12,24 @@ | 
| * GNU General Public License for more details. | 
| * | 
| * 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 {fixedPropertyDescriptor, getViewsForType, defineProperties, alloc, dealloc} = require("typedObjects/utils"); | 
| let {Reference, TypedReference} = require("typedObjects/references"); | 
| let {uint8, uint32} = require("typedObjects/primitiveTypes"); | 
| +let {createArrayType} = require("typedObjects/arrayTypes"); | 
|  | 
| -const STATE_UNINITIALIZED = 0; | 
| -const STATE_CREATED = 1; | 
| -const STATE_RELEASING = 2; | 
| +const STATE_UNINITIALIZED = exports.STATE_UNINITIALIZED = 0; | 
| +const STATE_CREATED = exports.STATE_CREATED = 1; | 
| +const STATE_RELEASING = exports.STATE_RELEASING = 2; | 
|  | 
| /** | 
| * List of registered types (typeId is the index in that array). | 
| */ | 
| let types = []; | 
|  | 
| function fromReference(reference) | 
| { | 
| @@ -42,51 +43,18 @@ function fromReference(reference) | 
| }); | 
| } | 
| else | 
| return null; | 
| } | 
|  | 
| function create() | 
| { | 
| -  let {bufferIndex, byteOffset} = this.firstFree; | 
| -  if (bufferIndex >= 0) | 
| -  { | 
| -    // There is still a free spot, simply move on firstFree reference | 
| -    [this.firstFree.bufferIndex, this.firstFree.byteOffset] = | 
| -        [this.firstFree.targetBufferIndex, this.firstFree.targetByteOffset]; | 
| -  } | 
| -  else | 
| -  { | 
| -    let viewTypes = this.viewTypes; | 
| -    let views = this.views; | 
| -    let byteLength = this.byteLength | 0; | 
| -    let bufferSize = this.bufferSize | 0; | 
| - | 
| -    // Create new buffer and use the first element of it | 
| -    let buffer = new ArrayBuffer(byteLength * bufferSize); | 
| -    bufferIndex = (this.buffers.push(buffer) | 0) - 1; | 
| -    byteOffset = 0; | 
| -    for (let i = 0, l = viewTypes.length | 0; i < l; i++) | 
| -      views[i].push(new viewTypes[i](buffer)); | 
| - | 
| -    // Mark last element of the new buffer as the last free spot | 
| -    this.firstFree.bufferIndex = bufferIndex; | 
| -    this.firstFree.byteOffset = (bufferSize - 1) * byteLength; | 
| -    this.firstFree.targetBufferIndex = -1; | 
| - | 
| -    // Make each remaining element of the new buffer point to the next one | 
| -    for (let i = bufferSize - 2; i >= 1; i--) | 
| -    { | 
| -      let nextByteOffset = this.firstFree.byteOffset; | 
| -      this.firstFree.byteOffset = nextByteOffset - byteLength; | 
| -      this.firstFree.targetBufferIndex = bufferIndex; | 
| -      this.firstFree.targetByteOffset = nextByteOffset; | 
| -    } | 
| -  } | 
| +  let [bufferIndex, byteOffset] = alloc(this.firstFree, this.byteLength, | 
| +      this.bufferSize, this.buffers, this.viewTypes, this.views); | 
|  | 
| let result = Object.create(this.proto, { | 
| typeId: fixedPropertyDescriptor(this.typeId), | 
| bufferIndex: fixedPropertyDescriptor(bufferIndex), | 
| byteOffset: fixedPropertyDescriptor(byteOffset) | 
| }); | 
|  | 
| result._state = STATE_UNINITIALIZED; | 
| @@ -111,54 +79,48 @@ function free(obj) | 
| 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; | 
| +    dealloc(this.firstFree, obj.bufferIndex, obj.byteOffset); | 
| } | 
| } | 
|  | 
| function createGetter(offset) | 
| { | 
| offset = offset | 0; | 
|  | 
| let views = Array.prototype.slice.call(arguments, 1); | 
| let reference = new Reference(types, views); | 
| -  return function() | 
| +  return function(bufferIndex, byteOffset) | 
| { | 
| -    reference.bufferIndex = this.bufferIndex | 0; | 
| -    reference.byteOffset = (this.byteOffset | 0) + offset; | 
| +    reference.bufferIndex = bufferIndex | 0; | 
| +    reference.byteOffset = (byteOffset | 0) + offset; | 
| return fromReference(reference); | 
| }; | 
| } | 
|  | 
| function createSetter(typeId, offset) | 
| { | 
| typeId = typeId | 0; | 
| offset = offset | 0; | 
|  | 
| let views = Array.prototype.slice.call(arguments, 2); | 
| let reference = new Reference(types, views); | 
| -  return function(value) | 
| +  return function(bufferIndex, byteOffset, value) | 
| { | 
| if (value && !isInstance(typeId, value)) | 
| throw new Error("Incompatible type"); | 
|  | 
| -    reference.bufferIndex = this.bufferIndex | 0; | 
| -    reference.byteOffset = (this.byteOffset | 0) + offset; | 
| +    reference.bufferIndex = bufferIndex | 0; | 
| +    reference.byteOffset = (byteOffset | 0) + offset; | 
|  | 
| if ((this._state | 0) > STATE_UNINITIALIZED) | 
| { | 
| let oldValue = fromReference(reference); | 
| if (oldValue) | 
| oldValue.release(); | 
| } | 
|  | 
| @@ -236,22 +198,31 @@ function extend(parentTypeInfo, typeDesc | 
| if (name in methods) | 
| type = createSubclassMethod(type, methods[name].value); | 
| methods[name] = fixedPropertyDescriptor(type); | 
| } | 
| else | 
| throw new Error("Unrecognized type " + type + " given for property " + name); | 
| } | 
|  | 
| +  // Combine inherited watchers with the ones specified for this object | 
| +  let watchers = parentTypeInfo && parentTypeInfo.watchers; | 
| +  if (meta.watch) | 
| +  { | 
| +    watchers = Object.create(watchers); | 
| +    for (let key in meta.watch) | 
| +      watchers[key] = meta.watch[key]; | 
| +  } | 
| + | 
| let proto = {}; | 
| let buffers = []; | 
| let viewTypes = []; | 
| let views = []; | 
| let cleanupValues = []; | 
| -  let byteLength = defineProperties(proto, properties, viewTypes, views, 0, cleanupValues); | 
| +  let byteLength = defineProperties(proto, properties, viewTypes, views, 0, watchers, 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); | 
| @@ -279,16 +250,17 @@ function extend(parentTypeInfo, typeDesc | 
| 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, | 
| +    watchers: watchers, | 
| buffers: buffers, | 
| viewTypes: viewTypes, | 
| views: views, | 
| cleanupValues: cleanupValues, | 
| typeId: typeId, | 
| parentTypeInfo: parentTypeInfo, | 
| constructor: constructor, | 
| destructor: destructor | 
| @@ -300,16 +272,17 @@ function extend(parentTypeInfo, typeDesc | 
|  | 
| 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)), | 
| +    Array: fixedPropertyDescriptor(createArrayType.bind(null, result)), | 
|  | 
| createGetter: fixedPropertyDescriptor(createGetter), | 
| createSetter: fixedPropertyDescriptor(createSetter.bind(null, typeId)) | 
| }); | 
| types.push(typeInfo); | 
| return result; | 
| } | 
|  | 
|  |