Commit graph

1696 commits

Author SHA1 Message Date
Aliaksandr Kalenik
2a288f6d53 LibJS: Invalidate PrototypeChainValidity when prototype shape is a dict
Adds missing PrototypeChainValidity invalidation for add/set/delete
operations on dictionary-mode prototype shapes.
2025-09-22 18:41:23 +02:00
Aliaksandr Kalenik
1d41cf10f4 LibJS: Optimize keys deduplication in get_object_property_iterator()
This change implements a part responsible for this invariant in a more
efficient way:
"Enumerating the properties of the target object includes enumerating
properties of its prototype, and the prototype of the prototype, and so
on, recursively; but a property of a prototype is not processed if it
has the same name as a property that has already been processed by the
iterator's next method."

Previously we inserted `(key, enumerable)` pairs into an
`OrderedHashTable`. That always built and maintained a hash table, even
when no prototype-level filtering was needed.

Now we:
- Collect only enumerable keys into `Vector<PropertyKey>`.
- Track `seen_non_enumerable_properties` so a non-enumerable own
  property still shadows prototype properties with the same name.
- Lazily materialize `HashTable<PropertyKey>` only if we encounter an
  enumerable property on a prototype and must check for duplicates. In
  the common case materialization is avoided, because default Object or
  Array prototype properties are non-enumerable.
2025-09-21 15:06:32 +02:00
Aliaksandr Kalenik
451c947c3f LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
  keys.
- For each key, call internal_get_own_property() to obtain a
  descriptor and check `[[Enumerable]]`.

While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.

This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-21 15:06:32 +02:00
Timothy Flynn
5bd867f1dc LibJS: Revert inadvertent observable Temporal change
This is an editorial change in the Temporal proposal. See:
6274e5d
2025-09-19 11:24:02 -04:00
Aliaksandr Kalenik
c3b0eabf18 LibJS: Capture PrototypeChainValidity before executing internal_get()
- Capture PrototypeChainValidity before invoking `internal_get()`. A
  getter may mutate the prototype chain (e.g., delete itself). Capturing
  earlier ensures such mutations invalidate the cached entry and prevent
  stale GetById hits.
- When caching, take PrototypeChainValidity from the base object
  (receiver), not from the prototype where the property was found.
  Otherwise, changes to an intermediate prototype between the base
  object and the cached prototype object go unnoticed, leading to
  incorrect cache hits.
2025-09-18 15:56:20 +02:00
Jelle Raaijmakers
c31eff6a47 Everywhere: Use Optional<T>::ensure() where useful
No functional changes.
2025-09-17 12:01:18 -04:00
Aliaksandr Kalenik
d45f8a3081 LibJS: Add inline caching for adding new own properties to objects
We already had IC support in PutById for the following cases:
- Changing an existing own property
- Calling a setter located in the prototype chain

This was enough to speed up code where structurally identical objects
(same shape) are processed in a loop:
```js
const arr = [{ a: 1 }, { a: 2 }, { a: 3 }];
for (let obj of arr) {
    obj.a += 1;
}
```

However, creating structurally identical objects in a loop was still
slow:
```js
for (let i = 0; i < 10_000_000; i++) {
    const o = {};
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```

This change addresses that by adding a new IC type that caches both the
source and target shapes, allowing property additions to be fast-pathed
by directly jumping to the shape that already includes the new property.
2025-09-17 12:44:44 +02:00
Aliaksandr Kalenik
a54215c07d LibJS: Make internal_define_own_property() save added property offset
...in `PropertyDescriptor`. This is required for the upcoming change
that needs to know offset of newly added properties to set up inline
caching.
2025-09-17 12:44:44 +02:00
Aliaksandr Kalenik
85e029b2e7 LibJS+LibWeb: Inline fast path for Value::to_object()
Adds inline implementation for the most common case when `Value` is
already an object.

