- Prefer VM::current_realm() over GlobalObject::associated_realm()
- Prefer VM::heap() over GlobalObject::heap()
- Prefer Cell::vm() over Cell::global_object()
- Prefer Wrapper::vm() over Wrapper::global_object()
- Inline Realm::global_object() calls used to access intrinsics as they
will later perform a direct lookup without going through the global
object
Instead of passing a GlobalObject everywhere, we will simply pass a VM,
from which we can get everything we need: common names, the current
realm, symbols, arguments, the heap, and a few other things.
In some places we already don't actually need a global object and just
do it for consistency - no more `auto& vm = global_object.vm();`!
This will eventually automatically fix the "wrong realm" issue we have
in some places where we (incorrectly) use the global object from the
allocating object, e.g. in call() / construct() implementations. When
only ever a VM is passed around, this issue can't happen :^)
I've decided to split this change into a series of patches that should
keep each commit down do a somewhat manageable size.
This is a continuation of the previous five commits.
A first big step into the direction of no longer having to pass a realm
(or currently, a global object) trough layers upon layers of AOs!
Unlike the create() APIs we can safely assume that this is only ever
called when a running execution context and therefore current realm
exists. If not, you can always manually allocate the Error and put it in
a Completion :^)
In the spec, throw exceptions implicitly use the current realm's
intrinsics as well: https://tc39.es/ecma262/#sec-throw-an-exception
This is a continuation of the previous two commits.
As allocating a JS cell already primarily involves a realm instead of a
global object, and we'll need to pass one to the allocate() function
itself eventually (it's bridged via the global object right now), the
create() functions need to receive a realm as well.
The plan is for this to be the highest-level function that actually
receives a realm and passes it around, AOs on an even higher level will
use the "current realm" concept via VM::current_realm() as that's what
the spec assumes; passing around realms (or global objects, for that
matter) on higher AO levels is pointless and unlike for allocating
individual objects, which may happen outside of regular JS execution, we
don't need control over the specific realm that is being used there.
After the Intl MV is implemented, returning a copy of the desired value
here may involve copying non-trivial data. Instead, return an enum to
indicate which decision was made.
This becomes more of an issue when implementing the Intl mathematical
value, where negative zero is treated as a special enum value. In that
case, we already previously changed the value from -0 to +0 in step 1b.
Entering the branch for step 4 will then set it back to -0.
The math that follows after these steps worked fine with both +0/-0, but
assertions will be reached in the Intl MV implementation.
These were changed to i8 while investigating the issues fixed by commit
9e50f25. When [[RoundingIncrement]] is implemented, some of these will
be invoked with [[RoundingIncrement]]'s value, which can be up to 5000.
Change these to i32, and wrap them with AK::Checked for good measure.
Also change a couple helpers that are always comparing against zero to
not need an explicit check.
The largest exponents we compute are on the order of 10^21 (governed by
the maximumSignificantDigits option, which has a max value of 21). That
is too large to fit into the i64 we were using when multiplying this
exponent by the value to be formatted.
Instead, split up the logic to multiply that value by this exponent
based on the value's underlying type:
Number: Do not cast the result of pow() to an i64, and perform the
follow-up multiplication with doubles.
BigInt: Do not use pow(). Instead, compute the exponent as a BigInt
from the start, then perform the follow-up multiplication with that
BigInt.
In the main spec, [[UseGrouping]] can be true or false. In V3, it may be
one of:
auto: Respect the per-locale preference for grouping.
always: Ignore per-locale preference for grouping and always insert
the grouping separator (note: true is now an alias for always).
min2: Ignore per-locale preference for grouping and only insert the
grouping separator if the primary group has at least 2 digits.
false: Ignore per-locale preference for grouping and never insert
the grouping separator.
This contains minimal changes to parse newly added and modified options
from the Intl.NumberFormat V3 proposal, while maintaining main spec
behavior in Intl.NumberFormat.prototype.format. The parsed options are
reflected only in Intl.NumberFormat.prototype.resolvedOptions and the js
REPL.
This also allows removing a bit of a BigInt hack to resolve plurality of
BigInt numbers (because the AOs used in ResolvePlural support BigInt,
wherease the naive Unicode::select_pattern_with_plurality did not).
We use cardinal form here; the number format patterns in the CLDR align
with the cardinal form of the plural rules.
Intl.NumberFormat is meant to format both Number and BigInt types. To
prepare for formatting BigInt types, this generalizes our NumberFormat
implementation to operate on Value instances rather than doubles. All
arithmetic is moved to static helpers that can now be updated with
BigInt semantics.
Other Intl objects, such as PluralRules, are to be treated as a
NumberFormat object in some AOs. There's only a handful of fields which
are to be shared between those objects - move them to a base class for
shared reuse.
This also updates the couple of NumberFormat AOs that are meant to
operate on these NumberFormat-like objects.
Alternatively, we could just have objects like PluralRules inherit from
NumberFormat directly. But that messes up the is<NumberFormat> runtime
checks, so this feels safer.
Before LibUnicode generated methods were weakly linked, we had a public
method (get_locale_currency_mapping) for retrieving currency mappings.
That method invoked one of several style-specific methods that only
existed in the generated UnicodeLocale.
One caveat of weakly linked functions is that every such function must
have a public declaration. The result is that each of those styled
methods are declared publicly, which makes the wrapper redundant
because it is just as easy to invoke the method for the desired style.
There are a few algorithms in TR-35 that need to replace digits before
returning any results to callers. For example, when formatting time zone
offsets, a string like "GMT+12:34" must have its digits replaced with
the default numbering system for the desired locale.
This is a normative change in the Intl spec:
https://github.com/tc39/ecma402/commit/f0f66cf
There are two main changes here:
1. Converting BigInt/Number objects to mathematical values.
2. A change in how ToRawPrecision computes its exponent and significant
digits.
For (1), we do not yet support BigInt number formatting, thus already
have coerced Number objects to a double. When BigInt is supported, the
number passed into these methods will likely still be a Value, thus can
be coereced then.
For (2), our implementation already returns the expected edge-case
results pointed out on the spec PR.
This is a normative change in the Intl spec:
https://github.com/tc39/ecma402/commit/f0f66cf
Our implementation is unaffected by this change. LibUnicode pre-computes
positive, negative, and signless format patterns, so we already format
negative infinity correctly. Also, the CLDR does not contain specific
locale-dependent strings for negative infinity anyways.
The general idea when ENABLE_UNICODE_DATABASE_DOWNLOAD is OFF has been
that the Intl APIs will provide obviously incorrect results, but should
not crash. This regressed a bit with NumberFormat and DateTimeFormat.
There are 443 number system objects generated, each of which held an
array of number system symbols. Of those 443 arrays, only 39 are unique.
To uniquely store these, this change moves the generated NumericSymbol
enumeration to the public LibUnicode/NumberFormat.h header with a pre-
defined set of symbols that we need. This is to ensure the generated,
unique arrays are created in a known order with known symbols. While it
is unfortunate to no longer discover these symbols at generation time,
it does allow us to ignore unwanted symbols and perform less string-to-
enumeration conversions at lookup time.
Currently, we generate separate data files for locale and number format
related tables/methods, but provide public accessors for all of the data
in one Locale.h file. Rather than continuing this trend for date-time,
relative time, etc. formatting, it's a bit easier to reason about if the
public accessors are also in separate files.
This wasn't the case for compact patterns, but unit patterns can contain
multiple (up to 2, really) identifiers that must each be recognized by
LibJS.
Each generated NumberFormat object now stores an array of identifiers
parsed. The format pattern itself is encoded with the index into this
array for that identifier, e.g. the compact format string "0K" will
become "{number}{compactIdentifier:0}".
This field is currently used to store the StringView into the compact
name/symbol in the format string. Units will need to store a similar
field, so rename the field to be more generic, and extract the parser
for it.
Instead of currency pattern lookups within select_currency_unit_pattern,
rename the method to select_pattern_with_plurality and accept any list
of patterns. This method will be needed for units.