[gjs/wip/ptomato/classes: 1/8] class: Stop using custom __name__ property



commit 188254fb8f93d21924087cbd3f68bee6596514ed
Author: Philip Chimento <philip chimento gmail com>
Date:   Mon Jul 10 23:19:40 2017 -0700

    class: Stop using custom __name__ property
    
    Since ES6 classes are functions, if you declare 'class Foo {}' then
    'Foo.name === "Foo"'. We had a custom __name__ property on Lang.Class
    instances, but instead mimic the normal JS class and put a name property
    on the class.
    
    That is, the equivalent to 'instance.__name__' is now
    'instance.constructor.name'.
    
    This could potentially break client code if any was using this, but the
    double underscore made it clear enough that the property should have been
    internal.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785652

 NEWS                                       |    8 +++++
 installed-tests/js/testClass.js            |    4 ++
 installed-tests/js/testGObjectClass.js     |    4 ++
 installed-tests/js/testGObjectInterface.js |   10 ++++--
 installed-tests/js/testInterface.js        |    4 ++
 modules/lang.js                            |   43 +++++++++++++--------------
 modules/overrides/GObject.js               |   15 +++++++++-
 7 files changed, 61 insertions(+), 27 deletions(-)
---
diff --git a/NEWS b/NEWS
index 4963c63..a0138e3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+NEXT
+----
+
+- Backwards-incompatible change: The private "__name__" property on Lang.Class
+  instances is removed. Code should not have been using this, but if it did
+  then it will need to be changed. It is replaced by the "name" property on
+  class constructors, which is standard for ES6 classes.
+
 Version 1.48.6
 --------------
 
diff --git a/installed-tests/js/testClass.js b/installed-tests/js/testClass.js
index dfac15c..13a3f3e 100644
--- a/installed-tests/js/testClass.js
+++ b/installed-tests/js/testClass.js
@@ -102,6 +102,10 @@ describe('Class framework', function () {
         expect(newMagic instanceof MagicBase).toBeTruthy();
     });
 