1.47x improvement on the following benchmark:
```js
const o = {};
for (let i = 0; i < 10_000_000; i++) {
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```
2025-09-15 12:16:58 +02:00
Aliaksandr Kalenik
0076ab74b2 LibJS: Don't attempt to cast property key to number in PutByIdWithThis
Property key for this instruction cannot be a number.
2025-09-13 20:02:28 +02:00
Aliaksandr Kalenik
e81833423b LibJS: Add PutByNumericId and change PutById to be string key only
Previously, PutById constructed a PropertyKey from the identifier,
which coerced numeric-like strings to numbers. This moves that decision
to bytecode generation: the bytecode generator now emits PutByNumericId
for numeric keys and PutById for string keys. This removes per-execution
parsing from the interpreter.

1.4x speedup on the following microbenchmark:
```js
const o = {};
for (let i = 0; i < 10_000_000; i++) {
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```
2025-09-13 20:02:28 +02:00
Pavel Shliak
4f0e8236a0 LibJS: Make class accessors non-enumerable
According to ECMA-262 §15.4.5 (MethodDefinitionEvaluation),
getters and setters defined in class bodies
must create property descriptors with
[[Enumerable]]: false. Previously we incorrectly marked them enumerable.

This patch updates `ClassMethod::class_element_evaluation` so that both
getter and setter descriptors use `.enumerable = false`.
2025-09-07 08:35:10 -04:00
Luke Wilde
18c0739bbb LibJS: Copy base object of LHS of assignment to preserve eval order
Previously, the given test would create an object with the test
property that pointed to itself.

This is because `temp = temp.test || {}` overwrote the `temp` local
register, and `temp.test = temp` used the new object instead of the
original one it fetched.

Allows https://www.yorkshiretea.co.uk/ to load, which was failing in
Gsap library initialization.
2025-09-02 12:59:52 +01:00
Ali Mohammad Pur
4462348916 Everywhere: Slap some [[clang::lifetimebound]] where appropriate
This first pass only applies to the following two cases:
- Public functions returning a view type into an object they own
- Public ctors storing a view type

This catches a grand total of one (1) issue, which is fixed in
the previous commit.
2025-09-01 11:11:38 +02:00
Andreas Kling
f89afe8e27 LibJS: Allocate context up front in SuperCallWithArgumentArray
This also removes the last user of Interpreter's argument buffer
allocation API, which we've used to repeatedly shoot ourselves in the
foot. Good-bye!
2025-08-31 15:24:37 +02:00
Andreas Kling
996ea109b3 LibJS: Allocate context up front when calling with argument array
This necessitated splitting CallWithArgumentArray into three variants,
one for each call type (call, construct and direct eval).
2025-08-31 15:24:37 +02:00
Andreas Kling
e5b07858a2 LibJS: Allocate Call{Construct,DirectEval,Builtin) contexts up front
We already do this for normal Call contexts, so this is just continuing
to propagate the same pattern to other instructions.

Fixes #6026
2025-08-31 15:24:37 +02:00
Luke Wilde
b17783bb10 Everywhere: Change west consts caught by clang-format-21 to east consts 2025-08-29 18:18:55 +01:00
Timothy Flynn
2fa6655dcb LibJS: Ensure NudgeToCalendarUnit is given a non-zero duration sign
Otherwise, we trip internal Temporal spec assertions.

