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

Enumeration order of interface members #432

Open
tobie opened this Issue Sep 1, 2017 · 14 comments

Comments

Projects
None yet
4 participants
@tobie
Collaborator

tobie commented Sep 1, 2017

Spec both says:

The order that members appear [on an interface] has significance for property enumeration in the ECMAScript binding.

and

The order of appearance of an interface definition and any of its partial interface definitions does not matter.

This seems contradictory. Which one is it? And more precisely, how is enumeration order defined across partials if at all.

@annevk

This comment has been minimized.

Collaborator

annevk commented Sep 1, 2017

It's important to some extent for compatibility. E.g., we cannot change the order of some members on the 2D API as that would break sites. How much order is important in general is unknown and proposals have come up for randomizing it and seeing what breaks...

@tobie

This comment has been minimized.

Collaborator

tobie commented Sep 1, 2017

So enumeration order matters between some members defined within the same interface or partial interface declaration. I see.

@domenic domenic referenced this issue Oct 3, 2017

Merged

Add mixins #433

7 of 7 tasks complete
@tobie

This comment has been minimized.

Collaborator

tobie commented Oct 4, 2017

So if we want to define this precisely, here's a proposal so there's some basis for discussion:

  1. Let |members| be a new [=ordered set=].
  2. Let |stack| be the result of invoking [=create an inheritance stack=] for [=interface=] |I|.
  3. [=iteration/While=] |stack| is not [=stack/empty=]:
    1. Let |interface| be the result of [=stack/popping=] from |stack|.
    2. [=set/Append=] [=For each|each=] |member| of |interface|'s
      [=interface member|members=] to |members|.
    3. [=For each=] |partialInterface| of |interface| declared in the same specification as |interface|,
      in the order in which it appears in the specification:
      1. [=set/Append=] [=For each|each=] |member| of |partialInterface|'s
        [=interface member|members=] to |members|.
    4. [=For each=] |specification| that declares a [=partial interface=] of |interface|,
      lexicographically ordered by |specification|'s short name:
      1. [=For each=] |partialInterface| of |interface| declared in |specification|,
        in the order in which it appears in |specification|:
        1. [=set/Append=] [=For each|each=] |member| of |partialInterface|'s
          [=interface member|members=] to |members|.
    5. [=For each=] |mixin| [=included=] by |interface|, declared in the same specification as |interface|,
      in the order in which it appears in the specification:
      1. [=set/Append=] [=For each|each=] |member| of |mixin|'s
        [=mixin member|members=] to |members|.
      2. [=For each=] |partialMixin| of |mixin| declared in the same specification as |mixin|,
        in the order in which it appears in the specification:
        1. [=set/Append=] [=For each|each=] |member| of |partialMixin|'s
          [=mixin member|members=] to |members|.
      3. [=For each=] |specification| that declares a [=partial mixin=] of |mixin|,
        lexicographically ordered by |specification|'s short name:
        1. [=For each=] |partialMixin| of |mixin| declared in |specification|,
          in the order in which it appears in |specification|:
          1. [=set/Append=] [=For each|each=] |member| of |partialMixin|'s
            [=mixin member|members=] to |members|.
    6. [=For each=] |specification| that declares a [=mixin=] [=included=] by |interface|,
      lexicographically ordered by |specification|'s short name:
      1. [=For each=] |mixin| [=included=] by |interface| in |specification|,
        in the order in which it appears in |specification|:
        1. [=For each=] |member| of |mixin|'s [=mixin member|members=]:
          1. [=set/Append=] |member| to |members|.
        2. [=For each=] |partialMixin| of |mixin| declared in the same specification as |mixin|,
          in the order in which it appears in the specification:
          1. [=set/Append=] [=For each|each=] |member| of |mixin|'s
            [=mixin member|members=] to |members|.
        3. [=For each=] |specification| that declares a [=partial mixin=] of |mixin|,
          lexicographically ordered by |specification|'s short name:
          1. [=For each=] |partialMixin| of |mixin| declared in |specification|,
            in the order in which it appears in |specification|:
            1. [=set/Append=] [=For each|each=] |member| of |partialMixin|'s
              [=mixin member|members=] to |members|.
  4. Return |members|.
@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Oct 4, 2017

Interfaces don't really have a concept of which specification they're declared in attached to them. And in particular:

  1. On a text level which specification is mutable, and sometimes there are multiple specifications that do it at once. Right now that's all OK as long as they all agree.
  2. Right now, just concatenating all IDL together is a reasonable (and possibly desirable) thing to do.

