| 01:00 | <Rob Palmer> | And we begin day 2! |
| 01:11 | <Michael Ficarra> | I think array iterators not closing when you call return was a mistake |
| 01:12 | <ljharb> | i think return was a mistake |
| 01:13 | <Michael Ficarra> | the generator protocol maybe, but return is fine |
| 01:16 | <ljharb> | separately, i'm not actually sure when you'd want to close the database connection - in most apps i've worked in, the database stays open and the connection is shared/pooled, and times out eventually |
| 01:33 | <dminor> | Michael Ficarra: Sorry, accidentally deleted your topic while clearing the +1s |
| 01:33 | <rkirsling> | these 1 or 2 or 2.7 presentations are a pretty amusing modern TC39 phenomenon |
| 01:35 | <snek> | that and conditional consensus |
| 01:35 | <snek> | its nice we have ways to speed things up though |
| 01:35 | <rkirsling> | conditional consensus is good efficiency |
| 01:37 | <Justin Ridgewell> | @bakkot Can you link your async iterator slides when you’re done |
| 01:38 | <mgaudet|travelling> | Dumb question: Where is CreateAsyncIteratorFromClosure defined? (re: async interator helpers-- it links to the main spec where I can't find it) |
| 01:39 | <Michael Ficarra> | @mgaudet|travelling https://github.com/tc39/ecma262/pull/3628 |
| 01:40 | <mgaudet|travelling> | thanks :) |
| 01:44 | <waldemar> | What happens if you get an infinite loop inside a spec function? Is there any defined behavior? |
| 01:45 | <Richard Gibson> | I guess the algorithm would just never terminate until the host process does |
| 01:46 | <snek> | i don't think any spec function current or proposed lets you get into a true infinite loop, they all predicate on user code |
| 01:51 | <bakkot> | sorry the async iterators item was so incoherent, I'd meant to do prep this weekend but I was/am down with some sort of horrible virus and am not able to think clearly enough for reasoning about concurrency code |
| 01:51 | <bakkot> | No special casing for infinite loops; the spec technically just says you have to keep doing it forever |
| 01:52 | <bakkot> | this is part of its general refusal to admit any resource constraints |
| 01:52 | <Richard Gibson> | maybe, but not if we introduce a built-in infinite iterator |
| 02:03 | <Christian Ulbrich> | forgive me for my naiivety (or for the lack of knowing the proper spelling), but composites are tought to be immutable, aren't they? |
| 02:03 | <nicolo-ribaudo> | Yes |
| 02:03 | <nicolo-ribaudo> | Shallowly |
| 02:04 | <Christian Ulbrich> | Shallowly? |
| 02:04 | <nicolo-ribaudo> | They can contain mutable stuff |
| 02:04 | <snek> | is implementation concern the only factor for interned vs unique? or are there other constraints? |
| 02:04 | <Christian Ulbrich> | c1.wallet.amount is not immutable? |
| 02:04 | <bakkot> | correct |
| 02:04 | <nicolo-ribaudo> | You can make wallet a composite too |
| 02:05 | <bakkot> | can but shouldn't, wallets have identity |
| 02:05 | <bakkot> | my wallet is not your wallet just because we both have no money |
| 02:05 | <Justin Ridgewell> | @keith_miller Is spec convienence causing real slowdowns for TAs? |
| 02:07 | <Christian Ulbrich> | so ```
|
| 02:07 | <snek> | i would hope that composites are never mutable |
| 02:07 | <snek> | in either case |
| 02:07 | <nicolo-ribaudo> | No, those two wallets are different objects, so the composites are different |
| 02:07 | <Justin Ridgewell> | You need nested composites, right? |
| 02:07 | <nicolo-ribaudo> | But these would be the same:
|
| 02:08 | <Justin Ridgewell> | The alternative is wallet = { amount: 20 }; c1 = Composite({ … Nicolo beat me to it. |
| 02:12 | <keith_miller> | Yeah, 100% |
| 02:12 | <Christian Ulbrich> | Mathieu Hofman: We also have current custom Iterability with Symbol.iterator. |
| 02:12 | <bakkot> | please do not look at the current spec, it means nothing |
| 02:12 | <bakkot> | I should remove it |
| 02:12 | <Justin Ridgewell> | @Olivier Flückiger Does V8 have this same problem? |
| 02:12 | <keith_miller> | In particular because you have to do n shape checks for each of the different views |
| 02:12 | <Justin Ridgewell> | Should we be splitting the methods onto the sepcific TA subtype classes? |
| 02:13 | <Mathieu Hofman> | That is ok-ish. The place it shows up in syntax (array spread) was a mistake |
| 02:13 | <keith_miller> | Sometimes it will get inlined enough that we can prove only one shape shows up but there's many examples I've seen where that doesn't happen |
| 02:14 | <snek> | my memory of v8 is that it generally does a switch over different object kinds. i guess that could be compiled to a bunch of if/else branches but hopefully it does better than that |
| 02:15 | <snek> | which is certainly still some overhead, but its not a function of the number of things you're switching over |
| 02:15 | <Christian Ulbrich> | Mathieu Hofman: Array.prototype[Symbol.iterator] = function* () { yield 2; yield 3; } |
| 02:15 | <keith_miller> | The problem is that you also need to make the index access polymorphic |
| 02:15 | <keith_miller> | So it's not just a check you're in bounds and load |
| 02:16 | <keith_miller> | It's a switch over the element size then pick the right load for the active size |
| 02:16 | <snek> | yeah it remains polymorphic until it can be found to be exactly 1 kind |
| 02:16 | <keith_miller> | Right but that wouldn't be a problem if each of the views had their own copy of the various methods |
| 02:17 | <Christian Ulbrich> | Mathieu Hofman: What I am trying to say, and I am leaning towards some, what I think Ron said, custom equality is not a bad thing per sè. We already have so many things in the language, that are powerful. |
| 02:17 | <Justin Ridgewell> | Let’s fix that. Doing something for convenience in the spec is much less important that actual impl difficulties. |
| 02:18 | <snek> | its definitely web reality at this point |
| 02:18 | <keith_miller> | There's some compatibility risk although I don't know how high it is in practice |
| 02:18 | <snek> | i've seen code in the wild using TypedArray.prototype.foo.call |
| 02:18 | <snek> | actually i guess that doesn't actually preclude adding more methods on the subclasses |
| 02:19 | <keith_miller> | Maybe you could just shadow the current one |
| 02:19 | <keith_miller> | It would be weird but I doubt anyone would really know |
| 02:20 | <snek> | we did a similar change moving around where some error properties were located in webkit and chrome and no one noticed |
| 02:20 | <Mathieu Hofman> | That's just prototype pollution. Which is a much more general problem. But yeah you could have an array instance with an shadowed Symbol.iterator that does something weird, and I don't like that array spread would be affected by that. |
| 02:21 | <bakkot> | I think it is not weird to get custom behavior when spreading an object which happens to be IsArray but which has a custom Symbol.iterator |
| 02:21 | <bakkot> | as to the Array prototype Symbol.iterator pollution problem, I have a proposal for fixing that! |
| 02:22 | <snek> | ah actually it was the opposite direction https://github.com/WebKit/WebKit/commit/96efcf1f1c011efd5946d053272581e0a3d6c3b2 (my only webkit commit 😄) |
| 02:35 | <Olivier Flückiger> | What exactly is the spec convenience we are talking about? That they all share a common proto? |
| 02:35 | <Christian Ulbrich> | Looks like, native equality comes quite at some cost, of those interned objects not being so native anymore... |
| 02:39 | <Justin Ridgewell> | Yah, and the methods are defined on the common proto |
| 02:40 | <Justin Ridgewell> | Though apparently index access is also slower than it should be |
| 02:43 | <Christian Ulbrich> | Would this be a first for the language? Is there any object, that is specially handled somewhere, somehow, besides being an object, I mean apart from null? |
| 02:43 | <Olivier Flückiger> | It's probably not our main worry for TA... But in principle all for not doing convenience in the spec if it makes implementation harder. |
| 02:46 | <snek> | also worth mentioning that a lot of TA baggage comes from khronos, not necessarily people making convenient spec decisions :P |
| 02:48 | <ljharb> | document.all |
| 02:49 | <Justin Ridgewell> | khronos? |
| 02:49 | <ljharb> |
(google ai summary) |
| 02:50 | <snek> | yes they were developed for webgl and later merged into ecma262 |
| 02:50 | <rkirsling> | that's the origin of all the detachedness f-ery |
| 02:51 | <Christian Ulbrich> | ljharb: In what context? This is a DOM API. |
| 02:52 | <Justin Ridgewell> | https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-IsHTMLDDA-internal-slot |
| 02:52 | <Justin Ridgewell> | “Is HTML Document Dot All" |
| 02:52 | <bakkot> | In the case ljharb describes, where developers know about weirdness or already have something wrong, throwing seems better? |
| 02:53 | <snek> | throw as early as possible |
| 02:53 | <bakkot> | Like I cannot imagine wanting to leak here, on purpose |
| 02:53 | <bakkot> | If someone doesn't know about this they should learn by the language telling them |
| 02:53 | <bakkot> | Not by mysterious leaks |
| 02:53 | <ljharb> | i mean an alternative would just be, require composites to have at least 1 object |
| 02:54 | <bakkot> | That is also terrible |
| 02:54 | <ljharb> | why? |
| 02:54 | <snek> | because it breaks them |
| 02:54 | <ljharb> | no existing code uses composites, how does that break anything |
| 02:54 | <bakkot> | Because you want to be able to use them for, like, pairs of numbers |
| 02:54 | <snek> | if you want a composite of primitive types you then need to keep around some special identity object to put in them |
| 02:54 | <bakkot> | Coordinates in a map, eg |
| 02:54 | <ljharb> | make one symbol and stick it in every composite |
| 02:55 | <bakkot> | That is so bad |
| 02:55 | <Christian Ulbrich> | I find waldemar argument convincing. If we have prior art of handling something specifically (although I think the document.all to be very exotic (hihi)), then I'd rather prefer a fail-fast approach, then having to accept memory leaks. |
| 02:55 | <ljharb> | imo it's far less bad than making some objects not be weakable |
| 02:56 | <bakkot> | The benefit of keeping the consistency for weak maps is so, so far from being worth the cost of having to do that |
| 02:56 | <rkirsling> | document.all is like, one of the most horrifying things in the web platform, to be certain; that's why [[IsHTMLDDA]] is a sort of resentment-coded name |
| 02:56 | <bakkot> | As you already pointed out, weak maps are super niche! |
| 02:56 | <ljharb> | to me this all seems to lead back to "they need to be primitives or they shouldn't exist in the language" |
| 02:57 | <ljharb> | we shouldn't commit minor crimes just because the thing we want to add shouldn't be an object but is forced to be one |
| 02:57 | <bakkot> | Ok that is a position you could have, but at that point this question does not matter |
| 02:58 | <snek> | i don't find weakmaps to be the fundamental primitive by which i decide what a value is. rather weakmap is a somewhat strange api /because/ it exposes the reality of values. |
| 02:58 | <bakkot> | We are talking about what happens assuming that isn't the world we're in |
| 02:58 | <Christian Ulbrich> | I have a feeling, that the discussion is very academic. What would be a real-world library using WeakSets that would be easily affected by this? |
| 02:59 | <ljharb> | also what would be a real-world application where this would be the biggest memory leak in the app |
| 02:59 | <rkirsling> | kinda feel sorry for Yusuke's queue item getting shoved to the bottom |
| 02:59 | <snek> | fwiw most use of weak apis do not deal with arbitrary values, they are almost always contained within a single layer of a codebase |
| 02:59 | <Chengzhong Wu> | why is Yusuke's topic kept pushing down the queue? feel like new topics are added before Yusuke's |
| 03:00 | <bakkot> | Next time we talk about this I vote implementations get priority |
| 03:01 | <Olivier Flückiger> | We should really not add objects that cannot be collected. Websites running out of memory is really a terrible issue to debug for all parties involved. |
| 03:01 | <bakkot> | I care a lot more about what is implementable than about our design opinions unconstrained by implementation reality |
| 03:07 | <bakkot> | it is easy to imagine, for example, something which is serializing values to be passed to and from a worker, and uses a WeakMap to cache the serialization, with the serializations happening very frequently (it's a common pattern when splitting some computations to be done off the main thread, for example), and then it starts passing Composites some of the time, and now it is leaking a lot |
| 03:08 | <bakkot> | that is an unusually easy example of a leak to track down, and is also very much a thing which would happen with totally normal use of WeakMaps |
| 03:08 | <bakkot> | throwing is much, much better here, so that the developer learns that actually WeakMaps do not work for Composites. |
| 03:41 | <Aki> | Congratulations to eemeli and Ashley Claymore for being the only speakers yesterday—including myself—who made sure the appropriate info was recorded in the notes. |
| 03:43 | <Aki> | bakkot, ryzokuken , ptomato , Mikhail Barash , FYT, nicolo-ribaudo , Richard Gibson , dminor , Michael Ficarra , ljharb , and James M Snell : here's what's missing https://kdrive.infomaniak.com/app/share/1777368/c4d6dca3-6847-4674-bf1d-87d2d0370871 |
| 03:45 | <bakkot> | fwiw speakers generally aren't the ones who add the link to the slides, I don't typically check for that after having done my presentation |
| 03:46 | <bakkot> | (usually note takers do) |
| 03:46 | <Aki> | Yeah the notes are in really rough shape this meeting. |
| 03:46 | <bakkot> | anyway added slides link to editor update item |
| 03:47 | <Chengzhong Wu> | sometimes an agenda item was added without a link to slides. In this case, note takers are not able to add the link for the speakers in the notes. Please add the link after done the presentation |
| 03:54 | <ljharb> | what on earth is kdrive, i was so confused when i wasn't logged in to google sheets and then i realized it wasn't sheets |
| 04:04 | <Ashley Claymore> | it jumps to labels: https://github.com/v8/v8/blob/638e32793f854790284bf613eca204550e1c8e56/src/builtins/builtins-collections-gen.cc#L211-L215 |
| 04:06 | <snek> | that's just a single branch though. i was referring to these more: https://github.com/v8/v8/blob/638e32793f854790284bf613eca204550e1c8e56/src/builtins/builtins-typed-array-gen.cc#L546-L601 |
| 04:06 | <Mathieu Hofman> | One motivation for them to be objects is to allow them to contain mutable objects. That is not acceptable as primitives |
| 04:07 | <Ashley Claymore> | that leaks in a worse and more subtle way. Which is why I don't want the answer to be: every composite must contain something non-forgable |
| 04:08 | <Mathieu Hofman> | Membrane style libraries that keep an association from any object to another object they created. We also have a classification library where we'll check the shape of a deeply frozen object and remember the classification. |
| 04:09 | <Ashley Claymore> | I requested during my presentation that we keep the conversation to the slides, and not extend the conversation to a wider conversation. I would rather do that in a separate meeting that specifically talks about that - with prepared slides to reference. |
| 04:12 | <bakkot> | it should still be called Semaphore 💢 |
| 04:13 | <rkirsling> | it seems like Governor would be a confusingly disconnected name if it's located right on globalThis but not sure whether that's the intent |
| 04:14 | <rbuckton> | I think Semaphore is the right name, but only if its API is usable both for async coordination and thread coordination (with shared structs), which is certainly feasible. |
| 04:20 | <bakkot> | I think the only thing necessary to make it work for thread coordination is for it to be structured-cloneable? |
| 04:21 | <bakkot> | with the internal state not being shared not cloned, obviously |
| 04:21 | <bakkot> | I guess the spec would require some atomics stuff, probably |
| 04:21 | <rbuckton> | No, it wouldn't be structured clonable, it would be shared (just like Mutex, Condition) |
| 04:21 | <rbuckton> | Structured cloneable isn't shareable |
| 04:24 | <rbuckton> | Ideally it would be the same kind of object as we want Mutex to be (shared with a per-realm prototype). |
| 04:24 | <snek> | structured cloneable includes shared values |
| 04:25 | <rbuckton> | Structured clone requires postmessage. Shareable only requires assignment |
| 04:25 | <rbuckton> | You can't assign a structured clonable object to a property on a shared struct, only primitive and shareable values |
| 04:26 | <snek> | oh you are referring to a new category of value based on shared struct ok |
| 04:26 | <rbuckton> | Yes. |
| 04:27 | <bakkot> | sorry, I meant prior to shared structs existing |
| 04:27 | <Mathieu Hofman> | You could make it structure clonable, and when we have shared struct redefine is as a shared object, the distinction is not observable without shared structs anyway |
| 04:27 | <rbuckton> | Mutex and Condition both fall under this category and are essentially shared structs. |
| 04:27 | <bakkot> | we need inter-thread coordination even without shared structs |
| 04:27 | <bakkot> | and this proposal will probably go forward before that one |
| 04:27 | <rkirsling> | oh, I see "resource governor" is a SQL thing, so that must be the origin of this terminology? |
| 04:28 | <rbuckton> | IMO we shouldn't make it structured clonable, we should make it frozen and specially handled in postMessage. |
| 04:28 | <snek> | fwiw structured cloneable is a superset of basically everything, except host resources that explicitly can't support it |
| 04:28 | <rbuckton> | That would allow us to hedge |
| 04:28 | <rbuckton> | The difference is that something structured clonable will create a new instance in another worker thread. A shared thing is the same instance in another thread. |
| 04:29 | <Ashley Claymore> | https://github.com/v8/v8/blob/638e32793f854790284bf613eca204550e1c8e56/src/builtins/builtins-collections-gen.cc#L1887-L1898 has more branches - haven't looked at exactly what this compiles to - if it's a jump table |
| 04:29 | <snek> | is SharedArrayBuffer a sharable value? |
| 04:30 | <rbuckton> | No |
| 04:30 | <rbuckton> | Unfortunately |
| 04:30 | <Andreu Botella (🕑 JST, at TC39)> | SharedArrayBuffer is cloned, i.e. posting it back and forth doesn't preserve equality, but it points to a shared internal thing |
| 04:30 | <snek> | well then we can make up whatever rules we want in the structured clone algo for "shared" values |
| 04:31 | <snek> | but yeah i see your point |
| 04:32 | <rbuckton> | Also, Shared Structs is already at Stage 2. Its delays have been mostly been as a result of both champions having major job changes in the past few months. |
| 04:33 | <Mathieu Hofman> | Again I maintain that having an object being sharable over postMessage doesn't prevent it from also becoming directly a shared object |
| 04:33 | <rbuckton> | I'm hoping to dedicate more time to getting it to Stage 2.7 in the coming year, though ideally I would like someone from V8 to take on @syg's co-champion role as he's indicated he is unable to participate regularly going forward. |
| 04:33 | <Mathieu Hofman> | (except maybe for all the exotic SAB stuff) |
| 04:34 | <Mathieu Hofman> | rbuckton: you might want to chat with Olivier Flückiger |
| 04:35 | <rbuckton> | Minor point: The audio coming from the room is very quiet compared to those joining remotely, so I either can barely hear the room or my speakers are blown out by remote attendees depending on who I adjust the audio for. |
| 04:36 | <bakkot> | ... huh, audio's fine for me |
| 04:37 | <rbuckton> | The issue with structured clone is that a value going from thread A to thread B and back to thread A will not produce the same instance. |
| 04:37 | <bakkot> | Seems fine? |
| 04:37 | <bakkot> | I don't actually see a reason why that would matter for this. |
| 04:37 | <rbuckton> | That isn't what happens for shared structs. |
| 04:37 | <bakkot> | ... OK? |
| 04:37 | <rbuckton> | So if you change it from being cloneable to shareable, that's an observable change |
| 04:37 | <snek> | i don't particularly care what structured clone does as long as i get some sort of native shared state out of it, like SAB |
| 04:38 | <Rob Palmer> | New topic: how is the quality of the original transcription for the notes today? We had quality drop-off at 16:15 yesterday. |
| 04:38 | <Mathieu Hofman> | how ? |
| 04:38 | <bakkot> | doing fine so far |
| 04:39 | <rbuckton> | Correct me if I'm wrong, but you can't currently rely on reference equality for something like SAB bouncing between two threads, can you? |
| 04:39 | <snek> | you can not |
| 04:40 | <snek> | and i don't care about it maintaining reference equality |
| 04:40 | <rbuckton> | You can rely on reference equality for this case with Shared Structs |
| 04:40 | <snek> | i care about it for shared structs, sure |
| 04:40 | <rbuckton> | I care about something like Semaphore behaving like a shared struct, so maintaining reference equality. |
| 04:41 | <rbuckton> | If it starts out behaving like SAB, we have to be ok with changing that behavior later. |
| 04:41 | <snek> | yeah i have no strong opinion over whether it behaves like a shared struct or not |
| 04:42 | <snek> | either way it can be shared between threads somehow |
| 04:42 | <snek> | that's good enough for me |
| 04:42 | <rbuckton> | I think we can work out a solution that hedges towards availability of shared structs. |
| 04:42 | <Mathieu Hofman> | right but if you admit there can be multiple objects identities that are backed by the same internal thing, all these objects could be shared, it's just you cannot rely on identity to differentiate them, which I don't think is a requirement? |
| 04:43 | <rbuckton> | I think you need to be able to rely on identity. |
| 04:43 | <Mathieu Hofman> | what's the use case? |
| 04:43 | <rbuckton> | We can't do SAB because it has quirks like this. |
| 04:43 | <bakkot> | sorry, who's talking? for the notes |
| 04:44 | <Chris de Almeida> | Oli |
| 04:44 | <James M Snell> | bakkot: +1 on everything there with AbortSignal... if you want some help on getting that proposal going in WHATWG, I'm more than happy to help |
| 04:44 | <rbuckton> | Not having Semaphore behave differently than Mutex when writing code. It's a major quirk that you would run into regularly. |
| 04:44 | <bakkot> | note, thanks, will probably take you up on that |
| 04:45 | <rbuckton> | IMO, with structs at Stage 2 we should renew the effort to get that proposal to Stage 2.7 and perhaps we won't run into a timing issue. |
| 04:45 | <jkup> | I'm missing the context around the can of worms / current issues with the state of AbortSignal, is there anything good I can read to get up to speed? |
| 04:46 | <rbuckton> | Or we ship this proposal without CountingGovernor since it can be implemented in userland until such time as both proposals have hit stage 4. |
| 04:46 | <bakkot> | there's a bunch of things, some of it here: https://matrixlogs.bakkot.com/WHATWG/2025-09-25#L18 |
| 04:47 | <rbuckton> | I can discuss some of my concerns at length after the plenary, if you'd like. |
| 04:47 | <bakkot> | just negate all of those bullets and that's how it currently works |
| 04:47 | <danielrosenwasser> | Is it just me or do people have wildly different mic volumes today? |
| 04:47 | <rkirsling> | that was blastingly loud, yeah |
| 04:47 | <Chris de Almeida> | not just you |
| 04:47 | <Mathieu Hofman> | rbuckton: I understand how identity preservations is important for actors, but these seem like very specialized tools purely meant to implement low level synchronization, and I fail to understand the use case where identity preservation matters here. I do understand it may be beneficial to in the future share these tools over existing shared object instead of postMessage |
| 04:47 | <danielrosenwasser> | yeah, but a lot of the presenters have had extremely low mics today |
| 04:47 | <rbuckton> | bakkot: How is your audio OK? I can barely hear michael with my audio set to a level where Steve Hick's audio doesn't make my ears bleed. |
| 04:48 | <bakkot> | maybe my computer or browser is doing some equalization? idk |
| 04:48 | <snek> | can you adjust audio per-person? |
| 04:48 | <rbuckton> | Not that I can find |
| 04:49 | <James M Snell> | I think there's a fair number of folks potentially interested in this discussion. Maybe organizing a call would be worthwhile? |
| 04:50 | <Jake Archibald> | bakkot: I'm interested in the abort controller stuff |
| 04:51 | <Jake Archibald> | (I'm the original author of the abort controller spec. pls don't hate me) |
| 04:51 | <nicolo-ribaudo> | Me too |
| 04:52 | <bakkot> | I don't hate you, I just wish you'd given userland the nice behavior that the web platform gets, is all |
| 04:52 | <bakkot> | users deserve nice things too |
| 04:52 | <Mathieu Hofman> | Kris Kowal has opinions too |
| 04:53 | <Jake Archibald> | bakkot: is the behaviour gap documented anywhere? |
| 04:53 | <Ashley Claymore> | I just wish AbortSignal wasn't an EventTarget. |
| 04:53 | <Ashley Claymore> | But I also use it on the web all the time and thanks Jake Archibald for adding it! |
| 04:53 | <bakkot> | lots of random griping on the web, but the best place I've got to hand is https://matrixlogs.bakkot.com/WHATWG/2025-09-25#L18 |
| 04:54 | <bakkot> | specifically, for abort listeners added from web machinery instead of userland, you have these things:
users consumers of AbortSignal don't get any of those |
| 04:54 | <James M Snell> | Grab the pitchforks everyone... we know who to chase down |
| 04:54 | <rbuckton> | I'll admit I've had a number of gripes (some vocal, some not so) with the AbortController API, but my biggest frustration was that it felt very rushed out while I was in the middle of trying to get cancellation into the core language. |
| 04:55 | <James M Snell> | I'll put together a doodle poll early next week and post it to get a call set up |
| 04:55 | <snek> | if we add EventTarget and DOMException to 262 this becomes a very simple problem to solve |
| 04:55 | <ljharb> | what i wish is that abortsignal hadn't been shipped before notifying TC39 that it was happening :-/ |
| 04:55 | <Michael Ficarra> | https://en.wikipedia.org/wiki/Governor_(device) |
| 04:55 | <rkirsling> | whoa |
| 04:55 | <rbuckton> | That's precisely one of my gripes. |
| 04:56 | <Jake Archibald> | rbuckton: interesting. I was told at the time that cancellation primitives has been abandoned in TC39. Hm. |
| 04:56 | <bakkot> | I refuse to write a standard library function which has as one of its steps "and then call .addEventListener('abort', fn)" |
| 04:57 | <bakkot> | even DOM consumers of AbortSignal don't stoop to that |
| 04:57 | <rbuckton> | Certainly not. See https://github.com/tc39/proposal-cancellation |
| 04:58 | <danielrosenwasser> | I feel like keyCount reads less awkwardly than keysLength. |
| 04:59 | <ljharb> | that's a reasonable thing to bikeshed within stage 2 :-) |
| 04:59 | <rkirsling> | or generally xsLength -> xCount |
| 04:59 | <ptomato> | sign me up for the abort call plz 😄 |
| 04:59 | <Justin Ridgewell> | Promise cancellation as a 3rd state was abandoned, not cancellation |
| 05:00 | <Ashley Claymore> | do web-machinery things also remove their listener once their task has finished? A thing I've come across is: long lived abortSignal with lots of shortLived user tasks and it leaks listeners |
| 05:00 | <bakkot> | yes |
| 05:00 | <danielrosenwasser> | I think getOwnPropertyNameCount is actually worse 😅 |
| 05:00 | <rbuckton> | My major concerns with AbortController are:
|
| 05:00 | <bakkot> | see https://dom.spec.whatwg.org/#run-the-abort-steps |
| 05:01 | <James M Snell> | I'm gonna totally lose track of everyone interested in this chat. I'll post the doodle link likely on monday and will send invites to anyone who responds |
| 05:01 | <bakkot> | "Empty signal’s abort algorithms." is the bit which removes existing web-machinery listeners |
| 05:01 | <Jake Archibald> | bakkot: The reason AbortSignal uses events is just because that's kinda the standard on the web platform for callbacks. But given there are other benefits to avoiding events, your proposal seems reasonable |
| 05:01 | <eemeli> | Almost always when I find a need to call Object.keys(...).length, all I really want to know is whether I have an empty {} object, or something else. |
| 05:01 | <bakkot> | or, wait, I think I read your question backwards? |
| 05:02 | <bakkot> | I don't actually know but I don't think it's observable, because the listeners are purely internal |
| 05:02 | <rbuckton> | Can you imagine if DOM shipped Future using DOM events instead of then? IIRC, that wasn't even on the table for Future before it was adopted by 262 as Promise. |
| 05:02 | <Christian Ulbrich> | What is there to be gained? Is this really totally un-optimizable for implementers, if it such a common pattern? |
| 05:03 | <Ashley Claymore> | I kinda want abortSignal listener add to have Symbol.dispose |
| 05:03 | <Mathieu Hofman> | TCQ doesn't let me add a reply |
| 05:03 | <jkup> | see Nic's clarifying Q :) |
| 05:03 | <ptomato> | hey! I hoist the options bags |
| 05:03 | <Michael Ficarra> | yeah this completely perplexes me |
| 05:04 | <Michael Ficarra> | refresh? I had to re-auth recently |
| 05:05 | <Ashley Claymore> | static interned inline composites could be safely hoisted by tools 😎 |
| 05:05 | <Jake Archibald> | bakkot:
Web specs never listen to events internally. |
| 05:05 | <Christian Ulbrich> | I mean apart from a theoretical point of view, how often is this used in critical paths? |
| 05:06 | <bakkot> | well... but like... why |
| 05:06 | <bakkot> | like if it's recognized that web platform should get to use better infrastructure... why doesn't userland get to use better infrastructure? |
| 05:06 | <Chris de Almeida> | reply button is back! 🎉 |
| 05:06 | <rbuckton> | My proposal originally had something like:
|
| 05:06 | <ljharb> | the many many use cases that ruben and i looked over were definitely not just needing that simple a semantic |
| 05:07 | <Jake Archibald> | bakkot: it's not a better vs worse thing. It's the same reason why internal methods are called rather than directly calling exposed interfaces. |
| 05:08 | <rkirsling> | this discussion as a whole feels very like, supportive of a problem statement and not a solution, hm |
| 05:08 | <bakkot> | it could be using entirely internal methods and still same the same machinery, though |
| 05:08 | <snek> | keith_miller: what was that 40% metric specifically? |
| 05:08 | <Christian Ulbrich> | so it is about counting keys, properties or symbols and wether they are enumerable or not, how about them configurable? :D |
| 05:08 | <eemeli> | Any idea what sort of percentage is testing the value against 0, vs. doing somethign else? |
| 05:08 | <bakkot> | there's no reason that AbortController needs to have a separate privileged list of web-platform things to trigger on abort, instead of just adding web-level things to the existing list of callbacks to call |
| 05:09 | <Jake Archibald> | bakkot: similar to JS, if you override globalThis.Array, that isn't called by internal JS things that create arrays |
| 05:09 | <ljharb> | the vast vast majority where it's just counting care about enumerable properties and/or symbols |
| 05:10 | <nicolo-ribaudo> | Code that goes through the optimizing compiler |
| 05:10 | <bakkot> | I'm not asking about literally doing .addEventListener, just about having only a single internal list of things to trigger on abort, instead of having two lists |
| 05:10 | <Ashley Claymore> | the one place this does happen (which is evil) is Array.prototype.toString dynamically looks up Array.prototype.join - not the original. |
| 05:10 | <Jake Archibald> | bakkot: basically, it isn't a deliberate choice to give web devs something bad and retain something good for internal use - and taking it to the WHATWG with that as an immutable assumption will get in the way of progress |
| 05:10 | <Michael Ficarra> | tip for newcomers: some people find it a bit rude to look ahead on the queue and make assumptions about what somebody is going to say (and pre-reply, putting words in their mouth) |
| 05:10 | <keith_miller> | 40% of code on internal page loads or Speedometer get to optimizing tiers yeah |
| 05:11 | <Jake Archibald> | bakkot: otherwise I understand your proposal and it seems reasonable. I'm just giving context for why things were designed as they were |
| 05:11 | <bakkot> | sorry, to be clear I didn't think it was deliberately good for web and bad for users! just that the actual effect of splitting the machinery is that it has had the consequence that the user things are stuck with something with a bunch of bad properties which the web gets to avoid |
| 05:11 | <bakkot> | yes, thank you, I appreciate the context |
| 05:12 | <Aki> | Infomaniak, swiss hosting |
| 05:16 | <rbuckton> | I ended up implementing something like this (@esfx/canceltoken, docs here: https://esfx.js.org/esfx/api/canceltoken/canceltoken.html#_esfx_canceltoken_CancelToken_class). It even theoretically works in place of AbortSignal on the web as it repurposes an AbortSignal instance with custom [[Prototype]]. |
| 05:22 | <mgaudet|travelling> | chipmorningstar: https://bugzilla.mozilla.org/show_bug.cgi?id=1914502 shallowEquals in react is one of the hottest functions, and uses this pattern |
| 05:22 | <Jake Archibald> | bakkot: It'd be nice to keep throwIfAborted(). I guess JS will need an AbortError. As long as it passes err instanceof Error && err.name === 'AbortError', that seems fine |
| 05:23 | <Christian Ulbrich> | Yeah chipmorningstar I am totally with you. And giving what nicolo-ribaudo said, I think we have to see real usages in the wild with actual numbers in critical paths. |
| 05:23 | <rbuckton> | I'd also considered source.cancel(reason) so you could cancel with a specific reason, not just AbortError. |
| 05:24 | <snek> | it can also be HostCreateAbortError() |
| 05:24 | <Jake Archibald> | rbuckton: controller.abort() has that too |
| 05:25 | <Jake Archibald> | snek: yeah that's fine, I'd just like there to be a cross-platform way to tell an 'abort' from other things. |
| 05:25 | <nicolo-ribaudo> | Can we just have a cancellation protocol (obj[Symbol.subscribeForCancellation](cb)), and have web AbortSignal do that? And the web AbortSignal uses the good algorithm for it |
| 05:25 | <nicolo-ribaudo> | And then our APIs can just use that protocol |
| 05:25 | <Jake Archibald> | rbuckton: https://dom.spec.whatwg.org/#dom-abortcontroller-abort |
| 05:26 | <rbuckton> | See https://github.com/tc39/proposal-cancellation/issues/22. Back in 2018 the answer ended up being "No" |
| 05:27 | <nicolo-ribaudo> | The more time passes without a solution happening, the more people need to relax their constraint (if we gree that want some solution to happen) |
| 05:27 | <rbuckton> | Jake Archibald: if you haven't seen it, here is the Stage 0 cancellation proposal from 2017: https://github.com/tc39/proposal-cancellation/tree/master/stage0 |
| 05:28 | <Michael Ficarra> | I don't think the API name (in this proposal's case) is a Stage 2 concern |
| 05:28 | <nicolo-ribaudo> | But I don't see in that issue why the answer ended up being no |
| 05:29 | <bakkot> | is there a reason to want this to be a symbol-named protocol instead of a string-named protocol? Because my plan was to add a string-named whenAborted or something to AbortSignal, which does the good algorithm, and have consumers like Governor do a call to the user-observable .whenAborted, which would make it de-facto a protocol |
| 05:29 | <Michael Ficarra> | also percent of spec text that the name occupies does not make one bit of difference |
| 05:29 | <bakkot> | I think symbol-named protocols are good for things where you want the protocol implemented on arbitrary objects |
| 05:29 | <bakkot> | like iterator |
| 05:29 | <bakkot> | but don't see much reason for symbol-named protocols for cases like this |
| 05:29 | <rbuckton> | I'd been working with Domenic Denicola on a solution and ended up being told later that the protocol idea was not going to be accepted and only thing WHATWG would consider would be a host hook. |
| 05:29 | <Jake Archibald> | eemeli: do you know why Map & Set don't use count? |
| 05:29 | <nicolo-ribaudo> | That the web API already has one way to register, and it can be confusing to have two |
| 05:29 | <ljharb> | you think it needs to be decided before stage 2? |
| 05:30 | <rkirsling> | I don't want to object but this really feels premature |
| 05:30 | <nicolo-ribaudo> | Unless we want every of its users to consider the event-based API to be deprecated |
| 05:30 | <Michael Ficarra> | @ljharb no I was objecting to @eemeli's objection |
| 05:30 | <bakkot> | uhhhh yeah that's pretty much what I'm hoping for |
| 05:30 | <nicolo-ribaudo> | Then a string-based protocol is good |
| 05:30 | <bakkot> | it's got a bunch of footguns so that's desirable anyway |
| 05:31 | <rbuckton> | See https://github.com/tc39/proposal-cancellation/issues/22#issuecomment-916548469 |
| 05:32 | <nicolo-ribaudo> | It seems like the motivation there is that we have a good API on the web and we shouldn't use a second one. Kevin is now saying that it'd be good to provide a second API on the web (which would be good regardless of the ECMA-262 interop) |
| 05:32 | <rkirsling> | oh oops I see, I mistook the conclusion. I'm definitely okay with the conclusion Jordan stated |
| 05:33 | <eemeli> | No; I don't find their sizes awkward though. Is the story relevant here? |
| 05:33 | <Mathieu Hofman> | One reason why splitting proposals doesn't really make sense to me |
| 05:33 | <Jake Archibald> | eemeli: just that it's another word the platform uses for "number of items" |
| 05:33 | <Justin Ridgewell> | @rbuckton What was the proposal that prompted you to create Cancellation? |
| 05:34 | <Justin Ridgewell> | I remember there being something that needed the ability to cancel |
| 05:34 | <rbuckton> | I've always contended that we have a less than adequate API on the web with numerous footguns. We can possibly address the footguns, but that requires deprecating older parts of the API |
| 05:34 | <Jake Archibald> | Justin Ridgewell: on the web side, a lot was driven by cancellable fetch |
| 05:34 | <rbuckton> | Promise and async function. |
| 05:35 | <Justin Ridgewell> | Wasn’t there something more recent than that? |
| 05:35 | <Justin Ridgewell> | Those both predate my joining the committee |
| 05:35 | <Justin Ridgewell> | I remember there being something |
| 05:36 | <rbuckton> | Not really, I started working on the proposal before async function was finalized. |
| 05:36 | <Ashley Claymore> | A standard CancellationAsyncContext |
| 05:36 | <Ashley Claymore> | also if Observables was done in TC39 |
| 05:36 | <bakkot> | I think this would still count as the same API, just a different way of using it, and fwiw I think domenic agrees per https://matrixlogs.bakkot.com/WHATWG/2025-09-26#L7 |
| 05:37 | <eemeli> | Tbf my stated preference continues to be for isEmpty, but keysLength rubs me the wrong way because "keys length" is not grammatically correct English. |
| 05:38 | <rbuckton> | I'd already been using cancellation in .NET for years, and had an early implementation of Promise for JS back in 2015. When Promise was proposed for JS, I wanted cancellation for async programming. |
| 05:39 | <rbuckton> | I wanted cancellation for new Promise(init, token) and Promise.prototype.then(onfulfill, onreject, token) as a way to unregister callbacks as a more memory friendly version of p.then(() => { token.throwIfCanceled(); ... }). I also wanted it for dynamic import(). |
| 05:40 | <rbuckton> | What held up cancellation to the point that WHATWG shipped something before we could standardize, was a number of delegates wanting cancellation to be somehow transparent, which I was strongly opposed to. |
| 05:41 | <snek> | i don't love cancellation at the promise layer specifically, I think it leads to a footgun of stopping listening when you meant to stop the actual work that is happening, but cancellation in general seems quite nice. |
| 05:42 | <rbuckton> | I based that consideration on my experience with .NET's new Task() and Task.ContinueWith(), which both accept a cancellation token as a way to cancel work up-front and avoid memory leaks due to holding on to closures longer than necessary. |
| 05:44 | <snek> | detaching promise callbacks also seems useful |
| 05:44 | <rbuckton> | That's the same reasoning why I think AbortController/CancelSource needs a close()/[Symbol.dispose]() that unregisters all callbacks, to avoid memory leaks from entire cancellation graphs that can no longer be canceled. |
| 05:45 | <Justin Ridgewell> | Isn’t this effectively what fetch({ signal }) does? |
| 05:45 | <snek> | fetch's signal can actually cancel the native operation |
| 05:45 | <Ashley Claymore> | right. the signal should be given to the producer - not the result |
| 05:46 | <Ashley Claymore> | promise != task. promise == result |
| 05:46 | <bakkot> | a Promise is a producer of calls to its .then listeners |
| 05:47 | <Ashley Claymore> | program-counter++ is a producer of progress |
| 05:47 | <nicolo-ribaudo> | The .then callback can do actual significant work |
| 05:47 | <rbuckton> | Ignore the names. A .NET Task is virtually the same as a JS Promise. The only difference is that a .NET Task can delay starting. |
| 05:48 | <Ashley Claymore> | that's a big difference |
| 05:48 | <rbuckton> | In .NET, a Task generally represents an eventual result. |
| 05:48 | <rbuckton> | Yes, but in the Venn diagram of Task and Promise, Task covers Promise entirely. |
| 05:48 | <Ashley Claymore> | an async function that was lazy would have been very different |
| 05:49 | <Justin Ridgewell> | I think you’re confusing promise p1 and the callback’s returned result p2 in p2 = p1.then(() => work()) |
| 05:49 | <Ashley Claymore> | I agree that tasks have results |
| 05:49 | <snek> | my concern is when people write `fetch(url).then(cb, { signal })` instead of `fetch(url, { signal }).then(cb)` these do very different things while being very similar |
| 05:50 | <snek> | I also I think both use cases are valid |
| 05:50 | <Ashley Claymore> | I'm saying that even though a task can be a promise, that doesn't mean it's better. Restrictions are powerful |
| 05:50 | <keith_miller> | @erights Mark Miller (Agoric) MM: IIRC, you had strong opinions on WeakRef and the [[KeptValues]] list. Do you have thoughts on relaxing the behavior to match JSC in https://bugs.webkit.org/show_bug.cgi?id=301334. |
| 05:50 | <Aki> | Perhaps TG1 & WHATWG could take some inspiration from TG2 & W3C I18N: TG2 stage advancement requires asking for a horizontal review from W3C I18NWG, but does not require receiving it |
| 05:51 | <keith_miller> | TL;DR JSC only keeps the target alive as long as there exists some WeakRef that has deref()ed the target this cycle is still alive |
| 05:51 | <nicolo-ribaudo> | How do I add myself to the end of the queue? |
| 05:51 | <rbuckton> | Imagine a .NET
|
| 05:51 | <nicolo-ribaudo> | It keeps inserting my topic before James's |
| 05:51 | <jkup> | It's been doing that all day, that is not the normal behavior, right? |
| 05:52 | <nicolo-ribaudo> | It's now TCS instead of TCQ |
| 05:52 | <jkup> | People who submit New Topic's early during the presentation are kept at the bottom and often don't get to speak |
| 05:53 | <ljharb> | splitting them doesn't change that all the names will have to be consistent full stop |
| 05:54 | <Mathieu Hofman> | is this an observable difference? |
| 05:54 | <Mathieu Hofman> | (didn't read the issue yet) |
| 05:54 | <Mathieu Hofman> | let's chat on the break |
| 05:56 | <keith_miller> | Only when you have two WeakRefs that point to the same target. My thinking is that's probably impossible to mentally model that two WeakRefs point to the same target correctly anyway. |
| 05:56 | <keith_miller> | I also think (once you know the GC of a given engine) that the current behavior is a communications channel |
| 05:57 | <Mathieu Hofman> | So we're admitting we want the other methods? The Count suffix for the longer Object methods don't read well to me. Advancing keys separately opens the risk it advances as keyCount |
| 05:57 | <ljharb> | it doesn't |
| 05:58 | <bakkot> | I am admitting no such thing |
| 05:58 | <ljharb> | keysX won't advance with any X that wouldn't also be palatable for all the other methods, that would violate the "cross-cutting concerns" part of stage 2 (whether we have the other methods eventually or not) |
| 06:03 | <sffc> | I wrote out an example of loading JSON with ESM and non-UTF-8 encodings at https://github.com/eemeli/proposal-import-text/issues/6. It has the behavior I described yesterday: early error, triggered by syntax. I also collected some examples of prior art: https://github.com/eemeli/proposal-import-text/issues/5#issuecomment-3550911025 |
| 06:03 | <Michael Ficarra> | @sffc I don't think that makes a difference |
| 06:04 | <Michael Ficarra> | there are occasions when you won't get an early error, even though you have the wrong encoding, so any warning you want to convey to hosts about mismanaging the encoding for text also applies to JSON because it's the exact same error |
| 06:05 | <Michael Ficarra> | and we don't have such a warning about JSON, and I don't think we need it |
| 06:06 | <bakkot> | the encoding option doesn't make sense here because there is no reason to assume the resource is backed by bytes from the perspective of the importing machinery |
| 06:06 | <Eli Grey> | MIME types could include a universal codecs sub-parameter or similar |
| 06:06 | <bakkot> | that assumption is true in e.g. HTML, but whatwg is pretty dogmatic these days about interpreting any text over the wire as UTF-8 in any new APIs, so I don't think they'd be on board with allowing specifying any other encoding anyway |
| 06:06 | <nicolo-ribaudo> | Could HTML only support UTF-8, but throw if it detects somehow a non-UTF-8 encoding? |
| 06:06 | <Eli Grey> | and we could add a way to include the expected mime type |
| 06:07 | <nicolo-ribaudo> | The Content-Type header is already mime type + charset |
| 06:07 | <Eli Grey> | that's what i meant; re-using that if possible |
| 06:08 | <nicolo-ribaudo> | Btw, the scope of the proposal in TC39 is "if an import with type:text succeeds, it must return a string". HTML doesn't actually need our proposal to do type:text, our proposal just prevents returning random stuff there |
| 06:14 | <bakkot> | nicolo-ribaudo: are there slides for your "Module-declarations-like proposals in other areas of the web platform" topic? it sounds interesting but I might be asleep |
| 06:15 | <nicolo-ribaudo> | I recently added them to the agenda |
| 06:15 | <bakkot> | oh nice, just hadn't refreshed |
| 06:16 | <nicolo-ribaudo> | It's mostly code snippets though, I was planning to just say the descriptions out lound |
| 06:21 | <ljharb> | https://tc39.es/proposal-object-keys-length/, thanks eemeli and james |
| 06:24 | <James M Snell> | Spec text lgtm |
| 06:26 | <Michael Ficarra> | I'm gonna be real upset if this presentation doesn't mention call stack limit |
| 06:27 | <Chris de Almeida> | it does not |
| 06:28 | <Ashley Claymore> | or arguments limit |
| 06:29 | <Ashley Claymore> | there used to be a limit of unique Symbol.for calls in v8 but I think that's fixed now |
| 06:29 | <Richard Gibson> | strings have a limit of 2**53 - 1 code units, but what happens when trying to exceed that is not specified: https://github.com/tc39/ecma262/issues/2623 |
| 06:30 | <Ashley Claymore> | and some engines fail before getting to that length too right |
| 06:31 | <Richard Gibson> | all of them; it's a big limit |
| 06:34 | <snek> | i don't know how we can discuss any of these questions without a more concrete problem |
| 06:34 | <snek> | like |
| 06:34 | <snek> | basically every single expression in js can cause an oom |
| 06:35 | <snek> | what is the scope here |
| 06:35 | <ljharb> | i think it's less about avoiding OOMs and more about, in the presence of sufficient memory, knowing what ranges are guaranteed vs impossible vs "up to the implementation" |
| 06:35 | <snek> | what is sufficient memory |
| 06:35 | <Chris de Almeida> | it's an open-ended discussion |
| 06:36 | <Andreu Botella (🕑 JST, at TC39)> | assume infinite memory |
| 06:36 | <Andreu Botella (🕑 JST, at TC39)> | also assume a spherical frictionless cow |
| 06:36 | <snek> | well now we don't need any limits |
| 06:37 | <Chris de Almeida> | yes, I have proposed exactly that in TDZ |
| 06:37 | <Michael Ficarra> | IMO it would be nice to have the spec define minimum upper bounds |
| 06:37 | <Michael Ficarra> | otherwise how are you supposed to program this crazy machine? |
| 06:38 | <Chris de Almeida> | you will immediately have non-conformant impls, no? |
| 06:38 | <Michael Ficarra> | MINIMUM upper bound |
| 06:38 | <snek> | so like if a system doesn't have enough memory for that, and it can't throw a rangeerror up front, it has to just oom? |
| 06:38 | <Rob Palmer> | test262 implicitly defines certain minimum limits - maybe those could be turned into spec limits to make them explicit |
| 06:38 | <Christian Ulbrich> | having a non-orientable surface, too? |
| 06:38 | <Michael Ficarra> | it should never even start if it can't support the minimums |
| 06:39 | <bakkot> | test262 has some quite expensive tests which are skipped even on production-grade engines |
| 06:39 | <snek> |
|
| 06:40 | <bakkot> | https://github.com/v8/v8/blob/37adb436086fca29284fea8b8440bcda73bbb0fb/test/test262/test262.status#L393 |
| 06:40 | <keith_miller> | Summary of my chat Mathieu Hofman was that changing this seems not-insane so maybe we should think deeper thoughts about this |
| 06:40 | <Michael Ficarra> | For example, I make function calls with more than 1 argument sometimes. For some number of arguments N (N > 1), it's unsafe to do that because browsers may OOM. As a programmer, I'd like to know that my argument list length is less than N so that won't happen. |
| 06:41 | <mgaudet> | @erights Mark Miller (Agoric) MM: I should note that one of my current background tasks is investigating exactly this change |
| 06:42 | <eemeli> | The function body is fine, but the name is not: It needs to unpack into something that's valid English, and "keys length" is not. |
| 06:42 | <bakkot> | this number depends on the current size of your stack, so, good luck with that |
| 06:43 | <Michael Ficarra> | fair, I guess this is a more difficult limit than some of the others |
| 06:43 | <rkirsling> | honestly you could make N=5 there and just encourage people to write nicer code |
| 06:44 | <rkirsling> | oh wait I'm thinking of formal params and not concrete args |
| 06:44 | <bakkot> | array.push(...vals) is nice! |
| 06:44 | <rkirsling> | yeah, never mind |
| 06:45 | <bakkot> | oh yeah we should limit you to 5 formal parameters, I'm on board with that |
| 06:45 | <bakkot> | we have a function in our codebase which takes 13 and I feel bad every time I look at it |
| 06:47 | <Michael Ficarra> | I don't feel very bad about functions with excessively long parameter lists, in the same way I don't feel very bad about variables with excessively long names |
| 06:47 | <Michael Ficarra> | some things are just complicated and pretending otherwise just makes things worse |
| 06:48 | <Christian Ulbrich> | Yeah, but 13 params! |
| 06:48 | <Michael Ficarra> | I can count to 13 |
| 06:48 | <Richard Gibson> | https://en.wikipedia.org/wiki/Sorites_paradox |
| 06:49 | <Christian Ulbrich> | How? I only have 10 fingers! |
| 06:49 | <bakkot> | your parents didn't teach you to count binary on your fingers? |
| 06:50 | <Christian Ulbrich> | My dad showed me how to open a beer bottle with my |
| 06:50 | <ljharb> | 10 fingers and 10 toes, that should get you up to base 20 |
| 06:51 | <Ashley Claymore> | Chrome doesn't seem to kill the whole agent. If I OOM in a web worker in chrome, only the worker crashes |
| 06:51 | <Ashley Claymore> | the fail fast proposal was the whole agent cluster right? |
| 06:51 | <Mathieu Hofman> | yes, because of SAB |
| 06:51 | <rbuckton> | count each knuckle too |
| 06:52 | <Ashley Claymore> | I suspect that's likely the disagreement that @erights Mark Miller (Agoric) MM was remembering |
| 06:52 | <Ashley Claymore> | as a guess |
| 06:53 | <Mathieu Hofman> | might be able to say only crash the set of agents that have a SAB shared with |
| 06:53 | <Ashley Claymore> | crashing the main thread is the bad one |
| 06:53 | <Ashley Claymore> | as that's where the website is |
| 06:53 | <Mathieu Hofman> | but I also remember something about the OS killing interfering with that |
| 06:53 | <Ashley Claymore> | :D |
| 06:54 | <Mathieu Hofman> | if the main thread shares a SAB with a worker that failed, it's likely unsafe to let it continue |
| 06:55 | <bakkot> | yeah but as a user I prefer that to my whole tab crashing |
| 06:55 | <Ashley Claymore> | if the website is a random blog post, I don't want it to crash even if the analytics library has a bug |
| 06:55 | <Mathieu Hofman> | as a user you like your applications to misbehave ? |
| 06:55 | <James M Snell> | OOM's in V8 can be problematic. They do have a host hook to handle them but new OOM paths that crash instead of using the hook pop up a lot. In workers we have to actively watch out for these since we have thousands of isolates all in the same process... crashing hard is not really an option. |
| 06:55 | <Ashley Claymore> | when it's not critical, yes |
| 06:56 | <Michael Ficarra> | do you? the alternative might be that the JS can now read uninitialised/reclaimed memory |
| 06:56 | <Ashley Claymore> | some bugs in games actually lead to fun qwirks |
| 06:56 | <bakkot> | I like my applications to not crash more than I care about them maintaining arbitrary other invariants, yes |
| 06:57 | <bakkot> | there are very few webpages that I use where I would prefer a page crash over the sort of bugs likely to arise from a worker crashing, even a worker with access to a SAB |
| 06:57 | <Chengzhong Wu> | if the web page is show your wallet balance, and the balance is incorrect rather than crashing due to worker failure, it would cause human panic I think |
| 06:57 | <bakkot> | it is true that there are some very few applications where a crash is preferable, but they are very few |
| 06:58 | <ljharb> | ooh isn't that how some cultures did base 4 or 8 or something? |
| 06:58 | <James M Snell> | server side there are way more cases where crash is absolutely preferable |
| 06:58 | <Mathieu Hofman> | we very much advocate for a host hook. I didn't understand yusukesuzuki 's point because I would think host hooks are not affected by JS heap limits (unless they themselves decide to delegate to JS) |
| 06:58 | <James M Snell> | often way better to just crash, throw away current state, then start fresh |
| 06:59 | <Olivier Flückiger> | I mean you can always catch RangeError and terminate the process in node when you get it, not? |
| 06:59 | <yusukesuzuki> | Ah, sorry for misleading. I was mentioning to delegating to user defined logic after that. |
| 07:00 | <Mathieu Hofman> | And those webpage cannot be simply reloaded? I mean what page hold important state that would suffer from a reload and yet can sustain corruption ? |
| 07:00 | <James M Snell> | yeah, our host hook for OOM definitely does not defer to user code. We actually hard terminate the isolate and force a cold start fresh |
| 07:00 | <yusukesuzuki> | But also, if it is coming from system allocator exhausting memory, it is hard to guarantee that the handler can work safely too. |
| 07:01 | <Mathieu Hofman> | no because other code might catch it before it reaches the process unhandled handler |
| 07:02 | <Olivier Flückiger> | sure, but if it catches it then it takes responsibility to correctly handle it. |
| 07:04 | <James M Snell> | Yes, but there are, of course, cases where we just can't let the user code do that. Lots of cases where "correctly handle it" is a hard crash and cold start |
| 07:04 | <Mathieu Hofman> | lib A trigger OOM and let it throw, lib B made the call to lib A and handles the thrown error. lib A is now in inconsistent state. App has no clue of what happened |
| 07:05 | <Olivier Flückiger> | B should not have caught it if it can't ensure A stays consistent. How is that different from any other exception that is thrown by A. |
| 07:06 | <Mathieu Hofman> | yeah B shouldn't, lots of code out there doing things it shouldn't, and inflicts pain on the rest of the app |
| 07:06 | <James M Snell> | Unfortunately not that straightforward. some frameworks unconditionally catch in an attempt to be "helpful" |
| 07:07 | <Christian Ulbrich> | Looks like we need uncatchable Errors in JS too. |
| 07:07 | <Olivier Flückiger> | sure, but now you want a crash on all errors proposal? |
| 07:08 | <James M Snell> | oh no no ... never that. just don't want an All Failures Are Catchable proposal |
| 07:08 | <Mathieu Hofman> | we did advocate for a userland panic(). We are concerned about situations where invariants go out the window |
| 07:09 | <rkirsling> | ooh interesting, I did not see this on the agenda |
| 07:11 | <Olivier Flückiger> | I see. I still think that websites seem to be doing fine with handling range errors. |
| 07:12 | <James M Snell> | Websites yeah, I agree that's different. I'm largely thinking about server-side cases |
| 07:13 | <Mathieu Hofman> | web apps "think" they're doing fine, but they really have no clue. web sites could just reload |
| 07:17 | <sffc> | Right, I acknowledge this in my post. With non-UTF-8 JSON imports, you get an error sometimes: almost always when the source encoding is utf16, and occasionally when the source encoding is latin1. However, with non-UTF-8 Text imports, you get an error never. Sometimes is better than never. |
| 07:19 | <ljharb> | shane, why would it necessarily be an error case tho? |
| 07:20 | <sffc> | Sure, but some expressions are much more likely to cause an OOM than others. |
| 07:21 | <snek> | right that was my point |
| 07:21 | <snek> | that i thought the whole topic is too vague |
| 07:23 | <James M Snell> | definitely needs to be grounded a bit more. there's lots of nuance that gets lost without more detail |
| 07:26 | <rkirsling> | wait, we have 50 min of topic left for 30 min of day? |
| 07:27 | <mgaudet> | Huge props to everyone who does note-taking. The transcription can be very hard to follow sometimes! |
| 07:31 | <sffc> | Right, I agree with this type of sentiment. Ideally, I think the spec authors should use their judgement to decide a minimum for these things (such as length of a bigint/amount). An engine could throw errors for anything that would exceed the minimum and be compliant and also be able to run 99.9% of programs. But, engines are permitted to allow more than that. |
| 07:32 | <Chengzhong Wu> | the schedule says we have 15min overflow |
| 07:34 | <snek> | i'm still just like... you can make usable bigints in v8 that are larger than the entire minimum heap size for xs. so do we set the minimum really really low? if the minimum is too low it loses the point for having it |
| 07:35 | <Justin Ridgewell> | Or for (const el of divergences) { …; break; } would be first mode. |
| 07:36 | <sffc> | One of the things I was hoping to get out of the discussion today was just this. My position is that the spec should be more explicit when it comes to acknowledging implementation constraints. But, where or under what conditions should the spec be more explicit? This was question 1: "Under what circumstances should an implementation-defined limit be preferred over a spec-defined limit, and vice versa?" |
| 07:37 | <snek> | this is why i mentioned that it would be helpful to have a more concrete problem statement. |
| 07:38 | <snek> | like some sort of program you want to write but can't due to platform divergence |
| 07:39 | <James M Snell> | yep, understood. unfortunately I think it's too hard to talk about the general case. I think likely best to focus on a smaller set of specific cases with a definitive problem statement, then try expanding whatever decision we make there out to the other cases |
| 07:39 | <snek> | one example given was arr.push(...other) |
| 07:40 | <snek> | i would generally not write that code because i would assume it would throw somewhere |
| 07:40 | <ljharb> | or to close the iterator (if that matters) divergences.take(1).next().value |
| 07:40 | <snek> | so that could be something to explore |
| 07:40 | <snek> | but a solution to that could be adding array.extend rather than setting a minimum number of arguments |
| 07:40 | <snek> | i'm not pushing for anything in particular here, just pointing this out as an example |
| 07:42 | <sffc> | My "problem" is that I am an implementer and I want to write code that can make assumptions about how big a thing can be. I don't want to be forced to write code that is less efficient for the 99.9% case because the spec requires me to keep growing until I panic. |
| 07:42 | <snek> | do you have examples of this less efficient code? |
| 07:42 | <Justin Ridgewell> | Does take() close immediately, or during the next .next() (that returns { done: true }) |
| 07:44 | <sffc> | Yes, for example, I have the metadata of ICU/ICU4X decimals bitpacked into a u32, because I am able to make assumptions about how big the number can be. |
| 07:44 | <ljharb> | i think on the next() |
| 07:44 | <snek> | you mean to fit into the bitwise safe range of js numbers? |
| 07:44 | <sffc> | On the GitHub post, I show that importing a UTF-16 JSON file currently triggers a SyntaxError. |
| 07:45 | <ljharb> | right but i mean, if i'm choosing to import utf-16 text. why would that necessarily be an error |
| 07:47 | <rkirsling> | I have to agree that this presentation seems very "solution before problem statement" |
| 07:51 | <ljharb> | deep-equal has 23m downloads a week and that's just one package this proposal would obsolete. dequal has 28m. |
| 07:52 | <Justin Ridgewell> | I think this would actually work if the chars were > 256 code units |
| 07:52 | <Rob Palmer> | The repo leads with the problem statement, but this presentation does not. |
| 07:52 | <nicolo-ribaudo> | But " and { are not, right? |
| 07:53 | <Justin Ridgewell> | Correct, so you can’t use ”. But you could use 0x22 … |
| 07:53 | <Justin Ridgewell> | The problem is ” writes 0x22 0x00 |
| 07:54 | <Justin Ridgewell> | And the JSON parser will error on that unescaped nil char (because it’s a control char) |
| 07:54 | <nicolo-ribaudo> | You need a " at the beginning and one at the end |
| 07:54 | <nicolo-ribaudo> | So one of the 0x00 will be out of the quotes |
| 07:54 | <Justin Ridgewell> | No, you need 0x22 at the beginning |
| 07:54 | <Justin Ridgewell> | That will open the string |
| 07:54 | <Justin Ridgewell> | The problem is the 0x00 that immediately follows |
| 07:54 | <nicolo-ribaudo> | But then you need to close the string |
| 07:55 | <Justin Ridgewell> | That’s also possible with a high code unit char |
| 07:55 | <nicolo-ribaudo> | Oh you are saying if JSON was not ASCII-based for its separators? |
| 07:55 | <Justin Ridgewell> | With 100% confidience, I can fool this JSON parser using utf16-le |
| 07:55 | <sffc> | My position is that spec authors should decide whether such bigints are useful. The minimum shouldn't be "too low". The authors are in a better position to communicate that than implementers who want to make assumptions to improve memory use or performance. |
| 07:56 | <Justin Ridgewell> | It will parse, it won’t result in code points that the input actually feeds it |
| 07:56 | <Justin Ridgewell> | I just need to feed it a byte stream that can parse when interpreted as UTF-8. |
| 07:57 | <Justin Ridgewell> | That’s completely possible within utf16-le. |
| 07:57 | <Christian Ulbrich> | ljharb: But only if we are to standardize the same equalityAlgorithm as deepEqual. There are several algorithms for deepEquality, as presented before. |
| 07:57 | <nicolo-ribaudo> | What would the bytes be? If you have a string you need to close it. The text "" encoded as UTF-16 would result as either 0x22-0x00-0x22-0x00 or 0x00-0x22-0x00-0x22, which when decoded as UTF-8 is either "\0"\0 or \0"\0", both of which have a 0x00 out of the quotes |
| 07:57 | <sffc> | This might be true and is why I used the language "almost always" instead of "always" for utf16le causing a SyntaxError to be thrown |
| 07:57 | <Justin Ridgewell> | You cannot use the literal chars ” or {/}. |
| 07:57 | <ljharb> | i maintain at least 2 of them. but it's not actually all that important in practice which one is chosen imo, since they all check similar things |
| 07:58 | <nicolo-ribaudo> | Ok, then you can only do either numbers or booleans |
| 07:58 | <nicolo-ribaudo> | Both of which use ASCII characters |
| 07:58 | <Justin Ridgewell> | But there are other code points that emit the bytes 0x22, 0x7b/0x7d |
| 07:58 | <Justin Ridgewell> | Again, it’s an arbitrary byte stream being misinterpreted |
| 07:58 | <Justin Ridgewell> | I control the byte stream |
| 07:58 | <nicolo-ribaudo> | Oh you are not saying something that is both valid JSON when decoded as UTF-8 and UTF-16? |
| 07:59 | <Justin Ridgewell> | It won’t be valid JSON in UTF-16 |
| 07:59 | <nicolo-ribaudo> | Ok then I do not disagree |
| 07:59 | <nicolo-ribaudo> | But it seems very unlikely that you try to import as JSON something that was not meant to be JSON, and it randomly happen to work due to using the wrong encoding |
| 07:59 | <sffc> | You mean it won't be valid JSON in UTF-8? |
| 08:00 | <Justin Ridgewell> | I’m saying the opposite. |
| 08:00 | <Justin Ridgewell> | I can craft a utf16-le byte stream that will be valid JSON when interpreted as UTF-8. It will be invalid when interpreted as UTF-16. |
| 08:00 | <Justin Ridgewell> | I’m saying that the JSON parser is not actually enforcing UTF-8 encoded input, it’s just a dumb byte parser |
| 08:01 | <Justin Ridgewell> | Whether the stream is actually UTF-16 or UTF-8 is up to the implementer to decide |
| 08:02 | <sffc> | ok, yeah. You have UTF-16 that, if interpreted as UTF-8, happens to form syntactically valid JSON. But, if you were to convert the UTF-16 to code points and then to UTF-8, you would get something that is not valid JSON. I agree that such cases exist. |
| 08:02 | <Justin Ridgewell> | Correct |
| 08:09 | <Zb Tenerowicz (ZTZ/naugtur)> | But it seems very unlikely that you try to import as JSON something that was not meant to be JSON, and it randomly happen to work due to using the wrong encoding |
| 08:13 | <Richard Gibson> | which JSON parser is just consuming bytes? The ECMA-262 ParseJSON operates on a sequence of code points (which it gets from interpreting an ECMAScript string as UTF-16 per the normal convention). And even a hand-rolled one would implicitly interpret raw octets as something, be that ASCII, a UTF, EBCDIC, etc. |
| 08:14 | <Justin Ridgewell> | It’s not, per Shane’s issue. It’s interpreting it as a UTF-8 byte stream |
| 08:24 | <Jacob Smith> | I skipped the problem statements presented last time because they were already agreed last time; the proposal itself was blocked by its dependency on its sibling proposals. That dependency is now removed. This presented a refinement on a potential solution based on quite a lot of feedback from delegates (and userland authors). |
| 10:12 | <sffc> | Yes, for example, I have the metadata of ICU/ICU4X decimals bitpacked into a u32, because I am able to make assumptions about how big the number can be. |
| 17:28 | <bakkot> | the one that returns { done: true }; we discussed this in https://github.com/tc39/proposal-iterator-helpers/issues/219 |
| 22:25 | <James M Snell> | For the TypedArray Concat and TypedArray Find Within proposals.. the advancement to Stage 1 was contingent on me creating and transfering the github repos... that's been done:
In the coming week I'll be adding the proposed spec text and assuming things are looking good, I do plan to go for Stage 2 (at least) by the next plenary |
| 22:29 | <bakkot> | James M Snell: did you get a chance to talk about some of the other ideas in the slides? in particular I am very interested in have a equals method which (for non-float TAs) can be implemented as a memcmp |
| 22:30 | <bakkot> | (I wasn't able to stay up for the actual presentation, sorry) |
| 22:31 | <James M Snell> | Not everything. I wanted to see how these went before pushing my luck further ;-) ... I plan to bring a proposal for compare/equals for Stage 1 at the next plenary |
| 22:33 | <James M Snell> | but I will likely go ahead and start preparing that in parallel with these as it really ought to be pretty straightforward |
| 22:36 | <bakkot> | nice! yeah, I suspect those could probably get straight to stage 2, since the design space is smaller. mostly just: is there a separate .equals or just a .compare (IMO both for optimizability reasons, plus Temporal has established a precedent for having both) and how does equality float-backed TAs work (IMO SameValueZero) |
| 22:36 | <bakkot> | (though also I would be fine with leaving out .compare; it comes up a lot less than equality) |
| 23:03 | <keith_miller> | BTW, Shu wrote up the normative change: https://github.com/tc39/ecma262/compare/main...syg:ecma262:strongify-weakref |
| 23:09 | <kriskowal> | I, for one, have projects that use usermode versions of both .equals and .compare. |
| 23:32 | <James M Snell> | Yes I'm thinking both. I have use cases where both are used, tho equals Is by far the most common. Tho it is worth noting we can cover equals and compare with just compare if there are concerns |
| 23:37 | <bakkot> | in principle yes but for types other than Uint8Array you can't implement .compare with memcmp unless the engine can recognize that you're just checking the .compare() === 0 case, which it probably can't most of the time, so I would want .equals anyway just for performance reasons |
| 23:43 | <James M Snell> | Agreed. |