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

Update attribute setter/getters in various ways #217

Merged
merged 2 commits into from
Jan 20, 2017
Merged

Conversation

domenic
Copy link
Member

@domenic domenic commented Nov 1, 2016


Preview | Diff w/ current ED | Diff w/ base

@@ -1385,6 +1385,10 @@ ignored or an exception is thrown.
};
</pre>

The type of the attribute that is not [=read only=] must not be a [=promise type=]. Additionally,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The type of an attribute"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

<!-- https://www.w3.org/Bugs/Public/show_bug.cgi?id=18547#c9 -->
1. Otherwise, set |O| to the <emu-val>this</emu-val> value.
1. If |O| is a [=platform object=], then [=perform a security check=], passing |O|,
|realm|, |id|, and "method".
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"getter", not "method", right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


1. If |attribute|'s type is a [=promise type=], then:
1. Let |reject| be the initial value of [=%Promise%=].reject.
1. Return the result of calling |reject| with [=%Promise%=] as the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we ever decide how this should interact with [SameObject]? Seems to me like we should just disallow [SameObject] on Promise-returning attributes, or change the definition of [SameObject] or something; the current definition of [SameObject] just would not match this new behavior.

I'm still not 100% sure we want to make this behavior change, for what it's worth. What do UAs do in practice?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed [SameObject] to reflect this.

In practice UAs are waiting for us to make a decision here; they throw the error per the current spec but are uncomfortable with doing so.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed [SameObject] to reflect this.

I would really rather we just disallowed using [SameObject] on Promise-valued attributes if we take this route, given the weird behavior. If we do allow it, UAs have to start doing something weird themselves, because they can no longer trust [SameObject] annotations...

In particular, it's not obvious that these changes to [SameObject] are compatible with #212

they throw the error per the current spec but are uncomfortable with doing so.

I don't think Gecko is uncomfortable. It sounds like Blink is? Do you have references for the other UAs, by any chance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really rather we just disallowed using [SameObject] on Promise-valued attributes if we take this route, given the weird behavior.

I really don't think it's that weird. It's about the exception case. You don't require the same exception objects be thrown each time, so similarly, you shouldn't require the same promise object be returned each time.

Do you have references for the other UAs, by any chance?

WebKit: #186

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't think it's that weird. It's about the exception case.

The problem is that unlike sync functions, which have a return value and a possible exception which are exposed to the caller in different ways, Promise-returning functions use a return value to represent both things. This makes [SameObject] a pretty confusing thing to start with: is it referring to the Promise object returned, or to its resolution value in the non-exception case, for example? If we really reason by analogy to sync functions, it should be referring to the resolution value... but in practice people and the spec use it to mean the actual Promise return value. Which doesn't even have an explicit equivalent in the sync function case in JS; it would be a continuation in languages that have such things. So any reasoning by analogy here is broken right now anyway.

It's worth taking a step back for a second and asking what the point of [SameObject] really is and why we have it at all, then trying to figure out what it means to map that concept to promise-returning getters. Assuming that's really possible.

WebKit: #186

Thank you. And Edge?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth taking a step back for a second and asking what the point of [SameObject] really is and why we have it at all, then trying to figure out what it means to map that concept to promise-returning getters. Assuming that's really possible.

Right. I perhaps am not the best person for this, since I don't like these documentation-only extended attributes. But I tried to put myself in the shoes of someone who does, and an interpretation of "returns the same object in non-exceptional cases" seemed like a good thing to document.

Thank you. And Edge?

I haven't seen any preferences from them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Well, as another point of view, with my JIT implementor hat on, after this change all Promise-returning cases would be non-exceptional cases (because from a JIT's point of view they are), so my JIT would have to know that [SameObject] doesn't mean "the same object" in Promise-returning cases and hence can't be optimized the same way as other [SameObject] annotations...

<!-- https://www.w3.org/Bugs/Public/show_bug.cgi?id=18547#c9 -->
1. Otherwise, set |O| to the <emu-val>this</emu-val> value.
1. If |O| is a [=platform object=], then [=perform a security check=], passing |O|,
|realm|, |id|, and "method".
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"setter", not "method".

1. Perform [=!=] [=DefinePropertyOrThrow=](|F|, "length",
PropertyDescriptor<span class="prop-desc">{\[[Value]]: |length|, \[[Writable]]: <emu-val>false</emu-val>, \[[Enumerable]]: <emu-val>false</emu-val>, \[[Configurable]]: <emu-val>true</emu-val>}</span>).
<div algorithm>
To <dfn id="dfn-create-operation-function" lt="creating an operation function">create an operation function</dfn>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wish this reindenting were a separate commit....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separated out into #232, but this PR for now is on top of that so it's included here.

@domenic domenic force-pushed the getters-setters branch 5 times, most recently from f978d87 to 69e9c85 Compare November 10, 2016 19:16
@@ -1374,6 +1374,10 @@ ignored or an exception is thrown.
};
</pre>

