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.
This patch makes check_identifier_name_for_assignment_validity()
take a FlyString instead of a StringView. We then exploit this by
passing FlyString in more places via flystring_value().
This gives a ~1% speedup when parsing the largest Discord JS file.
When parsing identifiers, we ultimately want to sink the token string
into a FlyString anyway, and since Token may have a FlyString already
inside it, this allows us to bypass the costly FlyString(StringView).
This gives a ~3% speedup when parsing the largest Discord JS file.
Everyone who calls this already has a FlyString, so we were doing *way*
more work by pessimizing it to a StringView.
This gives a ~2% speedup when parsing the largest Discord JS file.
If the current character under the lexer cursor is ASCII, we don't need
to create a Utf8View to consume a full code point.
This gives a ~3% speedup when parsing the largest Discord JS file.
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.
This implements ordered sets using Maps with a sentinel value, and
includes some extra set tests.
Fixes#11004.
Co-Authored-By: davidot <davidot@serenityos.org>
Resolve TODOs suggesting the use of a strongly typed MarkedValueList
(a.k.a. MarkedVector<T>) in the following AOs:
- get_possible_instants_for()
- disambiguate_possible_instants()
Note: MarkedVector is still relatively new and has zero users right now,
so these changes don't affect any code other than the class itself.
Reasons for this are the rather limited API:
- Despite the name and unlike MarkedValueList, MarkedVector isn't
actually a Vector, it *wraps* a Vector. This means that plenty of
convenient APIs are unavailable and have to be exported on the class
separately and forwarded to the internal Vector, or need to go through
the exposed Span - both not great options.
- Exposing append(Cell*) and prepend(Cell*) on the base class means that
it was possible to append any Cell type, not just T! All the strong
typing guarantees are basically gone, and MarkedVector doesn't do much
more than casting Cells to the appropriate type through the exposed
Span.
All of this combined means that MarkedVector - in its current form -
doesn't provide much value over MarkedValueList, and that we have to
maintain two separate, yet almost identical classes.
Let's fix this!
The updated MarkedVector steals various concepts from the existing
MarkedValueList, especially the ability to copy. On the other hand, it
remains generic enough to handle both Cell* and Value for T, making
MarkedValueList effectively redundant :^)
Additionally, by inheriting from Vector we get all the current and
future APIs without having to select and expose them separately.
MarkedVectorBase remains and takes care of communicating creation and
destruction of the class to the heap. Visiting the contained values is
handled via a pure virtual method gather_roots(), which is being called
by the Heap's function of the same name; much like the VM has one.
From there, values are added to the roots HashTable if they are cells
for T = Value, and unconditionally for any other T.
As a small additional improvement the template now also takes an
inline_capacity parameter, defaulting to 32, and forwards it to the
Vector template; allowing for possible future optimizations of current
uses of MarkedValueList, which hard-codes it to 32.
This ensures that comparison between TypedArray names will be
essentially free (just a pointer comparison), which will allow us to
efficiently implement specification steps like:
"24. If srcType is the same as targetType, then"
efficiently.
This allows the host of LibJS (notably LibWeb in this case) to override
certain functions such as HostEnqueuePromiseJob, so it can do it's own
thing in certain situations. Notably, LibWeb will override
HostEnqueuePromiseJob to put promise jobs on the microtask queue.
This also makes promise jobs use AK::Function instead of
JS::NativeFunction. This removes the need to go through a JavaScript
function and it more closely matches the spec's idea of "abstract
closures"
The environment settings object is effectively the context a piece of
script is running under, for example, it contains the origin,
responsible document, realm, global object and event loop for the
current context. This effectively replaces ScriptExecutionContext, but
it cannot be removed in this commit as EventTarget still depends on it.
https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
Since VM::exception() no longer exists this is now useless. All of these
calls to clear_exception were just to clear the VM state after some
(potentially) failed evaluation and did not use the exception itself.