After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 562584 - GObject subclassing support
GObject subclassing support
Status: RESOLVED FIXED
Product: gjs
Classification: Bindings
Component: general
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gjs-maint
gjs-maint
Depends on:
Blocks:
 
 
Reported: 2008-11-28 16:15 UTC by Johan (not receiving bugmail) Dahlin
Modified: 2012-03-01 17:15 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Johan (not receiving bugmail) Dahlin 2008-11-28 16:15:41 UTC
Gjs should support GObject subclassing, considering the following example:

const Gtk = imports.gi.Gtk;

function MyDialog() {
  this._init();
};

MyDialog.prototype = {
  _init : function() {
     log("Created a MyDialog");
  };
};

MyDialog.prototype.__proto__ = Gtk.Dialog.prototype;
let dialog = new MyDialog();

Which should work, but it will currently throw the following error:

    JS ERROR: !!!   Exception was: Error: Object 0x82231c0 proto 0xb5903560 doesn't have a dynamically-registered class, it has Object
    JS ERROR: !!!     lineNumber = '0'
    JS ERROR: !!!     fileName = 'gjs_throw'
    JS ERROR: !!!     stack = 'Error("Object 0x82231c0 proto 0xb5903560 doesn't have a dynamically-registered class, it has Object")@:0
Comment 1 Havoc Pennington 2008-11-28 16:35:48 UTC
I think this would work a little differently (I didn't think assignment to __proto__ was even allowed)

Owen pointed out Rhino's approach:
http://www.mozilla.org/rhino/ScriptingJava.html

The Seed approach is the same, "new GType" used instead of "new JavaAdapter":
http://live.gnome.org/Seed

Comment 2 Johan (not receiving bugmail) Dahlin 2008-11-28 17:33:36 UTC
I was stumping over this and reading some js from mozilla:

https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/proto

There's even a nicer shortcut:

MyDialog.prototype = {
  __proto__ : Gtk.Dialog.prototype,
  _init : function() {
     log("Created a MyDialog");
  }
};

I think it would make sense to support that, as it is arguable the /native/ way of doing subclasses.

It's probably not too hard to implement either, just recursively look at obj_class.__proto__ when looking for a registered class in gjs.

I'm not sure about the Seed approach, it looks rather "C" with a thin thin wrapper, but perhaps that's what we want.
Comment 3 Havoc Pennington 2008-11-28 18:56:06 UTC
If this works:
__proto__ : Gtk.Dialog.prototype

I wonder if we should use it instead of the current Style_Guide.txt recommendation:
Lang.copyProperties(Base.prototype, Sub.prototype);

Lang.copyProperties won't work for subclassing anything with a C class other than Object, whether the C class is Array, RegExp, or some GObject thing. __proto__ in theory could support a C class if the C class is smart enough to traverse up the __proto__ chain to find an instance of itself.

I'm not sure there's a native way of doing subclasses in JavaScript - every JS codebase I've ever seen seems to do it differently (or in several different ways, worse). Prototypes really are not classes in some important ways, also. A thing that's a mess about JavaScript is that it _does_ have classes - but they are implemented in C! (Array, RegExp, Function, and so forth.)

If we do subclasses here, the JSClass* type of the subclass will be Object, which will have an object with the JSClass* for GtkDialog in its prototype chain. So an object with C class Object is derived via prototype chain from an object with C class GtkDialog.
Comment 4 Havoc Pennington 2008-12-01 18:09:09 UTC
Hmm, I think we do have to create a GType, because we need to install proxy functions in the vtable that call out to any overrides in the JS subclass. Also, there has to be a GObject* that we can pass in to C code, so whenever "new MyDialog()" is done that has to create a new GObject* inside the MyDialog.

However, if MyDialog has JSClass* for javascript Object, it has no private data or anything; for it to contain a GObject* we need the JSClass* for a MyDialog instance to point to a newly-created JSClass* specific to the MyDialog type.

This probably ends up looking similar to what Seed does, though obviously you could vary it a little. Perhaps:

let MyDialog = GLib.subclassGObject(Gtk.Dialog, 'MyDialog', {
  _init : function() {

   },

   // copy "do_" convention from pygtk? anyway the point is 
   // to override a virtual function you put it in here
   doShow : function() {

   }
});

This would create a new JSClass* for MyDialog, with its __proto__ going to Gtk.Dialog, and create a new GType for MyDialog also. (Basically it would create the type, then do a standard gobject wrapper like we already have; when creating the type, it would put proxies in the vtable slot inside class_init for anything that was overridden)
Comment 5 Tommi Komulainen 2008-12-01 20:31:50 UTC
For full support we need a GType, but if you just want to have a class to conveniently hold your data and additional methods (not overloaded) - fairly common situation, I think, we wouldn't strictly need the GType, right?
Comment 6 Havoc Pennington 2008-12-01 20:51:00 UTC
Even if you don't want to override anything, you still need a GObject*, so something has to call g_object_new() somewhere and somehow store the pointer in the instance such that Gtk.Dialog.prototype.show() and other methods can find it.

I don't see how to achieve that without registering a JSClass* for MyDialog, because you need the C hook that calls g_object_new(). Once you have to create a JSClass* using some special call in your JavaScript, you may as well create a GType at the same time (which allows overriding since you can replace stuff in class_init).

Side note, I'm not sure I am that huge a fan of the "subclassing widgets" style of organizing UI programs ... some toolkits are designed to work this way, so if you want a prefs dialog you subclass to write MyPrefsDialog, etc.

To me this tends to tangle the model with view and the logic with the display ... an example practical consequence is that it's pretty incompatible with the idea of a UI builder.

For me, the place you want to subclass widgets is to write a new custom widget with some sort of special rendering, i.e. the really worthwhile places to subclass widgets are the ones where you need to override.

We have our "foo.actor" pattern in our code, to me that pattern is a hack when what you really want is a new actor class - for example the Button class should be a subclass of ClutterActor, it shouldn't have-a ClutterActor.

However, in cases where we have a "model-ish" object, with domain-specific logic in it, often I think the "foo.actor" pattern is _correct_ and not hacky at all. Think of it as "give me an actor representing foo." If you have a Foo it should not be an actor. Maybe there's a thing called FooView that is-a actor. But I think subclassing ClutterActor for things that aren't "views" is often bad.

What I'm trying to say is, I think you usually want to override methods, and that's the main point of a GObject-subclassing feature, imo.
Comment 7 Giovanni Campagna 2012-03-01 17:15:02 UTC
Several years later, you can enjoy doing:

MyClass = new GObject.Class({
    Name: 'MyClass',
    Extends: GObject.Object
});

Fixed! :)

This problem has been fixed in the development version. The fix will be available in the next major software release. Thank you for your bug report.