01:39 | <Jack Works> | What is necessary for "compelling"? We've been using a non-portable node C++ addon for 5 years because this doesn't exist, does that count as a compelling reason? |
02:11 | <Randy Luecke> | You need it so strongly and you need a hack in the engine to do that? That's a bit strange Tolmasky (and I) work on a product where one of the major features is showing users their output values in a nice inspectable way. Basically any native object type (RegExp, Error, Promise, BooleanObject, NumberObject, etc) created in a different realm would be hard to identify without asking V8. You can see the high level problem here: https://runkit.com/me1000/63af94f29458cb00082a18e8 Browsers don't have node's vm module of course, but realms exist in browsers too (iframes, windows, workers) and you can pass values between them, to say nothing of the ShadowRealm proposal. |
02:13 | <Kris Kowal> | It would be nice in general if a host program could inspect a guest program without effecting observable side-effects. |
02:26 | <tolmasky> | Jack Works: I also wouldn't call it a hack to use public APIs of the engine to get this information. It is of course less ideal than being able to do it in language, especially considering that it seems to be more an accident of history which native objects can and can't be identified, especially considering almost all can be, with only seeming exceptions being Promise and Errors. I'd consider it "hackier" that this information is "leaked" by the language spec through various techniques (such as calling Boolean.prototype.toString(object) and detecting whether that throws an error), vs. the underlying engine's simple isBooleanObject() method). The fact that Array.isArray had to be introduced points to general utility here. |
02:29 | <tolmasky> | And to Kris Kowal 's point, its actually the case that Error and Promise can be identified, just not without triggering side effects (as mentioned above Promise.prototype.then.apply(object) can almost be used, but it changes the isHandled state, and structuredClone(object) instanceof Error can also almost be used, except structuredClone calls getters of the object and can thus change various things in the process) |
02:41 | <tolmasky> | I'd go so far as to argue that if you believe there is utility in "instanceof BuiltInType", then it should follow that a brand check is equally useful since "instanceof BuiltInType" immediately introduces the possibility of a bug if the object comes from another realm (a frequent occurrence in browsers where you have multiple window objects) -- this was the reason that Array.isArray was added in the first place, because "instanceof Array" would fail in those circumstances. Well... "instanceof Error", "instanceof RegExp", etc. etc. still fail under those circumstances. Not sure why individual cases need to be made for each of them. It is if nothing else very useful for making the simplest "in language" debug tools, like you find in code sandbox-type applications (of which there are many many, everything from the built-in consoles in browsers, to codesandbox.com, to runkit, etc. etc.), all of which regularly deal with the state of js objects in a separate frame (and thus realm). |
06:57 | <ljharb> | btw can someone explain to me why Object.assign(new Float32Array(1), [2147483647])[0] produces 2147483648 ? 2147483647 is a 31 bit number so it doesn't seem like it'd be an overflow thing. (i'm sure the answer is "floating point" but id love a better explanation) |
06:58 | <snek> | just because an integer occupies n bits doesn't mean a float with that many bits can represent it |
06:59 | <snek> | for example, many integers greater than 2^53 in our float64s |
07:00 | <ljharb> | ok, so it's not overflow exactly, it's just a gap that float32 can't represent? |
07:01 | <snek> | I would assume, I don't have any intuition of what values are representable by 32 bit floats |
07:01 | <ljharb> | hmm, ok |
07:02 | <snek> | there are ways to validate that. for example you could convert the underlying buffer to Uint32, and increment it by 1 at a time to see what values come out in the float32 |
07:05 | <ljharb> | thanks, that makes sense |
07:05 | <ljharb> | 2147483646 seems to be not representable either :-) |
07:09 | <bakkot> | ljharb: max safe integer for 32-bit floats is 2**24 |
07:10 | <ljharb> | aha, thanks |
07:10 | <bakkot> | https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limitations_on_integer_values |
20:02 | <Kris Kowal> | Yeah, floating point can express only as many integers as fit in the mantissa portion. Single precision floats are criminally smol. The neat thing about floats though is that they get one free bit of mantissa since the first bit is guaranteed to be 1! The microcontroller just always shifts it off when incrementing the mantissa. An optimization only possible in base 2. |