Commit graph

157 commits

Author SHA1 Message Date
Andreas Kling
8ff16c1b57 LibJS: Cache access to properties found in prototype chain
We already had fast access to own properties via shape-based IC.
This patch extends the mechanism to properties on the prototype chain,
using the "validity cell" technique from V8.

- Prototype objects now have unique shape
- Each prototype has an associated PrototypeChainValidity
- When a prototype shape is mutated, every prototype shape "below" it
  in any prototype chain is invalidated.
- Invalidation happens by marking the validity object as invalid,
  and then replacing it with a new validity object.
- Property caches keep a pointer to the last seen valid validity.
  If there is no validity, or the validity is invalid, the cache
  misses and gets repopulated.

This is very helpful when using JavaScript to access DOM objects,
as we frequently have to traverse 4+ prototype objects before finding
the property we're interested in on e.g EventTarget or Node.
2024-05-04 21:42:59 +02:00
Andreas Kling
493a04d5fe LibJS: Add PropertyLookupPhase enum to distinguish Object [[Get]] calls
We can now tell the difference between an own property access and a
subsequent (automatic) prototype chain access.

This will be used to implement caching of prototype chain accesses.
2024-05-04 21:42:59 +02:00
Matthew Olsson
ff00d21d58 Everywhere: Mark a bunch of function parameters as NOESCAPE
This fixes the relevant warnings when running LibJSGCVerifier. Note that
the analysis is only performed over LibJS-adjacent code, but could be
performed over the entire codebase. That will have to wait for a future
commit.
2024-04-09 09:10:44 +02:00
Timothy Flynn
d878975f95 AK+LibJS: Remove OFFSET_OF and its users
With the LibJS JIT removed, let's not expose pointers to internal
members.
2024-02-29 09:00:00 +01:00
Shannon Booth
23c5a7a0a3 LibJS: Expose const prototype getter in Object
This is used in the temporal proposal in an assertion.
2024-01-14 16:08:52 -07:00
Shannon Booth
d2710ad73f LibJS: Implement temporal AO SnapshotOwnProperties 2024-01-14 16:08:52 -07:00
Shannon Booth
107fa1fdb8 LibJS: Implement excluded values in CopyDataProperties
This is a change to this AO as part of the Temporal proposal.
2024-01-14 16:08:52 -07:00
Andreas Kling
3d92c26445 LibJS: Stop making shapes unique
We previously had a concept of unique shapes, which meant that they
couldn't be shared between multiple objects.

Object shapes became unique in three situations:

- They were the shape of the global object.
- They had more than 100 properties added to them.
- They had one or more properties deleted from them.

Unfortunately, unique shapes presented an annoying problem for inline
caches, and we added a "unique shape serial number" for being able to
tell that a unique shape had been mutated.

This patch gets rid of the concept of unique shapes, simplifying all
the caching code, since inline caches can now simply perform a shape
check and then we're good.

To make this possible, we now have the concept of delete transitions,
which occur when a property is deleted from a shape.

Note that this patch by itself introduces a performance regression in
some situtations, since we now create a lot more shapes, and marking
their property keys can be very heavy. This will be addressed in a
subsequent patch.
2023-12-11 20:36:15 +01:00
Andreas Kling
463931384d LibJS: Don't use Handle<Value> for JS::Object private fields
There's no reason to use handles here, we can just mark private element
values from objects that store them.
2023-12-10 09:44:26 +01:00
Andreas Kling
373ec387c1 LibJS: Add fast_is<ArrayIterator>() 2023-12-09 00:20:25 +01:00
Andreas Kling
73ceb475b9 LibJS: Add fast path for magical "length" property in LengthOfArrayLike
For Array objects, we can avoid a generic Get here since we know it has
magical "length" behavior anyway.
2023-12-09 00:20:25 +01:00
Andreas Kling
f47a14b9d6 LibJS: Use a premade shape when creating iterator result objects
Instead of going through the steps of creating an empty new object,
and adding two properties ("value" and "done") to it, we can pre-bake
a shape object and cache the property offsets.

This makes creating iterator result objects in the runtime much faster.