+    it('has a name', function () {
+        expect(Magic.name).toEqual('Magic');
+    });
+
     it('reports a sensible value for toString()', function () {
         let newMagic = new MagicBase();
         expect(newMagic.toString()).toEqual('[object MagicBase]');
diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js
index 0d81b96..3aaa938 100644
--- a/installed-tests/js/testGObjectClass.js
+++ b/installed-tests/js/testGObjectClass.js
@@ -209,6 +209,10 @@ describe('GObject class', function () {
         expect(myInstance3.construct).toEqual('quz');
     });
 
+    it('has a name', function () {
+        expect(MyObject.name).toEqual('MyObject');
+    });
+
     // the following would (should) cause a CRITICAL:
     // myInstance.readonly = 'val';
     // myInstance.construct = 'val';
diff --git a/installed-tests/js/testGObjectInterface.js b/installed-tests/js/testGObjectInterface.js
index 792b611..11914f2 100644
--- a/installed-tests/js/testGObjectInterface.js
+++ b/installed-tests/js/testGObjectInterface.js
@@ -157,6 +157,10 @@ describe('GObject interface', function () {
         expect(() => new AGObjectInterface()).toThrow();
     });
 
+    it('has a name', function () {
+        expect(AGObjectInterface.name).toEqual('AGObjectInterface');
+    });
+
     it('reports its type name', function () {
         expect(AGObjectInterface.$gtype.name).toEqual('ArbitraryGTypeName');
     });
@@ -169,12 +173,10 @@ describe('GObject interface', function () {
     });
 
     it('is implemented by a GObject class with the correct class object', function () {
-        expect(GObjectImplementingGObjectInterface.toString())
-            .toEqual('[object GObjectClass for GObjectImplementingGObjectInterface]');
         let obj = new GObjectImplementingGObjectInterface();
         expect(obj.constructor).toEqual(GObjectImplementingGObjectInterface);
-        expect(obj.constructor.toString())
-            .toEqual('[object GObjectClass for GObjectImplementingGObjectInterface]');
+        expect(obj.constructor.name)
+            .toEqual('GObjectImplementingGObjectInterface');
     });
 
     it('can be implemented by a class also implementing a Lang.Interface', function () {
diff --git a/installed-tests/js/testInterface.js b/installed-tests/js/testInterface.js
index 08f3b94..83188e3 100644
--- a/installed-tests/js/testInterface.js
+++ b/installed-tests/js/testInterface.js
@@ -107,6 +107,10 @@ describe('An interface', function () {
         expect(InterfaceRequiringOtherInterface instanceof Lang.Interface).toBeTruthy();
     });
 
+    it('has a name', function () {
+        expect(AnInterface.name).toEqual('AnInterface');
+    });
+
     it('cannot be instantiated', function () {
         expect(() => new AnInterface()).toThrow();
     });
diff --git a/modules/lang.js b/modules/lang.js
index 0957158..e40fefd 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -110,9 +110,8 @@ _Base.prototype._construct = function() {
     this._init.apply(this, arguments);
     return this;
 };
-_Base.prototype.__name__ = '_Base';
 _Base.prototype.toString = function() {
-    return '[object ' + this.__name__ + ']';
+    return `[object ${this.constructor.name}]`;
 };
 
 function _parent() {
@@ -167,7 +166,6 @@ function Class(params) {
 Class.__super__ = _Base;
 Class.prototype = Object.create(_Base.prototype);
 Class.prototype.constructor = Class;
-Class.prototype.__name__ = 'Class';
 
 Class.prototype.wrapFunction = function(name, meth) {
     if (meth._origin) meth = meth._origin;
@@ -188,7 +186,7 @@ Class.prototype.wrapFunction = function(name, meth) {
 }
 
 Class.prototype.toString = function() {
-    return '[object ' + this.__name__ + ' for ' + this.prototype.__name__ + ']';
+    return `[object ${this.constructor.name} for ${this.prototype.constructor.name}]`;
 };
 
 Class.prototype._construct = function(params) {
@@ -241,6 +239,12 @@ Class.prototype._construct = function(params) {
                             enumerable: false,
                             value: interfaces }
     });
+    Object.defineProperty(newClass, 'name', {
+        writable: false,
+        configurable: true,
+        enumerable: false,
+        value: name,
+    });
 
     interfaces.forEach((iface) => {
         iface._check(newClass.prototype);
@@ -305,10 +309,6 @@ Class.prototype._init = function(params) {
 
     Object.defineProperties(this.prototype, propertyObj);
     Object.defineProperties(this.prototype, {
-        '__name__': { writable: false,
-                      configurable: false,
-                      enumerable: false,
-                      value: name },
         'parent': { writable: false,
                     configurable: false,
                     enumerable: false,
@@ -384,7 +384,6 @@ Interface.UNIMPLEMENTED = function UNIMPLEMENTED () {
 Interface.__super__ = _Base;
 Interface.prototype = Object.create(_Base.prototype);
 Interface.prototype.constructor = Interface;
-Interface.prototype.__name__ = 'Interface';
 
 Interface.prototype._construct = function (params) {
     if (!params.Name)
@@ -395,7 +394,6 @@ Interface.prototype._construct = function (params) {
     newInterface.__super__ = Interface;
     newInterface.prototype = Object.create(Interface.prototype);
     newInterface.prototype.constructor = newInterface;
-    newInterface.prototype.__name__ = params.Name;
 
     newInterface._init.apply(newInterface, arguments);
 
@@ -404,6 +402,12 @@ Interface.prototype._construct = function (params) {
                             configurable: false,
                             enumerable: false,
                             value: this.constructor });
+    Object.defineProperty(newInterface, 'name', {
+        writable: false,
+        configurable: true,
+        enumerable: false,
+        value: params.Name,
+    });
 
     return newInterface;
 };
@@ -424,14 +428,13 @@ Interface.prototype._check = function (proto) {
             interfaces.indexOf(required) > interfaces.indexOf(this)) &&
             !(proto instanceof required));
     }).map((required) =>
-        // __name__ is only present on GJS-created classes and will be the most
-        // accurate name. required.name will be present on introspected GObjects
-        // but is not preferred because it will be the C name. The last option
-        // is just so that we print something if there is garbage in Requires.
-        required.prototype.__name__ || required.name || required);
+        // required.name will be present on JS classes, but on introspected
+        // GObjects it will be the C name. The alternative is just so that
+        // we print something if there is garbage in Requires.
+        required.name || required);
     if (unfulfilledReqs.length > 0) {
         throw new Error('The following interfaces must be implemented before ' +
-            this.prototype.__name__ + ': ' + unfulfilledReqs.join(', '));
+            `${this.constructor.name}: ${unfulfilledReqs.join(', ')}`);
     }
 
     // Check that this interface's required methods are implemented
@@ -439,12 +442,12 @@ Interface.prototype._check = function (proto) {
     .filter((p) => this.prototype[p] === Interface.UNIMPLEMENTED)
     .filter((p) => !(p in proto) || proto[p] === Interface.UNIMPLEMENTED);
     if (unimplementedFns.length > 0)
-        throw new Error('The following members of ' + this.prototype.__name__ +
+        throw new Error(`The following members of ${this.constructor.name}` +
             ' are not implemented yet: ' + unimplementedFns.join(', '));
 };
 
 Interface.prototype.toString = function () {
-    return '[interface ' + this.__name__ + ' for ' + this.prototype.__name__ + ']';
+    return `[interface ${this.constructor.name} for ${this.prototype.constructor.name}]`;
 };
 
 Interface.prototype._init = function (params) {
@@ -475,10 +478,6 @@ Interface.prototype._init = function (params) {
 
     Object.defineProperties(this.prototype, propertyObj);
     Object.defineProperties(this.prototype, {
-        '__name__': { writable: false,
-                      configurable: false,
-                      enumerable: false,
-                      value: name },
         '__requires__': { writable: false,
                           configurable: false,
                           enumerable: false,
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index ee30779..556b7b2 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -161,6 +161,13 @@ const GObjectMeta = new Lang.Class({
                                 enumerable: false,
                                 value: interfaces }
         });
+        // Overwrite the C++-set class name, as if it were an ES6 class
+        Object.defineProperty(newClass, 'name', {
+            writable: false,
+            configurable: true,
+            enumerable: false,
+            value: name,
+        });
 
         interfaces.forEach((iface) => {
             if (iface instanceof Lang.Interface)
@@ -189,7 +196,6 @@ GObjectMeta.MetaInterface = GObjectInterface;
 GObjectInterface.__super__ = Lang.Interface;
 GObjectInterface.prototype = Object.create(Lang.Interface.prototype);
 GObjectInterface.prototype.constructor = GObjectInterface;
-GObjectInterface.prototype.__name__ = 'GObjectInterface';
 
 GObjectInterface.prototype._construct = function (params) {
     if (!params.Name) {
@@ -222,6 +228,13 @@ GObjectInterface.prototype._construct = function (params) {
         enumerable: false,
         value: this.constructor
     });
+    // Overwrite the C++-set class name, as if it were an ES6 class
+    Object.defineProperty(newInterface, 'name', {
+        writable: false,
+        configurable: true,
+        enumerable: false,
+        value: params.Name,
+    });
 
     return newInterface;
 };


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]