The type of an attribute that is not [=read only=] must not be a [=promise type=]. Additionally,
the type of an attribute with any of the [=extended attributes=] [{{LenientThis}}],
[{{PutForwards}}], [{{Replaceable}}], or [{{SameObject}}] must not be a [=promise type=].
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there were requirements for [{{SameObject}}] promise type attributes. E.g. for https://drafts.csswg.org/css-font-loading/#font-face-set-ready, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but @bzbarsky prefers we don't use them, as discussed upthread.

- Fixes getters on the global when no explicit `this` is passed, similarly to what was already done for operations in a2b4599. Closes https://www.w3.org/Bugs/Public/show_bug.cgi?id=29421.
- Make promise getters reject in case of an error. Disallow promise types on attributes that will generate setters. Fixes #186 and fixes https://www.w3.org/Bugs/Public/show_bug.cgi?id=25048. As part of this, disallowed [SameObject] for promise-returning attributes, since different promises will be returned if the getter is used incorrectly.
- Fixes https://www.w3.org/Bugs/Public/show_bug.cgi?id=27905 by more clearly specifying the target (as was done previously for operations).
- Updated to use modern ES Get/Set abstract operations instead of [[Get]] and [[Put]].
- Also made minor fixes to the operations definition, e.g. wrapping it in <div algorithm> and remembering to actually return F.
@@ -1377,6 +1377,10 @@ ignored or an exception is thrown.
};
</pre>

The type of an attribute that is not [=read only=] must not be a [=promise type=]. Additionally,
the type of an attribute with any of the [=extended attributes=] [{{LenientThis}}],
[{{PutForwards}}], [{{Replaceable}}], or [{{SameObject}}] must not be a [=promise type=].
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the below means the same thing and is clearer:

Attributes whose type is [=promise type|promise=] must be [=read only=].
Additionally, they cannot have any of the [=extended attributes=]
[{{LenientThis}}], [{{PutForwards}}], [{{Replaceable}}], or [{{SameObject}}].