This is an editorial change in the Temporal spec. See:
30f8575
2025-08-29 01:14:20 +02:00
Timothy Flynn
0c038bf12e LibJS: Read user options in a Temporal AO sooner
This is a normative change in the Temporal proposal. See:
9924aa4
2025-08-29 01:14:20 +02:00
Timothy Flynn
355589a89e LibJS: Read user options in some Temporal toString methods sooner
This is a normative change in the Temporal proposal. See:
3eaaadf
2025-08-29 01:14:20 +02:00
ayeteadoe
3355b7fb1f Tests/LibJS: Enable test-js on Windows 2025-08-24 12:58:27 -06:00
ayeteadoe
6dbb59da77 LibJS: Export symbols causing linker errors in various consumers
After LibJS had its symbol exports optimized the targets
js, test-js, test262-runner, test-wasm, and LibWeb began to get linker
errors after the work to add Windows support for test-web and ladybird
targets. These extra JS_API annotations fix all those linker errors.
2025-08-23 16:04:36 -06:00
ayeteadoe
0a699132f3 WebContent: Enable in Windows CI 2025-08-23 16:04:36 -06:00
ayeteadoe
ee3c033de2 LibWebView: Enable in Windows CI 2025-08-23 16:04:36 -06:00
Idan Horowitz
66bd7fa530 LibJS: Add missing visit of Realm::m_builtins 2025-08-23 21:21:04 +02:00
CountBleck
a2dc6c4bbb LibJS: Keep the lengths of ArrayBuffers with unowned ByteBuffers fixed
The relevant type of ArrayBuffer DataBlock is now a struct containing
both a ByteBuffer* and a size_t size, and not just a ByteBuffer*, with
the size being that of the ByteBuffer. This type of DataBlock is only
used for WebAssembly.Memory (see commit 4fd43a8f96), meaning this
change won't affect any other code. This change is required to pass one
WPT subtest in wasm/jsapi/memory/grow.any.html, since old fixed-length
SharedArrayBuffers after a WebAssembly.Memory growth should keep their
length, while the new buffer after the growth will have the updated
length.
2025-08-23 08:26:23 +02:00
Timothy Flynn
5ec70bd00a Revert "LibJS: Revert common error types to only hold a single string"
This reverts commit 695bbcb991.

Despite improving performance on my Linux machine, this managed to tank
performance on the Linux benchmark machine.
2025-08-18 13:42:22 +02:00
Timothy Flynn
695bbcb991 LibJS: Revert common error types to only hold a single string
Before porting to UTF-16, these instances held a String. The port to
UTF-16 changed them to hold the original string as a StringView, and
lazily allocated the UTF-16 message as needed. This somehow negatively
impacting the zlib.js benchmark in the Octane suite.
2025-08-18 01:43:45 +02:00
Tete17
08284e0ef6 LibJS: Implement console.dirxml
This function actually gets tested in WPT.
2025-08-17 07:28:56 -04:00
Timothy Flynn
e314ca5e9d LibJS: Introduce ParseMonthCode and CreateMonthCode Temporal AOs
This is an editorial change in the Temporal proposal. See:
28357ea
32f4b02
f860ac6
e6f565d
2025-08-14 11:35:28 -04:00
Timothy Flynn
0f4d5d3abc LibJS: Remove no-op call to ValidateTemporalUnitValue
This is an editorial change in the Temporal proposal. See:
07c924b
2025-08-14 11:35:28 -04:00
Timothy Flynn
70db474cf0 LibJS+LibWeb: Port interned bytecode strings to UTF-16
This was almost a no-op, except we intern JS exception messages. So the
bulk of this patch is porting exception messages to UTF-16.
2025-08-14 10:27:08 +02:00
Timothy Flynn
cf61171864 LibJS: Port remaining bytecode identifiers to UTF-16 2025-08-14 10:27:08 +02:00
Timothy Flynn
829fd25264 LibJS: Add a UTF-16 variant of Value::to_string_without_side_effects 2025-08-14 10:27:08 +02:00
Timothy Flynn
c87122eb32 LibJS: Add a method to stringify a BigInt to UTF-16
And remove the ByteString variant while we are here.
2025-08-14 10:27:08 +02:00
Timothy Flynn
62d85dd90a LibJS: Port RegExp flags and patterns to UTF-16 2025-08-13 09:56:13 -04:00
Timothy Flynn
b955c9b2a9 LibJS: Port the Identifier AST (and related) nodes to UTF-16
This eliminates quite a lot of UTF-8 / UTF-16 churn.
2025-08-13 09:56:13 -04:00
Timothy Flynn
00182a2405 LibJS: Port the JS lexer and parser to UTF-16
This ports the lexer to UTF-16 and deals with the immediate fallout up
to the AST. The AST will be dealt with in upcoming commits.