47% speedup on this microbenchmark:

    function go(a) {
        for (const p of a) {
        }
    }
    const a = [];
    a.length = 1_000_000;
    go(a);
2023-12-08 00:54:05 +01:00
Andreas Kling
4699c81fc1 LibJS: Stop converting between Object <-> IteratorRecord all the time
This patch makes IteratorRecord an Object. Although it's not exposed to
author code, this does allow us to store it in a VM register.

Now that we can store it in a VM register, we don't need to convert it
back and forth between IteratorRecord and Object when accessing it from
bytecode.

The big win here is avoiding 3 [[Get]] accesses on every iteration step
of for..of loops. There are also a bunch of smaller efficiencies gained.

20% speed-up on this microbenchmark:

    function go(a) {
        for (const p of a) {
        }
    }
    const a = [];
    a.length = 1_000_000;
    go(a);
2023-12-07 14:06:34 +01:00
Andreas Kling
4cce181ece LibJS: Devirtualize Object::is_typed_array() 2023-11-26 19:32:51 +01:00
Andreas Kling
3c74dc9f4d LibJS: Segregate GC-allocated objects by type
This patch adds two macros to declare per-type allocators:

- JS_DECLARE_ALLOCATOR(TypeName)
- JS_DEFINE_ALLOCATOR(TypeName)

When used, they add a type-specific CellAllocator that the Heap will
delegate allocation requests to.

The result of this is that GC objects of the same type always end up
within the same HeapBlock, drastically reducing the ability to perform
type confusion attacks.

It also improves HeapBlock utilization, since each block now has cells
sized exactly to the type used within that block. (Previously we only
had a handful of block sizes available, and most GC allocations ended
up with a large amount of slack in their tails.)

There is a small performance hit from this, but I'm sure we can make
up for it elsewhere.

Note that the old size-based allocators still exist, and we fall back
to them for any type that doesn't have its own CellAllocator.
2023-11-19 12:10:31 +01:00
Simon Wanner
86b85aa68b LibJS: Introduce Builtins
Builtins are functions that can be detected during bytecode generation
and enable fast-paths in the JIT.
2023-11-17 19:06:25 +01:00
Andreas Kling
b532dedc91 LibJS/JIT: Add fast path for GetById of Array.length
Array.length is magical (since it has to reflect the number of elements
in the object's property storage).

We now handle it specially in jitted code, giving us a massive speed-up
on Kraken/ai-astar.js (and probably many other things as well) :^)
2023-11-12 19:57:27 +01:00
iliadsh
f91c3e9ac3 LibJS: Expose offset to m_indexed_properties 2023-11-10 08:22:44 +01:00
iliadsh
eb937631bb LibJS: Expose offset of may_interfere_with_indexed_property_access 2023-11-10 08:22:44 +01:00
iliadsh
89da731aa6 LibJS+LibWeb: Devirtualize may_interfere_with_indexed_property_access() 2023-11-10 08:22:44 +01:00
Andreas Kling
55e467c359 LibJS/JIT: Add fast path for cached PutById 2023-11-09 16:02:14 +01:00
Andreas Kling
b1b2ca1485 LibJS: Add basic monomorphic caching for PutById property access
This patch makes it possible for JS::Object::internal_set() to populate
a CacheablePropertyMetadata, and uses this to implement a basic
monomorphic cache for the most common form of property write access.
2023-11-09 16:02:14 +01:00
Andreas Kling
7df1692580 LibJS: Add Object::may_interfere_with_indexed_property_access() virtual
This function must return true if the object may intercept and customize
access to indexed properties (properties where the property name is a
non-negative integer.)

This will be used to implement fast path optimizations for array-like
accesses in subsequent commits.
2023-10-05 16:57:45 +02:00
Aliaksandr Kalenik
3a1f617fbf LibJS: Use Function as callback type in define_native_function/accessor
There is not need to use SafeFunction because
define_native_function or define_native_accessor will pass callback
forward to NativeFunction that uses HeapFunction to visit it.
2023-09-27 16:33:21 +02:00
Daniel Bertalan
65232b6681 LibJS: Mark classes and virtual functions final where possible
These cases were found with GCC's `-Wsuggest-final-{types,methods}`
warnings, which catch calls that could have been devirtualized had we
declared the functions `final` in the source.

