Constructing the HashMap in DeclarativeEnvironment was by far the most
expensive thing when making JavaScript function calls.
As it turns out, we don't really need this to be a HashMap in the first
place, as lookups are cached (by EnvironmentCoordinate) after the first
access, so after that we were not even looking in the HashMap, going
directly to the bindings Vector instead.
This reduces function_declaration_instantiation() from 16% to 9% when
idling in "Biolab Disaster". It also reduces has_binding() from 3% to
1% on the same content.
With these changes, we now actually get to idle a little bit between
game frames on my machine. :^)
This initial version lays down the basic foundation of IDL overload
resolution, but much of it will have to be replaced with the actual IDL
overload resolution algorithms once we start implementing more complex
IDL overloading scenarios.
Before this the event loop was spun until the state of the promise was
not pending, however it is possible that a promise has already been
fulfilled/rejected when awaiting it. This could then lead to a crash
below as it would not pump the event loop in such cases.
Although this change is in LibJS, it really only impacts any usage of
LibJS within a EventLoop environment such as LibWeb.
Instead of checking the state of the promise we know check that success
has a value which can only happen if either the fulfilled or rejected
closure set up by await are called.
This follows the ECMA402 spec and means String.prototype.localeCompare
will automatically become actually locale aware once StringCompare is
actually implemented based on UTS #10.
This commit adds an initial implementation (without any real locale
support) of Collator Compare Functions, as well as the matching
CompareStrings AO. These two are used to implement the ECMA402 version
of String.localeCompare() and Int.Collator.compare().
Resolves one FIXME where we can now pass a realm, and sets the length
correctly in a bunch of places that previously didn't.
Also reduces the number of "format function name string from arbitrary
PropertyKey" implementations, although two more remain present in the
AST (used with ECMAScriptFunctionObjects, which is a different beast).
Also take a length argument and set the name and length properties
internally, instead of at the call site. Additionally, allow passing a
realm, prototype, and prefix.
We were previously manually initializing them instead of just calling
GlobalObject::initialize_constructor, which aside from duplicating code
also meant we didn't set the required name property.
The JS behaviour of exponentiation on two number typed values is
not a simple matter of forwarding to ::pow(double, double). So,
this factors out the Math.pow logic to allow it to be shared with
Value::exp.
We have a fair amount of hard-coded keywords / aliases that can now be
replaced with real data from BCP 47. As a result, the also changes the
awkward way we were previously generating keys. Before, we were more or
less generating keywords as a CSV list of keys, e.g. for the "nu" key,
we'd generate "latn,arab,grek" (ordered by locale preference). Then at
runtime, we'd split on the comma. We now just generate spans of keywords
directly.
The ECMA verbiage for modulus is the mathematical definition implemented
by fmod, so let's just use that rather than trying to reimplement all
the edge cases.
A common use case in JS is pushing items in an array in a loop.
A simple test case of 100_000 pushes took around ~20 seconds.
This is due to the fact that any pushed element per definition is beyond
the current size of the array. This meant calling grow_storage_if_needed
which then grew the storage by 25%. But this was done on every single
push, growing the array just a little bigger than its current capacity.
Now we now first use capacity of the array and only grow if the array
is actually full.
This decreases the time for 100_000 to around 0.35 seconds.
One problem is that we never shrink the capacity of the array but this
was already an issue before this.
The spec version of canonical_numeric_index_string is absurdly complex,
and ends up converting from a string to a number, and then back again
which is both slow and also requires a few allocations and a string
compare.
Instead this patch moves away from using Values to represent canonical
a canonical index. In most cases all we need to know is whether a
PropertyKey is an integer between 0 and 2^^32-2, which we already
compute when we construct a PropertyKey so the existing is_number()
check is sufficient.
The more expensive case is handling strings containing numbers that
don't roundtrip through string conversion. In most cases these turn
into regular string properties, but for TypedArray access these
property names are not treated as normal named properties.
TypedArrays treat these numeric properties as magic indexes that are
ignored on read and are not stored (but are evaluated) on assignment.
For that reason there's now a mode flag on canonical_numeric_index_string
so that only TypedArrays take the cost of the ToString round trip test.
In order to improve the performance of this path this patch includes
some early returns to avoid conversion in cases where we can quickly
know whether a property can round trip.
This reverts commit 3a184f7841.
This broke a number of test262 tests under "TypedArrayConstructors".
The issue is that the CanonicalNumericIndexString AO should not fail
for inputs like "1.1", despite them not being integral indices.
Now we emit CreateVariable and SetVariable with the appropriate
initialization/environment modes, much closer to the spec.
This makes a whole lot of things like let/const variables, function
and variable hoisting and some other things work :^)
Instead of crashing on the spot, return a descriptive error that will
eventually continue its days as a javascript "InternalError" exception.
This should make random crashes with BC less likely.
The vast majority of objects do not, and are unlikely to ever need
indexed property storage. By delaying the creation of the backing
store of IndexedProperties we reduce the memory used by each object
and reduce allocation and deallocation by somewhere between 20 and
30%
When performing GetValue on a primitive type we do not need to perform
the ToObject conversion as it will resolve to a property on the
prototype object.
To avoid this we skip the initial ToObject conversion on the base value
as it only serves to get the primitive's boxed prototype. We further
specialize on PrimitiveString in order to get efficient behaviour
behaviour for the direct properties.
Depending on the tests anywhere from 20 to 60%, with significant loop
overhead.
The spec version of canonical_numeric_index_string is absurdly complex,
and ends up converting from a string to a number, and then back again
which is both slow and also requires a few allocations and a string
compare.
Instead lets use the logic we already have as that is much more
efficient.
This improves performance of all non-numeric property names.
In the ThrowCompletionOr constructors, the VERIFY statements are using
moved-from objects. We should not rely on those objects still being
valid after being moved.
Before this would assume that the element found in operator++ was still
valid when dereferencing it in operator*.
Since any code can have been run since that increment this is not always
valid.
To further simplify the logic of the iterator we no longer store the
index in an optional.