Commit graph

296 commits

Author SHA1 Message Date
Andreas Kling
4724b08a26 LibJS: Make MemberExpression::to_string_approximation() recursive
Previously, the function only handled a single level of member access,
producing strings like "<object>.isWall" for chained expressions like
"graphSet[j][k].isWall". Now it recurses through nested member
expressions, identifiers, string/numeric literals, and `this`.
2026-02-15 23:21:46 +01:00
Andreas Kling
2fd75d948b LibJS: Remove unused Program::global_declaration_instantiation() 2026-02-11 23:57:41 +01:00
Andreas Kling
e308e73120 LibJS: Move SharedFunctionInstanceData creation out of FunctionNode
Add static factory methods create_for_function_node() on
SharedFunctionInstanceData and update all callers to use them instead
of FunctionNode::ensure_shared_data().

This removes the GC::Root<SharedFunctionInstanceData> cache from
FunctionNode, eliminating the coupling between the RefCounted AST
and GC-managed runtime objects. The cache was effectively dead code:
hoisted declarations use m_functions_to_initialize directly, and
function expressions always create fresh instances during codegen.
2026-02-11 23:57:41 +01:00
Andreas Kling
0e0818a232 LibJS: Remove dead AST class evaluation code
Now that class construction is driven by ClassBlueprint, remove the
old AST-based class element evaluation and class constructor creation
code:

- ClassExpression::create_class_constructor()
- ClassMethod::class_element_evaluation()
- ClassField::class_element_evaluation()
- StaticInitializer::class_element_evaluation()
- ClassElement::ClassValue typedef
- update_function_name() and class_key_to_property_name() helpers

Also remove includes that are no longer needed.
2026-02-11 23:57:41 +01:00
Andreas Kling
a8a1aba3ba LibJS: Replace ScopePusher with ScopeCollector
Replace the ScopePusher RAII class (which performed scope analysis
in its destructor chain during parsing) with a two-phase approach:

1. ScopeCollector builds a tree of ScopeRecord nodes during parsing
   via RAII ScopeHandle objects. It records declarations, identifier
   references, and flags, but does not resolve anything.

2. After parsing completes, ScopeCollector::analyze() walks the tree
   bottom-up and performs all resolution: propagate eval/with
   poisoning, resolve identifiers to locals/globals/arguments, hoist
   functions (Annex B.3.3), and build FunctionScopeData.

Key design decisions:
- ScopeRecord::ast_node is a RefPtr<ScopeNode> to prevent
  use-after-free when synthesize_binding_pattern re-parses an
  expression as a binding pattern (the original parse's scope records
  survive with stale AST node pointers).
