WebIDL attribute reflection

Boris Zbarsky bzbarsky at mozilla.com
Sat Dec 29 22:42:08 PST 2012


On 12/29/12 11:08 AM, David Bruant wrote:
> Boris, what initially triggered my questioning of the inherited accessor
> setting is in this message:
> https://mail.mozilla.org/pipermail/es-discuss/2012-December/027435.html

OK, but that's a problem methods can have too, yes?  Not in this 
particular case, but in plenty of other cases...

Completely revamping how properties are handled to make this one case 
easier seems a bit extreme.  In my opinion.

That said, thank you for the pointer.  I do agree that this particular 
case would be a bit simpler if .global were an own property.

> Mostly concerns about a mix of API "securability" and convenience. I
> "demonstrate" that if every attribute is an inherited accessor, then,
> it's possible to secure the environment, but it can be at the expense of
> API usability up to asking people to tweak their code (which is an
> anti-goal when trying to secure your code from, say, an advertiser's code)

If you're trying to secure your code from an advertiser's code you want 
to not run the advertiser's code with your origin.  Anything else at 
best gives you the illusion of security...  Again, in my opinion.

> Implementations are free to do whatever they want as long as the
> observable behavior is conformant.

Yes, I understand that.  My point is that this makes it too easy to spec 
overcomplicated things that in fact can't be optimized sanely by 
implementations unless the specification writers are _very_ careful.

> WebIDL tries to close the gap between DOM semantics and ECMAScript
> semantics.

Yes, while at the same time converging with implementations.

> In an ES5 world, the only thing that can explain the magic
> behavior is getters/setters (own or inherited). In an ES6 world, proxies
> can be an explanation too.

Yes.

> For some cases like WindowProxy or NodeList, there will be no other way
> than using proxies  to specify these objects in an ECMAScript replicable
> semantics.

Indeed.

> I'm not sure I'm convinced by "it's more work than right now". If you
> had told me that there is a fundamental issue that implementors can't
> work around when exposing own data properties, I would have backed out,
> but you suggested than it's possible to create "yet another kind of
> thing internally which is not a proxy so it can be optimized sanely".

This is empirically true, since that's what JavaScriptCore does today, 
for example.  If I'm not misreading the code, it actually hangs its DOM 
property getters/setters of the prototype internally, makes them look 
like own properties on objects when reflected via 
getOwnPropertyDescriptor, and has magic to make a property lookup return 
the getter/setter pair internally and have their interpreter and JIT 
call those.

So in JavaScriptCore there are at least three different kinds of 
properties (there are more, because they have non-proxy objects with 
custom getOwnPropertyDescriptor hooks, like Document, but I'm 
simplifying): accessor properties, value properties, and magic 
properties that claim to be value properties when reflected but don't 
have a slot, are actually accessor properties under the hood, and 
pretend to be on a different object than the object they're really on.

This of course has the interesting side-effect that JavaScriptCore ends 
up having to claim the properties are non-configurable, because they 
don't actually live on the objects under the hood, so you have to 
disallow deleting them.  So this approach, in this particular 
implementation, doesn't address you "delete .global" issue anyway.  Try 
the testcase:

<script>
   var h = document.documentElement;
   alert(h.innerHTML);
   alert(JSON.stringify(Object.getOwnPropertyDescriptor(h,
                                                       "innerHTML")));
   delete h.innerHTML;
   alert(h.innerHTML);
</script>

> Is there some sort of way to use the getter/setter based implementation,
> but expose things as data property when asked via
> Object.getOwnPropertyDescriptor?

See above.

Again, in computer science pretty much everything is possible.  It's all 
just code.  It's doing the thing you want in finite time, both 
implementation time and run time, is the hard part.

> Out of curiosity, do you have an idea of how much cost the |this| check?

Cost in terms of what?

It depends on your implementation, obviously (e.g. it's really expensive 
in Gecko's XPConnect bindings from 6 years ago), but in Gecko's WebIDL 
bindings the cost of the "this" check (which we want to minimize no 
matter what, because methods have to do it!) is about 15-20 cycles on 
average last I measured (including whatever cache misses are involved 
every so often, etc).  It'll get a bit cheaper as we remove some more 
edge cases it has to deal with right now.  Oh, and in practice we 
usually only have to do it at JIT-compile time, not at runtime (unless 
you go out of your way to do .call or .apply on the getter/setter) 
because the combination of the type inference and guarding the JIT 
already does means that we'll get the equivalent of an IC miss (it's not 
quite an IC, but similar concept) if the observed type changes from what 
it was when the code was compiled.

The upshot is that a DOM getter returning an integer for our WebIDL 
bindings is a tad faster for me than an equivalent getter in JSC (which 
does not have to do the |this| check in its current setup), in any sort 
of testcase that actually gets compiled with IonMonkey.

-Boris


More information about the es-discuss mailing list