OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, |
3 * Copyright (C) 2006-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 Eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 | 104 |
105 function createSetter(typeId, offset) | 105 function createSetter(typeId, offset) |
106 { | 106 { |
107 typeId = typeId | 0; | 107 typeId = typeId | 0; |
108 offset = offset | 0; | 108 offset = offset | 0; |
109 | 109 |
110 let views = Array.prototype.slice.call(arguments, 2); | 110 let views = Array.prototype.slice.call(arguments, 2); |
111 let reference = new Reference(types, views); | 111 let reference = new Reference(types, views); |
112 return function(value) | 112 return function(value) |
113 { | 113 { |
114 if (value && value.typeId != typeId) | 114 if (value && !isInstance(typeId, value)) |
115 throw new Error("Incompatible type"); | 115 throw new Error("Incompatible type"); |
116 | 116 |
117 reference.bufferIndex = this.bufferIndex | 0; | 117 reference.bufferIndex = this.bufferIndex | 0; |
118 reference.byteOffset = (this.byteOffset | 0) + offset; | 118 reference.byteOffset = (this.byteOffset | 0) + offset; |
119 if (value) | 119 if (value) |
120 { | 120 { |
121 reference.typeId = value.typeId; | 121 reference.typeId = value.typeId; |
122 reference.targetBufferIndex = value.bufferIndex; | 122 reference.targetBufferIndex = value.bufferIndex; |
123 reference.targetByteOffset = value.byteOffset; | 123 reference.targetByteOffset = value.byteOffset; |
124 } | 124 } |
125 else | 125 else |
126 reference.typeId = -1; | 126 reference.typeId = -1; |
127 }; | 127 }; |
128 } | 128 } |
129 | 129 |
130 function ObjectType(properties, meta) | 130 /** |
| 131 * Overridden methods get the respective method of the superclass as the first |
| 132 * parameter. This function will create a wrapper function for the method that |
| 133 * forwards all arguments to the actual methods but also injects super as first |
| 134 * parameter. |
| 135 */ |
| 136 function createSubclassMethod(method, super_) |
| 137 { |
| 138 return function() |
| 139 { |
| 140 let args = [].slice.apply(arguments); |
| 141 args.unshift(() => super_.apply(this, arguments)); |
| 142 return method.apply(this, args); |
| 143 }; |
| 144 } |
| 145 |
| 146 function extend(parentTypeInfo, typeDescriptor, meta) |
131 { | 147 { |
132 if (typeof meta != "object" || meta == null) | 148 if (typeof meta != "object" || meta == null) |
133 meta = {}; | 149 meta = {}; |
134 | 150 |
135 let propList = []; | 151 let properties = Object.create(parentTypeInfo && parentTypeInfo.properties); |
136 let proto = {}; | 152 |
| 153 // Methods have to be actually copied here, prototypes won't work correctly |
| 154 // with Object.defineProperties(). |
| 155 let methods = Object.create(null); |
| 156 if (parentTypeInfo) |
| 157 for (let key in parentTypeInfo.methods) |
| 158 methods[key] = parentTypeInfo.methods[key]; |
| 159 |
137 let maxReferenceLength = TypedReference.byteLength | 0; | 160 let maxReferenceLength = TypedReference.byteLength | 0; |
138 for (let name in properties) | 161 for (let name in typeDescriptor) |
139 { | 162 { |
140 let type = properties[name]; | 163 let type = typeDescriptor[name]; |
141 if (type && typeof type.referenceLength == "number") | 164 if (type && typeof type.referenceLength == "number") |
142 { | 165 { |
| 166 if (name in methods) |
| 167 throw new Error("Property " + name + " masks a method with the same name
"); |
| 168 if (name in properties) |
| 169 { |
| 170 if (properties[name] == type) |
| 171 continue; |
| 172 else |
| 173 throw new Error("Cannot redefine type of property " + name + " in subc
lass"); |
| 174 } |
| 175 |
143 // Property with type | 176 // Property with type |
144 propList.push([name, type]); | 177 properties[name] = type; |
145 | 178 |
146 let referenceLength = type.referenceLength | 0; | 179 let referenceLength = type.referenceLength | 0; |
147 if (referenceLength > maxReferenceLength) | 180 if (referenceLength > maxReferenceLength) |
148 maxReferenceLength = referenceLength; | 181 maxReferenceLength = referenceLength; |
149 } | 182 } |
150 else if (typeof type == "function") | 183 else if (typeof type == "function") |
151 { | 184 { |
152 // Method | 185 // Method |
153 Object.defineProperty(proto, name, fixedPropertyDescriptor(type)); | 186 if (name in properties) |
| 187 throw new Error("Method " + name + " masks a property with the same name
"); |
| 188 |
| 189 if (name in methods) |
| 190 type = createSubclassMethod(type, methods[name].value); |
| 191 methods[name] = fixedPropertyDescriptor(type); |
154 } | 192 } |
155 else | 193 else |
156 throw new Error("Unrecognized type " + type + " given for property " + nam
e); | 194 throw new Error("Unrecognized type " + type + " given for property " + nam
e); |
157 } | 195 } |
158 | 196 |
| 197 let proto = {}; |
159 let buffers = []; | 198 let buffers = []; |
160 let viewTypes = []; | 199 let viewTypes = []; |
161 let views = []; | 200 let views = []; |
162 let byteLength = defineProperties(proto, propList, viewTypes, views, 0); | 201 let byteLength = defineProperties(proto, properties, viewTypes, views, 0); |
| 202 Object.defineProperties(proto, methods); |
163 | 203 |
164 // Round up to be a multiple of the maximal property size | 204 // Round up to be a multiple of the maximal property size |
165 byteLength = ((byteLength - 1) | (maxReferenceLength - 1)) + 1; | 205 byteLength = ((byteLength - 1) | (maxReferenceLength - 1)) + 1; |
166 | 206 |
167 // We need to be able to store a typed reference in the object's buffer | 207 // We need to be able to store a typed reference in the object's buffer |
168 byteLength = Math.max(byteLength, TypedReference.byteLength) | 0; | 208 byteLength = Math.max(byteLength, TypedReference.byteLength) | 0; |
169 let typedReferenceViews = getViewsForType(TypedReference, viewTypes, views); | 209 let typedReferenceViews = getViewsForType(TypedReference, viewTypes, views); |
170 | 210 |
| 211 // Take constructor from meta parameter, allow calling superclass constructor. |
| 212 let constructor = parentTypeInfo && parentTypeInfo.constructor; |
| 213 if (meta.hasOwnProperty("constructor") && typeof meta.constructor == "function
") |
| 214 { |
| 215 if (constructor) |
| 216 constructor = createSubclassMethod(meta.constructor, constructor); |
| 217 else |
| 218 constructor = meta.constructor; |
| 219 } |
| 220 |
171 let typeId = types.length | 0; | 221 let typeId = types.length | 0; |
172 let typeInfo = { | 222 let typeInfo = { |
173 byteLength: byteLength, | 223 byteLength: byteLength, |
174 bufferSize: "bufferSize" in meta ? Math.max(meta.bufferSize | 0, 2) : 128, | 224 bufferSize: "bufferSize" in meta ? Math.max(meta.bufferSize | 0, 2) : 128, |
175 firstFree: new TypedReference(typeId, typedReferenceViews), | 225 firstFree: new TypedReference(typeId, typedReferenceViews), |
176 proto: proto, | 226 proto: proto, |
| 227 properties: properties, |
| 228 methods: methods, |
177 buffers: buffers, | 229 buffers: buffers, |
178 viewTypes: viewTypes, | 230 viewTypes: viewTypes, |
179 views: views, | 231 views: views, |
180 typeId: typeId, | 232 typeId: typeId, |
181 constructor: (meta.hasOwnProperty("constructor") && typeof meta.constructor
== "function" ? meta.constructor : null) | 233 parentTypeInfo: parentTypeInfo, |
| 234 constructor: constructor |
182 }; | 235 }; |
183 | 236 |
184 let result = create.bind(typeInfo); | 237 let result = create.bind(typeInfo); |
185 Object.defineProperties(result, { | 238 Object.defineProperties(result, { |
186 byteLength: fixedPropertyDescriptor(byteLength), | 239 byteLength: fixedPropertyDescriptor(byteLength), |
187 | 240 |
188 referenceLength: fixedPropertyDescriptor(Reference.byteLength), | 241 referenceLength: fixedPropertyDescriptor(Reference.byteLength), |
189 viewTypes: fixedPropertyDescriptor(Reference.viewTypes), | 242 viewTypes: fixedPropertyDescriptor(Reference.viewTypes), |
190 | 243 |
191 typeId: fixedPropertyDescriptor(typeId), | 244 typeId: fixedPropertyDescriptor(typeId), |
| 245 extend: fixedPropertyDescriptor(extend.bind(null, typeInfo)), |
| 246 isInstance: fixedPropertyDescriptor(isInstance.bind(null, typeId)), |
192 | 247 |
193 createGetter: fixedPropertyDescriptor(createGetter), | 248 createGetter: fixedPropertyDescriptor(createGetter), |
194 createSetter: fixedPropertyDescriptor(createSetter.bind(null, typeId)) | 249 createSetter: fixedPropertyDescriptor(createSetter.bind(null, typeId)) |
195 }); | 250 }); |
196 types.push(typeInfo); | 251 types.push(typeInfo); |
197 return result; | 252 return result; |
198 } | 253 } |
199 exports.ObjectType = ObjectType; | 254 |
| 255 function isInstance(typeId, obj) |
| 256 { |
| 257 typeId = typeId | 0; |
| 258 |
| 259 // TODO: This could be optimized by compiling the list of all subclasses for |
| 260 // each type up front. Question is whether this is worth it. |
| 261 let typeInfo = types[obj.typeId | 0]; |
| 262 while (typeInfo) |
| 263 { |
| 264 if ((typeInfo.typeId | 0) == typeId) |
| 265 return true; |
| 266 typeInfo = typeInfo.parentTypeInfo; |
| 267 } |
| 268 return false; |
| 269 } |
| 270 |
| 271 let ObjectBase = exports.ObjectBase = extend(null, { |
| 272 equals: function(obj) |
| 273 { |
| 274 if (!obj) |
| 275 return false; |
| 276 return this.typeId == obj.typeId && this.bufferIndex == obj.bufferIndex && t
his.byteOffset == obj.byteOffset; |
| 277 } |
| 278 }, null); |
| 279 |
| 280 exports.ObjectType = ObjectBase.extend; |
OLD | NEW |