This patch introduces the "environment coordinate" concept, which
encodes the distance from a variable access to the binding it ends up
resolving to.
EnvironmentCoordinate has two fields:
- hops: The number of hops up the lexical environment chain we have
to make before getting to the resolved binding.
- index: The index of the resolved binding within its declarative
environment record.
Whenever a variable lookup resolves somewhere inside a declarative
environment, we now cache the coordinates and reuse them in subsequent
lookups. This is achieved via a coordinate cache in JS::Identifier.
Note that non-strict direct eval() breaks this optimization and so it
will not be performed if the resolved environment has been permanently
screwed by eval().
This makes variable access *significantly* faster. :^)
Since non-strict direct eval() can insert new bindings into a
surrounding var scope, we cannot safely cache some assumptions about
environment chain layout after eval() has taken place.
Since eval() is rare, let's do what other engines do and simply
deoptimize in its presence. This patch adds a new "permanently screwed"
flag to JS::Environment that will be set on the entire variable
environment chain upon non-strict direct eval().
This commit introduces the main infrastructure used for turning register
values into user-facing values that can be printed by strace. This
includes the ability to copy data from a particular memory address in
the traced process. On top of this, (partial) formatting has been added
for the most common I/O operations (open, read, write, lseek, close,
stat, fstat).
Moving the formatting of strace's output into a separate function will
allow us to introduce more complexity into the formatting logic without
touching the main body of the program.
The new function uses a switch statement to select how to format the
arguments and result depending on the syscall. At this point we only
include the default formatting, where the registers are simply dumped,
but later on we can add specializations for each system call we want to
support.
'static' for a function means that the symbol shall not be made public
for the result of the current compilation unit. This does not make sense
in a header, especially not if it's a large function that is used in
more than one place and not that performance-sensitive.
Previously: Length (and all nearly all of its inline method
definitions) depended on the definition of class CalculatedStyleValue.
Meanwhile, CalculatedStyleValue (and nearly all of its namespaced
structs) depended on the definition of class Length.
Thus, a compilation unit that (for example) only contains
#include <Userland/Libraries/LibWeb/CSS/Length.h>
would fail to compile.
This patch resolves this issue by pushing the inline definition of
various Web::CSS::Length methods into a different file.
Previously: CSSImportRule::loaded_style_sheet() (and others) depend on
the definition of class CSSStyleSheet. Meanwhile,
CSSStyleSheet::template for_each_effective_style_rule (and others)
depend on the definition of class CSSImportRule.
This hasn't caused any problems so far because CSSStyleSheet.h happened
to be always included after CSSImportRule.h (in part due to alphabetical
ordering).
However, a compilation unit that (for example) only contains
#include <Userland/Libraries/LibWeb/CSSImportRule.h>
would fail to compile.
This patch resolves this issue by pushing the inline definition of
Web::CSS::CSSStyleSheet::for_each_effective_style_rule and
for_first_not_loaded_import_rule into a different file, and adding the
missing headers.
Previously, class SQL::Key
depends on def class SQL::IndexDef (because inline def index())
depends on def class SQL::KeyPartDef (inline def key_definition())
depends on def class SQL::ColumnDef (because base class)
depends on def class SQL::Relation (because base class)
depends on def class SQL::Key (because inline def hash()).
This hasn't caused any problems so far because Meta.h happened to be
always included after Key.h (in part due to alphabetical ordering).
However, a compilation that for example only contains
#include <Userland/Libraries/LibSQL/Key.h>
would fail to compile.
This patch resolves this issue by pushing the inline definition of
SQL::Relation::hash() into a different file. Yes, this might reduce
performance marginally, but this gets it to compile again.
In QtCreator (and under weird configurations with gcc), this used to
fail with the error messages like: "error: member of anonymous union
redeclares '___'".
This patch gives each member a unique name.
Example failure:
IDAllocator.h only pulls in AK/Hashtable.h, so any compilation unit that
includes AK/IDAllocator.h without including AK/Traits.h before it used
to be doomed to fail with the cryptic error message "In instantiation of
'AK::HashTable<T, TraitsForT, IsOrdered>::Iterator AK::HashTable<T,
TraitsForT, IsOrdered>::find(const T&) [with T = int; TraitsForT =
AK::Traits: incomplete type 'AK::Traits<int>' used in nested name
specifier".
VM::resolve_binding() can now return a Reference that knows the exact
binding index if it's pointing into a DeclarativeEnvironment.
Reading/writing through the Reference will now use direct environment
access when possible.
This patch adds two DeclarativeEnvironment APIs:
- get_binding_value_direct()
- set_mutable_binding_direct()
These work identically to their non-direct-suffixed counterparts, but
take an index instead of a bound name. This will allow someone who has
a binding index to get/set that binding directly without any additional
hash lookups.
The previous storage for DeclarativeEnvironment looked like this:
HashMap<FlyString, Binding> m_bindings;
This patch changes that to:
HashMap<FlyString, size_t> m_names;
Vector<Binding> m_bindings;
The main goal here is to give each binding an index that can ultimately
be cached and used for optimized environment accesses.
The memory and CPU graphs fail to display anything when the memory size
is larger than 2**31 bytes, because of the small range of int. This
commit makes replaces the type with size_t. Hopefully nobody will have
18 quintillion bytes of memory before this gets replaced. :^)
Per the spec, only a BlockContainer" can have line boxes, so let's not
clutter up every Layout::Box with line boxes.
This also allows us to establish an invariant that BFC and IFC always
operate on a Layout::BlockContainer.
Note that if BlockContainer has all block-level children, its line boxes
are not used for anything. They are only used in the all inline-level
children scenario.
List item markers will never have children, so let's mark them as such,
which now allows our layout system to skip over their "insides" and
going straight to positioning instead.
Some boxes cannot have children (most commonly replaced elements),
and so there is nothing meaningful inside them to layout.
We now use the can_have_children() flag to quickly skip over such boxes
instead of creating a formatting context and other pointless busywork.
There's a subtle difference here. A "block box" in the spec is a
block-level box, while a "block container" is a box whose children are
either all inline-level boxes in an IFC, or all block-level boxes
participating in a BFC.
Notably, an "inline-block" box is a "block container" but not a "block
box" since it is itself inline-level.
The CSS spec uses the name "block formatting context root" when talking
about a box that establishes a BFC. So let's call it BFC::root() in our
code as well, instead of the less specific BFC::context_box().
We only froward String setting and FlagPost creation for now, due to the
other performance events being nonsensical to forward.
We also record these signposts in the optionally generated profile.
Until now, we've internally thought of the CSS "display" property as a
single-value property. In practice, "display" is a much more complex
property that comes in a number of configurations.
The most interesting one is the two-part format that describes the
outside and inside behavior of a box. Switching our own internal
representation towards this model will allow for much cleaner
abstractions around layout and the various formatting contexts.
Note that we don't *parse* two-part "display" yet, this is only about
changing the internal representation of the property.
Spec: https://drafts.csswg.org/css-display
Instead of doing layout synchronously whenever something changes,
we now use a basic event loop timer to defer and coalesce relayouts.
If you did something that requires a relayout of the page, make sure
to call Document::set_needs_layout() and it will get coalesced with all
the other layout updates.
There's lots of room for improvement here, but this already makes many
web pages significantly snappier. :^)
Also, note that this exposes a number of layout bugs where we have been
relying on multiple relayouts to calculate the correct dimensions for
things. Now that we only do a single layout in many cases, these kind of
problems are much more noticeable. That should also make them easier to
figure out and fix. :^)