Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/typedObjects/objectTypes.js

Issue 5656302898380800: Issue 260 - [Typed objects] Implement type inheritance (Closed)
Patch Set: Renamed isinstance to isInstance Created May 2, 2014, 7:13 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/typedObjects.js ('k') | lib/typedObjects/references.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/typedObjects/objectTypes.js
===================================================================
--- a/lib/typedObjects/objectTypes.js
+++ b/lib/typedObjects/objectTypes.js
@@ -106,94 +106,175 @@ 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)
{
- if (value && value.typeId != typeId)
+ if (value && !isInstance(typeId, value))
throw new Error("Incompatible type");
reference.bufferIndex = this.bufferIndex | 0;
reference.byteOffset = (this.byteOffset | 0) + offset;
if (value)
{
reference.typeId = value.typeId;
reference.targetBufferIndex = value.bufferIndex;
reference.targetByteOffset = value.byteOffset;
}
else
reference.typeId = -1;
};
}
-function ObjectType(properties, meta)
+/**
+ * Overridden methods get the respective method of the superclass as the first
+ * parameter. This function will create a wrapper function for the method that
+ * forwards all arguments to the actual methods but also injects super as first
+ * parameter.
+ */
+function createSubclassMethod(method, super_)
+{
+ return function()
+ {
+ let args = [].slice.apply(arguments);
+ args.unshift(() => super_.apply(this, arguments));
+ return method.apply(this, args);
+ };
+}
+
+function extend(parentTypeInfo, typeDescriptor, meta)
{
if (typeof meta != "object" || meta == null)
meta = {};
- let propList = [];
- let proto = {};
+ let properties = Object.create(parentTypeInfo && parentTypeInfo.properties);
+
+ // Methods have to be actually copied here, prototypes won't work correctly
+ // with Object.defineProperties().
+ let methods = Object.create(null);
+ if (parentTypeInfo)
+ for (let key in parentTypeInfo.methods)
+ methods[key] = parentTypeInfo.methods[key];
+
let maxReferenceLength = TypedReference.byteLength | 0;
- for (let name in properties)
+ for (let name in typeDescriptor)
{
- let type = properties[name];
+ let type = typeDescriptor[name];
if (type && typeof type.referenceLength == "number")
{
+ if (name in methods)
+ throw new Error("Property " + name + " masks a method with the same name");
+ if (name in properties)
+ {
+ if (properties[name] == type)
+ continue;
+ else
+ throw new Error("Cannot redefine type of property " + name + " in subclass");
+ }
+
// Property with type
- propList.push([name, type]);
+ properties[name] = type;
let referenceLength = type.referenceLength | 0;
if (referenceLength > maxReferenceLength)
maxReferenceLength = referenceLength;
}
else if (typeof type == "function")
{
// Method
- Object.defineProperty(proto, name, fixedPropertyDescriptor(type));
+ if (name in properties)
+ throw new Error("Method " + name + " masks a property with the same name");
+
+ 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);
}
+ let proto = {};
let buffers = [];
let viewTypes = [];
let views = [];
- let byteLength = defineProperties(proto, propList, viewTypes, views, 0);
+ let byteLength = defineProperties(proto, properties, viewTypes, views, 0);
+ 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.
+ 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 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,
typeId: typeId,
- constructor: (meta.hasOwnProperty("constructor") && typeof meta.constructor == "function" ? meta.constructor : null)
+ parentTypeInfo: parentTypeInfo,
+ constructor: constructor
};
let result = create.bind(typeInfo);
Object.defineProperties(result, {
byteLength: fixedPropertyDescriptor(byteLength),
referenceLength: fixedPropertyDescriptor(Reference.byteLength),
viewTypes: fixedPropertyDescriptor(Reference.viewTypes),
typeId: fixedPropertyDescriptor(typeId),
+ extend: fixedPropertyDescriptor(extend.bind(null, typeInfo)),
+ isInstance: fixedPropertyDescriptor(isInstance.bind(null, typeId)),
createGetter: fixedPropertyDescriptor(createGetter),
createSetter: fixedPropertyDescriptor(createSetter.bind(null, typeId))
});
types.push(typeInfo);
return result;
}
-exports.ObjectType = ObjectType;
+
+function isInstance(typeId, obj)
+{
+ typeId = typeId | 0;
+
+ // TODO: This could be optimized by compiling the list of all subclasses for
+ // each type up front. Question is whether this is worth it.
+ let typeInfo = types[obj.typeId | 0];
+ while (typeInfo)
+ {
+ if ((typeInfo.typeId | 0) == typeId)
+ return true;
+ typeInfo = typeInfo.parentTypeInfo;
+ }
+ return false;
+}
+
+let ObjectBase = exports.ObjectBase = extend(null, {
+ equals: function(obj)
+ {
+ if (!obj)
+ return false;
+ return this.typeId == obj.typeId && this.bufferIndex == obj.bufferIndex && this.byteOffset == obj.byteOffset;
+ }
+}, null);
+
+exports.ObjectType = ObjectBase.extend;
« no previous file with comments | « lib/typedObjects.js ('k') | lib/typedObjects/references.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld