-
Notifications
You must be signed in to change notification settings - Fork 165
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
[wip] First pass at adding internal slots #495
Conversation
Otherwise +1 to this, this fulfills my desires! |
So we agree that this would only be for custom slots, right? i.e. the following snippet would have both a
So I'm not really sure whether initial values are useful or not. But if we think so, then the idea would be to add them to both slots and attributes:
|
Yes, exactly. I just want all the information that's meant to be attached to an object to be together in its IDL definition; it makes the shape of the object much more obvious.
Okay, but I don't think either should have it. As I said before, the only things you can set in WebIDL syntax are IDL primitives, and it's not all that common for the value of slots (custom or attr-based) to be primitives. |
|
Thanks, @annevk!
I feel like that's an implementation detail, but I'm not hostile to adding a dedicated extended attribute if there's consensus for that.
I agree on all counts. This is also something we can add on at a later stage.
Not sure what you mean by that.
Agreed.
That's interesting. I need to let it sink in a bit. Maybe "this" or your alternative proposition here can be added separately if we're not sure of them yet. |
Especially as this grows extra complexity such as IDL syntax, [NoSlot], default values, etc. I become increasingly convinced this does not pay for itself. Introducing What would change my mind is implementations stating that they would use this information in the IDL to help generate good code. But I find this quite unlikely given how current code generation works. In short, I don't think this is an improvement over what we're doing today. |
I'm not quite sure, as an implementor to what extent I would end up generating slots based on IDL information anyway. If I'm generating the entire implementation (e.g. this is what Gecko does with a number of Event subclasses), then it makes sense to generate internal slots, of course. Right now we do this precisely by generating slots for all the attributes. This only works for cases in which there is nothing interesting going on with the slots other than values being stored in the constructor and then read and maybe set via attributes. But there are lots of cases in which whether there's a slot is an implementation decision. Let me give one "simple" example. In Gecko, all three of "local name", "qualified name", and "tagName" are computed ahead of time and stored on the element. But they're not really stored in slots on the element itself, in implementation terms. What's stored in a slot is a pointer to a shared data structure (shared across all elements with the same namespace/prefix/localName in the element's node document) that stores all these bits. That means we compute them once on a per-document basis (instead of the spec's per-get computation, getters are fast, etc. This all works because all of this state is immutable. But nothing requires things to work this way. A different implementation could make different tradeoffs here in terms of what's stored in slots and what's computed and what's shared and so forth. |
2¢: I really like the idea of listing all the slots in the I'd like specs to define the type of all of their slots. When the slot is reflected by an attribute, it makes sense to use a WebIDL type for the slot. But lots of slots have types that aren't representable by WebIDL. For example, |
So just to clarify, the 3 options @jyasskin refers to above were discussed on irc and are:
To which I'll add @annevk's option:
|
I hate (3) for the same reason @jyasskin said. I prefer (1) or (4) (4 being just an optimization on 1), but I could live with (2) if necessary.
Yes, strongly agree, and having a "type" that means it's defined in prose instead sounds good. |
The talk about types makes me further think these should not be in the IDL syntax, which has its own type system, but in a separate table, which can use the spec/infra type system. |
I don't think IDL's type system should be different; it's a subset. When IDL says "USVString" that should convert a JavaScript string to a scalar value string (and vice versa when used for a return value). "ByteString" should convert a JavaScript string to a byte sequence (and a byte sequence to a JavaScript string for the return value). I don't think it should be separate. I want the class/interface description to take care of these so it's clear they're allocated when the class/interface is (and IDL can say as much). Describing each object with two pieces of data would still allow for that, but it gets messier as you first have to do some kind of "find the other bits of data" step. We should make it clear that implementations can have different strategies and use pointers to share slot data and such, but that's already the case for all algorithms. I don't think that takes away that specifications should strive for clarity and a single object description provides that. |
Ehhhhhh, I just really want this to be as easy as possible for people, so I can build it into Bikeshed automatically. We have too many people accidentally referring to public APIs in spec text, and requiring them to write up a totally separate thing that's non-obvious but looks like it should be an automatic obvious sort of thing (that is, a You're right, tho, about types - I dunno what I was thinking, since "I want to declare types in IDL" is exactly opposed to "I don't think we can generally put default values in IDL". Typing is nice, but it's not compatible with the current mix of IDL and Infra values. |
Can we prioritize this? The spec as written seems fine to me; maybe adding |
I've observed accidental exposure of public APIs in early draft specifications due to the lack of this feature as well. What was the motivation for creating all of these implicit slots, and then needing a As for type system, I like the idea of keeping these internal slots untyped (matches current usage). Could we make the ECMAScript undefined value the default (matching current usage), or would this be too big of a layering violation given how we don't have Minor: ECMAScript switched to internal slots all starting with a capital letter. Now, lower case internal slot names look ugly to me. I'm fine with using lowercase if others prefer it, though. |
The reason to not require them all to be explicit is that literally almost every single attribute needs a slot. Almost every attribute is a getter/setter pair that manipulates the internal slot; the only exceptions are the rare "weird" attributes that map to more complicated stuff like (And in the implicit-slot world, |
I wanted attributes to be somewhat automatically backed by slots for these cases:
I don't really want to block on this though, having shared understanding and a consistent editorial policy around private state is much more important to me. |
I don't think the percentage of slot-backed attribute is as high as 95+%. Especially if you take into account special setter logic that isn't just "set to the value", but also performs some state-checking or validation. That said, it's probably over 50%. (We'd need to do a survey to be sure.) My preferred way of threading this needle would be that slots should be explicit, but it should be easy to auto-generate getters (and optionally setters) from the slots. For example
(Lots of other permutations of these possible; these are intentionally not consistent to illustrate how there are lots of choices.) My general feeling is also opposite @annevk's; I don't think there's any value in creating yet another convention for private state, and hoping people use it, without adding some code/spec-generation benefits at the same time, of the sort listed in his bullet points. Just relocating the same information from |
Another possible idea, similar to Domenic's above: slots are generally declared separately, but there is a special construct to declare a slot and a attribute for it together, e.g.:
The idea here would be that the public attribute name is derived from the internal slot name, rather than the other way around. This would be somewhat analogous to certain possible JS decorator idioms (e.g., Anyway, seems like this will take more discussion, and it'd be worth landing the part about |
Having to do extra stuff isn't relevant to my point. In all those cases, the definition of the slot would be absolutely identical boilerplate, endlessly repeated. Much better if Bikeshed just does it for you automatically, so can jump straight into referring to it. The % of attributes which don't use anything like a slot at all, and instead are purely backed by getter/setter code, is very small. Just look at any random spec. Note that I'm not implying any guarantees about what's in the slot. Just that most attributes store/retrieve something from private storage on the instance. The % of attributes that do nothing but store/retrieve an IDL value from an internal slot is likely high enough to be worth automatically addressing, but that's, I think, a larger discussion. As per earlier discussion, having most slots actually written in the IDL is... weird. As stated by others earlier, slots don't necessarily have the same types as attributes; they can take Infra types too. I'd be okay with a very low-information way of indicating additional private slots (beyond what's used just to back corresponding attributes). For example, in https://drafts.csswg.org/css-font-loading/#fontface-interface, all of the attributes have trivial backing slots, just storing/retrieving an IDL value. In addition, the interface has
would be nice, as it (along with all the implicit attribute backing slots) tells you precisely what data will be associated with the object right there in the IDL, rather than requiring digesting all the associated prose. |
2c: it would be great if if slots have a type information associated with them. |
@marcoscaceres could you elaborate? We discussed that upthread I think and the conclusion was that since we don't have a unified type system (yet) that would not really be possible to do. |
Sure, my experience has been that internal slots generally only hold booleans, strings, promises, dictionaries, or other IDL types. For example, generally speaking list<string> [[serializedMethodData]]
list<string> [[serializedModifierData]]
object [[details]]
PaymentOptions [[options]]
string [[state]]
boolean [[updating]]
promise [[acceptPromise]]
PaymentResponse [[response]] Which are either IDL type or infra types. Ideally, I'd like to just declare them upfront in the IDL. So I guess part of the ask is for us to try to unify on the infra types as primitives. Thus, would really like something like this: interface PaymentRequest : EventTarget {
slot list<string> [[serializedMethodData]];
slot list<string> [[serializedModifierData]];
slot object [[details]];
slot PaymentOptions [[options]];
slot string [[state]];
slot boolean [[updating]];
slot Promise [[acceptPromise]];
slot PaymentResponse [[response]];
/* good old IDL attributes, operations, etc. */
}; |
I really like this part of @domenic's suggestion: interface Whatever {
readonly attribute DOMString foo <- [[foo]];
} Which would give It should be possible to do things like: interface Whatever {
[EnforceRange] unsigned long long [[bar]];
} In this case interface Whatever {
[[thing]];
} In this case, The distinction I want from slots (maybe this is stating the obvious) is that the values may not be changed/mutated from another thread. |
@lukewagner's proposed evolution of WebAssembly/WebIDL bindings, presented in the June 2019 Wasm CG F2F, points a path towards a new, stronger specification-internal type system that we might reference here. However, I'm not sure if we can consider WebIDL types or infra structures as a formal enough type system to use here just yet. |
Throw how? Slots are only interacted with in spec prose, not by author code. |
Spec prose can also throw. Eg step 2 of https://dom.spec.whatwg.org/#selectors. |
Let's close this and keep the discussion open in #258, with the closed-but-outdated PR as a good example of one direction. |
This is a rough first pass at adding internal slots and a "this" keyword to WebIDL.
I originally intended to write a design doc, but thought it would be easier to work directly in the spec adding comments as issues where needed.
I think this captures the key parts of the discussions that happened in #258 and on https://www.w3.org/Bugs/Public/show_bug.cgi?id=27354.
High-level comments more than welcomed.
TL;DR:
this
keyword in WebIDLfoo.[[slot]]
).Preview | Diff