Re: GSJ GIRepository VS Seed



On Tue, 2016-03-22 at 18:27 +0000, Andrea Giammarchi wrote:
Thanks Giovanni, that would solve only if `GLocalFile` will still
implements everything monkey-patched on `Gio.File` otherwise the lie
is gonna be a disaster in terms of expectations:

```js
Gio.File.prototype.allFiles = function () { /* do something */ };
Gio.File.new_for_path('/').allFiles(); // throws an error if it
doesn't inherit
```

Right, that's exactly what you cannot do: they are separate objects,
changing one does not affect the other.

To restate my concern: I would like to have instances implementing
interfaces be runtime aware of changes to such interfaces, otherwise
there's no way to patch upfront in a consistent way any prototype
which is not future proof or polyfill friendly, because you cannot
patch instances unless you wrap all of them, which is the Proxy mess
I've recently dropped due performance implications (faster on
bootstrap, catastrophic while running)

That will require changes in gjs I am afraid.
The code involved is in gi/object.cpp at object_instance_new_resolve()
for object prototypes, and gi/interface.cpp at interface_new_resolve()
for "interface" prototypes.

The functions are called to "resolve" a property, that is, to make it
lazily appear when the code is referencing it.
One way to do so is to modify object_instance_new_resolve(), check if a
property with that name exists on the interface object, then resolve to
an accessor property that forwards to the interface.
A little hackish, and probably with a lot of overhead to define the
accessor property in C++, but it should work.

Is anyhow clearer what I am expecting? Yeah, I understand it won't
work now ... but at least I hope it's clear.

Best Regards

Cheers,

Giovanni






On Tue, Mar 22, 2016 at 6:05 PM, Giovanni Campagna <scampa giovanni g
mail.com> wrote:
Ok, let's try a factual response...

On Tue, 2016-03-22 at 17:24 +0000, Andrea Giammarchi wrote:
 There is not even any Gio.LocalFile known to JS.

```js
const a = imports.gi.Gio.File.new_for_path('./a.js');

// here the "now known to JS" Gio.LocalFile.prototype
Object.getPrototypeOf(a).shenanigans = true;

const b = imports.gi.Gio.File.new_for_path('./b.js');

// all instances affected indeed
print(b.shenanigans); // true
```

 It's not exposed in the GIR file, so there is never a prototype
object created in the first place

Yes, there is a prototype object created. Every JS object with
custom
behavior has a custom prototype object in SpiderMonkey, that's just
how
it is.

This prototype object is "GLocalFile", and it's stored in an
invisible
object that exists just to reference it, as well as other hidden
classes that appear through GType but not through introspection.

It's supposed to be mostly invisible, but it's unavoidable.

There is:

```js
const a = imports.gi.Gio.File.new_for_path('./a.js');
// here the *shared* Gio.LocalFile.prototype
Object.getPrototypeOf(a);
```

 In GJS, you can check if an object implements an interface with
myObj.constructor.implements(Gio.File).

That's good to know, thanks, yet if you read first messages of
this
thread it was about patching upfront and not at runtime.
I understand I can find at runtime pretty much anything I want,
but
since there is an introspection ability, why are there
undocumented
instances around with undocumented prototypes?

Or better, why `Gio.File.new` creates something unrelated with
`Gio.File.prototype` or `Gio.File` methods ?
Since this super secret thing is easily leaked, why not fixing
this
instead of saying that it shouldn't be known?

You can't fix that.

The truth is, Gio.File is a lie.
Indeed, Gio.File.prototype.replace_contents !==
(Gio.File.new_for_path('/foo')).replace_contents

What it means is that Gio.File is an object that exists only to
hold
methods that quack like the actual interface methods, if you call
them
explicitly with say
Gio.File.prototype.replace_contents.call(file, "bla")
, but has nothing to do with the interface methods exposed on each
object.

The reason for this is that prototype inheritance is single, but a
GObject class can have multiple interfaces, so there is no good
place
to put Gio.File.prototype on the prototype chain from file to
Object.prototype (in a way that's generic and consistent with say,
Gtk.Label and Gtk.Buildable).
So what happens is that every class that also implements an
interface
will resolve all interface methods on its own prototype.

In the Gtk.Label case, Gtk.Label implements Gtk.Buildable and
Gtk.Widget implements Gtk.Buildable, both visible in the GIR, which
means
Gtk.Label.prototype.hasOwnProperty('custom_tag_start') === true
and
Gtk.Widget.prototype.hasOwnProperty('custom_tag_start') === true

In the GLocalFile case, GLocalFile implements Gio.File, and we know
that from GType at runtime, so
window.<invisible
name>.GLocalFile.prototype.hasOwnProperty('replace_contents') ===
true
but you don't know that unless you poke at Object.getPrototypeOf

Yes, this is very awkward if you monkey patch prototypes, but
that's
just how it is.

The reason I've asked is that I've discovered there are hidden
classes the GIR won't tell me, doesn't know, but **are** on my
way.

 I don't believe Spidermonkey would support overloading
instanceof
for this

`instanceof` is the most easily "overloaded" ( not actually
overloaded, it just checks
`rightSide.prototype.isPrototypeOf(leftSide)` ) operator which is
why
I am asking if this would ever be solved.

Now, if we tell a lie, we should at least be consistent about it,
and
that's why
Gio.File.new_for_path('/') instanceof Gio.File
should return true

Checking the prototype would not work, but instanceof can be
overloaded
"properly", because after all we have access to the C API of SM and
we
can do what we want.

Indeed, that's https://bugzilla.gnome.org/show_bug.cgi?id=587030
which has had patches for a while and probably needs a rebase, but
would make the Gio.File lie less visible to programmers.

Hope this clarifies the situation, and cheers,

Giovanni

Attachment: signature.asc
Description: This is a digitally signed message part



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