- Parser::scope_collector() returns the override collector if set
  (for synthesize_binding_pattern's nested parser), ensuring all
  scope operations route to the outer parser's scope tree.
- FunctionNode::local_variables_names() delegates to its body's
  ScopeNode rather than copying at parse time, since analysis runs
  after parsing.
2026-02-10 02:05:20 +01:00
Andreas Kling
52ddc15fb3 LibJS: Redesign AST dump with unicode tree drawing
Replace the old indentation-based AST dump with a new tree-drawing
approach using unicode box characters. Each node now also shows its
source position as @line:column, and additional internal state:

- Identifier: [argument:N] vs [variable:N], declaration kind
  (var/let/const), [global], [in-eval-scope]
- FunctionNode: [strict], [arrow], [direct-eval], [uses-this],
  [uses-this-from-environment], [might-need-arguments]
- Program: (script)/(module), [strict], [top-level-await]
- YieldExpression: [yield*] for delegation

Dump code is moved from AST.cpp into a new ASTDump.cpp file.
2026-02-10 02:05:20 +01:00
Andreas Kling
88d715fc68 LibJS: Eliminate HashMap operations in SFID by caching parser data
Cache necessary data during parsing to eliminate HashMap operations
in SharedFunctionInstanceData construction.

Before: 2 HashMap copies + N HashMap insertions with hash computations
After: Direct vector iteration with no hashing

Build FunctionScopeData for function scopes in the parser containing:
- functions_to_initialize: deduplicated var-scoped function decls
- vars_to_initialize: var decls with is_parameter/is_function_name
- var_names: HashTable for AnnexB extension checks
- Pre-computed counts for environment size calculation
- Flags for "arguments" handling

Add ScopeNode::ensure_function_scope_data() to compute the data
on-demand for edge cases that don't go through normal parser flow
(synthetic class constructors, static initializers, module wrappers).

Use this cached data directly in SFID with zero HashMap operations.
2026-01-25 23:08:36 +01:00
Jelle Raaijmakers
ae20ecf857 AK+Everywhere: Add Vector::contains(predicate) and use it
No functional changes.
2026-01-08 15:27:30 +00:00
Andreas Kling
3ee80b23d3 LibJS: Store full realized SourceRange with each AST node
We were spending way too much time converting unrealized source ranges
into line/column pairs on real web content.

This improves JS parsing speed on x.com by 1.13x
2025-12-29 13:36:01 +01:00
Andreas Kling
3610a31279 LibJS: Pack members of the AST Identifier class better
A bit of creative structure packing brings this from 80 to 56 bytes.
This is hugely impactful on x.com where we have roughly ~2.3 million
Identifier objects after loading the home feed.

In other words, this reduces memory usage on that page by up to 55 MiB.

We should eventually discard most of the AST after parsing, but that
will require more architectural work so this is a nice stopgap
improvement before then.
2025-12-21 15:13:47 -06:00
Andreas Kling
63eccc5640 LibJS: Don't make extra copies of every JS function's source code
Instead, let functions have a view into the AST's SourceCode object's
underlying string data. The source string is kept alive by the AST, so
it's fine to have views into it as long as the AST exists.

Reduces memory footprint on my x.com home feed by 65 MiB.
2025-12-21 10:06:04 -06:00
Andreas Kling
ece0b72e3c LibJS: Don't set [[HomeObject]] for non-method object properties
This fixes an issue where we'd incorrectly retain objects via the
[[HomeObject]] slot. This common pattern was affected:

    Object.defineProperty(o, "foo", {
        get: function() { return 123; }
    });

Above, the object literal would get assigned to the [[HomeObject]]
slot even though "get" is not a "method" per the spec.

This frees about 30,000 objects on my x.com home feed.
2025-12-17 12:50:17 -06:00
Andreas Kling
cb23d65625 LibJS: Pass JS::Value directly to string formatting functions
We don't need to call .to_string_without_side_effects() when passing
a JS::Value in for string formatting. The Formatter will do it for us.
2025-12-09 21:44:13 -06:00
Andreas Kling
3fb678b376 LibGC: Delete operators ! and bool from GC::Ref
The GC::Ref smart pointer is always non-null, so there's no need for it
to be convertible to bool.

This exposed a small number of unnecessary null checks which we remove.
2025-10-29 21:20:10 +01:00
Andreas Kling
9312a9f86f LibJS: Move InstantiateOrdinaryFunctionExpression into interpreter
This is execution time stuff and doesn't belong in the AST.
2025-10-27 21:14:33 +01:00
Andreas Kling
44fa9566a8 LibJS: Generate bytecode for the BlockDeclarationInstantiation AO
This necessitated adding some new instructions for creating mutable and
immutable bindings.
2025-10-27 21:14:33 +01:00
Andreas Kling
3a38040c82 LibJS: Make SharedFunctionInstanceData GC-allocated 2025-10-27 21:14:33 +01: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
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
Timothy Flynn
cf61171864 LibJS: Port remaining bytecode identifiers to UTF-16 2025-08-14 10:27:08 +02: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
0efa98a57a LibJS+LibWeb+WebContent: Port JS::PropertyKey to UTF-16
This has quite a lot of fall out. But the majority of it is just type or
UDL substitution, where the changes just fall through to other function
calls.

By changing property key storage to UTF-16, the main affected areas are:
* NativeFunction names must now be UTF-16
* Bytecode identifiers must now be UTF-16
* Module/binding names must now be UTF-16
2025-08-05 07:07:15 -04:00
Aliaksandr Kalenik
b559965448 LibJS+LibWeb: Replace StringOrSymbol usage with PropertyKey
- Avoids unnecessary conversions between StringOrSymbol and PropertyKey
  on the hot path of property access.
- Simplifies the code by removing StringOrSymbol and using PropertyKey
  directly. There was no reason to have a separate StringOrSymbol type
  representing the same data as PropertyKey, just with the index key
  stored as a string.

PropertyKey has been updated to use a tagged pointer instead of a
Variant, so it still occupies 8 bytes, same as StringOrSymbol.

12% improvement on JetStream/gcc-loops.cpp.js
12% improvement on MicroBench/object-assign.js
7% improvement on MicroBench/object-keys.js
2025-05-17 10:08:37 -04:00
Aliaksandr Kalenik
db480b1f0c LibJS: Preserve information about local variables declaration kind
This is required for upcoming change where we want to emit ThrowIfTDZ
for assignment expressions only for lexical declarations.
2025-05-06 12:06:23 +02:00
Andreas Kling
bf1b754e91 LibJS: Optimize reading known-to-be-initialized var bindings
`var` bindings are never in the temporal dead zone (TDZ), and so we
know accessing them will not throw.

We now take advantage of this by having a specialized environment
binding value getter that doesn't check for exceptional cases.

1.08x speedup on JetStream.
2025-05-04 02:31:18 +02:00
Timothy Flynn
15faaeb2bb LibJS: Remove [[VarNames]] from GlobalEnvironment
This is a normative change in the ECMA-262 spec. See:
ed75310
2025-04-29 07:33:08 -04:00
Timothy Flynn
3867a192a1 LibJS: Update spec steps / links for the import-assertions proposal
This proposal has reached stage 4 and been merged into the main ECMA-262
spec. See:

4e3450e
2025-04-29 07:33:08 -04:00
Aliaksandr Kalenik
2d732b2251 LibJS: Skip allocating locals for arguments that allowed to be local
This allows us to get rid of instructions that move arguments to locals
and allocate smaller JS::Value vector in ExecutionContext by reusing
slots that were already allocated for arguments.

With this change for following function:
```js
function f(x, y) {
    return x + y;
}
```

we now produce following bytecode:
```
[   0]    0: Add dst:reg6, lhs:arg0, rhs:arg1
[  10]       Return value:reg6
```

instead of:
```
[   0]    0: GetArgument 0, dst:x~1
[  10]       GetArgument 1, dst:y~0
[  20]       Add dst:reg6, lhs:x~1, rhs:y~0
[  30]       Return value:reg6
```
2025-04-26 11:02:29 +02:00
Aliaksandr Kalenik
0f14c70252 LibJS: Use Identifier to represent CatchClause parameter names
By doing that we consistently use Identifier node for identifiers and
also enable mechanism that registers identifiers in a corresponding
ScopePusher for catch parameters, which is necessary for work in the
upcoming changes.
2025-04-22 21:57:25 +02:00
Andreas Kling
4a5863bcdb LibJS: Remove unnecessary FunctionObject::name() virtual
This allows us to remove the BoundFunction::m_name field, which we
were initializing with a formatted FlyString on every function binding,
despite never using it for anything.
2025-04-10 04:01:00 +02:00
Andreas Kling
2a9b6f1d97 LibJS: Move computation out of the ECMAScriptFunctionObject constructor
We were doing way too much computation every time an ESFO was
instantiated. This was particularly sad, since the results of these
computations were identical every time!

This patch adds a new SharedFunctionInstanceData object that gets
shared between all instances of an ESFO instantiated from some kind of
AST FunctionNode.

~5% speedup on Speedometer 2.1 :^)
2025-04-08 18:52:35 +02:00
Andreas Kling
4209b18b88 LibJS: Add ECMAScriptFunctionObject::create_from_function_node() helper
This gives us a shared entry point for every situation where we
instantiate a function based on a FunctionNode from the AST.
2025-04-08 18:52:35 +02:00
Andreas Kling
ef4e7b7945 LibJS: Make JS parser emit accurate this insights for constructors
This way we don't have to handle it when instantiating the constructor.
2025-04-08 18:52:35 +02:00
Andreas Kling
3cf50539ec LibJS: Make Value() default-construct the undefined value
The special empty value (that we use for array holes, Optional<Value>
when empty and a few other other placeholder/sentinel tasks) still
exists, but you now create one via JS::js_special_empty_value() and
check for it with Value::is_special_empty_value().