The lexer will still accept UTF-8 strings as input, and will transcode
them to UTF-16 for lexing. This doesn't actually incur a new allocation,
as we were already converting the input StringView to a ByteString for
each lexer.

One immediate logical benefit here is that we do not need to know off-
hand how many UTF-8 bytes some special code points occupy. They all
happen to be a single UTF-16 code unit. So instead of advancing the
lexer by 3 positions in some cases, we can just always advance by 1.
2025-08-13 09:56:13 -04:00
Timothy Flynn
eb74781a2d LibJS: Keep the lookahead lexer alive after parsing its next token
Currently, the lexer holds a ByteString, which is always heap-allocated.
When we create a copy of the lexer for the lookahead token, that token
will outlive the lexer copy. The token holds a couple of string views
into the lexer's source string. This is fine for now, because the source
string will be kept alive by the original lexer.

But if the lexer were to hold a String or Utf16String, short strings
will be stored on the stack due to SSO. Thus the token will hold views
into released stack data. We need to keep the lookahead lexer alive to
prevent UAF on views into its source string.
2025-08-13 09:56:13 -04:00
Timothy Flynn
8472e469f4 AK+LibJS+LibWeb: Recognize that our UTF-16 string is actually WTF-16
For the web, we allow a wobbly UTF-16 encoding (i.e. lonely surrogates
are permitted). Only in a few exceptional cases do we strictly require
valid UTF-16. As such, our `validate(AllowLonelySurrogates::Yes)` calls
will always succeed. It's a wasted effort to ever make such a check.

This patch eliminates such invocations. The validation methods will now
only check for strict UTF-16, and are only invoked when needed.
2025-08-13 09:56:13 -04:00
Timothy Flynn
99d7e08dff AK: Templatize GenericLexer for UTF-16 strings
We now define GenericLexer as a template to allow using it with UTF-16
strings. To keep existing users happy, the template is defined in the
Detail namespace. Then AK::GenericLexer is an alias for a char-based
view, and AK::Utf16GenericLexer is an alias for a char16-based view.
2025-08-13 09:56:13 -04:00
Timothy Flynn
28d9d3a2c7 AK+Libraries: Reduce API surface of GenericLexer a bit
* Remove completely unused methods.
* Deduplicate methods that were overloaded with both StringView and
  char const* parameters.

A future commit will templatize GenericLexer by char type. This patch
serves to make that a tiny bit easier.
2025-08-13 09:56:13 -04:00
Timothy Flynn
e2b245add1 LibJS: Handle out-of-range prefixed numbers in Token::double_value
This regressed in cd15b1a2c9.

If a prefixed number is out-of-range of a u64, stroul would previously
fall back to ULONG_MAX. This patch restores that behavior.
2025-08-10 13:35:37 +02:00
Aliaksandr Kalenik
4b3a87eb14 LibJS: Add fast path for Array.prototype.shift
Makes `MicroBench/array-prototype-shift.js` 100x faster on my machine.

Progress on https://github.com/LadybirdBrowser/ladybird/issues/5725
2025-08-08 18:10:14 +02:00
Ali Mohammad Pur
4e2845847b LibJS: Add a fast-path to <Int32>.to_uint8()
This was showing up in a profile as hot.
2025-08-08 12:54:06 +02:00
Timothy Flynn
cd15b1a2c9 LibJS: Use AK's number parsing over stroul in JS::Token
This gives us a drop-in replacement for UTF-16 strings.
2025-08-07 02:05:50 +02:00
Timothy Flynn
20995c620f LibJS: Remove MarkupGenerator
It is unused.
2025-08-07 02:05:50 +02:00
Timothy Flynn
3920194bca LibJS: Move regulating and balancing logic into ISODateSurpasses
This is an editorial change in the Temporal proposal. See:
eddb77f
2025-08-05 11:18:08 -04:00
Timothy Flynn
a95d3e2a5e LibJS: Split ISO and non-ISO Temporal calendar operations
This is an editorial change in the Temporal proposal. See:
47042f2
2025-08-05 11:18:08 -04:00