To reproduce, Link Time Optimization needs to be enabled. The easiest
way to achieve this is to set the `CMAKE_INTERPROCEDURAL_OPTIMIZATION`
cache variable to `ON`. The `.incbin` directive in LibCompress' Brotli
decompressor might needs to be changed to an absolute path for this to
work.

This commit also removes a pair of unused virtual functions.
2023-08-13 18:05:09 +02:00
Andreas Kling
18c54d8d40 LibJS: Make Cell::initialize() return void
Stop worrying about tiny OOMs.

Work towards #20405
2023-08-08 07:39:11 +02:00
Aliaksandr Kalenik
6b191ab73d LibJS+LibWeb: Add fast_is<DOM::Node> for JS::Object
Solves problem that is<DOM::Node, JS::Object>() is quite hot in
profiles while loading https://www.postgresql.org/about/featurematrix/.
2023-07-14 05:55:41 +02:00
Andreas Kling
cf6792ec40 LibJS/Bytecode: Invalidate inline caches on unique shape mutation
Since we can't rely on shape identity (i.e its pointer address) for
unique shapes, give them a serial number that increments whenever a
mutation occurs.

Inline caches can then compare this serial number against what they
have seen before.
2023-07-11 00:14:50 +02:00
Andreas Kling
52cd671163 LibJS: Make Object::internal_get() reveal the used property offset
This function now takes an optional out parameter for callers who would
like to what kind of property we ended up getting.

This will be used to implement inline caching for property lookups.

Also, to prepare for adding more forms of caching, the out parameter
is a struct CacheablePropertyMetadata rather than just an offset. :^)
2023-07-09 12:54:06 +02:00
Timothy Flynn
c911781c21 Everywhere: Remove needless trailing semi-colons after functions
This is a new option in clang-format-16.
2023-07-08 10:32:56 +01:00
Andreas Kling
620c495d31 LibJS: Skip lazy intrinsic allocation check for objects without them
Most JS::Objects don't have lazily-allocated intrinsic properties,
so let's avoid doing hash lookups by putting a flag on JS::Object that
tells us whether it's present in s_intrinsics.

Takes CPU time spent in those hash lookups from 1-2.5% to nothing on
various JS heavy pages.
2023-06-12 06:40:49 +02:00
Andreas Kling
5617dd1c83 LibJS: Store PrivateElement values in Handle<Value>
This fixes an issue where private element values were not always
protected from GC. I found two instances where this was happening:

- ECMAScriptFunctionObject did not mark m_private_methods
- ClassDefinitionEvaluation had two Vector<PrivateElement> that were
  opaque to the garbage collector, and so if GC occurred while
  constructing a class instance, some or all of its private elements
  could get incorrectly collected.
2023-06-02 10:33:12 +02:00
Andreas Kling
cfe663435e LibWeb+LibJS: Don't lazily construct web prototypes in cell constructors
It's not safe to allocate from the GC heap while in the constructor of a
GC heap cell. (Because if this ends up triggering a collection, we may
end up trying to call through an uninitialized vtable).

This was already done safely in the initialize() virtual in much of
LibJS and LibWeb. This patch moves the logic for prototypes, mixins,
and CSSStyleDeclaration as well.

Fixes a long-standing GC crash that was pretty easy to reproduce by
refreshing https://vercel.com/
2023-05-21 14:23:58 +02:00
Ben Wiederhake
560133a0c6 Everywhere: Remove unused DeprecatedString includes 2023-04-09 22:00:54 +02:00
Matthew Olsson
7c0c1c8f49 LibJS+LibWeb: Wrap raw JS::Cell*/& fields in GCPtr/NonnullGCPtr 2023-03-15 08:48:49 +01:00
Linus Groh
be0dcd465f LibJS: Fix return type of Object::create_method_property()
This doesn't return a completion in the spec as it doesn't need to
propagate any errors. It's also unused right now, which is probably why
no one noticed.
2023-03-11 17:32:07 +00:00
Timothy Flynn
2692db8699 LibJS+Everywhere: Allow Cell::initialize overrides to throw OOM errors
Note that as of this commit, there aren't any such throwers, and the
call site in Heap::allocate will drop exceptions on the floor. This
commit only serves to change the declaration of the overrides, make sure
they return an empty value, and to propagate OOM errors frm their base
initialize invocations.
2023-01-29 00:02:45 +00:00
Andreas Kling
4abdb68655 LibJS: Remove Object(Object& prototype) footgun
This constructor was easily confused with a copy constructor, and it was
possible to accidentally copy-construct Objects in at least one way that
we dicovered (via generic ThrowCompletionOr construction).