The main idea here is to make it very unlikely to accidentally create an
unexpected special empty value.
2025-04-05 11:20:26 +02:00
Andreas Kling
de424d6879 LibJS: Make Completion.[[Value]] non-optional
Instead, just use js_undefined() whenever the [[Value]] field is unused.
This avoids a whole bunch of presence checks.
2025-04-05 11:20:26 +02:00
Andreas Kling
6bb0d585e3 LibJS: Elide function wrapper for class field literal initializers
If a class field initializer is just a simple literal, we can skip
creating (and calling) a wrapper function for it entirely.

1.44x speedup on JetStream3/raytrace-private-class-fields.js
1.53x speedup on JetStream3/raytrace-public-class-fields.js
2025-04-01 23:55:20 +02:00
Andreas Kling
7477002e46 LibJS: Keep parsed function parameters in a shared data structure
Instead of making a copy of the Vector<FunctionParameter> from the AST
every time we instantiate an ECMAScriptFunctionObject, we now keep the
parameters in a ref-counted FunctionParameters object.

This reduces memory usage, and also allows us to cache the bytecode
executables for default parameter expressions without recompiling them
for every instantiation. :^)
2025-03-27 15:00:43 +00:00
Andreas Kling
46a5710238 LibJS: Use FlyString in PropertyKey instead of DeprecatedFlyString
This required dealing with *substantial* fallout.
2025-03-24 22:27:17 +00:00
Timothy Flynn
85b424464a AK+Everywhere: Rename verify_cast to as
Follow-up to fc20e61e72.
2025-01-21 11:34:06 -05:00
Andreas Kling
3bfb0534be LibGC: Rename MarkedVector => RootVector
Let's try to make it a bit more clear that this is a Vector of GC roots.
2024-12-26 19:10:44 +01:00
Pavel Shliak
6f81b80114 Everywhere: Include HashMap only where it's actually used 2024-12-09 12:31:16 +01:00
Shannon Booth
f87041bf3a LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:

 * JS::NonnullGCPtr -> GC::Ref
 * JS::GCPtr -> GC::Ptr
 * JS::HeapFunction -> GC::Function
 * JS::CellImpl -> GC::Cell
 * JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
