Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web IDL best practices for platform object construction/initialization #1060

Open
rakuco opened this issue Nov 5, 2021 · 1 comment
Open

Comments

@rakuco
Copy link
Member

rakuco commented Nov 5, 2021

I recently stumbled upon #882 while looking for language for specifying an interface's constructor steps, and that got me thinking of what's supposed to happen when:

  1. There's no constructor but a new platform object needs to be created and initialized.
    https://w3c.github.io/screen-wake-lock/#the-request-method, for example, has a step that says "Let lock be a new WakeLockSentinel object with its type attribute set to type". It sounds a bit too handwavy to me, but I am not sure what the stricter approach would be. Referring to Web IDL new as in "Let lock be a [=new=] WakeLockSentinel" solves half of it -- but how do you communicate the "with its type attribute set to type" part? Does one define a "Construct a WakeLockSentinel given type" operation that calls [=new=], initializes a new [[Type]] internal slot and returns the new platform object (or doing it inline in the WakeLock.request() steps if that happens in only one location)?

  2. There's a constructor but other algorithms also need to create a platform object.
    I couldn't find a <dfn> in Web IDL I could link to that corresponds to "create this platform object and invoke its constructor steps with these arguments". https://webidl.spec.whatwg.org/#create-an-interface-object comes close, but it looks more low-level than what I am looking for. Is one expected to go down the "define a 'initalize MyInterface with arg1 and arg2' algorithm" route, make the constructor steps just say "Invoke 'initalize MyInterface with this, arg1 and arg2'" and make other algorithms that need to create a platform object say "1. Let foo be a [=new=] MyInterface, 2. Perform "initialize MyInterface with foo, arg1 and arg2"? Isn't that redundant?

@domenic
Copy link
Member

domenic commented Nov 5, 2021

but how do you communicate the "with its type attribute set to type" part?

You cannot set attributes to values. You can only define getter/setter steps for them. So you'd want to define an internal slot (either using the "[[Type]]" style or just the "has an associated type" style) and have the getter return that. Then, after newing up your object, you can set its internal slot value.

(Some older specs, or newer specs copying older specs, set attributes to values. This is bad and nonsensical. The only perhaps-reasonable exception is for Event subclasses, since those have a strange kind of autogenerated structure that isn't well-defined right now. I personally try to distinguish those cases by using the not-defined-anywhere magic word "Initialize" in the hopes of making it clear that we're doing something a bit different.)

Example: https://wicg.github.io/app-history/#navigate-event-destination defines a class, its internal slots, and its getter steps. https://wicg.github.io/app-history/#fire-a-push-or-replace-navigate-event creates one and modifies its internal slots before handing it off to other algorithms.

Is one expected to go down the "define a 'initalize MyInterface with arg1 and arg2' algorithm" route

Yes.

In general it's not redundant because it's very rare that the constructor steps are exactly the same as the spec-creation steps. For example constructor steps often perform validation on the arguments. Or they take Web IDL types (e.g. USVString) instead of spec types (e.g. URL record).

What's left in common is generally only very simple things of the form "Set x's y to yValue". And usually there are not very many of those. In my personal opinion, as a matter of editorial style, creating an algorithm which wraps those steps is obfuscating, i.e. I find "create a MyInterface with arg1, arg2, and arg3" less clear than spelling out "Let x be a new MyInterface / set x's slot1 to arg1 / set x's slot2 to arg2 / set x's slot3 to arg3". So unless you have many call sites in the spec, and many internal slots to initialize, the savings from such a centralized algorithm are not worth it IMO.


Hope this helps, and happy to take PRs or suggestions for how to better document this sort of thing. I guess a lot of it comes down to us still not having documented internal slots very well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants