| 01:26 | <Bakkot> | ok I found it it didn't change in 2019 |
| 01:26 | <Bakkot> | it is quoted here: https://www.quora.com/Why-do-you-think-modulus-is-not-defined-for-floating-point-numbers |
| 01:29 | <Bakkot> | but... uh... this is wrong, right? like, set x = 9, y = 5, and then it says to find "n" as "the integer nearest the exact number x/y", i.e. the integer nearest 9/5, i.e. 2, and then r = x - y * n, i.e. r=9 - 5 * 2, i.e. r = -1 |
| 01:30 | <Bakkot> | which is... not what remainder(9, 5) is |
| 01:30 | <Bakkot> | ... right? |
| 01:30 | <Bakkot> | am I crazy? |
| 01:31 | <devsnek> | > Floating point remainder. This is not like a normal modulo operation, it can be negative for two positive numbers. It returns the exact value of x–(round(x/y)·y). |
| 01:33 | <Bakkot> | whyyyyy |
| 01:33 | <devsnek> | computers be that way sometimes |
| 01:33 | <Bakkot> | ok so when number::remainder says it "the floating-point remainder r from a dividend n and a divisor d is defined by the mathematical relation [...]", that doesn't actually mean the IEEE floating-point remainder |
| 01:38 | <devsnek> | right |
| 01:38 | <Bakkot> | it should probably not claim to be the floating-point remainder then |
| 01:39 | <Bakkot> | sidebar: was creating a demo to share this, got the best URL for it: https://repl.it/repls/ImpracticalDearestRuntimelibrary |
| 01:50 | <devsnek> | nice |
| 02:29 | <devsnek> | TIL es6 didn't have the ?/! macros |
| 02:35 | <shu> | we must remember what has been lost https://i.imgflip.com/49yuel.jpg |
| 02:37 | <devsnek> | your meme has an off-by-one error |
| 02:37 | <devsnek> | or off by three i guess |
| 02:38 | <shu> | wat |
| 02:39 | <ljharb> | lol |
| 02:43 | <devsnek> | shu: 5, 5.1, and 6 have no assigned doge |
| 02:45 | <rkirsling> | we don't ~talk~ meme about the "middle times" |
| 15:14 | <devsnek> | the second test in built-ins/String/raw/special-characters.js |
| 15:15 | <devsnek> | it turns \<CR>\<CR><LF>\<LF> |
| 15:16 | <devsnek> | into \<LF>\<LF>\<LF> |
| 15:16 | <devsnek> | does that sound correct |
| 16:47 | <devsnek> | Bakkot: in an ast, would you ever expect to see a node of type "ForBinding" |
| 16:47 | <Bakkot> | devsnek probably not |
| 16:47 | <Bakkot> | LHS of a for-of is either a let-style binding or the LHS of an assignment expression |
| 16:47 | <Bakkot> | woudl be weird to have the type not be... those |
| 16:48 | <Bakkot> | I guess one difference is that you can have a `const` binding which isn't initialized? |
| 16:48 | <Bakkot> | but doesn't seem worth splitting out |
| 16:48 | <devsnek> | i'm more trying to figure out the organization of uh |
| 16:48 | <devsnek> | when a certain production gets its own node type vs when it's just its children |
| 16:49 | <devsnek> | maybe a better way to ask is: would you ever expect to see { type: 'BindingProperty', SingleNameBinding: ... } |
| 16:50 | <devsnek> | instead of BindingProperty : SingleNameBinding just becoming SingleNameBinding |
| 17:04 | <Bakkot> | for most ASTs, you never have a node type which only has one legal parent |
| 17:05 | <Bakkot> | though I think sometimes it's unavoidable because the type system is too constrained |
| 17:07 | <Bakkot> | I guess "one legal parent" isn't quite right, but rather one legal _position_ |
| 17:52 | <Bakkot> | the exception to that is that sometimes you need a wrapper type to separate semantics: like `{ a, ...a }`, you need to differentiate between the two kinds of thing which are otherwise identical, so you might have a `SpreadProperty` which can only appear in objects |
| 18:01 | <devsnek> | i should get a bunch of parser nerds to review the engine262 ast when i finish it |
| 18:01 | <Bakkot> | just use shift's |
| 18:02 | <Bakkot> | inventing new ASTs makes things harder to use, it needs a pretty good justification |
| 18:03 | <devsnek> | well i'm not expecting anyone to use this |
| 18:03 | <devsnek> | outside of engine262 |
| 18:04 | <Bakkot> | yeah, but it is less overhead for people to contribute to your project, and less overhead for contributors to your project to contribute to other projects, if you use a standard AST |
| 18:05 | <devsnek> | i'd argue the opposite in this context actually |
| 18:05 | <Bakkot> | how so? |
| 18:05 | <devsnek> | it takes a lot of effort to map the spec's semantics to an ast that doesn't match |
| 18:06 | <Bakkot> | shift matches the spec pretty precisely |
| 18:06 | <Bakkot> | just elides intermediate nodes, which pretty much every AST does |
| 18:07 | <Bakkot> | and has slightly tighter constraints in some places where the spec enforces those constraints with early errors |
| 18:08 | <devsnek> | hm for example |
| 18:08 | <devsnek> | FunctionDeclaration with isAsync/isGenerator |
| 18:08 | <devsnek> | vs having GeneratorFunctionDeclaration and AsyncFunctionDeclaration |
| 18:09 | <Bakkot> | ah, sure, I can see the argument for having a lot more node types rather than having fields on nodes |
| 18:09 | <Bakkot> | I think it'll probably end up being a lot more code that way, though |
| 18:10 | <Bakkot> | like most places you want to treat those things identically |
| 18:10 | <devsnek> | i would say, engine262 has a lot of superfluous structure in order to have less "is this a node of x type" code |
| 18:10 | <Bakkot> | for Contains or whatever |
| 18:10 | <devsnek> | yeah |
| 18:10 | <devsnek> | its more annoying in static semantics for sure |
| 18:10 | <devsnek> | although the way we were writing static semantics with acorn was already annoying |
| 18:11 | <devsnek> | basically everything in the acorn version goes through this https://github.com/engine262/engine262/blob/master/src/ast.mjs |
| 18:11 | <devsnek> | never actually check `node.type` directly |
| 18:13 | <Bakkot> | fun |
| 18:13 | <Bakkot> | we usually just do `switch (node.type)` |
| 18:13 | <Bakkot> | well, actually, in practice we just have one place that does that and everything else uses it |
| 18:14 | <Bakkot> | https://github.com/shapesecurity/shift-reducer-js/blob/2732f575213b200a9acb9fe1a5da47e9b8f13c1e/gen/director.js |
| 18:14 | <Bakkot> | (ok, it uses an object with mappings rather than a switch, but close enough) |
| 18:15 | <devsnek> | ya saw that |
| 18:15 | <devsnek> | with the new ast i just switch on type as well: https://github.com/engine262/engine262/blob/parser/src/evaluator.mjs#L83 |
| 18:18 | <Bakkot> | yeah |
| 18:18 | <Bakkot> | I am generally quite happy with the tradeoff Shift makes with having the appropriate number of types of node |
| 18:19 | <devsnek> | if i was going to make a general purpose ast it would probably look like shift |
| 18:20 | <Bakkot> | every AST ends up being general purpose eventually |
| 18:20 | <Bakkot> | that's how we ended up with estree |
| 18:20 | <Bakkot> | (but yeah a custom one sounds reasonable for your case) |
| 18:21 | <devsnek> | i don't think you can even use the parser in engine262 without accidentally including the entire runtime |
| 18:21 | <Bakkot> | couldn't use estree without including an entire browser for a long time and that didn't stop anyone |
| 18:22 | <devsnek> | lol |
| 19:36 | <jmdyck> | Bakkot: Does https://github.com/tc39/ecma262/pull/2007#issuecomment-667271814 mean that you're going to want my review soon, or just that there will soon be a new version available for review? (Or possibly both) |
| 19:37 | <Bakkot> | that there will soon be a new version available for review |
| 19:37 | <Bakkot> | we're fixing the issues we're aware of (dates are indeed a particularly tricky one) |
| 19:38 | <Bakkot> | if you are able to review that one to find the issues we miss (of which I am sure there will be a number) that will be much appreciated |
| 19:40 | <Bakkot> | my current stumbling block is that operations `MakeDate` have math being performed on Numbers, and these _might_ have different semantics if you use IEEE floating point math rather than doing the math in the math domain and converting to/from at the boundary of the function, and I need to figure out if the difference is observable and, if so, which is intended/implemented |
| 19:40 | <Bakkot> | *operations like MakeDate |
| 19:41 | <Bakkot> | (ps ryzokuken or other datetime people, if you know the answer to the above question I would love to hear it) |
| 19:42 | <devsnek> | choose the most confusing behaviour so our successors don't have it too easy |
| 19:42 | <ryzokuken> | Damn |
| 19:43 | <ryzokuken> | I'm on phone can someone please link me to MakeDate? |
| 19:43 | <Bakkot> | https://tc39.es/ecma262/#sec-makedate |
| 19:43 | <Bakkot> | The abstract operation MakeDate takes arguments day (a Number) and time (a Number). It calculates a number of milliseconds. It performs the following steps when called: |
| 19:43 | <Bakkot> | If day is not finite or time is not finite, return NaN. |
| 19:43 | <Bakkot> | Return day × msPerDay + time. |
| 19:46 | <devsnek> | I doubt the difference is observable cuz I implemented that in engine262 using floating point math |
| 19:47 | <devsnek> | then again test262 might be lacking |
| 19:47 | <Bakkot> | that doesn't mean it's not observable, just means that the difference either is not exercised in the tests (extremely likely) or that the intention is to use floating point math (also likely) |
| 19:49 | <Bakkot> | I guess for now I will assume floating piont |
| 19:49 | <Bakkot> | part of my hesitation is that MakeTime explicitly says to use IEEE arithmetic |
| 19:49 | <Bakkot> | and none of the rest of them do |
| 19:50 | <ryzokuken> | Hmm |
| 19:50 | <Bakkot> | the MakeTime one is also confusing because it does two additions without parentheses, but IEEE arithmetic is not associative |
| 19:52 | <ryzokuken> | Can we just convert to real numbers? |
| 19:52 | <ryzokuken> | Before that math? |
| 19:52 | <Bakkot> | well, yes, that's the question |
| 19:53 | <Bakkot> | we could do that, but this might have observably different semantics |
| 19:53 | <ryzokuken> | So idk what the original intent was either |
| 19:53 | <ryzokuken> | Probably need to check in implemeters |
| 19:54 | <ryzokuken> | And no harm in specifying now if it doesn't break web reality, is there? |
| 19:54 | <ryzokuken> | Implementations* |
| 19:55 | <Bakkot> | well, two things: a.) if it's a normative change I'd have to bring it to plenary, and b.) implementations are unlikely to want to actually do arbitrary-precisino math |
| 19:59 | <ryzokuken> | Since the aim of the pr seems to be reducing ambiguity, we need to specify it one way or the other, I suppose. |
| 19:59 | <Bakkot> | it looks like in es5 this was explicitly not specified: "an implementation is permitted a choice of internal representations of time values, for example as a 64-bit signed integer or as a 64-bit floating-point value" |
| 19:59 | <ryzokuken> | Oh wow |
| 20:00 | <ryzokuken> | I didn't know we accepted integers like that. |
| 20:00 | <ryzokuken> | What about the date value? Surely that accepts floats. |
| 20:00 | <Bakkot> | it wouldn't be observable except that the arithmetic would be more precise |
| 20:02 | <ryzokuken> | I see |
| 20:03 | <ryzokuken> | Don't really know, to be honest 😅 |
| 20:04 | <ryzokuken> | I'll dig deeper into this |
| 21:21 | <Bakkot> | $ eshost -s -e 'Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740)' |
| 21:21 | <Bakkot> | #### Chakra, SpiderMonkey, V8, XS |
| 21:21 | <Bakkot> | 29312 |
| 21:21 | <Bakkot> | #### JavaScriptCore |
| 21:21 | <Bakkot> | NaN |
| 21:21 | <Bakkot> | JSC what are you doing |
| 21:22 | <devsnek> | Bakkot: https://gc.gy/63935557.png |
| 21:23 | <Bakkot> | Hah! Graal is _exactly_ what I was looking for there |
| 21:23 | <devsnek> | using u64? |
| 21:23 | <Bakkot> | mm, no, just parenthesizing differently |
| 21:23 | <devsnek> | oh no |
| 21:24 | <Bakkot> | `80063993375 * 3600000 + 29 * 60000 + 1 * 1000 - 2**58` is, in math, 29256 |
| 21:24 | <devsnek> | so quickjs wins |
| 21:24 | <Bakkot> | in double-precision floats, if you do your `+` left-associate, it's 29312 |
| 21:24 | <Bakkot> | if you parenthesize as 80063993375 * 3600000 + (29 * 60000 + 1 * 1000) - 2**58, it's 29248 |
| 21:25 | <devsnek> | what if you return nan |
| 21:25 | <Bakkot> | I think the way you end up with NaN is by time-clipping before doing the arithmetic, which is not allowed per spec |
| 21:26 | <devsnek> | > `return JSValue::encode(jsNumber(timeClip(ms)));` |
| 21:26 | <Bakkot> | is that from jsc's source? |
| 21:26 | <devsnek> | ya |
| 21:27 | <Bakkot> | sweet |
| 21:27 | <Bakkot> | link? |
| 21:27 | <Bakkot> | this is gonna be a fun test262 test |
| 21:27 | <devsnek> | https://github.com/WebKit/webkit/blob/de7fb331a9fb6ed6773508765f9498fb12f90b4d/Source/JavaScriptCore/runtime/DateConstructor.cpp#L173 |
| 21:27 | <Bakkot> | thank |
| 21:28 | <devsnek> | so glad we support -2**58 milliseconds |
| 21:29 | <devsnek> | and 80 billion hours |
| 21:35 | <jmdyck> | Yeah, it looks like TimeClip is the only place we enforce the restricted range for 'time values', and TimeClip is generally called very late in Date functions, so all the date-related operations are potentially dealing with "out of range" Number values. |
| 21:36 | <devsnek> | Bakkot: actually JSC is a lot scarier than i thought |
| 21:36 | <devsnek> | its not the clipping, its this https://github.com/WebKit/webkit/blob/de7fb331a9fb6ed6773508765f9498fb12f90b4d/Source/JavaScriptCore/runtime/DateConstructor.cpp#L84-L96 |
| 21:37 | <devsnek> | it throws everything away if the args don't fit in i32 |
| 21:38 | <Bakkot> | $ eshost -s -e 'Date.UTC(1970, 0, 1, 0, 0, 0, 2**32)' |
| 21:38 | <Bakkot> | #### Chakra, SpiderMonkey, V8, XS |
| 21:38 | <Bakkot> | 4294967296 |
| 21:38 | <Bakkot> | #### JavaScriptCore |
| 21:38 | <Bakkot> | NaN |
| 21:38 | <Bakkot> | lol |
| 21:38 | <devsnek> | :^) |
| 21:39 | <Bakkot> | that's like a month and a half into the start of the epoch |
| 21:39 | <Bakkot> | 2**32 is a totally reasonable number of milliseconds |
| 21:39 | <devsnek> | 🤷🏻 |
| 21:40 | <Bakkot> | I wonder why they even bother with timeClip, then |
| 21:42 | <devsnek> | wow i have a webkit bugzilla account |
| 21:44 | <devsnek> | https://bugs.webkit.org/show_bug.cgi?id=215034 |
| 21:46 | <rkirsling> | is there a test262 test |
| 21:47 | <devsnek> | i don't think so but i'm not 100% sure |
| 21:48 | <Bakkot> | at a quick glance, no |
| 21:49 | <Bakkot> | if jsc has a place which records expectations for test262, that would be an easy way to check |
| 21:49 | <devsnek> | am i the only one who uses `-ts` instead of `-s` in eshost |
| 21:49 | <Bakkot> | omitting the -t makes output which is more suitable for copy-pasting |
| 21:50 | <devsnek> | https://test262.report/browse/built-ins/Date?engines=javascriptcore |
| 21:59 | <shu> | Bakkot: product excellence is about not living in the past |
| 22:01 | <Bakkot> | shu: pretty sure I can come up with an example that may or may not be in the past depending on the implementation :P |
| 22:01 | <shu> | anyway this sounds like a fun friday activity |
| 22:09 | <Bakkot> | https://github.com/tc39/ecma262/pull/2120 |
| 22:09 | <Bakkot> | yeah floats are dumb |
| 22:09 | <Bakkot> | it started as a fun thursday activity |
| 22:09 | <Bakkot> | and we went until we got stuck |
| 22:09 | <Bakkot> | and then I got distracted trying to figure out of any of this was observably problematic |
| 22:19 | <jmdyck> | Bakkot: in 2120, the second parenthesization doesn't look right (to left) |
| 22:20 | <Bakkot> | jmdyck in the PR message, or the commit? |
| 22:20 | <Bakkot> | in the message, yeah, it's not right-to-left, but it is a legal parenthesization |
| 22:21 | <jmdyck> | PR msg: deepest term would be "(1 * 1000 + -288230376151711744)", no? |
| 22:21 | <jmdyck> | ah, ok |
| 22:22 | <Bakkot> | I guess the full right-to-left order has the same result as my mixed-order, so I might as well use that as my example |
| 22:48 | <shu> | we agreed to \8 \9 erroring inside template literals regardless of strictness, right? |
| 22:49 | <shu> | or... just strict mode? |
| 22:52 | <Bakkot> | shu: that was the existing behavior and has not changed |
| 22:52 | <Bakkot> | (for untagged templates only, of course) |
| 22:53 | <shu> | ah so it's an existing bug... |
| 22:53 | <shu> | i see SM and V8 both accept ``\8`` and ``\9`` |
| 22:53 | <shu> | that didn't quote the way i intended... |
| 23:35 | <devsnek> | am i blind or is there no definition of BindingInitialization for ForBinding |
| 23:35 | <Bakkot> | without looking I would guess you don't need one because of the chain production rule |
| 23:37 | <devsnek> | so ForBinding : BindingPattern and ForBinding: BindingIdentifier |
| 23:37 | <devsnek> | automatically go to 13.3.3.5 and 12.1.5? |
| 23:38 | <Bakkot> | > Unless explicitly specified otherwise, all chain productions have an implicit definition for every operation that might be applied to that production's left-hand side nonterminal. The implicit definition simply reapplies the same operation with the same parameters, if any, to the chain production's sole right-hand side nonterminal and then returns the result. |
| 23:38 | <devsnek> | i see |