Andreas Kling
10724a7cb3 LibJS: Use ConservativeVector when instantiating static class fields
This fixes an issue where a badly-timed garbage collection could swallow
a static field initializer.

Caught by running test262 in GC-on-every-allocation mode.
2024-11-12 17:38:21 +01:00
Andreas Kling
5aa1d7837f LibJS: Don't leak class field initializers
We were storing these in Handle (strong GC roots) hanging off of
ECMAScriptFunctionObject which effectively turned into world leaks.
2024-11-10 19:12:59 +01:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Andreas Kling
13d7c09125 Libraries: Move to Userland/Libraries/ 2021-01-12 12:17:46 +01:00
Andreas Kling
db790dda62 LibJS: Remove hand-rolled type information in JS AST in favor of RTTI 2021-01-01 19:34:07 +01:00
Andreas Kling
f48751a739 LibJS: Remove hand-rolled Object is_foo() helpers in favor of RTTI 2021-01-01 17:46:39 +01:00
AnotherTest
b34b681811 LibJS: Track source positions all the way down to exceptions
This makes exceptions have a trace of source positions too, which could
probably be helpful in making fancier error tracebacks.
2020-12-29 00:58:43 +01:00
AnotherTest
7c8d35600c Spreadsheet: Override visit_edges() and visit stored JS objects
...and don't let them leak out of their evaluation contexts.
Also keep the exceptions separate from the actual values.
This greatly reduces the number of assertions hit while entering random
data into a sheet.
2020-12-22 23:35:29 +01:00