the object that is the location of the property; and
* |S| is the [=attribute setter=] created given the attribute, the interface (or the interface
it's being mixed in to, if the interface is actually a mixin), and the [=relevant Realm=] of
the object that is the location of the property.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware this is purposefully copied from the text in the operations binding, but: s/the object that is the location of the property/the object that contains the property/ would read better.

Maybe change it everywhere? Or not at all for now also works. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go with not changing it for now as this area is related to the mixin mess and will get straightened out when that is.


Note: Although there is only a single property for an IDL attribute, since
accessor property getters and setters are passed a <emu-val>this</emu-val>
value for the object on which property corresponding to the IDL attribute is
accessed, they are able to expose instance-specific data.

Note: Note that attempting to assign to a property corresponding to a
Note: Attempting to assign to a property corresponding to a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't/shouldn't we express that normatively?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure it is. This was just a drive-by fix to not repeat "Note" twice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own education, where is that behavior defined normatively?

1. Let |R| be the result of performing the actions listed in the
description of |attribute| that occur on getting (or those listed in the description
of the inherited attribute, if this attribute is declared to
[=inherit its getter=]), on |O| if |O| is not <emu-val>null</emu-val>.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it hard to parse the meaning of the hanging conditional. I assume it means you carry out the getting in both case, but once it's carried with the |O| as this and the other one with undefined as this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there's no this formally (that's https://www.w3.org/Bugs/Public/show_bug.cgi?id=27301). The idea is not to provide anything at all; providing undefined isn't really correct.

1. Assert: |attribute|'s type is not a [=promise type=].
1. Let |steps| be the following series of steps:
1. If no arguments were passed, then
1. Let |V| be the value of the first argument passed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something unclear in the above two steps (either there's no argument or there is one).

1. Let |V| be the value of the first argument passed.
1. Let |id| be |attribute|'s [=identifier=].
1. Let |O| be <emu-val>null</emu-val>.
1. If |attribute| is not a [=static attribute=]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If |attribute| is a [=regular attribute=]:

1. Let |id| be |attribute|'s [=identifier=].
1. Let |O| be <emu-val>null</emu-val>.
1. If |attribute| is not a [=static attribute=]:
1. If the <emu-val>this</emu-val> value is <emu-val>null</emu-val> or
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If find all of the substeps of this step 5 super difficult to parse. I don't doubt that the algorithm is correct, but I really struggle to understand its intent. I can't imagine modifying something in here without accidentally breaking stuff.

Here's a proposed alternative (which is slightly less DRY, but hopefully a bit clearer). I added an optional note (prefixed with ?) to clarify the exit point of the substep. Maybe that helps (or is over the top—I'm not sure).

1.  If |attribute| is a [=regular attribute=]:
    1.  If the <emu-val>this</emu-val> value is <emu-val>null</emu-val> or
        <emu-val>undefined</emu-val>, set |O| to |realm|'s [=Realm/global object=].
        (This will subsequently cause a <emu-val>TypeError</emu-val> in a few steps, if
        the global object does not implement |target| and [{{LenientThis}}] is not
        specified.)
        <!-- https://www.w3.org/Bugs/Public/show_bug.cgi?id=18547#c9 -->
    1.  Otherwise, set |O| to the <emu-val>this</emu-val> value.
    1.  If |O| is a [=platform object=], then [=perform a security check=], passing |O|,
        |id|, and "setter".
     1.  Let |validThis| be `true` if |O| is a [=platform object=] that
         implements the interface |target|, or `false` otherwise.
     1.  If |validThis| is `true`, then:
         1.  If |attribute| is declared with a [{{LenientSetter}}] extended attribute, then:
             1.  Return <emu-val>undefined</emu-val>.
         1.  If |attribute| is declared with a [{{PutForwards}}] extended attribute, then:
             1.  Let |Q| be [=?=] [=Get=](|O|, |id|).
             1.  If [=Type=](|Q|) is not Object, then [=ECMAScript/throw=] a
                 <emu-val>TypeError</emu-val>.
             1.  Let |forwardId| be the identifier argument of the [{{PutForwards}}] extended
                 attribute.
             1.  Perform [=?=] [=Set=](|Q|, |forwardId|, |V|).
             1.  Return <emu-val>undefined</emu-val>.
         1.  If |attribute| is declared with the [{{Replaceable}}] extended attribute, then:
             1.  Perform [=?=] [=CreateDataProperty=](|O|, |id|, |V|).
             1.  Return <emu-val>undefined</emu-val>.
        
        ? Note: setters who are not special-cased here will
        ? continue being defined in the parent steps
        ? along with static attributes.
     1.  Otherwise if |attribute| was specified with the [{{LenientThis}}] [=extended attribute=],
         1.  If |attribute| is declared with the [{{Replaceable}}] extended attribute,
             then perform [=?=] [=CreateDataProperty=](|O|, |id|, |V|).
         1. Return <emu-val>undefined</emu-val>.
     1.  Otherwise, [=ECMAScript/throw=] a <emu-val>TypeError</emu-val>.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I don't really find that clearer; I like how you just check the conditions one after another in the existing version. Let's leave it for a follow-up, as we'd also want to change the analogous steps for operations.

1. Otherwise, set |O| to the <emu-val>this</emu-val> value.
1. If |O| is a [=platform object=], then [=perform a security check=], passing |O|,
|id|, and "setter".
1. Let |validThis| be <emu-val>true</emu-val> if |O| is a [=platform object=] that
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the booleans here really be just `true` ans `false`? These are not JS values, they're infra booleans, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably more correct I guess. I was thinking this is more like an ES spec algorithm where booleans are emu-val'ed, but this seems like a better place to lean toward web spec conventions than ES.


The value of the <emu-val>Function</emu-val> object’s “length”
property is the <emu-val>Number</emu-val> value <emu-val>1</emu-val>.
<dl class="switch">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using a switch here rather than a regular if..otherwise clause?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. We do it a lot, it seems. I guess because it's determining a value?

property is a <emu-val>String</emu-val> whose value is the
concatenation of “set ” and the identifier of the attribute.
1. Perform the actions listed in the description of |attribute| that occur on setting, on
|O| if |O| is not <emu-val>null</emu-val>.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as for the hanging conditional in the getter, here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this is pretty clear as-is, but we could do a different version perhaps as a follow-up if you have a proposal.

@tobie
Copy link
Collaborator

tobie commented Jan 19, 2017

A few nits and a few questions. LGTM otherwise.

@tobie tobie merged commit 4c9c298 into gh-pages Jan 20, 2017
@bzbarsky
Copy link
Collaborator

I'm really sorry for the horrible lag here. I finally had a chance to read through these changes, and they look great. There is one preexisting issue that I filed as #271

@bzbarsky
Copy link
Collaborator

Oh, and filed https://bugs.chromium.org/p/chromium/issues/detail?id=683310 for Blink not doing PutForwards per spec.

@bzbarsky
Copy link
Collaborator

And https://bugzilla.mozilla.org/show_bug.cgi?id=1332713 on updating Gecko to these changes.

@bzbarsky
Copy link
Collaborator

Also filed spec bugs on existing [SameObject] promises:

@bzbarsky
Copy link
Collaborator

And w3c/testharness.js#232 on idlharness no longer matching the spec now. And filed w3c/testharness.js#233 on what's left to do there.

@annevk
Copy link
Member

annevk commented Jan 31, 2017

It seems https://fetch.spec.whatwg.org/ was missed. It uses [SameObject] for trailers, a feature that isn't implemented yet.

@annevk
Copy link
Member

annevk commented Jan 31, 2017

Filed whatwg/fetch#473.

@bzbarsky
Copy link
Collaborator

Right, I only filed bugs on the things I found by disallowing [SameObject] in Gecko's IDL and seeing what failed...

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

Successfully merging this pull request may close these issues.

4 participants