Likewise "order within a specification" may not be a very well defined concept...

It's not clear to me that "|specification|'s short name" is either stable or unique....

In general, other than the arc/arcTo thing I'm not obviously aware of people depending on the order, fwiw.

@tobie

This comment has been minimized.

Collaborator

tobie commented Oct 4, 2017

So are you suggesting we should just no spec this for now?

@TimothyGu

This comment has been minimized.

Contributor

TimothyGu commented Oct 4, 2017

Or we can just enforce the relative order of members declared in the definition of an interface in a single IDL fragment, though that does cause questions like this:

interface A {
  DOMString a();
  DOMString b();
  DOMString a(DOMString arg);
};
@tobie

This comment has been minimized.

Collaborator

tobie commented Oct 4, 2017

Or we can just enforce the relative order of members declared in the definition of an interface in a single IDL fragment, though that does cause questions like this:

That doesn't define clearly what "in order" means in step 2 of collect attribute values.

@TimothyGu

This comment has been minimized.

Contributor

TimothyGu commented Oct 4, 2017

@tobie Well, can't we leave inter-IDL fragment order undefined/implementation-defined? Like isn't relative order within the IDL fragment all we really care about in real life?

@tobie

This comment has been minimized.

Collaborator

tobie commented Oct 4, 2017

Well, can't we leave inter-IDL fragment order undefined/implementation-defined?

I don't really have a strong opinion on this beyond wanting to resolve the current contradiction in the spec and needing to specify mixin member order in host interfaces.

Like isn't relative order within the IDL fragment all we really care about in real life?

For testing JSON order, that's sort of painful, but maybe nobody really cares about JSON order.

@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Oct 4, 2017

For the case @TimothyGu raises (overloads not next to each other), all hope is lost, kinda, because they could in fact be in different IDL fragments too. We could order them by "which one appears first in the global ordering we haven't figured out how to establish"....

I would probably be OK with ensuring things in a given IDL fragment stay in order. As for JSON... people shouldn't care about order there, but who knows whether they do. :(

@annevk

This comment has been minimized.

Collaborator

annevk commented Oct 5, 2017

Couple ideas:

  1. A simple global ordering could be lexicographical with the key being the contents of each of the fragments that end up being combined into one. It's definitely not great and if we organize specifications differently there might be some observable side effects.
  2. Another thing we could do is require random order between fragments, but specified order for a given fragment. That complicates testing and implementation more than the former strategy I think.
  3. We could continue to not care and hope the problem doesn't spread.
@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Oct 5, 2017

So in practice, how do web browsers get these IDL fragments into their IDL parser?

Presumably, they put them into files somehow. Is this a manual or automated process? Is the "fragment identity" preserved in any way during this process?

That is, having ordering depend on the concept of "IDL fragment" assumes that this concept even exists in practice in implementations. If it doesn't and would have to be painstakingly maintained by hand, then chances are it wouldn't get maintained....

@tobie

This comment has been minimized.

Collaborator

tobie commented Oct 5, 2017

Here's what I think Web developers might reasonably expect order to be:

  1. members are ordered alphabetically (case insensitive), just like in dev tools,
  2. members are ordered lexicographically, and
  3. members are ordered as listed in the spec (without really thinking that some APIs are split-up through partials, mixins, and different documents).

I have no idea what the perf cost of (1) and (2) would be. I find them sort of attractive if they're not too expensive.

The problem with (3) is that we're unclear what it means precisely. Could we settle on something were we say:

  • order within a single definition is respected,
  • order in between definition is vendor-specific,
  • overloads across multiple definitions might show up with any of those in a vendor specific way.
@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Oct 5, 2017

I have no idea what the perf cost of (1) and (2) would be.

I can't speak to other implementations, but in Gecko this would be a compile-time cost, not a runtime cost. This is also how dictionary members are already ordered.

Though, actually, that's not quite true. Gecko sets up properties on prototypes in "batches", operations separately from attributes, for various reasons. So right now in Gecko ordering is IDL order (ignoring the multiple fragment thing) within each of operations and attributes, but operations always come before attributes. That said, ordering within each of those lexicographically or so would be easy.

The problem is that the arc/arcTo thing we ran into in https://bugzilla.mozilla.org/show_bug.cgi?id=623437 needed arcTo to come before arc.

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