This patch adds a mandatory ConstructWithPrototypeTag parameter to the
constructor to disambiguate it.
2022-12-14 15:11:57 +01:00
Linus Groh
ddc6e139a6 LibJS: Convert Object::create() to NonnullGCPtr 2022-12-14 09:59:45 +00:00
Linus Groh
daec065fde LibJS: Move initialize_instance_elements() from VM to Object
This makes more sense as an Object method rather than living within the
VM class for no good reason. Most of the other 7.3.xx AOs already work
the same way.
Also add spec comments while we're here.
2022-12-07 00:23:51 +00:00
Linus Groh
6e19ab2bbc AK+Everywhere: Rename String to DeprecatedString
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
2022-12-06 08:54:33 +01:00
Timothy Flynn
12f9f3d9ef LibJS: Support instrinsic Object properties with deferred evaluation
For performance, it is desirable to defer evaluation of intrinsics that
are stored on the GlobalObject for every created Realm. To support this,
Object now maintains a global storage map to store lambdas that will
return the associated intrinsic when evaluated. Once accessed, the
instrinsic is moved from this global map to normal Object storage.

To prevent this flow from becoming observable, when a deferred intrinsic
is stored, we still place an empty object in the normal Object storage.
This is so we still create the metadata for the object, and in doing so,
can preserve insertion order of the Object storage. Otherwise, this will
be observable by way of Object.getOwnPropertyDescriptors.
2022-11-26 09:36:22 +01:00
Andreas Kling
e23fe8cf87 LibJS: Make define_native_foo() take SafeFunctions
We were taking AK::Function and then passing them along to
NativeFunction, which takes a SafeFunction. This works, since
SafeFunction will transparently wrap AK::Function in a CallableWrapper
when assigned, but it was causing us to accumulate thousands of
pointless wrappers around direct function pointers.

By using SafeFunction at every step of the setup call chain, we no
longer create any CallableWrappers for the majority of native functions
in LibJS. Also, the number of heap-registered SafeFunctions in a new
realm goes down from ~5000 to 5. :^)
2022-10-20 15:16:23 +02:00
Andreas Kling
35c9aa7c05 LibJS: Hide all the constructors!
Now that the GC allocator is able to invoke Cell subclass constructors
directly via friendship, we no longer need to keep them public. :^)
2022-08-29 03:24:54 +02:00
Andreas Kling
49fd92d92a LibJS: Make JS_OBJECT and JS_ENVIRONMENT forward to JS_CELL 2022-08-29 03:24:54 +02:00
Andreas Kling
6e973ce69b LibJS: Add JS_CELL macro and use it in all JS::Cell subclasses
This is similar to what we already had with JS_OBJECT (and also
JS_ENVIRONMENT) but sits at the top of the Cell inheritance hierarchy.
2022-08-29 03:24:54 +02:00
Linus Groh
72730422bb LibJS: Remove Shape::global_object() and Object::global_object()
Same reason as in commit 275dea9.
2022-08-28 16:36:56 +01:00
Linus Groh
e3895e6c80 LibJS: Pass Realm to define_native_{accessor,function}()
This is needed so that the allocated NativeFunction receives the correct
realm, usually forwarded from the Object's initialize() function, rather
than using the current realm.
2022-08-23 13:58:30 +01:00
Linus Groh
b465f46e00 LibJS: Remove GlobalObject parameter from native functions 2022-08-23 13:58:30 +01:00
Linus Groh
a022e548b8 LibJS: Replace GlobalObject with VM in Value AOs [Part 4/19]
This is where the fun begins. :^)
2022-08-23 13:58:30 +01:00