[gjs] lang: Lang.Class.implements()
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] lang: Lang.Class.implements()
- Date: Mon, 13 Jul 2015 21:05:56 +0000 (UTC)
commit e5ea75736addcec28e217e3c268220b1fedc628e
Author: Philip Chimento <philip endlessm com>
Date: Fri Jun 12 17:42:43 2015 -0700
lang: Lang.Class.implements()
This function does the work of g_type_is_a() and G_IS_...() for
interfaces. It allows us to make better assertions in our tests and fills
a need in GJS since there is no such thing as the instanceof operator for
interfaces.
Note. implements is a reserved word in ES6 but reserved words are allowed
as property names, and we use reserved words elsewhere in GJS as property
names as well.
(Collaboration by Philip Chimento <philip endlessm com> and Roberto
Goizueta <goizueta endlessm com>)
https://bugzilla.gnome.org/show_bug.cgi?id=751343
installed-tests/js/testGObjectClass.js | 2 +-
installed-tests/js/testGObjectInterface.js | 54 +++++++++++++++++++++------
installed-tests/js/testInterface.js | 48 +++++++++++++++++++-----
modules/lang.js | 17 +++++++++
modules/overrides/GObject.js | 11 ++++++
5 files changed, 109 insertions(+), 23 deletions(-)
---
diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js
index fb45acc..3cb368a 100644
--- a/installed-tests/js/testGObjectClass.js
+++ b/installed-tests/js/testGObjectClass.js
@@ -279,7 +279,7 @@ function testInterface() {
instance.init(new Gio.Cancellable);
JSUnit.assertEquals(true, instance.inited);
- // JSUnit.assertTrue(instance instanceof Gio.Initable)
+ JSUnit.assertTrue(instance.constructor.implements(Gio.Initable));
}
function testDerived() {
diff --git a/installed-tests/js/testGObjectInterface.js b/installed-tests/js/testGObjectInterface.js
index 6fce052..8ed04fd 100644
--- a/installed-tests/js/testGObjectInterface.js
+++ b/installed-tests/js/testGObjectInterface.js
@@ -117,12 +117,13 @@ const ImplementationOfTwoInterfaces = new Lang.Class({
});
function testGObjectClassCanImplementInterface() {
- // Test considered passing if no exception thrown
- new GObjectImplementingLangInterface();
+ // Test will fail if the constructor throws an exception
+ let obj = new GObjectImplementingLangInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
}
function testGObjectCanImplementInterfacesFromJSAndC() {
- // Test considered passing if no exception thrown
+ // Test will fail if the constructor throws an exception
const ObjectImplementingLangInterfaceAndCInterface = new Lang.Class({
Name: 'ObjectImplementingLangInterfaceAndCInterface',
Extends: GObject.Object,
@@ -132,7 +133,9 @@ function testGObjectCanImplementInterfacesFromJSAndC() {
this.parent(props);
}
});
- new ObjectImplementingLangInterfaceAndCInterface();
+ let obj = new ObjectImplementingLangInterfaceAndCInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+ JSUnit.assertTrue(obj.constructor.implements(Gio.Initable));
}
function testGObjectInterfaceIsInstanceOfInterfaces() {
@@ -149,8 +152,9 @@ function testGObjectInterfaceTypeName() {
}
function testGObjectCanImplementInterface() {
- // Test considered passing if no exception thrown
- new GObjectImplementingGObjectInterface();
+ // Test will fail if the constructor throws an exception
+ let obj = new GObjectImplementingGObjectInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
}
function testGObjectImplementingInterfaceHasCorrectClassObject() {
@@ -162,7 +166,7 @@ function testGObjectImplementingInterfaceHasCorrectClassObject() {
}
function testGObjectCanImplementBothGObjectAndNonGObjectInterfaces() {
- // Test considered passing if no exception thrown
+ // Test will fail if the constructor throws an exception
const GObjectImplementingBothKindsOfInterface = new Lang.Class({
Name: 'GObjectImplementingBothKindsOfInterface',
Extends: GObject.Object,
@@ -178,7 +182,9 @@ function testGObjectCanImplementBothGObjectAndNonGObjectInterfaces() {
required: function () {},
requiredG: function () {}
});
- new GObjectImplementingBothKindsOfInterface();
+ let obj = new GObjectImplementingBothKindsOfInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
}
function testGObjectCanImplementRequiredFunction() {
@@ -200,8 +206,9 @@ function testGObjectMustImplementRequiredFunction () {
}
function testGObjectDoesntHaveToImplementOptionalFunction() {
- // Test considered passing if no exception thrown
- new MinimalImplementationOfAGObjectInterface();
+ // Test will fail if the constructor throws an exception
+ let obj = new MinimalImplementationOfAGObjectInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
}
function testGObjectCanDeferToInterfaceOptionalFunction() {
@@ -215,8 +222,10 @@ function testGObjectCanChainUpToInterface() {
}
function testGObjectInterfaceCanRequireOtherInterface() {
- // Test considered passing if no exception thrown
- new ImplementationOfTwoInterfaces();
+ // Test will fail if the constructor throws an exception
+ let obj = new ImplementationOfTwoInterfaces();
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
+ JSUnit.assertTrue(obj.constructor.implements(InterfaceRequiringGObjectInterface));
}
function testGObjectInterfaceCanChainUpToOtherInterface() {
@@ -335,4 +344,25 @@ function testInterfaceIsOfCorrectTypeForMetaclass() {
JSUnit.assertTrue(MyMetaInterface instanceof GObject.Interface);
}
+function testSubclassImplementsTheSameInterfaceAsItsParent() {
+ const SubObject = new Lang.Class({
+ Name: 'SubObject',
+ Extends: GObjectImplementingGObjectInterface
+ });
+ let obj = new SubObject();
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
+ JSUnit.assertEquals('foobar', obj.interface_prop); // override not needed
+}
+
+function testSubclassCanReimplementTheSameInterfaceAsItsParent() {
+ const SubImplementer = new Lang.Class({
+ Name: 'SubImplementer',
+ Extends: GObjectImplementingGObjectInterface,
+ Implements: [ AGObjectInterface ]
+ });
+ let obj = new SubImplementer();
+ JSUnit.assertTrue(obj.constructor.implements(AGObjectInterface));
+ JSUnit.assertEquals('foobar', obj.interface_prop); // override not needed
+}
+
JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown);
diff --git a/installed-tests/js/testInterface.js b/installed-tests/js/testInterface.js
index 8c55f3b..c8b130f 100644
--- a/installed-tests/js/testInterface.js
+++ b/installed-tests/js/testInterface.js
@@ -104,8 +104,9 @@ function testInterfaceCannotBeInstantiated() {
}
function testObjectCanImplementInterface() {
- // Test considered passing if no exception thrown
- new ObjectImplementingAnInterface();
+ // Test will fail if the constructor throws an exception
+ let obj = new ObjectImplementingAnInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
}
function testObjectImplementingInterfaceHasCorrectConstructor() {
@@ -127,8 +128,9 @@ function testClassMustImplementRequiredFunction() {
}
function testClassDoesntHaveToImplementOptionalFunction() {
- // Test considered passing if no exception thrown
- new MinimalImplementationOfAnInterface();
+ // Test will fail if the constructor throws an exception
+ let obj = new MinimalImplementationOfAnInterface();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
}
function testObjectCanDeferToInterfaceOptionalFunction() {
@@ -181,8 +183,10 @@ function testObjectCanOverrideInterfaceSetter() {
}
function testInterfaceCanRequireOtherInterface() {
- // Test considered passing if no exception thrown
- new ImplementationOfTwoInterfaces();
+ // Test will fail if the constructor throws an exception
+ let obj = new ImplementationOfTwoInterfaces();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+ JSUnit.assertTrue(obj.constructor.implements(InterfaceRequiringOtherInterface));
}
function testRequiresCanBeEmpty() {
@@ -240,23 +244,28 @@ function testClassMustImplementRequiredInterfacesInCorrectOrder() {
}
function testInterfacesCanBeImplementedOnAParentClass() {
- // Test considered passing if no exception thrown
+ // Test will fail if the constructor throws an exception
const ObjectInheritingFromInterfaceImplementation = new Lang.Class({
Name: 'ObjectInheritingFromInterfaceImplementation',
Extends: ObjectImplementingAnInterface,
Implements: [ InterfaceRequiringOtherInterface ],
});
- new ObjectInheritingFromInterfaceImplementation();
+ let obj = new ObjectInheritingFromInterfaceImplementation();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+ JSUnit.assertTrue(obj.constructor.implements(InterfaceRequiringOtherInterface));
}
function testInterfacesCanRequireBeingImplementedOnASubclass() {
- // Test considered passing if no exception thrown
+ // Test will fail if the constructor throws an exception
const ObjectImplementingInterfaceRequiringParentObject = new Lang.Class({
Name: 'ObjectImplementingInterfaceRequiringParentObject',
Extends: ObjectImplementingAnInterface,
Implements: [ InterfaceRequiringOtherInterface, InterfaceRequiringClassAndInterface ]
});
- new ObjectImplementingInterfaceRequiringParentObject();
+ let obj = new ObjectImplementingInterfaceRequiringParentObject();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+ JSUnit.assertTrue(obj.constructor.implements(InterfaceRequiringOtherInterface));
+ JSUnit.assertTrue(obj.constructor.implements(InterfaceRequiringClassAndInterface));
}
function testObjectsMustSubclassIfRequired() {
@@ -272,4 +281,23 @@ function testInterfaceMethodsCanCallOtherInterfaceMethods() {
JSUnit.assertEquals('interface private method', obj.usesThis());
}
+function testSubclassImplementsTheSameInterfaceAsItsParent() {
+ const SubObject = new Lang.Class({
+ Name: 'SubObject',
+ Extends: ObjectImplementingAnInterface
+ });
+ let obj = new SubObject();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+}
+
+function testSubclassCanReimplementTheSameInterfaceAsItsParent() {
+ const SubImplementer = new Lang.Class({
+ Name: 'SubImplementer',
+ Extends: ObjectImplementingAnInterface,
+ Implements: [ AnInterface ]
+ });
+ let obj = new SubImplementer();
+ JSUnit.assertTrue(obj.constructor.implements(AnInterface));
+}
+
JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown);
diff --git a/modules/lang.js b/modules/lang.js
index 0f64807..5179fd9 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -227,6 +227,9 @@ Class.prototype._construct = function(params) {
newClass._init.apply(newClass, arguments);
let interfaces = params.Implements || [];
+ // If the parent already implements an interface, then we do too
+ if (parent instanceof Class)
+ interfaces = interfaces.filter((iface) => !parent.implements(iface));
Object.defineProperties(newClass.prototype, {
'__metaclass__': { writable: false,
@@ -246,6 +249,20 @@ Class.prototype._construct = function(params) {
return newClass;
};
+/**
+ * Check whether this class conforms to the interface "iface".
+ * @param {object} iface a Lang.Interface
+ * @returns: whether this class implements iface
+ * @type: boolean
+ */
+Class.prototype.implements = function (iface) {
+ if (_interfacePresent(iface, this.prototype))
+ return true;
+ if (this.__super__ instanceof Class)
+ return this.__super__.implements(iface);
+ return false;
+};
+
Class.prototype._init = function(params) {
let name = params.Name;
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index 7e9e9d5..8c9dfda 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -134,6 +134,8 @@ const GObjectMeta = new Lang.Class({
throw new TypeError('GObject.Class used with invalid base class (is ' + parent + ')');
let interfaces = params.Implements || [];
+ if (parent instanceof Lang.Class)
+ interfaces = interfaces.filter((iface) => !parent.implements(iface));
let gobjectInterfaces = _getGObjectInterfaces(interfaces);
let propertiesArray = _propertiesAsArray(params.Properties);
@@ -166,6 +168,15 @@ const GObjectMeta = new Lang.Class({
});
return newClass;
+ },
+
+ // Overrides Lang.Class.implements()
+ implements: function (iface) {
+ if (iface instanceof GObject.Interface) {
+ return GObject.type_is_a(this.$gtype, iface.$gtype);
+ } else {
+ return this.parent(iface);
+ }
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]