<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Linus Groh</title>
  <subtitle></subtitle>
  <link href="https://linus.dev/feed.xml" rel="self" />
  <link href="https://linus.dev/" />
  <updated>2025-04-29T00:00:00Z</updated>
  <id>https://linus.dev/</id>
  <author>
    <name>Linus Groh</name>
    <email>mail@linusgroh.de</email>
  </author>
  <entry>
    <title>Kiesel Devlog #12: Write Once, Run Anywhere</title>
    <link href="https://linus.dev/posts/kiesel-devlog-12/" />
    <updated>2025-04-29T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-12/</id>
    <content type="html">&lt;p&gt;Your favorite JavaScript Engine turned two years old yesterday! I&#39;ll use that as an excuse to write another blog post — not just about the birthday though 🥳&lt;/p&gt;&lt;h2 id=&quot;some-numbers&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#some-numbers&quot;&gt;Some Numbers&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Kiesel is about 80,000 lines of Zig right now, give or take. A lot of it is spec comments so the amount of &lt;em&gt;actual&lt;/em&gt; code is a bit lower. There are a few new contributors but I&#39;m still responsible for 98% of commits, so that&#39;s definitely the largest codebase I&#39;ve authored!&lt;/p&gt;&lt;p&gt;Let&#39;s start a spreadsheet:&lt;/p&gt;&lt;div style=&quot;width: 100%; overflow: auto;&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Age&lt;/th&gt;&lt;th&gt;Lines Of Code&lt;/th&gt;&lt;th&gt;Commits&lt;/th&gt;&lt;th&gt;Contributors&lt;/th&gt;&lt;th&gt;test262&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;62768&lt;/td&gt;&lt;td&gt;1435&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;50.4%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;83627&lt;/td&gt;&lt;td&gt;2106&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;75.1%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;h2 id=&quot;the-roadmap&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#the-roadmap&quot;&gt;The Roadmap&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Let&#39;s take a look at what&#39;s planned next!&lt;/p&gt;&lt;p&gt;Nothing. This is vibe coding before vibe coding was cool (sans the markov chain). If it&#39;s not fun I&#39;m not doing it. If it is fun I might do it, maybe. No plans :^)&lt;/p&gt;&lt;h2 id=&quot;and-now-for-something-completely-different&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#and-now-for-something-completely-different&quot;&gt;And Now For Something Completely Different&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When I learned Python as a kid I kept reading about portability and that Python is a good fit for it because your applications will run on Windows, macOS, and Linux. These days I know that&#39;s bullshit, good luck finding a language that &lt;em&gt;doesn&#39;t&lt;/em&gt; do that. Writing code that runs on the three major operating systems isn&#39;t difficult, it&#39;s the default.&lt;/p&gt;&lt;p&gt;Let&#39;s make it difficult!&lt;/p&gt;&lt;h2 id=&quot;platform-abstractions&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#platform-abstractions&quot;&gt;Platform Abstractions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;At some point I started experimenting with porting Kiesel to some rather obscure targets, &lt;a href=&quot;https://linus.dev/posts/kiesel-devlog-9/&quot;&gt;like this one&lt;/a&gt;. The process was always the same: taking a fresh copy of the source code, adding hacks wherever needed until it builds and runs (two very different stages of porting!), taking a funny screenshot, maybe saving the diff somewhere, moving on.&lt;/p&gt;&lt;p&gt;Even if I tried to actually commit those changes it wouldn&#39;t have been maintainable. In C you end up with &lt;code&gt;#ifdef&lt;/code&gt; soup and the Zig equivalent is not much nicer. After the third or fourth time I decided that having support for all of those odd devices &lt;em&gt;out of the box&lt;/em&gt; would be nice and started working on a platform abstraction. I later discovered that V8 has something similar, although less flexible — &lt;a href=&quot;https://v8.github.io/api/head/classv8_1_1Platform.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;v8::Platform&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The default implementation that Kiesel provides is largely built on top of the Zig standard library and avoids making any assumptions about the underlying platform. You don&#39;t have to think about anything:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; platform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Agent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Platform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; platform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deinit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives you:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A garbage-collecting allocator that &lt;a href=&quot;https://github.com/ivmai/bdwgc#portability&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;works on a large number of platforms&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Writers for stdout/stderr&lt;/li&gt;&lt;li&gt;TTY configuration for pretty-printing that takes &lt;a href=&quot;https://bixense.com/clicolors/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;NO_COLOR&lt;/code&gt; and &lt;code&gt;CLICOLOR_FORCE&lt;/code&gt;&lt;/a&gt; into account&lt;/li&gt;&lt;li&gt;Information about the native stack bounds for stack overflow detection, if supported&lt;/li&gt;&lt;li&gt;A neutral default locale for Intl (BCP 47 &lt;code&gt;und&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;A function to retrieve the current system time, if supported&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Funnily enough, despite being the world&#39;s most portable language, the bits of Kiesel that rely on C are the first to fall apart when you leave the happy path of &lt;code&gt;x86_64-linux&lt;/code&gt;. Even though the Zig toolchain has fantastic support for giving you access to libc when cross-compiling that doesn&#39;t work on all targets.&lt;/p&gt;&lt;p&gt;In particular we need to deal with the absence of any of these:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ivmai/bdwgc&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;bdwgc&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/quickjs-ng/quickjs/blob/master/libregexp.h&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;libregexp&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/unicode-org/icu4x&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ICU4X&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The allocator can easily be swapped out with &lt;code&gt;malloc()&lt;/code&gt; or similar at the expense of leaks, regular expressions can operate in a stub mode where they pretend to work but don&#39;t, and support for Intl can be removed at build time.&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ zig build -Denable-libgc=false -Denable-libregexp=false -Denable-intl=false&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far, so good! But what happens when other parts of the default platform implementation stop working?&lt;/p&gt;&lt;p&gt;One such example is UEFI which is not supported by regular &lt;code&gt;std.io&lt;/code&gt; APIs, at least until &lt;a href=&quot;https://github.com/ziglang/zig/pull/22226&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ziglang/zig#22226&lt;/a&gt; is merged. Various other things don&#39;t work either so let&#39;s make our own platform:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allocator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uefi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pool_allocator&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stdout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AnyWriter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stderr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AnyWriter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; platform&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Agent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Platform&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// UEFI pool allocator, memory is leaked instead of GC&#39;d&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gc_allocator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; allocator&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gc_allocator_atomic &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; allocator&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Custom writers using the UEFI console&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stdout&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stderr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stderr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Coloring is only supported using ANSI escape codes and the&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Windows console API, not UEFI console (yet!)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tty_config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;no_color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Stack overflow detection is not supported&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stack_info &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Build without Intl, default locale is void&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default_locale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// This happens to have an implementation for UEFI :^)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;milliTimestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Additionally Zig provides &lt;a href=&quot;https://ziglang.org/documentation/master/std/#std.Options&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;std.Options&lt;/code&gt;&lt;/a&gt; as a way to customize certain behavior of the standard library at compile time, which comes in handy too. In the case of UEFI we set a no-op &lt;code&gt;logFn&lt;/code&gt; until &lt;code&gt;std.io&lt;/code&gt; learns about the UEFI console, as well as a custom implementation of &lt;code&gt;cryptoRandomSeed&lt;/code&gt;, which tries to use &lt;code&gt;std.posix.getrandom()&lt;/code&gt; by default. And well, UEFI is very much not POSIX.&lt;/p&gt;&lt;p&gt;You can find the full code for this in &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/src/branch/main/src/uefi.zig&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;src/uefi.zig&lt;/code&gt;&lt;/a&gt;. And just like that we can boot into JavaScript!&lt;/p&gt;&lt;h2 id=&quot;showcases&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#showcases&quot;&gt;Showcases&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;linux%2C-macos%2C-windows&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#linux%2C-macos%2C-windows&quot;&gt;Linux, macOS, Windows&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To prove my earlier point: of course Kiesel runs without issues on the latest versions of Linux, macOS, and Windows. It&#39;s using statically linked musl by default so it&#39;ll work on pretty much any distro. The REPL — a nod to CPython — includes OS-specific instructions for exiting it (EOF works differently on Windows, as everything does).&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/linux.png&quot; alt=&quot;Kiesel REPL running on Linux&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/macos.png&quot; alt=&quot;Kiesel REPL running on macOS&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/windows.png&quot; alt=&quot;Kiesel REPL running on Windows&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Thanks to &lt;a href=&quot;https://tech.lgbt/@SarahGreyWolf&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Sarah&lt;/a&gt; for this screenshot!&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;linux%2C-but-weird&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#linux%2C-but-weird&quot;&gt;Linux, But Weird&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Still Linux but we&#39;re leaving desktop user territory.&lt;/p&gt;&lt;p&gt;Here&#39;s Kiesel running on my projector at home, which came with Linux 4.9 (Android 9) out of the box. Zig officially &lt;a href=&quot;https://github.com/ziglang/zig/blob/399da543e54a439b0bae396dadfb77bc339069d4/lib/std/Target.zig#L453&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;supports Linux ≥ 5.10&lt;/a&gt; at the time of writing and may use syscalls that fail miserably on older versions as a result, but for the most part it works. If you ever get an &lt;code&gt;error.Unexpected&lt;/code&gt; back this is probably the reason.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/linux-arm.png&quot; alt=&quot;Kiesel REPL running on Linux (arm)&quot;&gt;&lt;/p&gt;&lt;p&gt;Do you have a mainframe at home? If so, Kiesel for IBM Z may be what you need. I&#39;m pretty sure I know people who this applies to, but for now I&#39;m using a LinuxONE VM that IBM gave me for open source purposes :^)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/linux-s390x.png&quot; alt=&quot;Kiesel REPL running on Linux (s390x)&quot;&gt;&lt;/p&gt;&lt;p&gt;To make sure Kiesel also works on less common big-endian systems (you&#39;re probably reading this on little-endian) &lt;a href=&quot;https://famfo.xyz&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://donotsta.re/emoji/neocat/neocat_blep_256.png);&quot;&gt;famfo&lt;/a&gt; once tested it on ppc64.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/linux-powerpc64.png&quot; alt=&quot;Kiesel REPL running on Linux (powerpc64)&quot;&gt;&lt;/p&gt;&lt;h3 id=&quot;windows%2C-but-ancient&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#windows%2C-but-ancient&quot;&gt;Windows, But Ancient&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Together &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; and I managed to run Kiesel on Windows NT 5.2, a.k.a. Windows Server 2003. I no longer have the patches but it involved finding DLLs online that backport APIs from newer Windows versions. Don&#39;t ask why it&#39;s running from the SeaMonkey installation directory.&lt;/p&gt;&lt;p&gt;Understandably Zig only wants to deal with &lt;a href=&quot;https://github.com/ziglang/zig/blob/399da543e54a439b0bae396dadfb77bc339069d4/lib/std/Target.zig#L588&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Windows 10 and above&lt;/a&gt; nowadays so this is &lt;em&gt;very much&lt;/em&gt; not supported.&lt;/p&gt;&lt;p&gt;Hey, we should do that again and document it &amp;lt;3&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/windows-nt52.png&quot; alt=&quot;Kiesel REPL running on Windows NT 5.2&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Thanks to &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; for this screenshot!&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;openbsd&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#openbsd&quot;&gt;OpenBSD&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Not too long ago one of the OpenBSD developers &lt;a href=&quot;https://bsd.network/@tobhe/113956808894873467&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;reached out&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Needed a newer Zig and a few tweaks, but I got it running. One more platform to add to the list :)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;One of the tweaks turned out to be a &lt;a href=&quot;https://github.com/ziglang/zig/pull/22831&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;bug in &lt;code&gt;std.c&lt;/code&gt;&lt;/a&gt; that I fixed upstream. Unfortunately it&#39;s not possible to cross-compile for OpenBSD as Zig can&#39;t provide a libc:&lt;/p&gt;&lt;pre class=&quot;language-error&quot;&gt;&lt;code class=&quot;language-error&quot;&gt;error: error: unable to find or provide libc for target &#39;x86_64-openbsd.7.3...7.6-none&#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I might look into setting up a VM so I can at least check if the build still works once in a while :^)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/openbsd.png&quot; alt=&quot;Kiesel REPL running on OpenBSD 7.6&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Thanks to &lt;a href=&quot;https://tobhe.de&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Tobias&lt;/a&gt; for this screenshot!&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;uefi&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#uefi&quot;&gt;UEFI&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The Zig standard library already wraps large parts of the UEFI API, so I had to try. And sure enough, it worked!&lt;/p&gt;&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/alimpfard);&quot;&gt;CxByte&lt;/a&gt; who kindly added UEFI support to his &lt;a href=&quot;https://github.com/alimpfard/zigline&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;zigline&lt;/a&gt; library you can now boot into a JavaScript REPL. I doubt this is the &lt;em&gt;first time ever&lt;/em&gt; this exists but I personally haven&#39;t seen any other projects doing it before :^)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/uefi.png&quot; alt=&quot;Kiesel REPL running on UEFI in QEMU&quot;&gt;&lt;/p&gt;&lt;p&gt;And of course I had to install it on bare metal too:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/uefi.gif&quot; alt=&quot;Kiesel REPL running on UEFI on a Thinkpad&quot;&gt;&lt;/p&gt;&lt;h3 id=&quot;nintendo-3ds&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#nintendo-3ds&quot;&gt;Nintendo 3DS&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I bought a used Nintendo 3DS, put homebrew on it, and then promptly buried it in a drawer.&lt;/p&gt;&lt;p&gt;Luckily someone already &lt;a href=&quot;https://github.com/zig-homebrew/zig-3ds&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;did the work&lt;/a&gt; of gluing devkitPro and the Zig toolchain together: let Zig output an object file compiled for &lt;code&gt;arm-freestanding-eabihf&lt;/code&gt;, throw that into devkitPro&#39;s &lt;code&gt;arm-none-eabi-gcc&lt;/code&gt; to get an ELF file, and finally feed that to &lt;code&gt;3dsxtool&lt;/code&gt;. It&#39;s not pretty but it works.&lt;/p&gt;&lt;p&gt;The first proof of concept was using a hardcoded snippet of JS, but over time I expanded it to use the software keyboard for an actual REPL, enabled pretty-printing, and ported more of the &lt;a href=&quot;https://codeberg.org/kiesel-js/runtime&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;runtime&lt;/a&gt;. There are some crashes left to fix but nowadays it works almost as well as the regular CLI!&lt;/p&gt;&lt;p&gt;The 3DS even ships with JS out of the box (the built-in browser is based on WebKit), but I&#39;m probably the first to use &lt;a href=&quot;https://github.com/tc39/proposal-iterator-helpers&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;iterator helpers&lt;/a&gt; on it.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-12/3ds.jpeg&quot; alt=&quot;Kiesel REPL running on a Nintendo 3DS&quot;&gt;&lt;/p&gt;&lt;h3 id=&quot;your-system-here%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#your-system-here%3F&quot;&gt;Your System Here?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;🥺👉👈&lt;/p&gt;&lt;h2 id=&quot;thanks!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-12/#thanks!&quot;&gt;Thanks!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;…to everyone who keeps expanding the &lt;em&gt;Kiesel Enterprise Cinematic Universe&lt;/em&gt; (mostly &lt;a href=&quot;https://eloydegen.com&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://donotsta.re/emoji/neocat/neocat_blep_256.png);&quot;&gt;Eloy&lt;/a&gt; tbh), &lt;a href=&quot;https://github.org/sno2&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/sno2);&quot;&gt;Carter&lt;/a&gt; for continuing to work on GC things I don&#39;t fully understand, &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; for providing CI infra so I can build for all the platforms no one uses, and in particular &lt;a href=&quot;https://github.com/alexrp&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Alex&lt;/a&gt; from the Zig team for the huge effort to &lt;a href=&quot;https://ziglang.org/download/0.14.0/release-notes.html#Target-Support&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;improve target support&lt;/a&gt; even more!&lt;/p&gt;&lt;p&gt;Let&#39;s see what platforms we can run JavaScript on in the next year :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #11: Community Edition</title>
    <link href="https://linus.dev/posts/kiesel-devlog-11/" />
    <updated>2024-09-01T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-11/</id>
    <content type="html">&lt;p&gt;Since the &lt;a href=&quot;https://linus.dev/posts/kiesel-devlog-10&quot;&gt;last devlog&lt;/a&gt; I&#39;ve barely done any work on Kiesel myself due to being on vacation, so this one is entirely dedicated to things other people contributed!&lt;/p&gt;&lt;p&gt;On that note: I created a Matrix space for the project where you can ask questions or just hang out and chat. I&#39;ll also try to post about in-progress things once in a while. Feel free to join!&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://matrix.to/#/%23kiesel%3Amatrix.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#kiesel:matrix.org&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://matrix.to/#/%23kiesel-general%3Amatrix.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#kiesel-general:matrix.org&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://matrix.to/#/%23kiesel-development%3Amatrix.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#kiesel-development:matrix.org&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;nan-boxing&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#nan-boxing&quot;&gt;NaN-Boxing&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It was clear we&#39;d need NaN-boxing in Kiesel eventually so I started investigating and doing some refactoring until &lt;a href=&quot;https://github.org/sno2&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/sno2);&quot;&gt;Carter&lt;/a&gt; offered to implement it and came back with a PR two days later (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/37&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#37&lt;/a&gt;)!&lt;/p&gt;&lt;p&gt;It provided the performance gains we hoped for and I&#39;m really happy with how it turned out :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-11/nan-boxing-comparison.png&quot; alt=&quot;Performance comparison of before and after the change&quot;&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;There are good explanations about NaN-boxing &lt;a href=&quot;https://piotrduperas.com/posts/nan-boxing/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://leonardschuetz.ch/blog/nan-boxing/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;here&lt;/a&gt;, I recommend you read those if you want to learn more.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;kiesel(1)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#kiesel(1)&quot;&gt;kiesel(1)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To my pleasant surprise &lt;a href=&quot;https://cve.cx&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://media.tech.lgbt/accounts/avatars/112/620/204/733/972/810/original/fe81ad68e8fb45c0.png);&quot;&gt;Clara&lt;/a&gt; wrote a traditional manual page for Kiesel: &lt;a href=&quot;https://docs.kiesel.dev/kiesel.1.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel(1)&lt;/a&gt;! We&#39;ll extend it over time and I can&#39;t wait to run &lt;code&gt;man kiesel&lt;/code&gt; once someone starts packaging it :^)&lt;/p&gt;&lt;h2 id=&quot;weak-references&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#weak-references&quot;&gt;Weak References&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Also implemented by &lt;a href=&quot;https://github.org/sno2&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/sno2);&quot;&gt;Carter&lt;/a&gt; were the types for working with weak references:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;WeakRef&lt;/code&gt; (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/42&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#42&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;WeakSet&lt;/code&gt; (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/44&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#44&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;WeakMap&lt;/code&gt; (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/45&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#45&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As well as some foundational work:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;FinalizationRegistry&lt;/code&gt; (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/40&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#40&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We attempted this twice before and it&#39;s great to see how everything fell into place after he figured out the initial &lt;abbr title=&quot;Garbage Collector&quot;&gt;GC&lt;/abbr&gt; integration!&lt;/p&gt;&lt;h2 id=&quot;uefi-interactive-repl&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#uefi-interactive-repl&quot;&gt;UEFI Interactive REPL&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Yes, you read that right. I take great pride in Kiesel being a portable piece of software, and the main limitation of its UEFI port was having to hardcode the JS being evaluated.&lt;/p&gt;&lt;p&gt;Luckily I managed to bait &lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/alimpfard);&quot;&gt;CxByte&lt;/a&gt; into resolving this critical issue (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/pulls/50&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/kiesel#50&lt;/a&gt;). All of the heavy lifting is done in &lt;a href=&quot;https://github.com/alimpfard/zigline/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;zigline&lt;/code&gt;&lt;/a&gt; itself so you can use this in your own projects!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-11/uefi-repl.png&quot; alt=&quot;Kiesel UEFI REPL&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;wikidata&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#wikidata&quot;&gt;Wikidata&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Thanks to &lt;a href=&quot;https://chaos.social/@sofia&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://assets.chaos.social/accounts/avatars/000/207/666/original/cfda1bf50e83311a.webp);&quot;&gt;sofia&lt;/a&gt; Kiesel recently got a &lt;a href=&quot;https://www.wikidata.org/wiki/Q127601694&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Wikidata entry&lt;/a&gt;! It&#39;s probably not quite relevant enough for a Wikipedia article but maybe we&#39;ll get there too some day :^)&lt;/p&gt;&lt;p&gt;You&#39;re welcome to add more data points if that&#39;s your thing, and while I&#39;ll not edit this myself I&#39;m always happy to fact-check or provide good sources!&lt;/p&gt;&lt;h2 id=&quot;it&#39;s-always-dns&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#it&#39;s-always-dns&quot;&gt;It&#39;s Always DNS&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Thanks to my friends at &lt;a href=&quot;https://beta.servfail.network/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;project SERVFAIL&lt;/a&gt; &lt;code&gt;kiesel.dev&lt;/code&gt; now uses globally distributed highly available home grown name servers:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ dig +noall +answer NS kiesel.dev
kiesel.dev.		4406	IN	NS	ns1.famfo.xyz.
kiesel.dev.		4406	IN	NS	ns2.famfo.xyz.
kiesel.dev.		4406	IN	NS	ns1.homecloud.lol.
kiesel.dev.		4406	IN	NS	miyuki.sakamoto.pl.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately they don&#39;t use any JS so I put some in a &lt;code&gt;TXT&lt;/code&gt; record.&lt;/p&gt;&lt;h2 id=&quot;marketing-go-brrr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-11/#marketing-go-brrr&quot;&gt;Marketing Go Brrr&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Speaking of SERVFAIL, I also managed to recruit their marketing manager!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://shift.gay&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://donotsta.re/emoji/neocat/neocat_woozy_256.png);&quot;&gt;Shebang&lt;/a&gt; has been hard at work producing misinformation about the project at an alarming rate. We gotta have some fun, too :^)&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to read the bangers (there&#39;s a lot of them)&lt;/strong&gt;&lt;/summary&gt;&lt;blockquote&gt;&lt;p&gt;As the new Kiesel marketing lead, I would like to announce some new Kiesel features coming your way (thread)&lt;/p&gt;&lt;blockquote&gt;&lt;ol&gt;&lt;li&gt;Kiesel will now be built on other Ecma technical committies, other than TC39. Meaning that yes, Kiesel will support being used as a CD, as per TC31: &lt;a href=&quot;https://ecma-international.org/technical-committees/tc31/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://ecma-international.org/technical-committees/tc31/&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;ol start=&quot;2&quot;&gt;&lt;li&gt;A new subscription tier will arrive to Kiesel soon. (Linus needs to eat somehow!) Kiesel Pro will allow you to access features such as Promises, Array prototype methods and booleans!&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;here’s a sneak peek:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;meow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token literal-property property&quot;&gt;PaywallError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; This feature requires a Kiesel™️ Pro subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;ol start=&quot;3&quot;&gt;&lt;li&gt;Kiesel will now be ported to those little phones that go &amp;quot;ayayaya im a little butterfly!&amp;quot;&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;ol start=&quot;4&quot;&gt;&lt;li&gt;Kiesel will support being PID 1, so you can go into userspace with Kiesel!&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;blockquote&gt;&lt;p&gt;I&#39;m sorry if you followed this account in hopes that I&#39;d be serious about announcing things about #SERVFAIL and/or Kiesel.&lt;/p&gt;&lt;p&gt;Because I definitely am. Anyways who wants a free Kiesel Pro product key??&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;wow congratulations to this completely uninvolved party that definitely would not lie about winning a giveaway!!!! @iro_miya you just won the Kiesel Pro key tell us all about how you are using it right now and that I definitely DMed it to you&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;hiiii, thank you so much for the Kiesel Pro key! I am using the Kiesel Pro key to open the Kiesel Pro door. I am currently chilling in the Kiesel Pro room. ​:ablobcatbongo:​&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;blockquote&gt;&lt;p&gt;test262.fyi now has #Kiesel Pro added. would you look at that! 173.9%!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://assets.chaos.social/media_attachments/files/113/005/032/739/722/745/original/fde2ae773fc59d36.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;if you can&#39;t find it on test262.fyi, it&#39;s because only Kiesel Pro users can see the value&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;blockquote&gt;&lt;p&gt;Reminder to Kiesel Pro customers that Kiesel Pro continues to pass 180% of the test262 suite&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;oh they&#39;re just throwing new ideas in there. IntermediateMap. Array.reverseReverse. Number.nullify. you name it, it&#39;s in Kiesel Pro&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;blockquote&gt;&lt;p&gt;did you know? @linus is the creator of Kiesel, Linux and runs a very successful Youtube channel called &amp;quot;Linus Tech Tips&amp;quot;. This is a true fact!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;i can say this per my kiesel contract, which stipulates i can say anything about kiesel&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;/details&gt;&lt;hr&gt;&lt;p&gt;That&#39;s all for now! It&#39;s really nice to see Kiesel slowly moving away from being &lt;em&gt;just me&lt;/em&gt; (it was like that on purpose, read some of the earlier devlogs for the history). Both code and non-code contributions are much appreciated, if you want to get involved please reach out!&lt;/p&gt;&lt;p&gt;Thanks to &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; for proof-reading this post &amp;lt;3&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #10: Let&#39;s Make It Fast!</title>
    <link href="https://linus.dev/posts/kiesel-devlog-10/" />
    <updated>2024-08-19T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-10/</id>
    <content type="html">&lt;p&gt;Over the last year and a bit Kiesel has grown beyond being a small experiment. I&#39;ve been churning out feature after feature, and while correctness has been of importance from day one, one thing hasn&#39;t been on my radar as much: performance.&lt;/p&gt;&lt;p&gt;You could go as far as saying Kiesel is &lt;em&gt;blazingly slow 🔥&lt;/em&gt;. Let&#39;s change that :^)&lt;/p&gt;&lt;h2 id=&quot;make-it-work&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#make-it-work&quot;&gt;Make It Work&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This part is done, it works. Some things better than others, the object model is rock solid for instance (&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/8262&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;a lesson learned from LibJS&lt;/a&gt;), while others fall apart if you look at them the wrong way (looking at you, &lt;code&gt;src/language/bytecode/Vm.zig&lt;/code&gt;). The secret here is to make it work &lt;em&gt;most of the time&lt;/em&gt;, which gets you pretty far.&lt;/p&gt;&lt;p&gt;At the time of writing &lt;a href=&quot;https://test262.fyi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Kiesel passes 59.2% of test262&lt;/a&gt;, so things are clearly going into a good direction.&lt;/p&gt;&lt;p&gt;I&#39;ve also been &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/95fe2aa34ffd701306df50c42d117f4e0db4ee9e&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;untying various bits of runtime code from the VM&lt;/a&gt; recently and am mentally prepared to throw that part away and start over. Making it work &lt;em&gt;all the time&lt;/em&gt; is on the horizon.&lt;/p&gt;&lt;h2 id=&quot;make-it-right&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#make-it-right&quot;&gt;Make It Right&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As mentioned I made sure that everything&#39;s following the spec as much as reasonably possible, which is another lesson I learned from working on LibJS. Back then I spent many months fixing countless correctness issues after excitement died down and most early contributors moved on. Let&#39;s just say MDN has fantastic docs but it&#39;s not a very good reference for writing a JS engine.&lt;/p&gt;&lt;p&gt;Of course there are still correctness bugs in Kiesel, some of them yet to be found, some of them known. The former will be flushed out by test262 eventually and I try to clearly mark all of the latter with TODOs. Frequently they are results of cutting corners for the sake of quick progress, such as number parsing &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/src/commit/aded9c94e04b8638f7a1e753c8324623b0c0edb4/src/types/language/Value.zig#L1400-L1422&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;which defers to Zig&#39;s &lt;code&gt;std.fmt.parseFloat()&lt;/code&gt; and &lt;code&gt;std.fmt.parseInt()&lt;/code&gt; with some basic upfront validation&lt;/a&gt;. Not having to write a floating point parser was a welcome shortcut at the time :^)&lt;/p&gt;&lt;p&gt;Oh, and &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/07ee8262e267d4c0ea281607c2e8e76a79ec2260&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;so&lt;/a&gt; &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/0493145110d4520183b4d2b9a8491ad79f189236&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/8f0b5ca029dcd9c79da5e7c990def0221a83fdfe&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;RegExp&lt;/a&gt; &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/1d97166f8fdcac1fbbf418d6573a8a1d0b5b3b42&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;parsing&lt;/a&gt; &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/ed0a0cf45f0475800060a866a96840b1c597f6d2&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;bugs&lt;/a&gt;. Probably need to rewrite that too.&lt;/p&gt;&lt;h2 id=&quot;make-it-fast&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#make-it-fast&quot;&gt;Make It Fast&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I&#39;m occasionally talking with &lt;a href=&quot;https://jason-williams.co.uk&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/jasonwilliams);&quot;&gt;Jason&lt;/a&gt; — original creator of the &lt;a href=&quot;https://boajs.dev&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Boa JS engine&lt;/a&gt; — about all things engine development. A few months ago he mentioned they set up a &lt;a href=&quot;https://boajs.dev/benchmarks&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;benchmarks page&lt;/a&gt; which uses an old version of the &lt;a href=&quot;https://github.com/mozilla/arewefastyet/tree/master/benchmarks/v8-v7&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;V8 Benchmark Suite&lt;/a&gt; to compare Boa against various other engines. I was keen to add Kiesel but at the time it couldn&#39;t even parse the entire benchmark suite, so we abandoned that idea.&lt;/p&gt;&lt;p&gt;Recently I tried again and a few bug fixes later Kiesel was able to run the whole thing to completion! There&#39;s just one problem: it was slow. Like &lt;em&gt;an hour while most engines complete within a few minutes&lt;/em&gt; slow. Armed with &lt;a href=&quot;https://man7.org/linux/man-pages/man1/perf.1.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;perf(1)&lt;/a&gt; and &lt;a href=&quot;https://www.speedscope.app&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;speedscope&lt;/a&gt; I got to work :^)&lt;/p&gt;&lt;h3 id=&quot;improving-expression-and-statement-parsing&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#improving-expression-and-statement-parsing&quot;&gt;Improving Expression and Statement Parsing&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/8f81cd5f84017172b488ee922f47e21fd5595562&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;The very first thing I found was a shocking amount of time spent in the parser! Kiesel has a hand-written parser using the &lt;a href=&quot;https://github.com/ikskuh/parser-toolkit&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;parser-toolkit&lt;/code&gt;&lt;/a&gt; library, which allows for patterns that lead to very readable code at the expense of becoming quite inefficient for large grammars.&lt;/p&gt;&lt;p&gt;We were effectively brute forcing our way through all possibilities using Zig&#39;s error-unwrapping &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;acceptPrimaryExpression&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AcceptError&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrimaryExpression&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;errdefer&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;restoreState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;RuleSet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptIdentifierReference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;identifier_reference&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;identifier_reference &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; identifier_reference &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;literal&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; literal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptArrayLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;array_literal&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;array_literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; array_literal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptObjectLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;object_literal&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object_literal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... snip ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UnexpectedToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This statement had 15 branches so in the worst case you&#39;d have 14 failed attempts of progressing the parser including state restores until arriving at the right accept function. We can do much better!&lt;/p&gt;&lt;p&gt;The code now looks at the next token and switches on it, which is less readable but means we can immediately pick the right code path:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;acceptPrimaryExpression&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AcceptError&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PrimaryExpression&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;errdefer&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;restoreState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; next_token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;orelse&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UnexpectedToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next_token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token label&quot;&gt;blk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;RuleSet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unreachable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label&quot;&gt;blk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;identifier&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yield&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;@&quot;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&quot; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptIdentifierReference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;identifier_reference&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;identifier_reference &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; identifier_reference &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... snip ...&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UnexpectedToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;@&quot;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&quot; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;array_literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptArrayLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;@&quot;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&quot; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptObjectLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;numeric&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acceptLiteral&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... snip ...&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UnexpectedToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;lazy-global-object-properties&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#lazy-global-object-properties&quot;&gt;Lazy Global Object Properties&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/77595aeb0659956ea329d93d0bf34e89c60aa445&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;I implemented intrinsics (the underlying objects behind things like &lt;code&gt;Array&lt;/code&gt; and &lt;code&gt;Array.prototype&lt;/code&gt;) with lazy initialization in mind from the beginning, allocating everything upfront is wasteful. This works by having a function like this returning &lt;code&gt;Allocator.Error!Object&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; realm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;intrinsics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;@&quot;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&quot;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the intrinsic has never been accessed before, it will be allocated on the heap and initialized. After that it&#39;s a guaranteed allocation-free lookup. The only problem is that the global object would initialize every single constructor and its prototype on startup, making this extra step pointless.&lt;/p&gt;&lt;p&gt;I had to do a bit of surgery to still create those properties upfront but only initialize their value (an intrinsic object) when it is first being looked up from the property storage. This makes creation of the global object and thus startup times much faster as demonstrated here by running an empty script:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/lazy-global-object-properties-comparison.png&quot; alt=&quot;Performance comparison of before and after the change&quot;&gt;&lt;/p&gt;&lt;h3 id=&quot;making-the-ast-less-allocation-heavy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#making-the-ast-less-allocation-heavy&quot;&gt;Making the AST Less Allocation-Heavy&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/3a033af6a47f9f6658ac7bd6b3564768eb1a5624&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;A common pattern in the &lt;abbr title=&quot;Abstract Syntax Tree&quot;&gt;AST&lt;/abbr&gt; is to recursively collect a list of something from a node and all of its children, e.g. a function&#39;s &lt;a href=&quot;https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;var declared names&lt;/em&gt;&lt;/a&gt; for scoping purposes. This used to be done with each node creating a &lt;code&gt;std.ArrayList&lt;/code&gt; for its own &lt;em&gt;var declared names&lt;/em&gt; and returning the final result as an immutable slice, i.e. &lt;code&gt;[]const ast.Identifier&lt;/code&gt;. Parent nodes would collect these and join them together.&lt;/p&gt;&lt;p&gt;All of this incurred a significant allocation overhead, especially with &lt;code&gt;std.ArrayList&lt;/code&gt; allocating more space upfront every time it needs to grow. With that in mind I figured a much better solution would be to only ever create one such list and pass it around to not waste any memory from merging and throwing away intermediate results. This means the caller now owns the &lt;code&gt;std.ArrayList&lt;/code&gt; and child nodes recursively pass it to &lt;code&gt;collectVarDeclaredNames()&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;const var_names = try code.varDeclaredNames(agent.gc_allocator);
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;defer agent.gc_allocator.free(var_names);
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;var var_names = std.ArrayList(ast.Identifier).init(agent.gc_allocator);
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;defer var_names.deinit();
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;try code.collectVarDeclaredNames(&amp;amp;var_names);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Results for this microbenchmark look as follows:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/ast-allocations-comparison.png&quot; alt=&quot;Performance comparison of before and after the change&quot;&gt;&lt;/p&gt;&lt;p&gt;Later on I also tried to cache these results across function runs, but the results didn&#39;t convince me.&lt;/p&gt;&lt;h3 id=&quot;optimizing-array-truncation-via-.length&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#optimizing-array-truncation-via-.length&quot;&gt;Optimizing Array Truncation via &lt;code&gt;.length&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/4ac1a346ca3afe5d8c49ab15dae555c32416d27b&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;Another interesting bottleneck I found was array truncation by setting the &lt;code&gt;length&lt;/code&gt; property to a lower value than before, e.g.:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a -&gt; [1, 2, 3]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In that case we would collect all numeric property keys, sort them in descending order, and delete them one by one up to the new length. If that sounds a lot like a backwards &lt;code&gt;for&lt;/code&gt; loop to you that&#39;s because it really should be one :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/array-length-profile-before.png&quot; alt=&quot;Sampling profile before the change&quot;&gt;&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/array-length-profile-after.png&quot; alt=&quot;Sampling profile after the change&quot;&gt;&lt;/p&gt;&lt;p&gt;I ended up with a hybrid solution that uses a threshold for switching between either mechanism to not turn something like this into a performance cliff:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
a&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1_000_000_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// One billion iterations to check for numeric properties to delete&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;avoiding-utf-8-conversion-churn-in-bindings&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#avoiding-utf-8-conversion-churn-in-bindings&quot;&gt;Avoiding UTF-8 Conversion Churn in Bindings&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/cde756369f4b1d9b8457301b638eabedab11148f&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;Strings in Kiesel have a complicated history: they used to be UTF-8 as that&#39;s all modern software should deal with, but are specified to be UTF-16 in ECMAScript and may contain unpaired surrogates which cannot be present in valid UTF-8. These shortcomings as well as the additional complexity from transcoding between UTF-8 and UTF-16 on the fly led to a &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/58dc46014733262760f7fb5f72336e5ccbc9ba5c&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;big refactor&lt;/a&gt; to store strings either as a sequence of &lt;code&gt;u8&lt;/code&gt; for ASCII or as a sequence of &lt;code&gt;u16&lt;/code&gt; for UTF-16. This makes indexing (accessing a single code unit) O(1) and generally simplifies a few things.&lt;/p&gt;&lt;p&gt;While doing that refactor I left the code dealing with bindings (the &lt;code&gt;foo&lt;/code&gt; in &lt;code&gt;let foo = 42;&lt;/code&gt;) untouched, these were still assumed to be UTF-8 (as is the source code that Kiesel parses). Once more this led to a number of on the fly conversions between UTF-8 and UTF-16, and even for the simple ASCII to UTF-8 case we&#39;d end up making a full copy of the string.&lt;/p&gt;&lt;p&gt;Long story short, I updated all of that to use the same underlying &lt;code&gt;String&lt;/code&gt; type as regular JS values and the problem went away :^)&lt;/p&gt;&lt;h3 id=&quot;shrinking-objects-for-memory-savings-fun-and-profit&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#shrinking-objects-for-memory-savings-fun-and-profit&quot;&gt;Shrinking Objects for &lt;s&gt;Memory Savings&lt;/s&gt; Fun and Profit&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/1a386de84176980e0623faec2ed86c58ea3570d1&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;], [&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/e972e9cf74eee6d64731fc854337db70bb2354fc&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;Not all performance work is about achieving better Big-O, there&#39;s another classic: &amp;quot;allocations are slow&amp;quot;. And well, that&#39;s not entirely wrong, especially when allocating means generating garbage that needs to be collected later on. You know, like in JS. I&#39;ve seen profiles where over time the heap would grow to 10GB+ and virtually all time was spent in garbage collection!&lt;/p&gt;&lt;p&gt;The number one reason for heap allocations in JS ought to be objects, so I looked into those. Right now the implementation is a really straightforward &lt;em&gt;property key&lt;/em&gt; (string, symbol, or number) to &lt;em&gt;property descriptor&lt;/em&gt; (value or getter/setter pair for accessors, three attributes stored as boolean flags) mapping. The only issue is that &lt;em&gt;partial&lt;/em&gt; property descriptors can exist in a few situations so all the fields have to be nullable (including a funky double-nullable &lt;code&gt;Object&lt;/code&gt;):&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PropertyDescriptor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;Value&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// 24 bytes&lt;/span&gt;
    writable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 2 bytes&lt;/span&gt;
    get&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// 24 bytes&lt;/span&gt;
    set&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// 24 bytes&lt;/span&gt;
    enumerable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 2 bytes&lt;/span&gt;
    configurable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2 bytes&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                              &lt;span class=&quot;token comment&quot;&gt;// 2 bytes (padding)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// -&gt; 80 bytes!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Additionally a populated property descriptor will have either of these fields set, never an intersection of them:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;value&lt;/code&gt;, &lt;code&gt;writable&lt;/code&gt;, &lt;code&gt;enumerable&lt;/code&gt;, &lt;code&gt;configurable&lt;/code&gt; (&amp;quot;data descriptor&amp;quot;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;get&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;enumerable&lt;/code&gt;, &lt;code&gt;configurable&lt;/code&gt; (&amp;quot;accessor descriptor&amp;quot;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With that in mind we can design this as a tagged enum instead:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// PropertyStorage.zig&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Entry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 8 bytes (enum tag + padding)&lt;/span&gt;
    accessor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        get&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 8 bytes&lt;/span&gt;
        set&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// 8 bytes&lt;/span&gt;
        attributes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3 bytes&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;                          &lt;span class=&quot;token comment&quot;&gt;// 5 bytes (padding)&lt;/span&gt;
                                &lt;span class=&quot;token comment&quot;&gt;// -&gt; 24 bytes&lt;/span&gt;
    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;token comment&quot;&gt;// 16 bytes&lt;/span&gt;
        attributes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3 bytes&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;                          &lt;span class=&quot;token comment&quot;&gt;// 5 bytes (padding)&lt;/span&gt;
                                &lt;span class=&quot;token comment&quot;&gt;// -&gt; 24 bytes&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attributes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;packed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;u3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        writable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        enumerable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        configurable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// -&gt; 32 bytes :^)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better!&lt;/p&gt;&lt;h3 id=&quot;avoiding-number-to-string-conversions-for-property-keys&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#avoiding-number-to-string-conversions-for-property-keys&quot;&gt;Avoiding Number-To-String Conversions for Property Keys&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;[&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/f3c26032b7edf1572d8879fad423157578b0d583&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;source&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;It&#39;s a common misconception that JS has &amp;quot;numeric&amp;quot; property keys; it does not. &lt;code&gt;foo[0]&lt;/code&gt; is the exact same thing as &lt;code&gt;foo[&amp;quot;0&amp;quot;]&lt;/code&gt; — yes, those wacky type coercions :^)&lt;/p&gt;&lt;p&gt;&lt;em&gt;However&lt;/em&gt;, engines can use the opportunity to store property keys that also happen to be positive integers as such internally, and Kiesel is no exception. For instance we can then &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/issues/29&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;store those property values in an actual array with linear indexing&lt;/a&gt; instead of a hashmap.&lt;/p&gt;&lt;p&gt;When it comes to these little unobservable diversions from the spec it&#39;s easy to miss additional work that no longer needs to be done. In this case I noticed something in the &lt;a href=&quot;https://tc39.es/ecma262/#sec-topropertykey&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ToPropertyKey&lt;/a&gt; abstract operation:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The abstract operation ToPropertyKey takes argument &lt;code&gt;argument&lt;/code&gt; (an ECMAScript language value) and returns either a normal completion containing a property key or a throw completion. It converts &lt;code&gt;argument&lt;/code&gt; to a value that can be used as a property key. It performs the following steps when called:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;1. Let key be ? ToPrimitive(argument, string).
2. If key is a Symbol, then
   a. Return key.
3. Return ! ToString(key).
&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;Since the spec only deals with strings and symbols as property keys it unconditionally does that ToString at the end, but we can insert an extra step to check coercing &lt;code&gt;key&lt;/code&gt; to a primitive value resulted in a number and then use that!&lt;/p&gt;&lt;h3 id=&quot;and-a-few-others%E2%80%A6&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#and-a-few-others%E2%80%A6&quot;&gt;And a Few Others…&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;…that I won&#39;t explain in detail here because they&#39;re a bit boring. Feel free to take a look yourself :^)&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/6b2da077bd0d0878054d952cf16c21d1e5c791b5&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;language: Determine if an arguments object will be needed during parsing&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/208804a3b23529bc95b5685169b0b799c6587c8e&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;gc: Enable parallel marking&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/732469ddb55cc53230d512400db4cf15955f1b5a&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;types: Compute string hashes once upfront&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;A number of commits related to making &lt;code&gt;Value&lt;/code&gt; fit into 8 bytes, which will get its own blog post!&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;results&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#results&quot;&gt;Results&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Was it worth it? Absolutely!&lt;/p&gt;&lt;p&gt;Comparing the last commit before the first optimization mentioned above (&lt;code&gt;33e5444&lt;/code&gt;) with &lt;code&gt;main&lt;/code&gt; (&lt;code&gt;e972e9c&lt;/code&gt; at the time of writing) we get massive improvements across the board — the V8 benchmark suite (here &lt;code&gt;combined.js&lt;/code&gt;) now &lt;strong&gt;completes in five minutes instead of 50&lt;/strong&gt;! We also peak at &lt;strong&gt;less than 2GB of memory instead of 9GB&lt;/strong&gt;.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/benchmark-comparison.png&quot; alt=&quot;Performance comparison of before and after the changes&quot;&gt;&lt;/p&gt;&lt;p&gt;This is using &lt;code&gt;-Doptimize=ReleaseFast&lt;/code&gt; builds and running on my AMD Ryzen 7 7840U Framework Laptop.&lt;/p&gt;&lt;p&gt;That was definitely good enough to participate in the nightly Boa benchmark run, so we finally added Kiesel last week. It started with a score of 33, which quickly climbed up to 56:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-10/boa-benchmarks-score.png&quot; alt=&quot;Graph showing the score for Boa and Kiesel over time&quot;&gt;&lt;/p&gt;&lt;p&gt;For comparison: Boa scores 85, LibJS 265, duktape 380, and QuickJS 900. V8 with JIT disabled scores ~2400. There is room for improvement :^)&lt;/p&gt;&lt;h2 id=&quot;the-future&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-10/#the-future&quot;&gt;The Future&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is just the beginning, and we&#39;re barely scratching the surface here. There&#39;s a long list of optimizations to look into in the future, just to name a few:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;NaN-boxing to shrink the fundamental &lt;code&gt;Value&lt;/code&gt; type from 16 to 8 bytes&lt;/li&gt;&lt;li&gt;Linear property storage for numeric properties (i.e. arrays) instead of throwing everything into a hashmap&lt;/li&gt;&lt;li&gt;&amp;quot;Object shapes&amp;quot;/&amp;quot;Hidden classes&amp;quot;&lt;/li&gt;&lt;li&gt;Fast local variables instead of implementing declarative environments with a simple hashmap and parent pointer like described (but not prescribed!) in the spec&lt;/li&gt;&lt;li&gt;Storing small strings in the value itself rather than heap-allocating them, we have some 7 bytes left after the tag&lt;/li&gt;&lt;li&gt;&lt;s&gt;A JIT compiler&lt;/s&gt; Please stop suggesting this&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I&#39;ll have to get back to feature development eventually and we&#39;re not aiming for a V8 competitor here, but squeezing more and more performance out of the engine over time will be fun!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #9: JavaScript on a Printer</title>
    <link href="https://linus.dev/posts/kiesel-devlog-9/" />
    <updated>2024-07-02T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-9/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Firstly: go and read &lt;a href=&quot;https://sdomi.pl/weblog/20-pwning-a-labelmaker/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Pwning a Brother labelmaker, for fun and interop!&lt;/a&gt; by &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; until the end!&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Super cool, right? Getting SSH credentials in response to my shitpost was not exactly what I expected but here we are. Let&#39;s briefly dive into how I ported &lt;a href=&quot;https://kiesel.dev&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;my toy JS engine&lt;/a&gt; to this Linux-based labelmaker :^)&lt;/p&gt;&lt;h2 id=&quot;initial-attempt&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-9/#initial-attempt&quot;&gt;Initial Attempt&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I knew the thing is powered by a 32-bit ARM CPU, so I quickly looked for a suitable Zig target:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ zig targets | jq .libc | grep arm
  &quot;armeb-linux-gnueabi&quot;,
  &quot;armeb-linux-gnueabihf&quot;,
  &quot;armeb-linux-musleabi&quot;,
  &quot;armeb-linux-musleabihf&quot;,
  &quot;arm-linux-gnueabi&quot;,
  &quot;arm-linux-gnueabihf&quot;,
  &quot;arm-linux-musleabi&quot;,
  &quot;arm-linux-musleabihf&quot;,
  &quot;arm-windows-gnu&quot;,&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;arm-linux-musleabi&lt;/code&gt; seemed the most promising (statically linked libc, softfloat) so I tried that, which seemingly worked fine:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ zig build -Dtarget=arm-linux-musleabi -Doptimize=ReleaseSafe
$ file ./zig-out/bin/kiesel
./zig-out/bin/kiesel: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alas, on the other side:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;# wget https://f.sakamoto.pl/linus/kiesel &amp;&amp; chmod +x kiesel
# ./kiesel
Illegal instruction
#&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Luckily the crash is obvious: we need to refine the compilation target instead of letting Zig pick a baseline CPU.&lt;/p&gt;&lt;h2 id=&quot;what&#39;s-inside%2C-exactly%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-9/#what&#39;s-inside%2C-exactly%3F&quot;&gt;What&#39;s Inside, Exactly?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Definitely not Intel:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# cat /proc/cpuinfo
Processor	: ARM926EJ-S rev 5 (v5l)
BogoMIPS	: 177.15
Features	: swp half thumb fastmult edsp java
CPU implementer	: 0x41
CPU architecture: 5TEJ
CPU variant	: 0x0
CPU part	: 0x926
CPU revision	: 5

Hardware	: Conexant DigiColor
Revision	: 0000
Serial		: 0000000000000000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Aha, it&#39;s the processor that first introduced &lt;a href=&quot;https://en.wikipedia.org/wiki/Jazelle&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;running Java bytecode in hardware&lt;/a&gt;, from 2001. Off to a good start!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;the first processor with Jazelle technology was the ARM926EJ-S.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Luckily &lt;a href=&quot;https://github.com/ziglang/zig/blob/cb308ba3ac2d7e3735d1cb42ef085edb1e6db723/lib/std/Target/arm.zig#L1896-L1902&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Zig has a target for that&lt;/a&gt;:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// std/Target/arm.zig&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cpu&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arm926ej_s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CpuModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm926ej_s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;llvm_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm926ej-s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;features &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;featureSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;Feature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;v5te&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which we can use like this:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// build.zig&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolveTargetQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpu_arch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpu_model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;explicit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpu&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arm926ej_s &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;os_tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;linux&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;abi &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;musleabi&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;baseline&lt;/code&gt; ARM CPU model specifies &lt;code&gt;v7a&lt;/code&gt; as its feature set, which explains the illegal instruction.&lt;/p&gt;&lt;h2 id=&quot;atomics%2C-atomics-everywhere&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-9/#atomics%2C-atomics-everywhere&quot;&gt;Atomics, Atomics Everywhere&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Next I was greeted with a sea of &lt;code&gt;__atomic_*&lt;/code&gt; linker errors. It appears this CPU doesn&#39;t have the features needed by Zig&#39;s &lt;a href=&quot;https://github.com/ziglang/zig/blob/master/lib/compiler_rt/atomics.zig&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;compiler_rt&lt;/code&gt; atomics implementation&lt;/a&gt; so it doesn&#39;t end up exporting any of the symbols.&lt;/p&gt;&lt;p&gt;I was eagerly starting to write stubs for a bunch of functions (what could go wrong!) but I soon realised we can improve the situation somewhat:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We can disable &lt;code&gt;Atomics&lt;/code&gt; and &lt;code&gt;SharedArrayBuffer&lt;/code&gt;, the two JS features needing support for atomics by the compiler&lt;/li&gt;&lt;li&gt;There is an option that tells the compiler to assume single-threaded code, which turns atomic operations into non-atomic ones in a bunch of places (e.g. &lt;code&gt;std.Thread.Mutex&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Building in release mode reduced the number of errors even further&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Now I was left with this, similar to what&#39;s been reported in &lt;a href=&quot;https://github.com/ziglang/zig/issues/4959&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ziglang/zig#4959&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;install
└─ install kiesel
   └─ zig build-exe kiesel ReleaseSafe arm-linux-musleabi 5 errors
error: ld.lld: undefined symbol: __sync_val_compare_and_swap_4
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(heap.PageAllocator.alloc)
error: ld.lld: undefined symbol: __sync_fetch_and_add_1
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(debug.handleSegfaultPosix)
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(debug.panicImpl)
error: ld.lld: undefined symbol: __sync_fetch_and_sub_1
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(debug.handleSegfaultPosix)
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(debug.panicImpl)
error: ld.lld: undefined symbol: __sync_val_compare_and_swap_1
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(crypto.tlcsprng.tlsCsprngFill)
error: ld.lld: undefined symbol: __sync_lock_test_and_set_1
    note: referenced by kiesel
    note:               /home/linus/Dev/kiesel/.zig-cache/o/b4dc1cb84146a4bec8e0e93957b1843c/kiesel.o:(once.Once((function &#39;do&#39;)).callSlow)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No problem, we can stub those out. One can get &lt;em&gt;very&lt;/em&gt; far with function stubs alone :^)&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__sync_fetch_and_add_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callconv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__sync_fetch_and_sub_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callconv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__sync_val_compare_and_swap_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callconv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__sync_val_compare_and_swap_4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callconv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u32&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__sync_lock_test_and_set_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callconv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;u8&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;panicked-during-a-panic.-aborting.&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-9/#panicked-during-a-panic.-aborting.&quot;&gt;&lt;code&gt;Panicked during a panic. Aborting.&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now we get further but still crash, with a somewhat amusing message:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;# ./kiesel
Illegal instruction at address 0x5bf4d4
Unable to dump stack trace: debug info stripped
Panicked during a panic. Aborting.
Aborted&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Turns out this goes away when building with both &lt;code&gt;-Denable-intl=false&lt;/code&gt; and &lt;code&gt;-Denable-libgc=false&lt;/code&gt;. &lt;code&gt;Intl&lt;/code&gt; is not needed for what we&#39;re trying to achieve here, and thanks to Zig&#39;s allocator pattern the GC is easily replaced with a &lt;code&gt;std.heap.c_allocator&lt;/code&gt;, at the expense of leaking memory.&lt;/p&gt;&lt;p&gt;A non-stripped release build would probably offer some insights, but I didn&#39;t look further.&lt;/p&gt;&lt;h2 id=&quot;it-works!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-9/#it-works!&quot;&gt;It Works!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As you saw in the blog post linked at the very beginning, this adventure has a good ending:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-9/js-on-a-printer.png&quot; alt=&quot;Downloading and running Kiesel on the aforementioned printer&quot;&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;This article is part of a series called &lt;em&gt;&amp;quot;Linus runs JS on things that absolutely shouldn&#39;t, but it&#39;s fun and Zig quite portable so he does it anyway&amp;quot;&lt;/em&gt;. Previous entries:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://donotsta.re/notice/AgVCwFcXUCjtwgmZM0&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;JavaScript on the 3DS&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://donotsta.re/notice/AiKPgaSHUBaCvhyE0O&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;JavaScript in UEFI&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To quote what I said to &lt;a href=&quot;https://goose.icu&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://goose.icu/img/avatar.png);&quot;&gt;CanadaHonk&lt;/a&gt; afterwards:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It&#39;s a printer. I ran JavaScript on a printer.&lt;/p&gt;&lt;/blockquote&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #8: SSR, but it&#39;s CGI</title>
    <link href="https://linus.dev/posts/kiesel-devlog-8/" />
    <updated>2024-05-19T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-8/</id>
    <content type="html">&lt;p&gt;So, I had an idea the other day. It&#39;s enough of a shitpost for April 1st but given we&#39;re barely through May and I don&#39;t want to wait that long you get to see it now :^)&lt;/p&gt;&lt;p&gt;TL;DR:&lt;/p&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;$ curl -i http://localhost:1337/cgi-bin/hello.js
&lt;span class=&quot;token response-status&quot;&gt;&lt;span class=&quot;token http-version property&quot;&gt;HTTP/1.0&lt;/span&gt; &lt;span class=&quot;token status-code number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token reason-phrase string&quot;&gt;OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;text/html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Kiesel/0.1.0-dev+c498d0465&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token text-html&quot;&gt;
Hello world!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;why%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#why%3F&quot;&gt;Why?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Yes.&lt;/p&gt;&lt;p&gt;On a more serious note, I have no idea how I even got to this point. The last time I touched CGI was about a decade ago, using Python at the time (&lt;a href=&quot;https://peps.python.org/pep-0594/#cgi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;which has its &lt;code&gt;cgi&lt;/code&gt; module scheduled for removal in 3.13&lt;/a&gt; o7).&lt;/p&gt;&lt;p&gt;SSR (Server Side Rendering) with JS has been pretty popular in recent years, but usually involves a lot of setup and bespoke configuration for each framework. I&#39;m sure this CGI-based version of SSR already exists for runtimes like Node but I haven&#39;t checked — naturally I&#39;ll use my own.&lt;/p&gt;&lt;p&gt;This is also the first time anyone has used Kiesel in production, probably!&lt;/p&gt;&lt;h2 id=&quot;how-it-works&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#how-it-works&quot;&gt;How It Works&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you&#39;ve never used CGI, all you need to know is that it stands for &lt;em&gt;Common Gateway Interface&lt;/em&gt; and basically means &amp;quot;run this script to process the request and return its output as the response&amp;quot;. Traditionally that&#39;s something like Perl or Python, but it can also be a compiled binary or shell script. &lt;strong&gt;Or JavaScript!&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc3875&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;RFC 3875&lt;/a&gt; goes into more detail if you&#39;re interested.&lt;/p&gt;&lt;p&gt;There are age-old CGI solutions for most popular web servers (e.g. FastCGI), but I&#39;m not interested in making this a proper thing. Instead I chose &lt;a href=&quot;https://git.sakamoto.pl/laudom/HTTP.sh&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;HTTP.sh&lt;/a&gt;, a web server and framework written in Bash.&lt;/p&gt;&lt;p&gt;It&#39;s mostly made by &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; and much more capable than it sounds! Definitely check it out, you don&#39;t always need to throw an entire Django at web-shaped problems :^)&lt;/p&gt;&lt;p&gt;To pull this off we more or less need to do the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Turn request data into something JS can read (e.g. JSON)&lt;/li&gt;&lt;li&gt;Run the &lt;code&gt;cgi-bin&lt;/code&gt; script with Kiesel while passing it said data somehow and capture the output&lt;/li&gt;&lt;li&gt;Split the output on the first double newline (&lt;code&gt;&#92;n&#92;n&lt;/code&gt;) to separate headers from the response body&lt;/li&gt;&lt;li&gt;Return a response&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;CGI has a number of well-known &lt;em&gt;Request Meta-Variables&lt;/em&gt; which can be passed to the script in any way, usually via environment variables or a runtime API. To keep things simple I decided to modify the script source before passing it to Kiesel and prepend an object. Additionally it provides parsed &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; params, as well as the full URL and user-agent string. &lt;code&gt;HTTP.sh&lt;/code&gt; does most of the heavy lifting (i.e. HTTP request parsing) here.&lt;/p&gt;&lt;p&gt;This makes interacting with the request data as simple as accessing a JS property, e.g. &lt;code&gt;CGI.vars.REQUEST_METHOD&lt;/code&gt; or &lt;code&gt;CGI.params.GET[&amp;quot;name&amp;quot;]&lt;/code&gt;. The full object shape looks like this:&lt;/p&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CGI&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  vars&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;AUTH_TYPE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;CONTENT_LENGTH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;GATEWAY_INTERFACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CGI/1.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;PATH_INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;REMOTE_ADDR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;REMOTE_HOST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;REQUEST_METHOD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;SCRIPT_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;SERVER_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;SERVER_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;SERVER_PROTOCOL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HTTP/1.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;SERVER_SOFTWARE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  params&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userAgent&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;examples&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#examples&quot;&gt;Examples&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;hello-world&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#hello-world&quot;&gt;Hello World&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;A basic hello world looks like this:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: text/html&#92;n&#92;nHello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://kiesel.dev/cgi-bin/hello.js&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Try it!&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;form-with-post-data&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#form-with-post-data&quot;&gt;Form with &lt;code&gt;POST&lt;/code&gt; data&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CGI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Try it!&lt;/p&gt;&lt;form action=&quot;https://kiesel.dev/cgi-bin/form.js&quot; method=&quot;POST&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;foo&quot; value=&quot;Hello&quot;&gt; &lt;input type=&quot;text&quot; name=&quot;bar&quot; value=&quot;World&quot;&gt; &lt;input type=&quot;submit&quot; value=&quot;Submit&quot; formtarget=&quot;_blank&quot;&gt;&lt;/form&gt;&lt;h3 id=&quot;visitor-counter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#visitor-counter&quot;&gt;Visitor Counter&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Using the existing &lt;a href=&quot;https://docs.kiesel.dev/#kiesel.cli.Kiesel.readFile_&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Kiesel.readFile()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.kiesel.dev/#kiesel.cli.Kiesel.writeFile&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Kiesel.writeFile()&lt;/code&gt;&lt;/a&gt; APIs we can even implement a database!&lt;/p&gt;&lt;p&gt;This may not scale, but it works:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: text/html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/tmp/db.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Kiesel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visits &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Kiesel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Visits: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visits&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://kiesel.dev/cgi-bin/db.js&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Try it!&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;evaluate-code-from-the-request&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#evaluate-code-from-the-request&quot;&gt;Evaluate Code From The Request&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: text/plain&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; code &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CGI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;s&gt;Try it!&lt;/s&gt; I&#39;ve chosen to not deploy publicly available RCE at this time. Here&#39;s what it would look like:&lt;/p&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;$ curl -i &#39;http://localhost:1337/cgi-bin/eval.js?code=Array(16).join(%22wat%22%20-%201)%20%2B%20%22%20Batman!%22&#39;
&lt;span class=&quot;token response-status&quot;&gt;&lt;span class=&quot;token http-version property&quot;&gt;HTTP/1.0&lt;/span&gt; &lt;span class=&quot;token status-code number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token reason-phrase string&quot;&gt;OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;text/plain&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Kiesel/0.1.0-dev+c498d0465&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token text-plain&quot;&gt;
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;use-at-your-own-risk&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-8/#use-at-your-own-risk&quot;&gt;Use At Your Own Risk&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The code is on Codeberg at &lt;a href=&quot;https://codeberg.org/kiesel-js/cgi-bin&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/cgi-bin&lt;/a&gt;. You&#39;re free to use it, but I will reiterate an important part of the MIT license here:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #7: Happy Birthday!</title>
    <link href="https://linus.dev/posts/kiesel-devlog-7/" />
    <updated>2024-04-28T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-7/</id>
    <content type="html">&lt;p&gt;As of today &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Kiesel&lt;/a&gt;, the JS engine I&#39;m building in my spare time, is one year old! This post is a short overview of where we are, how we got there, and where we&#39;re going.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;commit 929bb044ffcd1776c850a5b924f9992915277e58
Author: Linus Groh &lt;mail @linusgroh.de&gt;
Date:   Fri Apr 28 19:48:13 2023 +0100

    Initial commit

    Writing another JS engine seems like a fun project for learning Zig :^)&lt;/mail&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;why-are-you-doing-this%2C-again%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-7/#why-are-you-doing-this%2C-again%3F&quot;&gt;Why Are You Doing This, Again?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;What it says in that commit message is true, I did need a project to learn Zig. It&#39;s a simple yet powerful language so that didn&#39;t take very long. Nowadays I&#39;m doing this purely for fun :^)&lt;/p&gt;&lt;p&gt;I&#39;m happy if Kiesel will be useful to someone some day, but in the end that&#39;s not really my motivation. In fact, from my past involvement in open source projects I know that something having users means &lt;em&gt;more&lt;/em&gt; work for me. That&#39;s also the reason why I initially prevented anyone from participating by disabling both issues and PRs for the repo on Codeberg. That has changed since then but it allowed me to not worry about maintainership and just enjoy the process.&lt;/p&gt;&lt;p&gt;And that&#39;s still what this is: recreational programming at its best!&lt;/p&gt;&lt;h2 id=&quot;conformance&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-7/#conformance&quot;&gt;Conformance&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Last week Kiesel &lt;a href=&quot;https://donotsta.re/notice/Ah3w58fVad4rUvYxTE&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;passed 50% of test262&lt;/a&gt;, and it&#39;s still around that number — 50.4% at the time of writing. Progress has slowed a bit over time as more and more of the low hanging fruit were implemented.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-7/test262-graph.png&quot; alt=&quot;Graph showing test262 score over time&quot;&gt;&lt;/p&gt;&lt;p&gt;Something that really helped here is learning from the mistakes made in LibJS and avoid them entirely, including getting the object model and cross-realm interactions right from the beginning.&lt;/p&gt;&lt;p&gt;Another lesson learned from LibJS was that having spec comments on everything is incredibly helpful. You &lt;em&gt;will&lt;/em&gt; get things wrong otherwise, unless you run test262 from the beginning, which you can&#39;t because it needs a somewhat working runtime.&lt;/p&gt;&lt;p&gt;I enforced this in LibJS at some point, introduced it in Kiesel right from the beginning, and have been seeing this style in various other engines since then. It&#39;s great!&lt;/p&gt;&lt;h2 id=&quot;making-progress&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-7/#making-progress&quot;&gt;Making Progress&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you&#39;ve never worked on a project of this scale it can seem intimidating. I&#39;m somewhat surprised by the amount of progress myself, but the gist of it is: &lt;strong&gt;commit to making a little bit of progress frequently instead of a lot of progress infrequently&lt;/strong&gt; — at least for me the former works much better.&lt;/p&gt;&lt;p&gt;This extends to a few other patterns, like not blocking yourself on implementing something fully. Both async functions and generators are not fully implemented yet and useless for practical purposes. They don&#39;t support &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;yield&lt;/code&gt; respectively, so why would I commit that code?&lt;/p&gt;&lt;p&gt;Well, already having implemented 80% of what&#39;s needed means that those last missing pieces will be much easier to do than if I were to do everything at once :^)&lt;/p&gt;&lt;p&gt;You can see the different phases of development in the LOC graph below:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-7/line-graph.png&quot; alt=&quot;Graph showing lines of code over time&quot;&gt;&lt;/p&gt;&lt;p&gt;Side note: Those who know me will not be surprised that I have a Grafana dashboard for this :^)&lt;/p&gt;&lt;p&gt;Roughly it went like this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Getting the basics up and running (3 months)&lt;/li&gt;&lt;li&gt;Break during August 2023 (travelling, &lt;a href=&quot;https://events.ccc.de/camp/2023/infos/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;cccamp23&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;I got back to work and implemented a huge number of builtins (another 3 months)&lt;/li&gt;&lt;li&gt;At the end of 2023 things slowed down as I started a new job, but I continue to regularly work on various things ever since!&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Speaking of lines of code, &lt;a href=&quot;https://github.com/boyter/scc&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;scc&lt;/a&gt; currently reports:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ scc
───────────────────────────────────────────────────────────────────────────────
Language                 Files     Lines   Blanks  Comments     Code Complexity
───────────────────────────────────────────────────────────────────────────────
Zig                        113     62768     9273     14038    39457       5732
YAML                         4       130        8         3      119          0
Markdown                     2       136       36         0      100          0
License                      1        21        4         0       17          0
gitignore                    1         2        0         0        2          0
───────────────────────────────────────────────────────────────────────────────
Total                      121     63057     9321     14041    39695       5732
───────────────────────────────────────────────────────────────────────────────
Estimated Cost to Develop (organic) $1,289,044
Estimated Schedule Effort (organic) 15.15 months
Estimated People Required (organic) 7.56
───────────────────────────────────────────────────────────────────────────────
Processed 2533110 bytes, 2.533 megabytes (SI)
───────────────────────────────────────────────────────────────────────────────&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ignore the part that says I&#39;m a time traveller, 7.5 people, and someone owes me 1.2M USD.&lt;/p&gt;&lt;h2 id=&quot;side-quests&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-7/#side-quests&quot;&gt;Side Quests&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A couple of other projects started as a result of this one:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://codeberg.org/linus/icu4zig&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;linus/icu4zig&lt;/a&gt;: I started writing Zig bindings for the ICU4X library, in order to implement &lt;code&gt;Intl&lt;/code&gt; in Kiesel. Progress has stalled but will continue once I work on &lt;code&gt;Intl&lt;/code&gt; more.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/runtime&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/runtime&lt;/a&gt;: This is a web-compatible runtime (read: &lt;a href=&quot;https://wintercg.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;WinterCG&lt;/a&gt;) for Kiesel, still WIP. It&#39;s integrated into the Kiesel CLI by default so you won&#39;t even notice it exists separately, but this separation is important to me. Kiesel is an implementation of ECMA-262 and ECMA-402, nothing more.&lt;/p&gt;&lt;p&gt;I&#39;m working on it on and off but it&#39;s not as much of a priority to me as the rest of the project. It already has an official &lt;a href=&quot;https://runtime-keys.proposal.wintercg.org/#kiesel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;runtime key&lt;/a&gt; though!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/website&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kiesel-js/website&lt;/a&gt;: I bought &lt;code&gt;kiesel.dev&lt;/code&gt; and made a small website for it. It doesn&#39;t have much content at the moment but most importantly powers the &lt;a href=&quot;https://kiesel.dev/playground&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Wasm-based playground&lt;/a&gt; which I&#39;m still very happy about!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A web browser: this idea has been floating around in my head for a while, it&#39;s the natural extension of building a JS engine. I&#39;ve started working on something related yesterday but progress will only be shared with a few friends until I have more to show. Stay tuned :^)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;thanks!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-7/#thanks!&quot;&gt;Thanks!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;While this is still largely a me &lt;s&gt;problem&lt;/s&gt; project there were a handful of other contributors:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ git shortlog -sn
  1425  Linus Groh
     7  Carter Snook
     1  Ali Mohammad Pur
     1  Andrew Kaster
     1  Dominique Liberda&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Especially seeing &lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/alimpfard);&quot;&gt;CxByte&lt;/a&gt; and &lt;a href=&quot;https://github.com/ADKaster&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/ADKaster);&quot;&gt;Andrew&lt;/a&gt; show up with some small contributions made me quite happy, I haven&#39;t really kept in touch since leaving SerenityOS. Related fun fact: 100% of Kiesel contributors are also SerenityOS contributors. :^)&lt;/p&gt;&lt;p&gt;I&#39;ve also had countless conversations with &lt;a href=&quot;https://goose.icu&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://goose.icu/img/avatar.png);&quot;&gt;CanadaHonk&lt;/a&gt; and became friends with them over the last year while they were hacking on their JS engine, &lt;a href=&quot;https://porffor.goose.icu&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;porffor&lt;/a&gt;. It&#39;s been really fun to have that in the absence of a community around Kiesel! They also made &lt;a href=&quot;https://test262.fyi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262.fyi&lt;/a&gt; which I still look at almost daily.&lt;/p&gt;&lt;p&gt;Very recently &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; provided me with a much more powerful CI runner, &lt;a href=&quot;https://donotsta.re/notice/Ah5n5MjGnmilppH8aW&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;shortening build times from 35 minutes to less than 10&lt;/a&gt;. This should now allow me to look into running test262 in CI instead of relying on the nightly external test262 run to catch regressions. She&#39;s also the author of &lt;a href=&quot;https://donotsta.re/notice/AXpQcWMqknp3loce4O&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;this banger of a post&lt;/a&gt; &amp;lt;3&lt;/p&gt;&lt;p&gt;Lastly the folks from the &lt;a href=&quot;https://ziglang.org/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Zig project&lt;/a&gt; have been very responsive to my bug and regression reports, and I even &lt;a href=&quot;https://github.com/ziglang/zig/pulls?q=is%3Apr+author%3Alinusg+is%3Amerged&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;contributed&lt;/a&gt; a few patches myself. In a year Zig has already replaced Python as my favourite general purpose language, and I used that for close to a decade!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Overall this has been heaps of fun and I&#39;ll at least continue for another year. If &lt;a href=&quot;https://serenityos.social/@thakis/112305784167843390&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;this prediction&lt;/a&gt; is right I&#39;ll be done by then, and in the meantime you&#39;re welcome to join the fun :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #6: Catching up :^)</title>
    <link href="https://linus.dev/posts/kiesel-devlog-6/" />
    <updated>2024-01-29T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-6/</id>
    <content type="html">&lt;p&gt;Happy new year! Well, &lt;em&gt;new&lt;/em&gt; is a stretch, but it&#39;s been a while since I last did one of these. This post is gonna be all over the place, but I want to get you up to speed before focusing on more specific things again :^)&lt;/p&gt;&lt;h2 id=&quot;tl%3Bdr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#tl%3Bdr&quot;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The last (exactly!) three months can be summed up as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Kiesel has three new contributors! Considering the total number is four this is a lot 🎉&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Ali Mohammad Pur   Fri Oct 27 02:53:42 2023 +0330  cli+language: Use zigline to provide a nice syntax-highlighted REPL
Andrew Kaster      Fri Dec 22 12:00:35 2023 -0500  builtins: Bump comptime limit in reg_exp to 20,000
Dominique Liberda  Fri Dec 29 01:44:25 2023 +0100  builtins: Implement Promise.withResolvers()&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;273 commits to &lt;code&gt;main&lt;/code&gt;, with tons of new features and bug fixes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;test262 went from &lt;strong&gt;35.3%&lt;/strong&gt; to &lt;strong&gt;45.8%&lt;/strong&gt;, almost half way there 📈&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/test262-history-graph-kiesel.png&quot; alt=&quot;Screenshot of the test262.fyi website showing a history graph for Kiesel&quot;&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let&#39;s take a closer look :^)&lt;/p&gt;&lt;h2 id=&quot;a-better-repl&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#a-better-repl&quot;&gt;A Better REPL&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I already hinted towards this in the &lt;a href=&quot;https://linus.dev/posts/kiesel-devlog-5#classes&quot;&gt;last devlog&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/alimpfard);&quot;&gt;CxByte&lt;/a&gt; managed to yakbait himself into writing a &lt;a href=&quot;https://github.com/alimpfard/zigline&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;line editor in Zig&lt;/a&gt; and kindly integrated it into Kiesel&#39;s REPL!&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111484756603297688/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;p&gt;It still had a bug or two early on but things seem stable now — if you happen to notice any issues, please report them upstream.&lt;/p&gt;&lt;p&gt;Additionally, I added a preamble to the REPL to make it more informative instead of dropping you into a prompt right away:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ kiesel
Kiesel 0.1.0 [Zig 0.12.0-dev.2341+92211135f] on linux
Use Ctrl+D to exit.
&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;macos-ci-builds&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#macos-ci-builds&quot;&gt;macOS CI Builds&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Zig is excellent at cross compiling for other targets, but for the longest time I didn&#39;t add macOS CI builds because libgc includes &lt;code&gt;mach-o/getsect.h&lt;/code&gt; from the macOS SDK, which needs to be installed on the build host. Vendoring the file didn&#39;t seem desirable, so I simply ignored the problem — macOS users could still build from source.&lt;/p&gt;&lt;p&gt;After finding and integrating &lt;a href=&quot;https://github.com/mitchellh/zig-build-macos-sdk&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;this Zig package&lt;/a&gt;, the problem solved itself and there is now a &lt;code&gt;kiesel-macos-aarch64&lt;/code&gt; build on &lt;a href=&quot;https://files.kiesel.dev&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;files.kiesel.dev&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/ADKaster&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/ADKaster);&quot;&gt;Andrew&lt;/a&gt; for fixing the build after I unknowingly broke it, which lead to me thinking about this again :^)&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111625257617328898/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;promise.withresolvers()&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#promise.withresolvers()&quot;&gt;&lt;code&gt;Promise.withResolvers()&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is a new function (&lt;a href=&quot;https://github.com/tc39/proposals/commit/8fef293ac43c2ae15b4209ce1fec6347c9a0a583&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;stage 4 in November 2023&lt;/a&gt;) that implements the commonly used &lt;em&gt;deferred&lt;/em&gt; promise pattern. The proposal was championed by a colleague of mine, and I ended up contributing the &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/56593&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;upstream TypeScript definitions&lt;/a&gt; to help getting it ready for production use.&lt;/p&gt;&lt;p&gt;The spec text is seven lines so I resisted writing an implementation myself, knowing it could be a good first task for someone wanting to contribute to Kiesel — and it was! At 37c3 &lt;a href=&quot;https://sdomi.pl&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://sdomi.pl/.well-known/avatar);&quot;&gt;Domi&lt;/a&gt; and I sat down and did some Zig hacking together :^)&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111661004783336728/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;atomics%2C-dataview%2C-typedarray&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#atomics%2C-dataview%2C-typedarray&quot;&gt;&lt;code&gt;Atomics&lt;/code&gt;, &lt;code&gt;DataView&lt;/code&gt;, &lt;code&gt;TypedArray&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I finally got around to implementing these three:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;TypedArray&lt;/code&gt;: still completely unoptimized (using the regular object get/set code paths), but at least memory-wise a raw blob of bytes is already an improvement over using a regular array (which, in Kiesel, is still backed by a hashmap of property keys to JS values)&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView&lt;/code&gt;: Really happy about this, &lt;code&gt;ArrayBuffer&lt;/code&gt; is largely useless if you can&#39;t read and write bytes&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics&lt;/code&gt;: only vaguely useful as there are no shared array buffers yet — but hey, free test262 points&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/atomics-dataview-typedarray.png&quot; alt=&quot;Demo of Atomics, DataView, and TypedArray in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;internationalization&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#internationalization&quot;&gt;&lt;code&gt;Int&lt;/code&gt;ernationa&lt;code&gt;l&lt;/code&gt;ization&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;ECMA-402 (aka &lt;code&gt;Intl&lt;/code&gt;) was never that high on my list of priorities for Kiesel (and still isn&#39;t), but it&#39;s nice to have those APIs I suppose!&lt;/p&gt;&lt;p&gt;Unlike LibJS I opted to not implement my own &lt;a href=&quot;https://icu.unicode.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ICU&lt;/a&gt; replacement and chose &lt;a href=&quot;https://github.com/unicode-org/icu4x&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;ICU4X&lt;/a&gt; instead (using their C API as ICU4X is written in Rust 🦀).&lt;/p&gt;&lt;p&gt;Generally speaking you can get away with using C APIs in Zig directly, but in this case the code looked horrendous:&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111422322167490964/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;p&gt;I decided to write a simple wrapper as a standalone library — and that&#39;s how &lt;a href=&quot;https://codeberg.org/linus/icu4zig&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;icu4zig&lt;/a&gt; was born!&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111437883703206998/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;p&gt;So far I&#39;ve used it to implement locale handling, adding other objects will require more work on icu4zig first.&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111456662626308168/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented &lt;code&gt;Intl&lt;/code&gt; builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Intl.getCanonicalLocales()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.baseName&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.calendar&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.caseFirst&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.collation&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.hourCycle&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.language&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.maximize()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.minimize()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.numberingSystem&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.numeric&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.region&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.script&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl.Locale.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Intl[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h2 id=&quot;annex-b-has-joined-the-chat&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#annex-b-has-joined-the-chat&quot;&gt;Annex B Has Joined the Chat&lt;/a&gt;&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-additional-ecmascript-features-for-web-browsers&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;strong&gt;B Additional ECMAScript Features for Web Browsers&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The ECMAScript language syntax and semantics defined in this annex are required when the ECMAScript host is a web browser. The content of this annex is normative but optional if the ECMAScript host is not a web browser.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Implementing it means:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I get more test262 points&lt;/li&gt;&lt;li&gt;You can now use Kiesel to implement a web browser. You&#39;re welcome.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Jokes aside, while these language features are &lt;em&gt;hopefully&lt;/em&gt; not being relied upon in new code, there&#39;s existing JS out there that needs them. I&#39;ve added a compile time flag to make them opt out (&lt;code&gt;-Denable-annex-b=false&lt;/code&gt;), and given the implementation is not too invasive I&#39;m happy to have it around :^)&lt;/p&gt;&lt;p&gt;So far this includes builtins and and the &lt;code&gt;[[IsHTMLDDA]]&lt;/code&gt; internal slot (if you don&#39;t know what that means: good for you!), but none of the syntax changes.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/annex-b-html.png&quot; alt=&quot;Demo of string to HTML functions in the Kiesel REPL&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/annex-b-ishtmldda.png&quot; alt=&quot;Demo of the [[IsHTMLDDA]] internal slot in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented Annex B builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toGMTString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.compile()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.substr()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.anchor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.big()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.blink()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.bold()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.fixed()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.fontcolor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.fontsize()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.italics()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.link()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.small()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.strike()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.sub()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.sup()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.trimLeft()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.trimRight()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h2 id=&quot;performance-%F0%9F%9A%80%F0%9F%9A%80%F0%9F%9A%80&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#performance-%F0%9F%9A%80%F0%9F%9A%80%F0%9F%9A%80&quot;&gt;Performance 🚀🚀🚀&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;While Kiesel remains largely unoptimized — make it work, make it right (we are here), make it fast — I&#39;ve finally started profiling and implemented some quick wins:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/372fee817749e3228fb617b59d03d3bbe7a926f9&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Bytecode of functions is now cached&lt;/a&gt; after they are first called, no need to regenerate the same one on every call.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Determining the UTF-16 string length from the internal UTF-8 representation was incredibly heavy and is now &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/ddc752c39ea3f5e3043b9720af249c8ae7c37e7c&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;done only once per string&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Asking a string for one of its code units is now &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/dc4fa102cd8113e9465961519bc5117f79adce30&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;implemented using an iterator&lt;/a&gt; that doesn&#39;t require allocating a list.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I copied an &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/d5aed64eff25bfecf5ec764e2febd7a8a596d4a2&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;optimization for property lookups&lt;/a&gt; from LibJS, which in turn copied it from JavaScriptCore — when doing property lookups on primitives we don&#39;t need to create an object wrapper as per the spec, we can directly start looking on the respective prototype object, no own properties can exist on the wrapper object anyway!&lt;/p&gt;&lt;p&gt;The result can be seen in these flame graphs (before and after):&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/profile-before.png&quot; alt=&quot;A flame graph for Kiesel showing 550ms spent in value to object conversion&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-6/profile-after.png&quot; alt=&quot;A flame graph for Kiesel showing 270ms spent in value to object conversion&quot;&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In general, optimizing is both necessary and heaps of fun, so I&#39;ll definitely do more of that! Special shout-out to &lt;a href=&quot;https://goose.icu&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;CanadaHonk&lt;/a&gt; who was the source of yakbait for most of these while benchmarking against &lt;a href=&quot;https://porffor.goose.icu&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Porffor&lt;/a&gt; :^)&lt;/p&gt;&lt;h2 id=&quot;modules&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#modules&quot;&gt;Modules&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Not much happened here and you still can&#39;t fully import modules in Kiesel, but I got some basics up and running:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Parsing of various (not all!) import/export declarations&lt;/li&gt;&lt;li&gt;Parsing and evaluation of dynamic &lt;code&gt;import()&lt;/code&gt; calls&lt;/li&gt;&lt;li&gt;Module environments and namespace objects&lt;/li&gt;&lt;li&gt;Hooking up module resolution in the CLI, i.e. turning module specifiers into paths and reading files from disk&lt;/li&gt;&lt;/ul&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111354556931083114/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-6/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A few other bits that I didn&#39;t mention yet:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Added support for function parameter initializers (default values), &lt;code&gt;&#92;x&lt;/code&gt; and &lt;code&gt;&#92;u&lt;/code&gt; string escape sequences, and for-in/of statements (although the patch for that one is rather ugly)&lt;/li&gt;&lt;li&gt;I&#39;ve done a decently large refactor to rip printing and bytecode generation out of the AST itself, those are separate now. Not only did it become unwieldy, but this also makes me more confident doing a rewrite or the bytecode VM (it&#39;s kinda bad lol), or adding other backends (e.g. outputting 3rd party IR, AOC, JIT, …)&lt;/li&gt;&lt;/ul&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Atomics.add()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.and()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.exchange()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.isLockFree()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.load()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.or()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.store()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.sub()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics.xor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Atomics[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype.buffer&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype.byteLength&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype.byteOffset&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype.getFoo()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype.setFoo()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataView.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;decodeURI()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;decodeURIComponent()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;encodeURI()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;encodeURIComponent()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function.prototype[@@hasInstance]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.groupBy()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.fromEntries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.groupBy()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.allSettled()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.any()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.race()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.withResolvers()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype[@@match]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype[@@split]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.fromCharCode()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.fromCodePoint()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.codePointAt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.includes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.lastIndexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.match()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.replace(), sans substitutions&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.replaceAll()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.split()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.substring()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.toLowerCase()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.toUpperCase()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.trim()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.trimEnd()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.trimStart()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.prototype.description&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;TypedArray.BYTES_PER_ELEMENT&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;TypedArray.prototype.BYTES_PER_ELEMENT&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.from()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.of()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.at()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.buffer&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.byteLength&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.byteOffset&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.copyWithin()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.entries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.every()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.fill()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.filter()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.find()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.findIndex()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.findLast()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.findLastIndex()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.forEach()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.includes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.indexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.join()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.keys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.lastIndexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.length&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.map()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.reduce()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.reduceRight()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.reverse()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.slice()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.some()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.sort()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.subarray()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.toReversed()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.toSorted()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.values()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype.with()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype[@@iterator]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;%TypedArray%[@@species]&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;p&gt;Thanks for reading, until next time! :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #5: Progress powered by the Shadow web engine</title>
    <link href="https://linus.dev/posts/kiesel-devlog-5/" />
    <updated>2023-10-29T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-5/</id>
    <content type="html">&lt;h2 id=&quot;shadow-web-engine&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#shadow-web-engine&quot;&gt;&lt;em&gt;Shadow&lt;/em&gt; Web Engine&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is a project my friend &lt;a href=&quot;https://github.com/CanadaHonk/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;CanadaHonk&lt;/a&gt; started working on earlier this week — it&#39;s a simple web engine written in JS! More about that &lt;a href=&quot;https://goose.icu/introducing-shadow/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For JS execution within &lt;em&gt;Shadow&lt;/em&gt; they had the idea to use existing Wasm builds of JS engines instead of relying on the host browser, and to my surprise they picked SpiderMonkey and Kiesel for the initial proof of concept, switchable at runtime!&lt;/p&gt;&lt;p&gt;This resulted in me spending an evening fixing bugs and looking into missing features they asked about, including a few of the below. Of course this isn&#39;t anywhere near being a serious web engine just yet, but it&#39;s really cool to have Kiesel featured in one regardless :^)&lt;/p&gt;&lt;h2 id=&quot;classes&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#classes&quot;&gt;Classes&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I decided to implement classes even before &lt;em&gt;Shadow&lt;/em&gt; happened just to make some progress on language features again — they definitely win in the category &lt;em&gt;weirdest codegen&lt;/em&gt; across the entire engine, but other than that I&#39;m happy with how that turned out :^)&lt;/p&gt;&lt;p&gt;Supported features include class expressions/declarations, methods (of course), &lt;code&gt;constructor()&lt;/code&gt;, static and instance class fields, static blocks, as well as &lt;code&gt;super&lt;/code&gt; calls and properties (no private properties yet). You can see some of those in action &lt;a href=&quot;https://github.com/CanadaHonk/shadow/blob/04151393b8a8b5f4af78b798307c5d171293e87e/engine/js/ipc/inside.js#L39-L84&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I also added support for parsing async, generator, and async generator method definitions in classes or object literals, but they&#39;re just as non-functional at runtime as their function declaration counterparts.&lt;/p&gt;&lt;p&gt;Sharing a screenshot of an entire class being written on a single line with no syntax highlighting prompted &lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/alimpfard);&quot;&gt;CxByte&lt;/a&gt; (whom you might know from the SerenityOS project! 👋) to start working on a LibLine Zig port to improve the REPL, more on that soon!&lt;/p&gt;&lt;iframe src=&quot;https://octodon.social/@cxboog/111261693351047494/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;the-beginning-of-modules&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#the-beginning-of-modules&quot;&gt;The Beginning of Modules&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Don&#39;t get too excited just yet, there&#39;s not much working here! For now I implemented the distinction between &lt;em&gt;scripts&lt;/em&gt; and &lt;em&gt;modules&lt;/em&gt;, a flag for the CLI, some basic parsing of import declaration, and the &lt;code&gt;import.meta&lt;/code&gt; object.&lt;/p&gt;&lt;p&gt;I had very little to do with the module support in LibJS, so this is new territory for me :^)&lt;/p&gt;&lt;h2 id=&quot;other-language-features&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#other-language-features&quot;&gt;Other Language Features&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I implemented a couple more small language features:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Function rest parameters&lt;/li&gt;&lt;li&gt;Spreading in array and object literals&lt;/li&gt;&lt;li&gt;Unlabelled &lt;code&gt;break&lt;/code&gt; and &lt;code&gt;continue&lt;/code&gt; statements (yes, I went 6+ months without those 😅)&lt;/li&gt;&lt;li&gt;Optional expressions (&lt;code&gt;foo?.bar&lt;/code&gt;, &lt;code&gt;foo?.[&amp;quot;bar&amp;quot;]&lt;/code&gt;, &lt;code&gt;foo?.()&lt;/code&gt;) - I remember struggling with this when attempting an implementation in LibJS once, but here it&#39;s a very straightforward &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/113e6faed4804dfbf873060f18e507288872c3b3&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;140 line diff&lt;/a&gt;. Huh!&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;resizable-array-buffers&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#resizable-array-buffers&quot;&gt;Resizable Array Buffers&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These existed as a proposal for a while and finally got &lt;a href=&quot;https://github.com/tc39/ecma262/pull/3116&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;merged into the main spec&lt;/a&gt; a couple of weeks ago, so I promptly implemented the new functionality :^)&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111229873810744870/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;reporting-of-unhandled-promise-rejections&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#reporting-of-unhandled-promise-rejections&quot;&gt;Reporting of Unhandled Promise Rejections&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A small DX improvement; the CLI will now report these two cases at the end of script evaluation:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A promise was rejected without any handlers&lt;/li&gt;&lt;li&gt;A handler was added to an already rejected promise&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Under the hood this uses the same mechanism as the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;unhandledrejection&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/rejectionhandled_event&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;rejectionhandled&lt;/code&gt;&lt;/a&gt; events on the web.&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111273960579125672/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;improved-ci-builds&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#improved-ci-builds&quot;&gt;Improved CI Builds&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I continue to publish pre-built binaries on &lt;a href=&quot;https://files.kiesel.dev/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;files.kiesel.dev&lt;/a&gt;, with a few recent changes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Use musl for Linux builds&lt;/li&gt;&lt;li&gt;Strip executable in release modes&lt;/li&gt;&lt;li&gt;Addition of a Windows aarch64 build (because we can)&lt;/li&gt;&lt;li&gt;Addition of a Linux riscv64 build (also because we can)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-5/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype.maxByteLength&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype.resizable&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype.resize()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;JSON.parse()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;JSON.stringify()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.parseFloat()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.parseInt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype[@@matchAll]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype[@@search]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.endsWith()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.indexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.matchAll()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.search()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.startsWith()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Non-standard:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Kiesel.readFile()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.readLine()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.readStdin()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.sleep()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.writeFile()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;p&gt;test262 is still steadily going up, now at 35.3%. I check the graph every morning, seeing the small bump from last night&#39;s run is very motivating 📈&lt;/p&gt;&lt;p&gt;Collaborating with CanadaHonk so they can use Kiesel in the &lt;em&gt;Shadow&lt;/em&gt; web engine has been really fun, and I made lots of progress thanks to that! I&#39;m not sure how quickly modules will take shape from here on, but they create some exciting new abilities such as importable native modules:&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111294978895182558/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #4: The biggest update yet!</title>
    <link href="https://linus.dev/posts/kiesel-devlog-4/" />
    <updated>2023-10-11T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-4/</id>
    <content type="html">&lt;p&gt;It&#39;s been two weeks and 108 commits since the last devlog, and I&#39;m really happy with the progress I made! Let&#39;s take a look at all those shiny new features :^)&lt;/p&gt;&lt;p&gt;A quick check of &lt;a href=&quot;https://test262.fyi/#%7Ckiesel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262.fyi&lt;/a&gt; reveals that Kiesel now scores exactly 33%.&lt;/p&gt;&lt;h2 id=&quot;arraybuffer&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#arraybuffer&quot;&gt;ArrayBuffer&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;First up, &lt;code&gt;ArrayBuffer&lt;/code&gt;. They&#39;re not very capable and mostly serve as memory storage for other kinds of objects (&lt;code&gt;DataView&lt;/code&gt;, typed arrays), and are thus fairly easy to implement. The &lt;a href=&quot;https://github.com/tc39/proposal-resizablearraybuffer&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;resizable &lt;code&gt;ArrayBuffer&lt;/code&gt; proposal&lt;/a&gt; will make things slightly more complicated, but I chose a &lt;a href=&quot;https://ziglang.org/documentation/master/std/#A;std:ArrayList&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;std.ArrayList(u8)&lt;/code&gt;&lt;/a&gt; for the underlying buffer, which already is resizable.&lt;/p&gt;&lt;p&gt;I also added a non-standard &lt;code&gt;detachArrayBuffer()&lt;/code&gt; function to the &lt;code&gt;Kiesel&lt;/code&gt; namespace object, which exposes the &lt;a href=&quot;https://tc39.es/ecma262/#sec-detacharraybuffer&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;abstract operation of the same name&lt;/a&gt; — primarily for test262. Here is it in action, along with the REPL&#39;s pretty-printing:&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111173493529374713/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;p&gt;I haven&#39;t started working on the aforementioned typed arrays yet, but I think Zig has a nice way of writing a generic implementation that handles all the different integer types.&lt;/p&gt;&lt;h2 id=&quot;promise&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#promise&quot;&gt;Promise&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I previously implemented promises in LibJS, and it basically &lt;a href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-04-02-%E2%80%94-finishing-the-promises-pr&quot; target=&quot;_blank&quot;&gt;took me a year to finish&lt;/a&gt; — luckily doing it again in Kiesel was &lt;em&gt;much&lt;/em&gt; quicker :^)&lt;/p&gt;&lt;p&gt;I recorded most of the process in a two-part video series, first adding &lt;code&gt;Promise&lt;/code&gt; objects and then implementing promise reaction jobs:&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;vXy8AADmbpw&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;ODZrGJ_TEJo&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;h2 id=&quot;regexp&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#regexp&quot;&gt;RegExp&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To keep scope creep under control I decided to not write a JS-flavored regular expression library at this point, the same way I opted to use an existing library for GC. Both of these are fairly decoupled from everything else and easy to replace later on if desired.&lt;/p&gt;&lt;p&gt;I ended up using &lt;a href=&quot;https://bellard.org/quickjs/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;quickjs&lt;/a&gt;&#39;s libregexp:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;libregexp&lt;/strong&gt;: small and fast regexp library fully compliant with the Javascript ES 2019 specification.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I also briefly considered using SerenityOS&#39;s LibRegex, which is what LibJS uses, but adding C wrappers for a C++ library is a can of worms I wasn&#39;t keen to open.&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111201909602469291/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;p&gt;However, choosing to &lt;em&gt;not&lt;/em&gt; write this myself comes at a cost: C code is compiled with much less safety than Zig code, and I already &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/a3894523dc7ee930247cd53ec3192cffd72b39b2&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;found a bug&lt;/a&gt; that I had to patch (and at least one other crash I still need to investigate). These flaws aside, &lt;a href=&quot;https://ziglang.org/learn/overview/#integration-with-c-libraries-without-ffibindings&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;using C libraries from Zig&lt;/a&gt; is one of its major selling points, so the actual integration took me less than a day!&lt;/p&gt;&lt;p&gt;And a quick demo:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-4/regexp.png&quot; alt=&quot;Demo of RegExp objects in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;other-builtins&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#other-builtins&quot;&gt;Other Builtins&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Lastly I implemented a few remaining &lt;code&gt;Math&lt;/code&gt; functions, &lt;code&gt;parseInt()&lt;/code&gt;, a half-assed version of &lt;code&gt;parseFloat()&lt;/code&gt;, and bound function objects via &lt;code&gt;Function.prototype.bind()&lt;/code&gt;.&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.isView()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype.byteLength&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype.slice()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ArrayBuffer.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function.prototype.bind()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.atan2()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.fround()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.imul()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;parseFloat()&lt;/code&gt;, somewhat&lt;/li&gt;&lt;li&gt;&lt;code&gt;parseInt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.prototype.catch()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.prototype.finally()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.prototype.then()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.reject()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Promise.resolve()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.dotAll&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.exec()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.flags&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.global&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.hasIndices&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.ignoreCase&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.multiline&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.source&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.sticky&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.test()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.unicode&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;RegExp.prototype.unicodeSets&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h2 id=&quot;new-language-features&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#new-language-features&quot;&gt;New Language Features&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I continue to favor working on runtime, but I&#39;ve made some progress with language features as well:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Regular expression literals (effectively syntactic sugar for the &lt;code&gt;RegExp()&lt;/code&gt; constructor)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Hashbang comments [&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#hashbang_comments&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;MDN&lt;/a&gt;]&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The &lt;code&gt;new.target&lt;/code&gt; meta property [&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;MDN&lt;/a&gt;]&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Mapped &lt;code&gt;arguments&lt;/code&gt; objects [&lt;a href=&quot;https://tc39.es/ecma262/#sec-arguments-exotic-objects&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Spec&lt;/a&gt;]&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;An arguments exotic object is an exotic object whose array index properties map to the formal parameters bindings of an invocation of its associated ECMAScript function.&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Parsing and basic bytecode generation for more function types:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Async arrow functions&lt;/li&gt;&lt;li&gt;Async function expressions/declarations&lt;/li&gt;&lt;li&gt;Async generator expressions/declarations&lt;/li&gt;&lt;li&gt;Generator expressions/declarations&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;However, they cannot run their function bodies in a meaningful way yet.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-4/functions.png&quot; alt=&quot;Demo of pretty-printing different function objects in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;windows-build&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#windows-build&quot;&gt;Windows Build&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I personally haven&#39;t used Windows for anything in a long time, but I aim for Kiesel to work on all major operating systems. Thanks to &lt;a href=&quot;https://ziglang.org/learn/overview/#cross-compiling-is-a-first-class-use-case&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Zig&#39;s amazing cross-compilation capabilities&lt;/a&gt; testing that doesn&#39;t even involve VMs or dual-booting!&lt;/p&gt;&lt;p&gt;Someone on the Zig Discord server gave me a hint how to fix one of the issues I encountered when trying to build a Windows executable, so I went ahead and fixed a few more. And now we have a working Windows build!&lt;/p&gt;&lt;iframe src=&quot;https://chaos.social/@linusgroh/111213182360508843/embed&quot; class=&quot;mastodon-embed&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;h2 id=&quot;syntaxerror-source-hints&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#syntaxerror-source-hints&quot;&gt;SyntaxError Source Hints&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Syntax errors are edge cases and thus I haven&#39;t spent much effort on making them nice so far. Many of the error messages are not very useful, especially with the parser backtracking on unknown tokens and then telling you this nonsense:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ cat file.js
function foo() { # }
$ kiesel file.js
SyntaxError: Unexpected token &#39;function&#39; (file.js:1:1)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For the cases where the location is accurate this kind of line/column output is still not super helpful though, so I added &lt;em&gt;source hints&lt;/em&gt;:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ cat file.js
/[a-z]/x
$ kiesel file.js
/[a-z]/x
       ^
Uncaught exception: SyntaxError: Invalid RegExp flags (file.js:1:8)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better!&lt;/p&gt;&lt;h2 id=&quot;recordings-are-back!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-4/#recordings-are-back!&quot;&gt;Recordings Are Back!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As mentioned above: I started recording some of the work I do on Kiesel, and there will definitely be more of that!&lt;/p&gt;&lt;p&gt;Videos are uploaded to both &lt;a href=&quot;https://betamax.donotsta.re/c/linusgroh&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PeerTube&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/@LinusGroh&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;YouTube&lt;/a&gt;, and will be as irregular as ever :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #3: Accessors, Date/Map/Set, test262 history graph</title>
    <link href="https://linus.dev/posts/kiesel-devlog-3/" />
    <updated>2023-09-26T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-3/</id>
    <content type="html">&lt;h2 id=&quot;accessors-in-object-expressions&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-3/#accessors-in-object-expressions&quot;&gt;Accessors in Object Expressions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I first implemented the fairly similar object method definition syntax (&lt;code&gt;{ foo() {} }&lt;/code&gt;), but cheated with the AST representation — this would output the same structure as &lt;code&gt;{ foo: function() {} }&lt;/code&gt;, which obviously falls apart when the definition has a &lt;code&gt;get&lt;/code&gt; or &lt;code&gt;set&lt;/code&gt; prefix.&lt;/p&gt;&lt;p&gt;One small &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/7928e17b567a024ed357441a813d32d369ae2751&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;refactor&lt;/a&gt; later I had an &lt;code&gt;object_define_method&lt;/code&gt; bytecode instruction tailored to this use case, which then allowed me to &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/d49ad32d72dc6e82ed34d5b6145682d9ad8b4bd2&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;implement&lt;/a&gt; getters and setters easily by passing a &lt;code&gt;method&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, or &lt;code&gt;set&lt;/code&gt; enum argument to the instruction. The underlying accessor property mechanism was already working and used for some builtin objects.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/accessors.png&quot; alt=&quot;Demo of accessor properties in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;p&gt;The generated bytecode looks like this (the indices refer to the constant and function expression tables embedded in the executable):&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
Generated Bytecode
&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;
 &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_create
 &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
 &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; store_constant &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
 &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_define_method &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; store_constant &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_define_method &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; get&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; store_constant &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_define_method &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; set&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;end&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; fn foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;accessor&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;accessor&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or, in the case of a computed property (that &lt;code&gt;store_constant&lt;/code&gt; + &lt;code&gt;load&lt;/code&gt; sequence can be optimized to a single &lt;code&gt;load_constant&lt;/code&gt; instruction eventually):&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
Generated Bytecode
&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;
 &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_create
 &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
 &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; resolve_binding foo &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; get_value
 &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
 &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; store_constant &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_string_or_numeric_binary_operator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;operator&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; load
&lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object_define_method &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; get&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;end&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;new-builtins!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-3/#new-builtins!&quot;&gt;New Builtins!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;TL;DR:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Finished implementing &lt;code&gt;Array.prototype&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Added most of &lt;code&gt;Date&lt;/code&gt; including basic parsing, no non-UTC time zones yet&lt;/li&gt;&lt;li&gt;Added all of &lt;code&gt;Map&lt;/code&gt; and &lt;code&gt;Set&lt;/code&gt;, including their iterators&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This amounts to a almost 100 new functions!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/date.png&quot; alt=&quot;Demo of the Date object in the Kiesel REPL&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/set.png&quot; alt=&quot;Demo of the Set object in the Kiesel REPL&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/date-parsing.png&quot; alt=&quot;Demo of date string parsing in the Kiesel REPL&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/map.png&quot; alt=&quot;Demo of the Map object in the Kiesel REPL&quot;&gt;&lt;/p&gt;&lt;p&gt;A lot of this was fairly repetitive (&lt;code&gt;Date&lt;/code&gt; having a getter and setter for both local time and UTC, as well as &lt;code&gt;Map&lt;/code&gt; and &lt;code&gt;Set&lt;/code&gt; being nearly identical), but it was still fun to implement these :^)&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Array.prototype.concat()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.copyWithin()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.fill()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.flat()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.flatMap()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.reduce()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.reduceRight()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.reverse()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.slice()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.sort()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.splice()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.toReversed()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.toSorted()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.toSpliced()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.now()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.parse()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getDate()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getDay()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getFullYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getHours()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getMilliseconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getMinutes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getMonth()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getSeconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getTime()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getTimezoneOffset()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCDate()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCDay()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCFullYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCHours()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCMilliseconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCMinutes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCMonth()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.getUTCSeconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setDate()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setFullYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setHours()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setMilliseconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setMinutes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setMonth()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setSeconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setTime()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCDate()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCFullYear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCHours()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCMilliseconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCMinutes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCMonth()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.setUTCSeconds()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toDateString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toISOString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toJSON()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toLocaleDateString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toLocaleTimeString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toTimeString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.toUTCString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.prototype[@@toPrimitive]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Date.UTC()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.clear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.delete()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.entries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.forEach()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.get()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.has()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.keys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.set()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.size&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype.values()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype[@@iterator]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Map.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.max()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.min()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.add()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.clear()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.delete()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.entries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.forEach()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.has()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.keys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.size&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype.values()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype[@@iterator]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set.prototype[@@toStringTag]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.concat()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.repeat()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.slice()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h2 id=&quot;a-history-graph-for-test262.fyi&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-3/#a-history-graph-for-test262.fyi&quot;&gt;A History Graph for test262.fyi&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As mentioned in the previous devlog, &lt;a href=&quot;https://test262.fyi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262.fyi&lt;/a&gt; was updated to start collecting historic data instead of only keeping results for the last run. The obvious use case is a graph comparing all engines against the total number of tests, which I&#39;ve implemented using &lt;a href=&quot;https://www.chartjs.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Chart.js&lt;/a&gt;:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/test262-history-graph-kiesel.png&quot; alt=&quot;Screenshot of the test262.fyi website showing a history graph for Kiesel&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-3/test262-history-graph.png&quot; alt=&quot;Screenshot of the test262.fyi website showing a history graph for all engines&quot;&gt;&lt;/p&gt;&lt;p&gt;This isn&#39;t on the live site yet, but I hope to upstream it soon. You can also see that Kiesel passed the 30% mark two days ago! :^)&lt;/p&gt;&lt;h2 id=&quot;what&#39;s-next%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-3/#what&#39;s-next%3F&quot;&gt;What&#39;s Next?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Promises or typed arrays probably, both of which are relatively large and will probably have an entire devlog dedicated to them, which is why I&#39;m writing this one now to have a clean slate.&lt;/p&gt;&lt;p&gt;Ignoring async/await promises aren&#39;t used that much within ECMAScript itself, but typed arrays bring a whole lot of related functionality with them (&lt;code&gt;ArrayBuffer&lt;/code&gt;/&lt;code&gt;SharedArrayBuffer&lt;/code&gt;, &lt;code&gt;DataView&lt;/code&gt;, &lt;code&gt;Atomics&lt;/code&gt;).&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #2: Iterators, more math, and a bug in the Zig stdlib</title>
    <link href="https://linus.dev/posts/kiesel-devlog-2/" />
    <updated>2023-09-15T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-2/</id>
    <content type="html">&lt;p&gt;Welcome to the second devlog for the Kiesel JS engine! It hasn&#39;t been long since the last one, but I worked on a couple of fun things since then :^)&lt;/p&gt;&lt;h2 id=&quot;iterators&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-2/#iterators&quot;&gt;Iterators&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quoting myself from last week:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[…] two major features needed to unlock a bunch of runtime functionality are promises and iterators, so I&#39;ll likely work on those soon.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/202e0613b686dd2e8e4bedcdd2461fec9c52f575&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;that&#39;s what I did&lt;/a&gt; the next day. Turns out iterators are much simpler than I remembered — the entire &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/src/branch/main/src/types/spec/iterator.zig&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;implementation&lt;/a&gt; is less than 300 lines!&lt;/p&gt;&lt;p&gt;As expected this unlocked a few more builtins which I did next:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Array iterators (both &lt;code&gt;@@iterator&lt;/code&gt; and the &lt;code&gt;keys()&lt;/code&gt;/&lt;code&gt;values()&lt;/code&gt;/&lt;code&gt;entries()&lt;/code&gt; prototype functions)&lt;/li&gt;&lt;li&gt;String iterators (no key/value concept here, only &lt;code&gt;@@iterator&lt;/code&gt; giving back code points)&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.from()&lt;/code&gt;, which can collect items either from an iterable object or a plain array-like object&lt;/li&gt;&lt;li&gt;The &lt;code&gt;AggregateError&lt;/code&gt; constructor, which is commonly called with an array of errors but accepts an arbitrary iterable object&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-2/iterators.png&quot; alt=&quot;Demo of various iterator functions in Kiesel&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;more-math-functions&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-2/#more-math-functions&quot;&gt;More &lt;code&gt;Math&lt;/code&gt; Functions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I only implemented about half of the &lt;code&gt;Math&lt;/code&gt; object, so a bunch of functions were still missing. I picked mostly trigonometric and logarithmic functions next, all of which have an equivalent in Zig&#39;s &lt;code&gt;std.math&lt;/code&gt; library (or even compiler intrinsics) and thus were easy to implement.&lt;/p&gt;&lt;p&gt;Conveniently they are directly mirroring their equivalents from the standard C library in terms of behavior for &amp;quot;special&amp;quot; inputs (NaN, infinity, negative zero), as do the ones in ECMAScript.&lt;/p&gt;&lt;p&gt;This means that most steps can be folded into a single function call, e.g. for &lt;code&gt;Math.sin()&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token comment doc-comment&quot;&gt;/// 21.3.2.30 Math.sin ( x )&lt;/span&gt;
&lt;span class=&quot;token comment doc-comment&quot;&gt;/// https://tc39.es/ecma262/#sec-math.sin&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Agent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArgumentsList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 1. Let n be ? ToNumber(x).&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 2. If n is one of NaN, +0𝔽, or -0𝔽, return n.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 3. If n is either +∞𝔽 or -∞𝔽, return NaN.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 4. Return an implementation-approximated Number value representing the result of the&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//    sine of ℝ(n).&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;@sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While doing this I noticed two functions misbehaving in a subtle way: both &lt;code&gt;std.math.asinh()&lt;/code&gt; and &lt;code&gt;std.math.cbrt()&lt;/code&gt; would return &lt;code&gt;0&lt;/code&gt; for &lt;code&gt;-0&lt;/code&gt; even though they are supposed to preserve the sign. I filed issues &lt;a href=&quot;https://github.com/ziglang/zig/pull/17111&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17111&lt;/a&gt; and &lt;a href=&quot;https://github.com/ziglang/zig/pull/17112&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17112&lt;/a&gt;, which promptly got fixed in PRs &lt;a href=&quot;https://github.com/ziglang/zig/pull/17120&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17120&lt;/a&gt; and &lt;a href=&quot;https://github.com/ziglang/zig/pull/17121&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17121&lt;/a&gt; 🎉&lt;/p&gt;&lt;p&gt;This is of course much less likely to happen in mature language and library implementations, but I&#39;m more than happy to be an &amp;quot;early adopter&amp;quot; and improve the language for everyone by reporting/fixing such issues :^)&lt;/p&gt;&lt;h3 id=&quot;pretty-printing-in-scripts&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-2/#pretty-printing-in-scripts&quot;&gt;Pretty-Printing in Scripts&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The Kiesel REPL already implements basic pretty-printing, generally trying to print relevant internal object state instead of just serializing (enumerable) properties.&lt;/p&gt;&lt;p&gt;I decided to expose this functionality to scripts — similar to &lt;a href=&quot;https://docs.python.org/3/library/pprint.html#pprint.pprint&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;pprint.pprint()&lt;/code&gt;&lt;/a&gt; in Python — by adding an optional second argument for options to the non-standard &lt;code&gt;Kiesel.print()&lt;/code&gt; builtin:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Kiesel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;pretty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-2/pretty-printing.png&quot; alt=&quot;Demo of the Kiesel.print() function printing a proxy object without and with pretty-printing enabled&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-2/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://test262.fyi/#%7Ckiesel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262&lt;/a&gt; went up exactly 1%, now at 26%. &lt;a href=&quot;https://github.com/CanadaHonk/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;CanadaHonk&lt;/a&gt; also prepared data collection for making a graph of progress over time for each engine, which I&#39;m really looking forward to!&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of newly implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;AggregateError()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;AggregateError.prototype.message&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;AggregateError.prototype.name&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.from()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.entries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.filter()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.keys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.shift()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.unshift()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.values()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype[@@iterator]()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.acos()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.acosh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.asin()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.asinh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.atan()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.atanh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.cbrt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.cos()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.cosh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.exp()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.expm1()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.log()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.log10()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.log1p()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.log2()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.sin()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.sinh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.sqrt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.tan()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.tanh()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.at()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype[@@iterator]()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;p&gt;There also were a couple of people reaching out about how to file issues or contribute after the previous devlog ended up on &lt;em&gt;Lobsters&lt;/em&gt;. The short version is that I&#39;m treating this as a read-only open source project for the moment — the source code is freely available, and everyone is welcome to use and modify it. However I want to avoid falling back into a mode where I spend more time reviewing PRs and handling issues than making actual progress myself.&lt;/p&gt;&lt;p&gt;There are plenty of issues that I&#39;m aware of right now, and more that I&#39;m not currently aware of, but this isn&#39;t the time for a backlog. Feel free to reach out directly if you have any questions/suggestions/fixes for obvious mistakes (&lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/9c68f1c77cdd7ffa4491356982ee48fc3149ea3e&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;example&lt;/a&gt;). :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Kiesel Devlog #1: Now passing 25% of test262!</title>
    <link href="https://linus.dev/posts/kiesel-devlog-1/" />
    <updated>2023-09-10T00:00:00Z</updated>
    <id>https://linus.dev/posts/kiesel-devlog-1/</id>
    <content type="html">&lt;p&gt;Except for the &lt;a href=&quot;https://chaos.social/@linusgroh/110736697921011328&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;initial announcement&lt;/a&gt; and a couple of &lt;a href=&quot;https://chaos.social/tags/kiesel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;fedi posts&lt;/a&gt; I haven&#39;t talked much about the latest project I&#39;m working on - writing a JS engine from scratch in Zig!&lt;/p&gt;&lt;p&gt;A little over four months and exactly 600 &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commits/branch/main&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;commits&lt;/a&gt; later it now passes 25% of test262 (&lt;a href=&quot;https://chaos.social/@linusgroh/111011688266891967&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;20% only a week ago!&lt;/a&gt;), so this seems like a good time to write the first devlog :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://kiesel.dev/kiesel.svg&quot; title=&quot;Kiesel Logo&quot; alt=&quot;Simple drawing of a yellow pebble with JS written on it&quot; style=&quot;height: 300px&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;humble-beginnings&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#humble-beginnings&quot;&gt;Humble Beginnings&lt;/a&gt;&lt;/h2&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;commit 929bb044ffcd1776c850a5b924f9992915277e58
Author: Linus Groh &lt;mail @linusgroh.de&gt;
Date:   Fri Apr 28 19:48:13 2023 +0100

    Initial commit

    Writing another JS engine seems like a fun project for learning Zig :^)&lt;/mail&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There was nothing useful in this commit yet, it contains a hello world generated by &lt;code&gt;zig init-exe&lt;/code&gt;. Over the next few weeks I stubbed out some widely used language building blocks (&lt;code&gt;Agent&lt;/code&gt;, &lt;code&gt;Realm&lt;/code&gt;, &lt;code&gt;PropertyDescriptor&lt;/code&gt;, …), the fundamental &lt;code&gt;Value&lt;/code&gt; type, and most importantly the &lt;a href=&quot;https://tc39.es/ecma262/#sec-object-internal-methods-and-internal-slots&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;object model&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I spent the last three years or so working on LibJS, SerenityOS&#39;s JS engine used in the Ladybird browser — hence &lt;em&gt;another&lt;/em&gt;. Already having a solid mental model of the ECMAScript spec meant that I could fully focus on learning Zig as a new language instead having to wrap my head around JS language concepts.&lt;/p&gt;&lt;p&gt;Or, as &lt;a href=&quot;https://donotsta.re/notice/AXpQcWMqknp3loce4O&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;domi puts it&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;the &amp;quot;an average person writes two javascript engines in their life&amp;quot; factoid is false. A statistical person writes 0 javascript engines; Linus Groh, who writes them for fun is an outlier and should not have been counted&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And indeed, it is lots of fun :^)&lt;/p&gt;&lt;p&gt;Until I made a basic tokenizer, parser, and bytecode VM, everything was glued together by hand which looked something like this (adapted for a couple of API changes):&lt;/p&gt;&lt;pre class=&quot;language-zig&quot;&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token builtin-type keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; agent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; Agent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allocator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; agent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deinit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; Realm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initializeHostDefinedRealm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; object1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; builtins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; object1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;internalMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defineOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        object1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        PropertyKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PropertyDescriptor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; object2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; builtins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; object2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;internalMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; PropertyKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    std&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;debug&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;object2.foo = {any}&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;what&#39;s-implemented-so-far%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#what&#39;s-implemented-so-far%3F&quot;&gt;What&#39;s Implemented So Far?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After getting the basics up and running I mostly focused on implementing enough syntax and builtins to run &lt;a href=&quot;https://github.com/tc39/test262&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262&lt;/a&gt;, the official ECMAScript conformance test suite. This was planned from the very beginning so I could avoid having to write my own test runner and suite.&lt;/p&gt;&lt;p&gt;Not focusing on running test262 early on is also one of my big regrets from LibJS (as well as not getting objects and realms right from the beginning), so that was to be avoided.&lt;/p&gt;&lt;p&gt;Turns out: you don&#39;t need a huge amount of features for test262, which is great! So little in fact that I only implemented &lt;code&gt;for&lt;/code&gt; loops yesterday to get another test harness file working.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-1/test262.png&quot; alt=&quot;Results for Kiesel on test262.fyi, showing bar graphs for each subdirectory and a total of 25%&quot;&gt;&lt;/p&gt;&lt;h3 id=&quot;builtins&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#builtins&quot;&gt;Builtins&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;JS has a lot of built-in functions and keeps getting more, so this will take a while. Sometimes I target missing functions used in the test262 harness causing tests for implemented functionality to fail, but for the most part I randomly pick something that seems fun to work on :^)&lt;/p&gt;&lt;p&gt;Yes, there&#39;s a &lt;a href=&quot;https://codeberg.org/kiesel-js/kiesel/commit/e5c88889d9869950a104d9eca00b486e1a2755ae&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;partial implementation of &lt;code&gt;eval()&lt;/code&gt;&lt;/a&gt; (before pretty-printing and when the code was still on GitHub). Why do you ask?&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-1/eval.png&quot; alt=&quot;Screenshot of a commit implementing the eval() builtin with a demo of nested eval in the description&quot;&gt;&lt;/p&gt;&lt;p&gt;The famous demo from the &lt;a href=&quot;https://www.destroyallsoftware.com/talks/wat&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;Wat&lt;/em&gt; talk&lt;/a&gt; also works, and more recently I added proxies:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-1/wat.png&quot; alt=&quot;Code snippet from the Wat talk outputting &#39;NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman&#39;&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/kiesel-devlog-1/proxy.png&quot; alt=&quot;A simple Proxy demo in Kiesel showing the &#39;apply&#39; trap&quot;&gt;&lt;/p&gt;&lt;p&gt;I also added a couple of non-standard functions (mostly for test262), but they&#39;re implemented in the &lt;code&gt;kiesel&lt;/code&gt; utility, not the engine itself:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Kiesel.gc.collect()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.createRealm()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.evalScript()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Kiesel.print()&lt;/code&gt; (no &lt;code&gt;console&lt;/code&gt; object yet)&lt;/li&gt;&lt;/ul&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of currently implemented builtins 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;globalThis&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Infinity&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;NaN&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;eval()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;isFinite()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;isNaN()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.isArray()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.of()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.length&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.at()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.every()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.find()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.findIndex()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.findLast()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.findLastIndex()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.forEach()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.includes()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.indexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.join()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.lastIndexOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.map()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.pop()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.push()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.some()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Array.prototype.with()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;BigInt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;BigInt.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;BigInt.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;BigInt.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Boolean()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Boolean.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Boolean.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Error()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Error.prototype.message&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Error.prototype.name&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Error.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;NativeError()&lt;/code&gt; (&lt;code&gt;EvalError&lt;/code&gt;, &lt;code&gt;RangeError&lt;/code&gt;, &lt;code&gt;ReferenceError&lt;/code&gt;, &lt;code&gt;SyntaxError&lt;/code&gt;, &lt;code&gt;TypeError&lt;/code&gt;, &lt;code&gt;URIError&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;NativeError.prototype.message&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;NativeError.prototype.name&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function.prototype.apply()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function.prototype.call()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Function.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.E&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.LN10&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.LN2&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.LOG10E&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.LOG2E&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.PI&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.SQRT1_2&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.SQRT2&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.abs()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.ceil()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.clz32()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.floor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.pow()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.random()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.round()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.sign()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Math.trunc()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.EPSILON&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.MAX_VALUE&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.MIN_SAFE_INTEGER&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.MIN_VALUE&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.NaN&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.NEGATIVE_INFINITY&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.POSITIVE_INFINITY&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.isFinite()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.isInteger()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.isNaN()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.isSafeInteger()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Number.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.assign()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.create()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.defineProperties()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.defineProperty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.entries()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.freeze()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.getOwnPropertyDescriptor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.getOwnPropertyDescriptors()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.getOwnPropertyNames()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.getOwnPropertySymbols()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.getPrototypeOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.hasOwn()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.is()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.isExtensible()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.isFrozen()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.isSealed()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.keys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.preventExtensions()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.seal()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.setPrototypeOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.values()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.hasOwnProperty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.isPrototypeOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.propertyIsEnumerable()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.toLocaleString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Object.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Proxy()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Proxy.revocable()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.apply()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.construct()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.defineProperty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.deleteProperty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.get()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.getOwnPropertyDescriptor()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.getPrototypeOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.has()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.isExtensible()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.ownKeys()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.preventExtensions()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.set()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Reflect.setPrototypeOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.charAt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.charCodeAt()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;String.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.asyncIterator&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.for&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.hasInstance&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.isConcatSpreadable&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.iterator&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.keyFor&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.match&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.matchAll&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.prototype&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.replace&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.search&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.species&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.split&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.toPrimitive&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.toStringTag&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.unscopables&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.prototype.toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Symbol.prototype.valueOf()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h3 id=&quot;syntax&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#syntax&quot;&gt;Syntax&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I&#39;m using &lt;a href=&quot;https://layer8.space/@ikskuh&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;xq&lt;/a&gt;&#39;s fantastic &lt;a href=&quot;https://github.com/MasterQ32/parser-toolkit&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;parser-toolkit&lt;/code&gt;&lt;/a&gt; library. Translating the context-free grammar from the ECMAScript spec into that &lt;a href=&quot;https://chaos.social/@linusgroh/110396517501050470&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;isn&#39;t always straightforward&lt;/a&gt;, but so far I&#39;m not feeling awful about the parser. It&#39;s currently 1.4k lines and somehow hasn&#39;t turned into a pile of spaghetti code yet. :^)&lt;/p&gt;&lt;details&gt;&lt;summary&gt;&lt;strong&gt;Expand to see the full list of currently implemented syntax features 📝&lt;/strong&gt;&lt;/summary&gt;&lt;ul&gt;&lt;li&gt;Literals:&lt;ul&gt;&lt;li&gt;&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;/&lt;code&gt;null&lt;/code&gt; (&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1591&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;undefined&lt;/code&gt; is not a literal :^)&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;numbers&lt;/li&gt;&lt;li&gt;bigints&lt;/li&gt;&lt;li&gt;strings (no template literals or escapes)&lt;/li&gt;&lt;li&gt;arrays (including array holes)&lt;/li&gt;&lt;li&gt;objects (no function property shorthands)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Statements:&lt;ul&gt;&lt;li&gt;blocks&lt;/li&gt;&lt;li&gt;&lt;code&gt;var&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;while&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;do&lt;/code&gt;/&lt;code&gt;while&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt;/&lt;code&gt;finally&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;throw&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;return&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;debugger&lt;/code&gt;&lt;/li&gt;&lt;li&gt;empty&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Functions:&lt;ul&gt;&lt;li&gt;&lt;code&gt;function&lt;/code&gt; declarations&lt;/li&gt;&lt;li&gt;&lt;code&gt;function&lt;/code&gt; expressions&lt;/li&gt;&lt;li&gt;arrow functions&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Other expressions:&lt;ul&gt;&lt;li&gt;identifiers references&lt;/li&gt;&lt;li&gt;member expressions&lt;/li&gt;&lt;li&gt;call expressions&lt;/li&gt;&lt;li&gt;&lt;code&gt;new&lt;/code&gt; expressions&lt;/li&gt;&lt;li&gt;update expressions (prefix/suffix &lt;code&gt;++&lt;/code&gt;/&lt;code&gt;--&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;unary expressions (prefix &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;void&lt;/code&gt;, &lt;code&gt;typeof&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;binary expressions (&lt;code&gt;**&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;relational expressions (&lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;instanceof&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;equality expressions (&lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;===&lt;/code&gt;, &lt;code&gt;!==&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;logical expressions (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;??&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;conditional expressions (&lt;code&gt;a ? b : c&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;assignment expressions (&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;*=&lt;/code&gt;, &lt;code&gt;/=&lt;/code&gt;, &lt;code&gt;%=&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;&amp;amp;=&lt;/code&gt;, &lt;code&gt;^=&lt;/code&gt;, &lt;code&gt;|=&lt;/code&gt;, &lt;code&gt;**=&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;=&lt;/code&gt;, &lt;code&gt;||=&lt;/code&gt;, &lt;code&gt;??=&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;sequence expressions (&lt;code&gt;,&lt;/code&gt; operator)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;p&gt;Lexical declarations are parsed but use the same bytecode and scoping rules as &lt;code&gt;var&lt;/code&gt; declarations; this was to unbreak tests that relied on simple variable assignment using &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt; for unrelated functionality.&lt;/p&gt;&lt;p&gt;I prefer working on runtime over syntax/language, mostly because the abstractions in Kiesel for that are much nicer compared to parsing and bytecode generation. So if you&#39;re wondering why a certain language feature is missing: that&#39;s why.&lt;/p&gt;&lt;h2 id=&quot;what&#39;s-missing%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#what&#39;s-missing%3F&quot;&gt;What&#39;s Missing?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Everything else, of course :^)&lt;/p&gt;&lt;p&gt;I consider the entire latest ECMAScript draft to be in scope, only a handful of language features marked &lt;em&gt;Normative Optional, Legacy&lt;/em&gt; &lt;a href=&quot;https://chaos.social/@linusgroh/110977307510329256&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;will be skipped&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;There&#39;s no roadmap, but two major features needed to unlock a bunch of runtime functionality are promises and iterators, so I&#39;ll likely work on those soon. Lesser-used builtins like &lt;code&gt;Weak{Map,Ref,Set}&lt;/code&gt; or &lt;code&gt;Atomics&lt;/code&gt; are low on my priority list and will take longer to appear.&lt;/p&gt;&lt;p&gt;A huge deficiency is the current error reporting, which manifests in two ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;There are no tracebacks, so finding the exact error source can involve some guesswork and/or printing out the current bytecode offset:&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ cat -p foo.js
throw new Error(&quot;oh no!&quot;)
$ kiesel foo.js
Uncaught exception: Error: oh no!
$&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The majority of &lt;em&gt;early errors&lt;/em&gt; (syntax errors for certain invalid constructs) are not implemented, and the parser will generally backtrack to the construct it&#39;s currently parsing when encountering syntax it doesn&#39;t understand (&lt;code&gt;with&lt;/code&gt; statements in this case):&lt;/p&gt;&lt;pre class=&quot;language-console&quot;&gt;&lt;code class=&quot;language-console&quot;&gt;$ cat foo.js
if (foo) {
    // a comment
}

function bar() {
    with (baz) {}
}
$ kiesel foo.js
Uncaught exception: SyntaxError: Unexpected token &#39;function&#39; (foo.js:3:2)
$&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(there&#39;s also something going wrong with the source location but that&#39;s another story…)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;thoughts-on-zig&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#thoughts-on-zig&quot;&gt;Thoughts On Zig&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As I mentioned at the beginning, the whole point of this was to learn Zig, which I did — I&#39;m by no means an expert but would probably call myself proficient already. It&#39;s a joy to use, despite being a 0.x project including compiler bugs and breaking changes (which I was fully aware of from the beginning). They&#39;re a relatively small team with large ambitions, and I like that :^)&lt;/p&gt;&lt;p&gt;You can see what it&#39;s all about on the &lt;a href=&quot;https://ziglang.org/learn/overview/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;website&lt;/a&gt;. I did find the standard library lacking on a few occasions, e.g. there seems to be no function for checking if a &amp;quot;list of strings&amp;quot; (slices, &lt;code&gt;[]const []const u8&lt;/code&gt;) contains a string (&lt;code&gt;[]const u8&lt;/code&gt;) — and &lt;code&gt;for (haystack) |value| { if (std.mem.eql(u8, value, needle)) break true; } else false&lt;/code&gt; ain&#39;t it. I have this in a helper function for now :^)&lt;/p&gt;&lt;p&gt;This also happens to be the first time I learned a language fully out of interest (&lt;em&gt;I need a project for this language I want to learn&lt;/em&gt;), not out of necessity (&lt;em&gt;I need to learn a language for this project I want to work on&lt;/em&gt;)!&lt;/p&gt;&lt;h2 id=&quot;oh%2C-btw%E2%80%A6&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/kiesel-devlog-1/#oh%2C-btw%E2%80%A6&quot;&gt;Oh, btw…&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;…I also want to mention &lt;a href=&quot;https://github.com/CanadaHonk/porffor&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;porffor&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;a basic wip js aot optimizing wasm compiler in js&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It&#39;s another new JS engine project, (compiling JS to Wasm, written in JS), and I often talk to CanadaHonk while we&#39;re both hacking away on different things or comparing functionality or benchmarks for fun (porffor-compiled code runs on the V8 Wasm engine and thus always wins).&lt;/p&gt;&lt;p&gt;They also built the incredibly useful &lt;a href=&quot;https://test262.fyi/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262.fyi&lt;/a&gt; site, which involved forking and modernizing some existing tooling (esvu, eshost, test262-harness). And of course it features results from Kiesel :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Setting up a Self-Hosted Forgejo Actions Runner with Docker Compose</title>
    <link href="https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/" />
    <updated>2023-07-07T00:00:00Z</updated>
    <id>https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/</id>
    <content type="html">&lt;p&gt;I created an account on &lt;a href=&quot;https://codeberg.org/about&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Codeberg&lt;/a&gt; the other day with the intention of hosting some of my code there in the future. So far, 100% of my open source activity happens on GitHub, and while they do a lot of things well, this seems like a good time to try alternatives considering the ongoing &lt;em&gt;enshittification&lt;/em&gt; of major parts of the web.&lt;/p&gt;&lt;p&gt;I&#39;ve been aware of Codeberg for a while, and after a friend (hi n0toose!) confirmed that the underlying software (Forgejo, a fork of Gitea) supports disabling pull request for a repository — a feature &lt;a href=&quot;https://github.com/dear-github/dear-github/issues/84&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;famously missing on GitHub&lt;/a&gt; — I was fully on board.&lt;/p&gt;&lt;p&gt;They optionally provide a hosted instance of &lt;a href=&quot;https://woodpecker-ci.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Woodpecker CI&lt;/a&gt; for projects hosted on Codeberg, but I quickly ran into issues with workflows being slow and more importantly logs not loading, making the conversion from GitHub Actions a rather painful task. The other option is using &lt;a href=&quot;https://forgejo.org/2023-02-27-forgejo-actions/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Forgejo Actions&lt;/a&gt;, a relatively new feature building on top of &lt;a href=&quot;https://github.com/nektos/act&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;act&lt;/code&gt;&lt;/a&gt; for compatibility with existing GitHub Actions workflows. However, Codeberg currently doesn&#39;t provide hosted runners for this type of CI, so I looked into hosting my own :^)&lt;/p&gt;&lt;h2 id=&quot;setting-up-a-forgejo-runner&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/#setting-up-a-forgejo-runner&quot;&gt;Setting up a Forgejo Runner&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Installation of the Forgejo Runner is covered in their &lt;a href=&quot;https://forgejo.org/docs/next/admin/actions/#forgejo-runner&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;documentation&lt;/a&gt; and doesn&#39;t need me writing this post. They only describe running directly on the host and in LXC containers; but since my entire self-hosted software stack runs on Docker Compose this wasn&#39;t an option.&lt;/p&gt;&lt;h3 id=&quot;adding-the-docker-compose-service&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/#adding-the-docker-compose-service&quot;&gt;Adding the Docker Compose service&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Luckily a &lt;a href=&quot;https://code.forgejo.org/forgejo/runner/src/branch/main/Dockerfile&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/a&gt; is available, so I only had to figure out the configuration. Summarized we need the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I haven&#39;t found a pre-built variant of the docker image anywhere, so the first step is cloning the repo (to &lt;code&gt;src/forgejo-runner/&lt;/code&gt; in this case) and adding a service with a &lt;code&gt;build&lt;/code&gt; entry&lt;/li&gt;&lt;li&gt;The &lt;code&gt;ENTRYPOINT&lt;/code&gt; is a plain &lt;code&gt;/bin/forgejo-runner&lt;/code&gt;, so we add the &lt;code&gt;daemon&lt;/code&gt; argument&lt;/li&gt;&lt;li&gt;A volume mounting &lt;code&gt;/var/run/docker.sock&lt;/code&gt; from the host into the container so containers can be spawned for running workflows&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In YAML it looks like this:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;forgejo-runner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src/forgejo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daemon&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; /var/run/docker.sock&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, we can register the runner with a Forgejo instance, in my case Codeberg. Follow the steps outlined &lt;a href=&quot;https://forgejo.org/docs/next/admin/actions/#registration&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;here&lt;/a&gt; to obtain a registration token, then run:&lt;/p&gt;&lt;pre class=&quot;language-command&quot;&gt;&lt;code class=&quot;language-command&quot;&gt;docker compose run --rm --entrypoint sh forgejo-runner&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;…which will drop you into a shell within the container, from where we can invoke the registration:&lt;/p&gt;&lt;pre class=&quot;language-command&quot;&gt;&lt;code class=&quot;language-command&quot;&gt;forgejo-runner register&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After completing all prompts, a file &lt;code&gt;.runner&lt;/code&gt; should be created, the contents of which need to be copied to the host so we can mount it as a volume later.&lt;/p&gt;&lt;h3 id=&quot;configuring-the-forgejo-runner&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/#configuring-the-forgejo-runner&quot;&gt;Configuring the Forgejo Runner&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Additionally, create a config file for the runner (&lt;code&gt;config.yml&lt;/code&gt;) which will also be mounted as a volume and specified via a command line argument.&lt;/p&gt;&lt;p&gt;All available config options are explained in the &lt;a href=&quot;https://forgejo.org/docs/next/admin/actions/#configuration&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;docs&lt;/a&gt;, and if you&#39;re happy with the defaults you can skip this step. However, I&#39;d definitely recommend changing &lt;code&gt;runner.capacity&lt;/code&gt; to something greater than one, and if you want to keep caching enabled we&#39;ll need to tweak a few other things. Updated Docker Compose config:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;forgejo-runner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src/forgejo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daemon --config config.yml&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; /var/run/docker.sock&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/var/run/docker.sock
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./volume/.runner&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/.runner
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./volume/config.yml&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/config.yml&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While I&#39;m using bind mounts here, you can of course use any other available method of putting files into the container!&lt;/p&gt;&lt;h3 id=&quot;making-caching-work&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/#making-caching-work&quot;&gt;Making Caching Work&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;One of the hardest problems in computer science, and no exception here. Caching (e.g. for various setup actions and &lt;code&gt;actions/cache&lt;/code&gt;) is enabled by default, but the cache server will not be reachable from within the created workflow containers without intervention. This is because a random port is used by default (&lt;code&gt;cache.port&lt;/code&gt; set to &lt;code&gt;0&lt;/code&gt;), which is not exposed on the runner container. I also had to tweak the &lt;code&gt;container.network&lt;/code&gt; option to use Docker Compose&#39;s &lt;code&gt;docker_default&lt;/code&gt; network which the runner container is reachable on.&lt;/p&gt;&lt;p&gt;Change this accordingly if your Docker networking setup is more involved :^)&lt;/p&gt;&lt;p&gt;This is what the &lt;code&gt;config.yaml&lt;/code&gt; file looks like for the above:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; forgejo&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner
  &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker_default&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Additionally, expose port 8080 on the container:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;forgejo-runner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;expose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8080&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Optionally, you can change the cache directory (&lt;code&gt;/root/.cache&lt;/code&gt;) and/or create a separate volume for it.&lt;/p&gt;&lt;h2 id=&quot;putting-it-all-together&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/setting-up-a-self-hosted-forgejo-actions-runner-with-docker-compose/#putting-it-all-together&quot;&gt;Putting It All together&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Starting the container should now allow the daemon to run and show up as active on the Forgejo interface:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://assets.chaos.social/media_attachments/files/110/663/494/025/831/799/original/d6b0c295c9a0942a.png&quot; alt=&quot;Forgejo Runner management UI showing one idle registered runner&quot;&gt;&lt;/p&gt;&lt;p&gt;For me, importing existing workflow files written for GitHub Actions worked seamlessly after making the following changes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Moving them to &lt;code&gt;.forgejo/workflows/&lt;/code&gt; (optional but less confusing than keeping &lt;code&gt;.github/workflows/&lt;/code&gt; IMO)&lt;/li&gt;&lt;li&gt;Setting &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;jobs.&amp;lt;job_id&amp;gt;.runs-on&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;docker&lt;/code&gt; (&lt;em&gt;Labels&lt;/em&gt; in the screenshot above, not &lt;em&gt;Name&lt;/em&gt;!)&lt;/li&gt;&lt;li&gt;Setting &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainerimage&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;jobs.&amp;lt;job_id&amp;gt;.container.image&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;ghcr.io/catthehacker/ubuntu:act-22.04&lt;/code&gt;. Other images will work as well but might require additional setup steps, this one matches GitHub Actions closely.&lt;/li&gt;&lt;li&gt;Updating &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;jobs.&amp;lt;job_id&amp;gt;.steps[*].uses&lt;/code&gt;&lt;/a&gt; URLs, e.g. &lt;code&gt;uses: goto-bus-stop/setup-zig@v2&lt;/code&gt; becomes &lt;code&gt;uses: https://github.com/goto-bus-stop/setup-zig@v2&lt;/code&gt;. This is not needed for actions mirrored on Codeberg, e.g. &lt;code&gt;actions/checkout&lt;/code&gt; (&lt;code&gt;https://codeberg.org/actions/checkout&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Lastly, enable Actions in the repository settings, push some changes, and hope for the best :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://assets.chaos.social/media_attachments/files/110/663/594/627/737/686/original/af5b79d96a999b6c.png&quot; alt=&quot;Successful run of a Forgejo Action&quot;&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>This Week in Ladybird #4</title>
    <link href="https://linus.dev/posts/this-week-in-ladybird-4/" />
    <updated>2023-04-16T00:00:00Z</updated>
    <id>https://linus.dev/posts/this-week-in-ladybird-4/</id>
    <content type="html">&lt;p&gt;Overview of changes across the Ladybird Browser project in the week from April 10 to April 16 (plus everything merged Sunday evening) :^)&lt;/p&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In chronological order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#browser%3A-have-bookmarksbarwidget-signal-bookmark-changes-for-tab&quot;&gt;Browser: Have &lt;code&gt;BookmarksBarWidget&lt;/code&gt; signal bookmark changes for &lt;code&gt;Tab&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-resolve-more-background-related-properties&quot;&gt;LibWeb: Resolve more background-related properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-return-from-%22the-end%22-during-html-fragment-parsing&quot;&gt;LibWeb: Return from &amp;quot;the end&amp;quot; during HTML fragment parsing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libvideo%2Blibweb%3A-generalize-playbackmanager-for-use-within-libweb...and-use-it&quot;&gt;LibVideo+LibWeb: Generalize PlaybackManager for use within LibWeb...and use it&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-a-few-methods-on-writablestream-and-writablestreamdefaultwriter&quot;&gt;LibWeb: Implement a few methods on WritableStream and WritableStreamDefaultWriter&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-writablestreamdefaultwriter.write()-and-an-ao-involved-in-writablestream&#39;s-constructor&quot;&gt;LibWeb: Implement WritableStreamDefaultWriter.write() and an AO involved in WritableStream&#39;s constructor&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-convert-video-control-dimensions-from-csspixels-to-devicepixels&quot;&gt;LibWeb: Convert video control dimensions from CSSPixels to DevicePixels&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Bak%3A-get-google-street-view-working%2C-along-with-svg-viewboxes%2C-transforms%2C-scaling%2C-and-hit-testing!&quot;&gt;LibWeb+AK: Get Google Street View working, along with SVG viewboxes, transforms, scaling, and hit testing!&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-track-playback-position-and-display-a-timeline-on-the-layout-node&quot;&gt;LibWeb: Track playback position and display a timeline on the layout node&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-begin-adding-readablebytestream-interfaces&quot;&gt;LibWeb: Begin adding ReadableByteStream interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-update-spec-numbers-for-the-intl-numberformat-v3-proposal&quot;&gt;LibJS: Update spec numbers for the Intl NumberFormat v3 proposal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-url.canparse(url%2C-base)&quot;&gt;LibWeb: Implement URL.canParse(url, base)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-convert-calc()-to-spec&#39;s-internal-representation%2C-fixing-nested-calc()-in-the-process&quot;&gt;LibWeb: Convert &lt;code&gt;calc()&lt;/code&gt; to spec&#39;s internal representation, fixing nested &lt;code&gt;calc()&lt;/code&gt; in the process&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-handle-null-values-when-making-args-for-attributechangedcallback&quot;&gt;LibWeb: Handle null values when making args for attributeChangedCallback&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-create-a-video-document-for-video%2F-mime-types-on-navigation&quot;&gt;LibWeb: Create a video document for &lt;code&gt;video/&lt;/code&gt; MIME types on navigation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-integrate-readablebytestreamcontroller-into-readablestreamcontroller-type&quot;&gt;LibWeb: Integrate ReadableByteStreamController into ReadableStreamController type&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-comment-all-the-things&quot;&gt;LibJS: Comment all the things&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-make-intrinsics-getters-and-well-known-symbol-getters-return-nonnullgcptr&quot;&gt;LibJS: Make intrinsics getters and well-known symbol getters return NonnullGCPtr&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-whine-instead-of-dying-on-unexpected-box-during-line-layout&quot;&gt;LibWeb: Whine instead of dying on unexpected box during line layout&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-port-more-aos-to-%7Bnonnull%2C%7Dgcptr&quot;&gt;LibJS: Port more AOs to {Nonnull,}GCPtr&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-don&#39;t-match-the-root-node-of-htmlcollection&quot;&gt;LibWeb: Don&#39;t match the root node of HTMLCollection&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Bladybird%3A-add-support-for-more-css-cursors-in-ladybird&quot;&gt;LibWeb+Ladybird: Add support for more CSS cursors in Ladybird&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-set-comment&#39;s-prototype&quot;&gt;LibWeb: Set Comment&#39;s prototype&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-honor-column-gap-and-row-gap-css-properties-in-flex-layout&quot;&gt;LibWeb: Honor column-gap and row-gap CSS properties in flex layout&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-do-not-dereference-empty-optional-in-readablestream%3A%3Avisit_edges&quot;&gt;LibWeb: Do not dereference empty Optional in ReadableStream::visit_edges&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Fstreams%3A-couple-of-small-cleanups&quot;&gt;LibWeb/Streams: Couple of small cleanups&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-comment-all-the-things-(continued)&quot;&gt;LibJS: Comment all the things (continued)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-introduce-documentstate-and-update-sessionhistoryentry&quot;&gt;LibWeb: Introduce DocumentState and update SessionHistoryEntry&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#ladybird%2Blibwebview%3A-add--p%2F--enable-callgrind-profiling-option&quot;&gt;Ladybird+LibWebView: Add -P/--enable-callgrind-profiling option&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-scale-svg-stroke-width-based-on-viewbox&quot;&gt;LibWeb: Scale SVG stroke-width based on viewbox&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-honor-gap-between-flex-lines-when-using-align-content%3A-stretch&quot;&gt;LibWeb: Honor gap between flex lines when using align-content: stretch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-fix-multi-line-flex-column-layouts-with-auto-height-on-container&quot;&gt;LibWeb: Fix multi-line flex column layouts with auto height on container&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;browser%3A-have-bookmarksbarwidget-signal-bookmark-changes-for-tab&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#browser%3A-have-bookmarksbarwidget-signal-bookmark-changes-for-tab&quot;&gt;Browser: Have &lt;code&gt;BookmarksBarWidget&lt;/code&gt; signal bookmark changes for &lt;code&gt;Tab&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18096&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18096&lt;/a&gt; by &lt;a href=&quot;https://github.com/kemzeb&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kemzeb&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ensures that the bookmark button is updated correctly when a bookmark for the currently active tab is changed:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/60799661/228717689-6a5b364b-aee3-452b-8017-71c57db20676.gif&quot; alt=&quot;Screen recording of the SerenityOS Browser&#39;s bookmark functionality showcased&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-resolve-more-background-related-properties&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-resolve-more-background-related-properties&quot;&gt;LibWeb: Resolve more background-related properties&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18172&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18172&lt;/a&gt; by &lt;a href=&quot;https://github.com/krkk&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@krkk&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This adds support for resolving the following CSS properties so that &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Window.getComputedStyle()&lt;/code&gt;&lt;/a&gt; can return their actual values:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;background-attachment&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-clip&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-image&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-origin&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-position&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-repeat&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;background-size&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-return-from-%22the-end%22-during-html-fragment-parsing&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-return-from-%22the-end%22-during-html-fragment-parsing&quot;&gt;LibWeb: Return from &amp;quot;the end&amp;quot; during HTML fragment parsing&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18224&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18224&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This adds an early return to the HTML parser&#39;s &lt;a href=&quot;https://html.spec.whatwg.org/multipage/parsing.html#the-end&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;the end&lt;/em&gt; step&lt;/a&gt; when parsing HTML fragments, fixing some issues caused by the spec not matching web reality (Luke filed &lt;a href=&quot;https://github.com/whatwg/html/issues/8646&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;whatwg/html#8646&lt;/a&gt; for that back in December).&lt;/p&gt;&lt;p&gt;It also features the longest &lt;a href=&quot;https://github.com/SerenityOS/serenity/commit/f52ede23aa5399120d3271605cc7ea0cd28baa18&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;commit message&lt;/a&gt; you will ever see for a two line change!&lt;/p&gt;&lt;h2 id=&quot;libvideo%2Blibweb%3A-generalize-playbackmanager-for-use-within-libweb...and-use-it&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libvideo%2Blibweb%3A-generalize-playbackmanager-for-use-within-libweb...and-use-it&quot;&gt;LibVideo+LibWeb: Generalize PlaybackManager for use within LibWeb...and use it&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18264&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18264&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This updates the video element implementation in LibWeb to use the &lt;code&gt;PlaybackManager&lt;/code&gt; class from &lt;code&gt;LibVideo&lt;/code&gt;, which abstracts away the exact demuxer being used, provides buffering, and a few other things.&lt;/p&gt;&lt;p&gt;This is already being used in SerenityOS&#39;s VideoPlayer application, so future improvements once again benefit multiple parts of the system!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-a-few-methods-on-writablestream-and-writablestreamdefaultwriter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-a-few-methods-on-writablestream-and-writablestreamdefaultwriter&quot;&gt;LibWeb: Implement a few methods on WritableStream and WritableStreamDefaultWriter&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18265&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18265&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;More work on the Streams API, at this rate it might be feature-complete by the end of the month!&lt;/p&gt;&lt;p&gt;This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;WritableStream&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/getWriter&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStream.getWriter()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/abort&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStream.abort()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WritableStreamDefaultWriter&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/abort&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.abort()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/close&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.close()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/releaseLock&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.releaseLock()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-implement-writablestreamdefaultwriter.write()-and-an-ao-involved-in-writablestream&#39;s-constructor&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-writablestreamdefaultwriter.write()-and-an-ao-involved-in-writablestream&#39;s-constructor&quot;&gt;LibWeb: Implement WritableStreamDefaultWriter.write() and an AO involved in WritableStream&#39;s constructor&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18266&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18266&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/write&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.write()&lt;/code&gt;&lt;/a&gt; API and various related &lt;abbr title=&quot;Abstract Operation&quot;&gt;AO&lt;/abbr&gt;s.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-convert-video-control-dimensions-from-csspixels-to-devicepixels&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-convert-video-control-dimensions-from-csspixels-to-devicepixels&quot;&gt;LibWeb: Convert video control dimensions from CSSPixels to DevicePixels&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18270&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18270&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The video controls were initially developed on a HiDPI machine, and would thus look too large at 1x scaling. They now are sized in display-independent CSS pixels and converted to device pixels during painting.&lt;/p&gt;&lt;h2 id=&quot;libweb%2Bak%3A-get-google-street-view-working%2C-along-with-svg-viewboxes%2C-transforms%2C-scaling%2C-and-hit-testing!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Bak%3A-get-google-street-view-working%2C-along-with-svg-viewboxes%2C-transforms%2C-scaling%2C-and-hit-testing!&quot;&gt;LibWeb+AK: Get Google Street View working, along with SVG viewboxes, transforms, scaling, and hit testing!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18271&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18271&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This PR implements all sorts of things with the goal of getting Google street View actually working, including looking and moving around!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/19366641/232325630-4062626b-e0c3-44db-8c7f-cf01232309b2.mp4&quot;&gt;&lt;/video&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/19366641/232325657-d6d66716-ce87-49e9-a5cf-b917c2231c8a.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;And of course these features work in isolation too:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/230900029-67019eec-7036-433c-87f5-f4244eb2d768.png&quot; alt=&quot;Demo webpage for SVG transforms in Ladybird&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-track-playback-position-and-display-a-timeline-on-the-layout-node&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-track-playback-position-and-display-a-timeline-on-the-layout-node&quot;&gt;LibWeb: Track playback position and display a timeline on the layout node&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18281&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18281&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now keep track of the video&#39;s playback position and render a timeline accordingly. Seeking is not yet implemented, but this is of course a necessary prerequisite :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/5600524/231022618-06398006-9192-4692-84bf-f0a6770143f7.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-begin-adding-readablebytestream-interfaces&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-begin-adding-readablebytestream-interfaces&quot;&gt;LibWeb: Begin adding ReadableByteStream interfaces&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18283&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18283&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ReadableByteStreamController&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController/byobRequest&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableByteStreamController.byobRequest&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController/desiredSize&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableByteStreamController.desiredSize&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ReadableStreamBYOBRequest&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBRequest/view&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamBYOBRequest.view&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As it turns out, &lt;em&gt;BYOB&lt;/em&gt; stands for &lt;em&gt;bring your own buffer&lt;/em&gt; and I&#39;m somewhat surprised they named a Web API like that :^)&lt;/p&gt;&lt;p&gt;Nonetheless, very exciting to see some progress on the more efficient byte streams, too!&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-update-spec-numbers-for-the-intl-numberformat-v3-proposal&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-update-spec-numbers-for-the-intl-numberformat-v3-proposal&quot;&gt;LibJS: Update spec numbers for the Intl NumberFormat v3 proposal&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18286&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18286&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Much like &lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libjs%3A-update-spec-numbers-for-the-intl-enumeration-proposal&quot;&gt;this similar change from last week&lt;/a&gt;, the &lt;a href=&quot;https://github.com/tc39/proposal-intl-numberformat-v3&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Intl NumberFormat v3&lt;/a&gt; proposal has been merged into the main ECMA-402 spec, and we had to incorporate some final spec changes.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-url.canparse(url%2C-base)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-implement-url.canparse(url%2C-base)&quot;&gt;LibWeb: Implement URL.canParse(url, base)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18287&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18287&lt;/a&gt; by &lt;a href=&quot;https://github.com/networkException&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@networkException&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the brand new (last month) &lt;a href=&quot;https://github.com/whatwg/url/pull/763&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;URL.canParse()&lt;/code&gt;&lt;/a&gt; API:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/42888162/231174373-d4a51643-0caa-47c4-9074-706723a9bbea.png&quot; alt=&quot;Demo of URL.canParse() in a SerenityOS Browser JS console, next to the GitHub PR that added WPT tests for it&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-convert-calc()-to-spec&#39;s-internal-representation%2C-fixing-nested-calc()-in-the-process&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-convert-calc()-to-spec&#39;s-internal-representation%2C-fixing-nested-calc()-in-the-process&quot;&gt;LibWeb: Convert &lt;code&gt;calc()&lt;/code&gt; to spec&#39;s internal representation, fixing nested &lt;code&gt;calc()&lt;/code&gt; in the process&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18289&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18289&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This re-implements calculated CSS values according to the &lt;a href=&quot;https://www.w3.org/TR/css-values-4/#calc-internal&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;CSS-VALUES-4&lt;/a&gt; spec, fixing nested &lt;code&gt;calc()&lt;/code&gt; in the process and allowing us to implement other functionality from that spec more easily later on.&lt;/p&gt;&lt;p&gt;For example, this is the internal representation of &lt;code&gt;calc(100px + 30% - (120px / (2*4 + 3 )))&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230416214538if_/https://cdn.discordapp.com/attachments/1051591991013671045/1095348798856896603/image.png&quot; alt=&quot;Terminal screenshot showing the AST output of parsing &#39;calc(100px + 30% - (120px / (2*4 + 3 )))&#39;&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-handle-null-values-when-making-args-for-attributechangedcallback&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-handle-null-values-when-making-args-for-attributechangedcallback&quot;&gt;LibWeb: Handle null values when making args for attributeChangedCallback&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18293&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18293&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes incorrect handling of the &lt;code&gt;DeprecatedString&lt;/code&gt; null state, which also returns true for &lt;code&gt;is_empty()&lt;/code&gt; checks and would therefore create an empty JS string where a &lt;code&gt;null&lt;/code&gt; value was expected.&lt;/p&gt;&lt;p&gt;This makes a lot of custom elements on the YouTube video page appear!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/231292779-24b211a5-e0c7-4c20-bc0d-1de6edeb1ab8.png&quot; alt=&quot;SerenityOS March 2023 update video YouTube page in Ladybird before the fix, only the video element is shown&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/25595356/231292812-5eb3dde0-a2cc-454a-ba0d-e565f3a34132.png&quot; alt=&quot;SerenityOS March 2023 update video YouTube page in Ladybird after the fix, looking as intended&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-create-a-video-document-for-video%2F-mime-types-on-navigation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-create-a-video-document-for-video%2F-mime-types-on-navigation&quot;&gt;LibWeb: Create a video document for &lt;code&gt;video/&lt;/code&gt; MIME types on navigation&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18295&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18295&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We already create a basic DOM for images directly loaded via the URL bar so they can be shown in the web view, and now do the same for videos:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/25595356/231308051-a05ce0f0-10a1-486d-a175-702e1ca1479e.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;(video playback performance has improved a bit since then!)&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-integrate-readablebytestreamcontroller-into-readablestreamcontroller-type&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-integrate-readablebytestreamcontroller-into-readablestreamcontroller-type&quot;&gt;LibWeb: Integrate ReadableByteStreamController into ReadableStreamController type&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18297&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18297&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;More work on the Streams API. As a reviewer I&#39;m very happy that Matthew is doing this incrementally :^)&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-comment-all-the-things&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-comment-all-the-things&quot;&gt;LibJS: Comment all the things&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18336&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18336&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I dusted off an old branch with the aim of adding spec comments to everything in &lt;a href=&quot;https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS/Runtime&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;LibJS/Runtime/&lt;/code&gt;&lt;/a&gt; — we&#39;re not quite there yet, but it&#39;s a gradual process that often leads to small cleanups along the way :^)&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-make-intrinsics-getters-and-well-known-symbol-getters-return-nonnullgcptr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-make-intrinsics-getters-and-well-known-symbol-getters-return-nonnullgcptr&quot;&gt;LibJS: Make intrinsics getters and well-known symbol getters return NonnullGCPtr&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18343&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18343&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another ongoing effort in LibJS is updating raw pointers to either &lt;code&gt;GCPtr&lt;/code&gt; or &lt;code&gt;NonnullGCPtr&lt;/code&gt; — two lightweight wrappers around pointers to memory allocated on the JS heap and thus controlled by the garbage collector (GC).&lt;/p&gt;&lt;p&gt;In this case I updated all the &lt;a href=&quot;https://tc39.es/ecma262/#sec-well-known-intrinsic-objects&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;well-known intrinsic objects&lt;/a&gt; (built-in constructors and prototypes mostly, written as &lt;code&gt;%name%&lt;/code&gt; in the spec) and &lt;a href=&quot;https://tc39.es/ecma262/#sec-well-known-symbols&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;well-known symbols&lt;/a&gt; (&lt;code&gt;@@name&lt;/code&gt;) to return &lt;code&gt;NonnullGCPtr&lt;/code&gt;s instead of raw pointers:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; ArrayConstructor* Intrinsics::array_constructor();
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; NonnullGCPtr&amp;lt;ArrayConstructor&gt; Intrinsics::array_constructor();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Even though many of the intrinsics are lazily allocated, calling their accessors is what causes that so a null pointer is never returned.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-whine-instead-of-dying-on-unexpected-box-during-line-layout&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-whine-instead-of-dying-on-unexpected-box-during-line-layout&quot;&gt;LibWeb: Whine instead of dying on unexpected box during line layout&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18344&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18344&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This works around a &lt;code&gt;VERIFY()&lt;/code&gt; (assertion) in the &lt;em&gt;Inline Formatting Context&lt;/em&gt; that was slightly too optimistic and would cause crashes on various websites, including GitHub.&lt;/p&gt;&lt;p&gt;It&#39;s really annoying to have the entire web page crash because of some random unimplemented code path, so we largely avoid crashing macros like &lt;code&gt;TODO()&lt;/code&gt; and &lt;code&gt;VERIFY()&lt;/code&gt; in favour of no-op code paths that log a FIXME.&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-port-more-aos-to-%7Bnonnull%2C%7Dgcptr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-port-more-aos-to-%7Bnonnull%2C%7Dgcptr&quot;&gt;LibJS: Port more AOs to {Nonnull,}GCPtr&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18346&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18346&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Doing the same as above to the return types of more &lt;abbr title=&quot;Abstract Operation&quot;&gt;AO&lt;/abbr&gt;s.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-match-the-root-node-of-htmlcollection&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-don&#39;t-match-the-root-node-of-htmlcollection&quot;&gt;LibWeb: Don&#39;t match the root node of HTMLCollection&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18349&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18349&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;When a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLCollection&lt;/code&gt;&lt;/a&gt; is created, e.g. via &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Element.getElementsByTagName()&lt;/code&gt;&lt;/a&gt;, it traverses the subtree from that element, which we have two slightly different methods for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;for_each_in_inclusive_subtree_of_type()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;for_each_in_subtree_of_type()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It now correctly uses the latter to not include the element the method was called on.&lt;/p&gt;&lt;h2 id=&quot;libweb%2Bladybird%3A-add-support-for-more-css-cursors-in-ladybird&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Bladybird%3A-add-support-for-more-css-cursors-in-ladybird&quot;&gt;LibWeb+Ladybird: Add support for more CSS cursors in Ladybird&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18359&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18359&lt;/a&gt; by &lt;a href=&quot;https://github.com/srikavin&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@srikavin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This adds support for &lt;code&gt;cursor: not-allowed;&lt;/code&gt; in LibWeb, and maps more cursors from the LibGfx &lt;code&gt;StandardCursor&lt;/code&gt; enum to their Qt equivalents in Ladybird (previously, only the &lt;em&gt;hand&lt;/em&gt;, &lt;em&gt;i-beam&lt;/em&gt;, and &lt;em&gt;arrow&lt;/em&gt; cursors were mapped).&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-set-comment&#39;s-prototype&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-set-comment&#39;s-prototype&quot;&gt;LibWeb: Set Comment&#39;s prototype&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18362&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18362&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Quite possibly the best &lt;em&gt;bug silliness to fix effectiveness&lt;/em&gt; ratio we&#39;ve had so far. Simply by setting the correct prototype of any instantiated &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Comment&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Comment&lt;/code&gt;&lt;/a&gt; object, thumbnails started appearing on YouTube!&lt;/p&gt;&lt;p&gt;Luke wrote a summary of their investigation of this bug, check out the PR if you&#39;re curious :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/231991331-8514bb62-7283-40b1-bfc0-b54bc411789d.png&quot; alt=&quot;YouTube home page in Ladybird before the fix, thumbnails only show placeholders&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/25595356/231991391-889d3d2a-6e25-463a-b7d6-e290211b98fc.png&quot; alt=&quot;YouTube home page in Ladybird after the fix, thumbnails now have titles and channel icons&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-honor-column-gap-and-row-gap-css-properties-in-flex-layout&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-honor-column-gap-and-row-gap-css-properties-in-flex-layout&quot;&gt;LibWeb: Honor column-gap and row-gap CSS properties in flex layout&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18363&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18363&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another case of web reality not matching the specs; the PR description explains this change nicely:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This isn&#39;t actually part of CSS-FLEXBOX-1, but all major engines honor these properties in flex layout, and it&#39;s widely used on the web.&lt;/p&gt;&lt;p&gt;There&#39;s a bug open against the flexbox spec where fantasai says the algorithm will be updated in CSS-FLEXBOX-2: &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/2336&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;w3c/csswg-drafts#2336&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I&#39;ve added comments to all the places where we adjust calculations for gaps with &amp;quot;CSS-FLEXBOX-2&amp;quot; so we can find them easily. When that spec becomes available, we can add proper spec links.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%3A-do-not-dereference-empty-optional-in-readablestream%3A%3Avisit_edges&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-do-not-dereference-empty-optional-in-readablestream%3A%3Avisit_edges&quot;&gt;LibWeb: Do not dereference empty Optional in ReadableStream::visit_edges&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18367&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18367&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;A bug fix for &lt;code&gt;ReadableStream&lt;/code&gt;, which would dereference an empty &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt; (which causes a crash) during garbage collection if its controller hasn&#39;t been set yet.&lt;/p&gt;&lt;h2 id=&quot;libweb%2Fstreams%3A-couple-of-small-cleanups&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%2Fstreams%3A-couple-of-small-cleanups&quot;&gt;LibWeb/Streams: Couple of small cleanups&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18370&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18370&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Have you noticed I&#39;m in a spring cleaning mood yet? :^)&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-comment-all-the-things-(continued)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libjs%3A-comment-all-the-things-(continued)&quot;&gt;LibJS: Comment all the things (continued)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18374&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18374&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;More of that. I &lt;em&gt;think&lt;/em&gt; all the built-in objects are covered now…&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-introduce-documentstate-and-update-sessionhistoryentry&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-introduce-documentstate-and-update-sessionhistoryentry&quot;&gt;LibWeb: Introduce DocumentState and update SessionHistoryEntry&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18378&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18378&lt;/a&gt; by &lt;a href=&quot;https://github.com/kalenikaliaksandr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kalenikaliaksandr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Alexander has &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18219&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;adopted and continued&lt;/a&gt; Andreas&#39; refactor from &lt;em&gt;browsing contexts&lt;/em&gt; to &lt;em&gt;navigables&lt;/em&gt; so we can start merging those changes, this is the beginning of that :^)&lt;/p&gt;&lt;h2 id=&quot;ladybird%2Blibwebview%3A-add--p%2F--enable-callgrind-profiling-option&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#ladybird%2Blibwebview%3A-add--p%2F--enable-callgrind-profiling-option&quot;&gt;Ladybird+LibWebView: Add -P/--enable-callgrind-profiling option&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18383&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18383&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This adds an option to Ladybird to run WebContent processes in &lt;a href=&quot;https://valgrind.org/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;valgrind&lt;/a&gt; with &lt;a href=&quot;https://valgrind.org/docs/manual/cl-manual.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;callgrind&lt;/a&gt; enabled, which is useful for profiling on Linux! 🐧&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-scale-svg-stroke-width-based-on-viewbox&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-scale-svg-stroke-width-based-on-viewbox&quot;&gt;LibWeb: Scale SVG stroke-width based on viewbox&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18388&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18388&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This fixes the clipping of strokes in quite a few cases and now fixes the Gartic Phone logo :^)&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/232230832-4f85c9d3-3547-499c-89f3-5c20099442e4.png&quot; alt=&quot;Gartic Phone logo before the fix, not looking quite right&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/11597044/232230804-fb47fb0a-bfa8-4ddc-ba09-e19c470e1761.png&quot; alt=&quot;Gartic Phone logo after the fix, looking as intended&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-honor-gap-between-flex-lines-when-using-align-content%3A-stretch&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-honor-gap-between-flex-lines-when-using-align-content%3A-stretch&quot;&gt;LibWeb: Honor gap between flex lines when using align-content: stretch&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18400&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18400&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;As usual, layout changes are best explained by the author themselves :^)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We have to take the cross gap into account when calculating the &amp;quot;sum of flex line cross sizes&amp;quot; in &amp;quot;Handle &#39;align-content: stretch&#39;&amp;quot;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%3A-fix-multi-line-flex-column-layouts-with-auto-height-on-container&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#libweb%3A-fix-multi-line-flex-column-layouts-with-auto-height-on-container&quot;&gt;LibWeb: Fix multi-line flex column layouts with auto height on container&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18403&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18403&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When sizing a flex container with flex-direction:column under a max-content height constraint, we were incorrectly truncating the infinite available height to 0 when collecting flex items into lines.&lt;/p&gt;&lt;p&gt;This caused us to put every flex item in its own flex line, which is the complete opposite of what we want during max-content intrinsic sizing, as the layout would grow wide but not tall.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;screenshots&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-4/#screenshots&quot;&gt;Screenshots&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;During his CSS &lt;code&gt;calc()&lt;/code&gt; refactor, Sam had a happy little accident. Luke&#39;s been trying all sorts of things on YouTube that work now (after briefly breaking the thumbnails layout), which is really impressive! And of course, there&#39;s always that one website complaining about your ad blocker… we happen to have one. And something&#39;s wrong with our rendering of the mozilla logo :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230416214138if_/https://cdn.discordapp.com/attachments/1051591991013671045/1095342493060575362/image.png&quot; alt=&quot;Terminal screenshot showing an increasingly nested repetition of lines saying &#39;NEGATE:&#39;&quot; title=&quot;Shared by @AtkinsSJ on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230416213903if_/https://cdn.discordapp.com/attachments/830525031720943627/1096338784464945192/Screenshot_from_2023-04-14_04-55-47.png&quot; alt=&quot;YouTube home page in Ladybird, with the video thumbnails all over the place&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230416213934if_/https://cdn.discordapp.com/attachments/830525031720943627/1095723159161938003/image.png&quot; alt=&quot;YouTube live chat in Ladybird&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230416213750if_/https://cdn.discordapp.com/attachments/830525031720943627/1095456543941603468/image.png&quot; alt=&quot;YouTube video comments in Ladybird&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230416213840if_/https://cdn.discordapp.com/attachments/830525031720943627/1096344183251423312/image.png&quot; alt=&quot;tastingtable.com in Ladybird, with a popup complaining about our (basic) ad blocking capabilities&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230416214423if_/https://cdn.discordapp.com/attachments/830525031720943627/1095096435260805130/image.png&quot; alt=&quot;&#39;moz://a&#39; logo, with the second &#39;/&#39; and &#39;a&#39; leaving the image boundary&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;And that&#39;s all for this week! Lots of nice progress, video playback is improving every day, Street View basically works (pending some perf improvements), and I&#39;d not be surprised if Luke makes even more of YouTube work next week.&lt;/p&gt;&lt;p&gt;Looks like it&#39;s possible to build a browser after all :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>This Week in Ladybird #3</title>
    <link href="https://linus.dev/posts/this-week-in-ladybird-3/" />
    <updated>2023-04-09T00:00:00Z</updated>
    <id>https://linus.dev/posts/this-week-in-ladybird-3/</id>
    <content type="html">&lt;p&gt;Overview of changes across the Ladybird Browser project in the week from April 3 to April 9 (plus everything merged Sunday evening) :^)&lt;/p&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In chronological order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#ak%2Blibweb%3A-port-%7Bhtml%2Cuievents%2Cxhr%7D%3A%3Aeventnames-to-new-flystring&quot;&gt;AK+LibWeb: Port {HTML,UIEvents,XHR}::EventNames to new FlyString&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-introduce-customelementregistry-and-creating-custom-elements&quot;&gt;LibWeb: Introduce CustomElementRegistry and creating custom elements&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-borders-functionality-to-css-grid&quot;&gt;LibWeb: Add borders functionality to CSS Grid&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-background-position-x-and-background-position-y-longhand-longhands&quot;&gt;LibWeb: Implement &lt;code&gt;background-position-x&lt;/code&gt; and &lt;code&gt;background-position-y&lt;/code&gt; longhand longhands&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-fix-background-position-for-multi-layer-backgrounds&quot;&gt;LibWeb: Fix background-position for multi-layer backgrounds&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-multipart%2Fform-data-encoding-algorithm&quot;&gt;LibWeb: Implement multipart/form-data encoding algorithm&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-use-intrinsic-aspect-ratio-when-calculating-max-content-height&quot;&gt;LibWeb: Use intrinsic aspect ratio when calculating max content height&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-writablestream-and-writablestreamdefaultwriter-interfaces&quot;&gt;LibWeb: Add WritableStream and WritableStreamDefaultWriter interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-a-couple-of-error-handling-improvements&quot;&gt;LibWeb: A couple of error handling improvements&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-begin-support-for-videos-with-libvideo&quot;&gt;LibWeb: Begin support for videos with LibVideo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libjs%3A-update-spec-numbers-for-the-intl-enumeration-proposal&quot;&gt;LibJS: Update spec numbers for the Intl Enumeration proposal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-use-max-width-when-present-in-tableformattingcontext&quot;&gt;LibWeb: Use max-width when present in TableFormattingContext&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-a-few-idl-implementations-to-new-string&quot;&gt;LibWeb: Port a few IDL implementations to new String&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-ignore-preferred-width-when-calculating-intrinsic-width-of-block&quot;&gt;LibWeb: Ignore preferred width when calculating intrinsic width of block&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-transform-the-default-path-in-crc2d%23fill(canvasfillrule)&quot;&gt;LibWeb: Transform the default path in CRC2D#fill(CanvasFillRule)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-don&#39;t-put-abspos-grid%2Fflex-items-in-anonymous-wrapper&quot;&gt;LibWeb: Don&#39;t put abspos grid/flex items in anonymous wrapper&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-an-initial-implementation-of-crc2d.clip()&quot;&gt;LibWeb: Add an initial implementation of CRC2D.clip()&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-%7Bcustom%2Cmouse%2Cui%2Cwheel%2C%7Devent-to-new-string&quot;&gt;LibWeb: Port {Custom,Mouse,UI,Wheel,}Event to new String&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-the-writablestreamdefaultcontroller&quot;&gt;LibWeb: Add the WritableStreamDefaultController&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-htmlvideoelement-load%2C-play%2C-pause-and-draw-some-basic-controls&quot;&gt;LibWeb: Implement HTMLVideoElement &lt;code&gt;load&lt;/code&gt;, &lt;code&gt;play&lt;/code&gt;, &lt;code&gt;pause&lt;/code&gt; and draw some basic controls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#webp-lossless%3A-implement-pixel-bundling-for-color-indexing-transform&quot;&gt;webp lossless: Implement pixel bundling for color indexing transform&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%2Ffetch%3A-use-a-basic-filtered-response-for-redirect-navigations&quot;&gt;LibWeb/Fetch: Use a basic filtered response for redirect navigations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-rest-of-event-system-related-in-libweb-to-new-%7Bfly%7Dstring&quot;&gt;LibWeb: Port rest of event system-related in LibWeb to new {Fly}String&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-writablestream.close()&quot;&gt;LibWeb: Implement WritableStream.close()&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;ak%2Blibweb%3A-port-%7Bhtml%2Cuievents%2Cxhr%7D%3A%3Aeventnames-to-new-flystring&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#ak%2Blibweb%3A-port-%7Bhtml%2Cuievents%2Cxhr%7D%3A%3Aeventnames-to-new-flystring&quot;&gt;AK+LibWeb: Port {HTML,UIEvents,XHR}::EventNames to new FlyString&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17825&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17825&lt;/a&gt; by &lt;a href=&quot;https://github.com/kennethmyhra&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kennethmyhra&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ports various event name enumerations (think &lt;code&gt;mouseup&lt;/code&gt;, &lt;code&gt;pageshow&lt;/code&gt;, …) from &lt;code&gt;DeprecatedFlyString&lt;/code&gt; to &lt;code&gt;FlyString&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-introduce-customelementregistry-and-creating-custom-elements&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-introduce-customelementregistry-and-creating-custom-elements&quot;&gt;LibWeb: Introduce CustomElementRegistry and creating custom elements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18092&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18092&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CustomElementRegistry&lt;/code&gt;&lt;/a&gt; interface along with the underlying infrastructure required to create custom elements, the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html#cereactions&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;[CEReactions]&lt;/code&gt; IDL extended attribute&lt;/a&gt;, and the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:defined&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;:defined&lt;/code&gt; CSS pseudo class&lt;/a&gt;!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-add-borders-functionality-to-css-grid&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-borders-functionality-to-css-grid&quot;&gt;LibWeb: Add borders functionality to CSS Grid&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18148&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18148&lt;/a&gt; by &lt;a href=&quot;https://github.com/martinfalisse&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@martinfalisse&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Borders affect layout, and were previously not considered in a &lt;em&gt;Grid Formatting Context&lt;/em&gt;. Now they are:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45781926/229359962-91af56f0-b9d5-49f6-af35-5da01c76a910.png&quot; alt=&quot;Demo CSS Grid with borders in Ladybird&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-background-position-x-and-background-position-y-longhand-longhands&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-background-position-x-and-background-position-y-longhand-longhands&quot;&gt;LibWeb: Implement &lt;code&gt;background-position-x&lt;/code&gt; and &lt;code&gt;background-position-y&lt;/code&gt; longhand longhands&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18157&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18157&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/229381435-2fdb2802-b29e-4e61-85a3-6f908e4848f4.png&quot; alt=&quot;Not ready for implementation - That sign can&#39;t stop me because I can&#39;t read!&quot;&gt;&lt;/p&gt;&lt;p&gt;On a more serious note, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-position-x&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;background-position-x&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-position-y&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;background-position-y&lt;/code&gt;&lt;/a&gt; longhands of the &lt;code&gt;background-position&lt;/code&gt; property, which itself is a longhand of &lt;code&gt;background&lt;/code&gt; have been widely supported in other engines for well over a decade (at least the single-value syntax), and are used on many websites, despite being part of a CSS spec that claims to be &lt;em&gt;Not Ready For Implementation&lt;/em&gt; yet.&lt;/p&gt;&lt;p&gt;One such website is Steam (notice the left carousel button), before and after:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/229381669-3e258ac8-6c56-47b9-b269-d18e06d91b1d.png&quot; alt=&quot;Steam carousel with the left button pointing to the right&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/11597044/229381681-9681d8da-c892-4206-8736-9a14f848bb01.png&quot; alt=&quot;Steam carousel with the left button pointing to the left&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-fix-background-position-for-multi-layer-backgrounds&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-fix-background-position-for-multi-layer-backgrounds&quot;&gt;LibWeb: Fix background-position for multi-layer backgrounds&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18167&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18167&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes a regression of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Backgrounds_and_Borders/Using_multiple_backgrounds&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;multiple backgrounds&lt;/a&gt; rendering caused by the previous feature:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230411192931if_/https://cdn.discordapp.com/attachments/1051591991013671045/1092357859762774048/image.png&quot; alt=&quot;CSS multi-background demo in Ladybird, working and broken in two windows&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-multipart%2Fform-data-encoding-algorithm&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-multipart%2Fform-data-encoding-algorithm&quot;&gt;LibWeb: Implement multipart/form-data encoding algorithm&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18169&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18169&lt;/a&gt; by &lt;a href=&quot;https://github.com/kennethmyhra&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kennethmyhra&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart/form-data-encoding-algorithm&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;multipart/form-data&lt;/code&gt; encoding algorithm&lt;/a&gt; from the HTML spec, allowing us to use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;XMLHttpRequest.send()&lt;/code&gt;&lt;/a&gt; with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FormData&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;FormData&lt;/code&gt;&lt;/a&gt; objects!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-use-intrinsic-aspect-ratio-when-calculating-max-content-height&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-use-intrinsic-aspect-ratio-when-calculating-max-content-height&quot;&gt;LibWeb: Use intrinsic aspect ratio when calculating max content height&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18173&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18173&lt;/a&gt; by &lt;a href=&quot;https://github.com/matcool&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@matcool&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This fixes images in a flex container with row direction being stretched when given a specific width, but auto height.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Before and after:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/26722564/229638452-056da0aa-b56a-418b-b9f8-17b31713bc28.png&quot; alt=&quot;OpenGraph image of this blog post in two Ladybird windows, one of them stretched and one with the right proportions&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-add-writablestream-and-writablestreamdefaultwriter-interfaces&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-writablestream-and-writablestreamdefaultwriter-interfaces&quot;&gt;LibWeb: Add WritableStream and WritableStreamDefaultWriter interfaces&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18174&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18174&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Matthew is continuing his work on bringing the Streams API to LibWeb! This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;WritableStream&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/WritableStream&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStream()&lt;/code&gt;&lt;/a&gt; constructor&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/locked&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStream.locked&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WritableStreamDefaultWriter&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/WritableStreamDefaultWriter&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter()&lt;/code&gt;&lt;/a&gt; constructor&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/closed&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.closed&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/desiredSize&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.desiredSize&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter/ready&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultWriter.ready&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-a-couple-of-error-handling-improvements&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-a-couple-of-error-handling-improvements&quot;&gt;LibWeb: A couple of error handling improvements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18181&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18181&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This is preparation for…&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-begin-support-for-videos-with-libvideo&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-begin-support-for-videos-with-libvideo&quot;&gt;LibWeb: Begin support for videos with LibVideo&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18183&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18183&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We finally have an initial implementation of video playback in LibWeb, thanks to Tim and everyone who previously worked on LibVideo for general video support in SerenityOS!&lt;/p&gt;&lt;p&gt;There&#39;s still a fair number of limitations (decodes within the page&#39;s WebContent process, no audio, hardcoded to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Matroska&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Matroska&lt;/a&gt; demuxer and &lt;a href=&quot;https://en.wikipedia.org/wiki/VP9&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;VP9&lt;/a&gt; decoder, i.e. WebM) but at the same time it&#39;s of course 100% more functional than what we had before :^)&lt;/p&gt;&lt;p&gt;See (and I highly suggest trying, too!) for yourself:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/5600524/229940634-aaadfa31-7ef8-4df3-b19f-9962f52661fb.mp4&quot;&gt;&lt;/video&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/5600524/229946302-7e86b332-e5ef-47d7-9335-21bf624e0fe8.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-update-spec-numbers-for-the-intl-enumeration-proposal&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libjs%3A-update-spec-numbers-for-the-intl-enumeration-proposal&quot;&gt;LibJS: Update spec numbers for the Intl Enumeration proposal&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18194&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18194&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://tc39.es/proposal-intl-enumeration/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Intl Enumeration API&lt;/a&gt; (i.e &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/supportedValuesOf&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Intl.supportedValuesOf()&lt;/code&gt;&lt;/a&gt; function) reached stage 4 and was merged into the main ECMA-402 spec, so we had to update some references.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-use-max-width-when-present-in-tableformattingcontext&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-use-max-width-when-present-in-tableformattingcontext&quot;&gt;LibWeb: Use max-width when present in TableFormattingContext&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18195&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18195&lt;/a&gt; by &lt;a href=&quot;https://github.com/martinfalisse&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@martinfalisse&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Elements with &lt;code&gt;display: table&lt;/code&gt; now respect &lt;code&gt;max-width&lt;/code&gt; if given. Previously the created &lt;em&gt;Table Formatting Context&lt;/em&gt; would take up the full available width.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-port-a-few-idl-implementations-to-new-string&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-a-few-idl-implementations-to-new-string&quot;&gt;LibWeb: Port a few IDL implementations to new String&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18197&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18197&lt;/a&gt; by &lt;a href=&quot;https://github.com/kennethmyhra&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kennethmyhra&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ports the following interfaces from &lt;code&gt;DeprecatedString&lt;/code&gt; to &lt;code&gt;String&lt;/code&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;FocusEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;KeyboardEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;…as well as an internal class (&lt;code&gt;AbstractBrowsingContext&lt;/code&gt;).&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-ignore-preferred-width-when-calculating-intrinsic-width-of-block&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-ignore-preferred-width-when-calculating-intrinsic-width-of-block&quot;&gt;LibWeb: Ignore preferred width when calculating intrinsic width of block&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18204&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18204&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another week went by without me having any clue about layout stuff, so I&#39;ll let Andreas explain &lt;img src=&quot;https://cdn.discordapp.com/emojis/933441581258768394.png&quot; width=&quot;22&quot; style=&quot;position: relative; top: 2px;&quot; title=&quot;:yakkie:&quot; alt=&quot;:yakkie: emote from the SerenityOS Discord server&quot;&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When calculating the intrinsic width of a block-level box, we now ignore the preferred width entirely, and not just when the preferred width should be treated as auto.&lt;/p&gt;&lt;p&gt;The condition for this was both confused and wrong, as it looked at the available width around the box, but didn&#39;t check for a width constraint on the box itself.&lt;/p&gt;&lt;p&gt;Just because the available width has an intrinsic sizing constraint doesn&#39;t mean that the box is undergoing intrinsic sizing. It could also be the box&#39;s containing block!&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%3A-transform-the-default-path-in-crc2d%23fill(canvasfillrule)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-transform-the-default-path-in-crc2d%23fill(canvasfillrule)&quot;&gt;LibWeb: Transform the default path in CRC2D#fill(CanvasFillRule)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18206&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18206&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes a bug where we didn&#39;t apply the canvas&#39;s current transform in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fill&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CanvasRenderingContext2D.fill()&lt;/code&gt;&lt;/a&gt; when called with a fill rule, resulting in incorrect placement of the filled path.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/230428646-b0a7d5b4-8c41-49c7-b5b7-e8ca6b973d4d.png&quot; alt=&quot;&#39;Factory Balls forever&#39; game in Ladybird, with the title placed incorrectly&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/25595356/230428694-a446fc8e-4f86-4f50-9e36-a0990c920814.png&quot; alt=&quot;&#39;Factory Balls forever&#39; game in Ladybird, with the title placed correctly&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-put-abspos-grid%2Fflex-items-in-anonymous-wrapper&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-don&#39;t-put-abspos-grid%2Fflex-items-in-anonymous-wrapper&quot;&gt;LibWeb: Don&#39;t put abspos grid/flex items in anonymous wrapper&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18208&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18208&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Grid and flex containers have their own rules for abspos items, so we shouldn&#39;t try to be clever and put them in the &amp;quot;current&amp;quot; anonymous wrapper block. That behavior is primarily for the benefit of block &amp;amp; inline layout.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%3A-add-an-initial-implementation-of-crc2d.clip()&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-an-initial-implementation-of-crc2d.clip()&quot;&gt;LibWeb: Add an initial implementation of CRC2D.clip()&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18212&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18212&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CanvasRenderingContext2D.clip()&lt;/code&gt;&lt;/a&gt; API, resulting in major rendering improvements on Google Street View!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230411192931if_/https://cdn.discordapp.com/attachments/830525031720943627/1093573857983610890/Screenshot_from_2023-04-06_17-27-40.png&quot; alt=&quot;Google Street View in Ladybird, with the 3D view distorted&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230411192931if_/https://cdn.discordapp.com/attachments/830525031720943627/1093573876849582190/Screenshot_from_2023-04-06_17-30-00.png&quot; alt=&quot;Google Street View in Ladybird, with the 3D view almost looking as intended&quot;&gt;&lt;/p&gt;&lt;p&gt;And even those little missing sections got fixed in the end:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230411192931if_/https://cdn.discordapp.com/attachments/830525031720943627/1093709549233520640/image.png&quot; alt=&quot;Google Street View in Ladybird, with the 3D view looking as intended&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-port-%7Bcustom%2Cmouse%2Cui%2Cwheel%2C%7Devent-to-new-string&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-%7Bcustom%2Cmouse%2Cui%2Cwheel%2C%7Devent-to-new-string&quot;&gt;LibWeb: Port {Custom,Mouse,UI,Wheel,}Event to new String&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18215&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18215&lt;/a&gt; by &lt;a href=&quot;https://github.com/kennethmyhra&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kennethmyhra&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ports the following classes from &lt;code&gt;DeprecatedString&lt;/code&gt; to &lt;code&gt;String&lt;/code&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CustomEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;MouseEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/UIEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;UIEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WheelEvent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-add-the-writablestreamdefaultcontroller&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-add-the-writablestreamdefaultcontroller&quot;&gt;LibWeb: Add the WritableStreamDefaultController&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18223&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18223&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements &lt;code&gt;WritableStreamDefaultController&lt;/code&gt; with its two APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController/signal&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultController.signal&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController/error&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStreamDefaultController.error()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-implement-htmlvideoelement-load%2C-play%2C-pause-and-draw-some-basic-controls&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-htmlvideoelement-load%2C-play%2C-pause-and-draw-some-basic-controls&quot;&gt;LibWeb: Implement HTMLVideoElement &lt;code&gt;load&lt;/code&gt;, &lt;code&gt;play&lt;/code&gt;, &lt;code&gt;pause&lt;/code&gt; and draw some basic controls&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18233&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18233&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;After video playback was initially implemented to start automatically and with no way of manually or programmatically controlling it, our implementation of the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element now gains play/pause controls, as well as the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/load&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLMediaElement.load()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLMediaElement.play()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLMediaElement.pause()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/paused&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLMediaElement.paused&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/5600524/230734615-8d7a62b9-a8c0-4eec-a9e5-1efe4177a644.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;h2 id=&quot;webp-lossless%3A-implement-pixel-bundling-for-color-indexing-transform&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#webp-lossless%3A-implement-pixel-bundling-for-color-indexing-transform&quot;&gt;webp lossless: Implement pixel bundling for color indexing transform&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18235&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18235&lt;/a&gt; by &lt;a href=&quot;https://github.com/nico&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@nico&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Nico has been (incrementally! &lt;img src=&quot;https://cdn.discordapp.com/emojis/883457694789406732.png&quot; width=&quot;22&quot; style=&quot;position: relative; top: 2px;&quot; title=&quot;:yakkiss:&quot; alt=&quot;:yakkiss: emote from the SerenityOS Discord server&quot;&gt;) writing a WebP decoder in LibGfx, which means it not only adds support for that format to virtually anything using images in SerenityOS, but also LibWeb!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;With this, the webp lossless decoder is feature complete :^)&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230411192932if_/https://cdn.discordapp.com/attachments/830739873119207426/1094328596211974218/Screen_Shot_2023-04-08_at_2.29.58_PM.png&quot; alt=&quot;Correctly decoded Tux WebP image in the SerenityOS Browser&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%2Ffetch%3A-use-a-basic-filtered-response-for-redirect-navigations&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%2Ffetch%3A-use-a-basic-filtered-response-for-redirect-navigations&quot;&gt;LibWeb/Fetch: Use a basic filtered response for redirect navigations&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18248&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18248&lt;/a&gt; by &lt;a href=&quot;https://github.com/kalenikaliaksandr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kalenikaliaksandr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements an &lt;a href=&quot;https://github.com/whatwg/fetch/commit/8f109835dcff90d19caed4b551a0da32d9d0f57e&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;upstream fix in the Fetch spec&lt;/a&gt;, which we need for Alexander&#39;s ongoing work on implementing the &lt;a href=&quot;https://github.com/whatwg/html/pull/6315&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;massive navigables refactor&lt;/a&gt;!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-port-rest-of-event-system-related-in-libweb-to-new-%7Bfly%7Dstring&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-port-rest-of-event-system-related-in-libweb-to-new-%7Bfly%7Dstring&quot;&gt;LibWeb: Port rest of event system-related in LibWeb to new {Fly}String&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18255&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18255&lt;/a&gt; by &lt;a href=&quot;https://github.com/kennethmyhra&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kennethmyhra&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ports the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/EventTarget&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;EventTarget&lt;/code&gt;&lt;/a&gt; interface from &lt;code&gt;Deprecated{Fly,}String&lt;/code&gt; to &lt;code&gt;{Fly,}String&lt;/code&gt;, as well as various internal classes and functions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;DOMEventListener&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;fire_progress_event()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;fire_a_page_transition_event()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;fire_a_synthetic_pointer_event()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;fire_keyboard_event()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;fire_webgl_context_event()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-implement-writablestream.close()&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#libweb%3A-implement-writablestream.close()&quot;&gt;LibWeb: Implement WritableStream.close()&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18263&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18263&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/close&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;WritableStream.close()&lt;/code&gt;&lt;/a&gt; API.&lt;/p&gt;&lt;h2 id=&quot;screenshots&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-3/#screenshots&quot;&gt;Screenshots&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;MacDue and Lubrsi spent some time visually debugging Google Maps, so here are some fun screenshots from that :^)&lt;/p&gt;&lt;p&gt;Even the smallest bug in an image decoder can yield images that are way off from the intended results, and Nico shared some wrongly decoded WebP images, too!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230411192932if_/https://cdn.discordapp.com/attachments/830525031720943627/1093619926142562485/image.png&quot; alt=&quot;Google Street View (outdoors) with a custom debug overlay showing the rendered 3D view tiles&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230411192932if_/https://cdn.discordapp.com/attachments/830525031720943627/1093645949076770937/image.png&quot; alt=&quot;Google Street View (indoors) with a custom debug overlay showing the rendered 3D view tiles&quot; title=&quot;Shared by @MacDue on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230411192932if_/https://cdn.discordapp.com/attachments/830739873119207426/1093345133249970316/image.png&quot; alt=&quot;Incorrectly decoded Tux WebP image&quot; title=&quot;Shared by @thakis on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230411192933if_/https://cdn.discordapp.com/attachments/830739873119207426/1093323016810811412/image.png&quot; alt=&quot;Incorrectly decoded Tux WebP image, part 2&quot; title=&quot;Shared by @thakis on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230411192933if_/https://cdn.discordapp.com/attachments/830739873119207426/1093311678940192768/image.png&quot; alt=&quot;PureCSS Character in Ladybird, looking very much broken&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;And that&#39;s all for week 3! I hope you&#39;re just as excited about the new video support as I am, more work for abstracting the specific codec away, as well as buffering are &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18264&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;already in progress&lt;/a&gt; :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>This Week in Ladybird #2</title>
    <link href="https://linus.dev/posts/this-week-in-ladybird-2/" />
    <updated>2023-04-02T00:00:00Z</updated>
    <id>https://linus.dev/posts/this-week-in-ladybird-2/</id>
    <content type="html">&lt;p&gt;Overview of changes across the Ladybird Browser project in the week from March 27 to April 2 (plus everything merged Sunday evening) :^)&lt;/p&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In chronological order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%3A-open-target-_blank-links-in-new-tab&quot;&gt;Ladybird: Open target _blank links in new tab&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-custom-element-name-validation-and-fix-bug-in-element-name-validation&quot;&gt;LibWeb: Implement custom element name validation and fix bug in element name validation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-force-relayout-whole-page-on-programmatic-scroll&quot;&gt;LibWeb: Don&#39;t force relayout whole page on programmatic scroll&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%3A-enable-and-document-build-and-debug-steps-with-xcode&quot;&gt;Ladybird: Enable and document build and debug steps with Xcode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#browser%2Bladybird%3A-show-current-zoom-level-in-the-view-menu&quot;&gt;Browser+Ladybird: Show current zoom level in the view menu&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-stop-returning-the-left-padding-for-resolved-padding-bottom&quot;&gt;LibWeb: Stop returning the left padding for resolved padding-bottom&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-condition-to-early-return-in-intrinsic-sizing-for-tfc&quot;&gt;LibWeb: Fix condition to early return in intrinsic sizing for TFC&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-handle-flex-column-items-with-auto-height-that-depends-on-auto-width&quot;&gt;LibWeb: Handle flex column items with auto height that depends on auto width&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-clamp-fit-content-widths-in-flex-layout-to-min%2Fmax-width&quot;&gt;LibWeb: Clamp fit-content widths in flex layout to min/max-width&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#tests%2Flibweb%3A-add-layout-test-for-layout-fix-in-pr-%2315780&quot;&gt;Tests/LibWeb: Add layout test for layout fix in PR #15780&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%2Blibwasm%3A-implement-and-use-the-%22reset-the-memory-buffer%22-steps&quot;&gt;LibWeb+LibWasm: Implement and use the &amp;quot;reset the Memory buffer&amp;quot; steps&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%2Bbrowser%3A-add-(reset)-zoom-level-button-to-toolbar&quot;&gt;Ladybird+Browser: Add (reset) zoom level button to toolbar&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-parse-element.style-url-functions-relative-to-the-document&quot;&gt;LibWeb: Parse Element.style url functions relative to the document&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-resolve-percentage-vertical-align-values-against-line-height&quot;&gt;LibWeb: Resolve percentage vertical-align values against line-height&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-htmlanchorelement.text-and-.referrerpolicy-properties&quot;&gt;LibWeb: Implement HTMLAnchorElement.text and .referrerPolicy properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-crc2d.imagesmoothingenabled&quot;&gt;LibWeb: Implement CRC2D.imageSmoothingEnabled&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-remove-redundant-invocation-of-children-changed-in-htmlparser&quot;&gt;LibWeb: Remove redundant invocation of children changed in HTMLParser&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-re-sort-stylesheetlist-on-every-new-sheet-insertion&quot;&gt;LibWeb: Don&#39;t re-sort StyleSheetList on every new sheet insertion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-actually-visit-rules-and-media-queries-in-imported-style-sheets&quot;&gt;LibWeb: Actually visit rules and media queries in imported style sheets&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-split-calculatedstylevalue-out-of-stylevalue.%7Bh%2Ccpp%7D&quot;&gt;LibWeb: Split CalculatedStyleValue out of StyleValue.{h,cpp}&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libjs%3A-add-fast-path-to-value%3A%3Ato_u32()-if-value-is-a-positive-i32&quot;&gt;LibJS: Add fast path to Value::to_u32() if Value is a positive i32&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-begin-implementing-the-streams-api&quot;&gt;LibWeb: Begin implementing the Streams API&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%2Bbrowser%3A-add-tooltip-to-reset-zoom-level-button&quot;&gt;Ladybird+Browser: Add tooltip to reset zoom level button&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-churn-html%3A%3Aeventloop-while-in-microtask-checkpoint&quot;&gt;LibWeb: Don&#39;t churn HTML::EventLoop while in microtask checkpoint&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-application-of-intrinsic-aspect-ratio-to-flex-column-items&quot;&gt;LibWeb: Fix application of intrinsic aspect ratio to flex column items&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%2Btests%3A-allow-subdirectories-in-layout_test.sh-and-add-grid-tests&quot;&gt;LibWeb+Tests: Allow subdirectories in layout_test.sh and add grid tests&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libjs%3A-fix-parsing-of-mixed-coalesceexpression-and-logical%7Band%2Cor%7Dexpression&quot;&gt;LibJS: Fix parsing of mixed &lt;code&gt;CoalesceExpression&lt;/code&gt; and &lt;code&gt;Logical{AND,OR}Expression&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-a-few-font-scaling-issues&quot;&gt;LibWeb: Fix a few font scaling issues&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-apply-element-inline-style-%26-presentational-hints-to-associated-pseudo-elements&quot;&gt;LibWeb: Don&#39;t apply element inline style &amp;amp; presentational hints to associated pseudo elements&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;ladybird%3A-open-target-_blank-links-in-new-tab&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%3A-open-target-_blank-links-in-new-tab&quot;&gt;Ladybird: Open target _blank links in new tab&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18001&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18001&lt;/a&gt; by &lt;a href=&quot;https://github.com/Coderdreams&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Coderdreams&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This makes Ladybird (as in the Qt application) behavior match Browser (as in the LibGUI application) for the following actions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; links open in a new activated tab&lt;/li&gt;&lt;li&gt;Links clicked while Ctrl is pressed open in a new background tab&lt;/li&gt;&lt;li&gt;Links middle-clicked open in a new background tab&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the future we might make these configurable, but for now these are better defaults most people will be familiar with :^)&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-custom-element-name-validation-and-fix-bug-in-element-name-validation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-custom-element-name-validation-and-fix-bug-in-element-name-validation&quot;&gt;LibWeb: Implement custom element name validation and fix bug in element name validation&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18005&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18005&lt;/a&gt; by &lt;a href=&quot;https://github.com/srikavin&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@srikavin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now &lt;a href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;validate the name of custom elements&lt;/a&gt; according to the spec so that valid names create an &lt;code&gt;HTMLElement&lt;/code&gt; while invalid names create an &lt;code&gt;HTMLUnknownElement&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/9142257/227149092-aaa3fed1-9f2c-4ad0-ab14-15b04843a385.png&quot; alt=&quot;Demo of document.createElement() with valid and invalid element names in Ladybird&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-force-relayout-whole-page-on-programmatic-scroll&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-force-relayout-whole-page-on-programmatic-scroll&quot;&gt;LibWeb: Don&#39;t force relayout whole page on programmatic scroll&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18051&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18051&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements two optimizations for page scrolling:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Reuse the layout tree if it is already up-to-date instead of always forcing a layout&lt;/li&gt;&lt;li&gt;Skip this completely when scrolling to (0, 0), which is always known to be a valid position and doesn&#39;t require an upfront layout update&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Note that JS-invoked page scroll currently uses a different code path, so we&#39;re possibly missing out on this optimization in some places :^)&lt;/p&gt;&lt;h2 id=&quot;ladybird%3A-enable-and-document-build-and-debug-steps-with-xcode&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%3A-enable-and-document-build-and-debug-steps-with-xcode&quot;&gt;Ladybird: Enable and document build and debug steps with Xcode&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18052&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18052&lt;/a&gt; by &lt;a href=&quot;https://github.com/ADKaster&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@ADKaster&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I&#39;m not an Xcode user myself, but it&#39;s always nice to have proper documentation for more development platforms! Now if someone could document the steps to debug a WebContent process with gdb… 😅&lt;/p&gt;&lt;h2 id=&quot;browser%2Bladybird%3A-show-current-zoom-level-in-the-view-menu&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#browser%2Bladybird%3A-show-current-zoom-level-in-the-view-menu&quot;&gt;Browser+Ladybird: Show current zoom level in the view menu&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18054&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18054&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now tell you the current zoom level in both Browser and Ladybird, which you previously had to estimate yourself! The zoom actions have moved into a submenu for that to happen:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/227797522-365f87f9-ae86-4855-9889-708c35acdd76.png&quot; alt=&quot;Zoom menu in Browser showing 120% zoom&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/11597044/227797586-0968f9e8-fca0-4013-ae78-495a4256ea95.png&quot; alt=&quot;Zoom menu in Ladybird showing 140% zoom&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-stop-returning-the-left-padding-for-resolved-padding-bottom&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-stop-returning-the-left-padding-for-resolved-padding-bottom&quot;&gt;LibWeb: Stop returning the left padding for resolved padding-bottom&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18060&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18060&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I accidentally broke this 8 months ago and nobody noticed. 😅&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I&#39;m not sure how we managed to do &lt;em&gt;that&lt;/em&gt; but thanks for the fix Sam!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-fix-condition-to-early-return-in-intrinsic-sizing-for-tfc&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-condition-to-early-return-in-intrinsic-sizing-for-tfc&quot;&gt;LibWeb: Fix condition to early return in intrinsic sizing for TFC&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18064&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18064&lt;/a&gt; by &lt;a href=&quot;https://github.com/kalenikaliaksandr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kalenikaliaksandr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes an incorrect &lt;code&gt;auto&lt;/code&gt; height of a &lt;em&gt;Table Formatting Context&lt;/em&gt; (TFC).&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-handle-flex-column-items-with-auto-height-that-depends-on-auto-width&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-handle-flex-column-items-with-auto-height-that-depends-on-auto-width&quot;&gt;LibWeb: Handle flex column items with auto height that depends on auto width&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18066&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18066&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Layout is like magic to me, so I&#39;ll trust you to understand this from the title alone :^)&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-clamp-fit-content-widths-in-flex-layout-to-min%2Fmax-width&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-clamp-fit-content-widths-in-flex-layout-to-min%2Fmax-width&quot;&gt;LibWeb: Clamp fit-content widths in flex layout to min/max-width&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18072&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18072&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;More layout magic:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In situations where we need a width to calculate the intrinsic height of a flex item, we use the fit-content width as a stand-in. However, we also need to clamp it to any min-width and max-width properties present.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;tests%2Flibweb%3A-add-layout-test-for-layout-fix-in-pr-%2315780&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#tests%2Flibweb%3A-add-layout-test-for-layout-fix-in-pr-%2315780&quot;&gt;Tests/LibWeb: Add layout test for layout fix in PR #15780&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18074&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18074&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;More tests = more good, even when the fix was from five months ago!&lt;/p&gt;&lt;h2 id=&quot;libweb%2Blibwasm%3A-implement-and-use-the-%22reset-the-memory-buffer%22-steps&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%2Blibwasm%3A-implement-and-use-the-%22reset-the-memory-buffer%22-steps&quot;&gt;LibWeb+LibWasm: Implement and use the &amp;quot;reset the Memory buffer&amp;quot; steps&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18079&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18079&lt;/a&gt; by &lt;a href=&quot;https://github.com/alimpfard&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@alimpfard&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements &lt;a href=&quot;https://webassembly.github.io/spec/js-api/#reset-the-memory-buffer&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;reset the Memory buffer&lt;/em&gt;&lt;/a&gt; from the WebAssembly JS API spec, which is part of our &lt;a href=&quot;https://github.com/SerenityOS/serenity/issues/17993&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;checklist of features required for the Ruffle Flash Player emulator&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;ladybird%2Bbrowser%3A-add-(reset)-zoom-level-button-to-toolbar&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%2Bbrowser%3A-add-(reset)-zoom-level-button-to-toolbar&quot;&gt;Ladybird+Browser: Add (reset) zoom level button to toolbar&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18080&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18080&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Similar to Firefox we also show a button to quickly reset the current zoom level next to the URL bar when it is not 100%.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-parse-element.style-url-functions-relative-to-the-document&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-parse-element.style-url-functions-relative-to-the-document&quot;&gt;LibWeb: Parse Element.style url functions relative to the document&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18081&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18081&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;url()&lt;/code&gt;s in CSS applied via &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLElement.style&lt;/code&gt;&lt;/a&gt; are parsed now relative to that element&#39;s document URL.&lt;/p&gt;&lt;p&gt;This fixes the images on Steam&#39;s store carousel, for example:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/228395125-95711ee0-2af4-4376-8148-e393fecb49f3.png&quot; alt=&quot;store.steampowered.com in Ladybird, before the fix&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/25595356/228395140-1ed1bf2c-b012-4ba7-baf6-d4105b0ce619.png&quot; alt=&quot;store.steampowered.com in Ladybird, after the fix&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-resolve-percentage-vertical-align-values-against-line-height&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-resolve-percentage-vertical-align-values-against-line-height&quot;&gt;LibWeb: Resolve percentage vertical-align values against line-height&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18084&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18084&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Even more&lt;/em&gt; layout fixes. Andreas really did a lot of that!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-htmlanchorelement.text-and-.referrerpolicy-properties&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-htmlanchorelement.text-and-.referrerpolicy-properties&quot;&gt;LibWeb: Implement HTMLAnchorElement.text and .referrerPolicy properties&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18086&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18086&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement/referrerPolicy&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;HTMLAnchorElement.referrerPolicy&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;HTMLAnchorElement.text&lt;/code&gt;, an alias for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Node.textContent&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;However we don&#39;t use the value of &lt;code&gt;referrerPolicy&lt;/code&gt; yet when following the link.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-crc2d.imagesmoothingenabled&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-implement-crc2d.imagesmoothingenabled&quot;&gt;LibWeb: Implement CRC2D.imageSmoothingEnabled&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18088&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18088&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CanvasRenderingContext2D.imageSmoothingEnabled&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CanvasRenderingContext2D.imageSmoothingQuality&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This means that pages can now choose whether we use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms#Bilinear_interpolation&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;bilinear interpolation&lt;/em&gt;&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms#Nearest-neighbor_interpolation&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;nearest-neighbor interpolation&lt;/em&gt;&lt;/a&gt; (i.e. pixelated) when painting scaled images on a canvas. Previously &lt;em&gt;bilinear interpolation&lt;/em&gt; was always used, matching the default of &lt;code&gt;imageSmoothingEnabled = true&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Note that &lt;code&gt;imageSmoothingQuality&lt;/code&gt; is not actually honored as LibGfx doesn&#39;t support this concept yet.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/5954907/228608469-049b6f76-3acd-4908-acda-219ee2c17f70.png&quot; alt=&quot;MDN demo of imageSmoothingEnabled in Ladybird&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-remove-redundant-invocation-of-children-changed-in-htmlparser&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-remove-redundant-invocation-of-children-changed-in-htmlparser&quot;&gt;LibWeb: Remove redundant invocation of children changed in HTMLParser&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18098&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18098&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;A simple fix to avoid doing duplicate work, one of the easiest ways to make code faster :^)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Setting the &lt;code&gt;data&lt;/code&gt; of a text node already triggers &lt;em&gt;children changed&lt;/em&gt; per spec, so there&#39;s no need for an explicit call.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-re-sort-stylesheetlist-on-every-new-sheet-insertion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-re-sort-stylesheetlist-on-every-new-sheet-insertion&quot;&gt;LibWeb: Don&#39;t re-sort StyleSheetList on every new sheet insertion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18099&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18099&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another performance optimization:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This was causing a huge slowdown when loading some pages with weirdly huge number of style sheets. For example, &lt;code&gt;amazon.com&lt;/code&gt; has over 200 style elements, which meant we had to resort the &lt;code&gt;StyleSheetList&lt;/code&gt; 200 times. Instead of sorting, we now look for the correct insertion point when adding new style sheets, and we start the search from the end, which is where style sheets are typically added in the vast majority of cases.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And the result:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This removes a 600ms time sink when loading Amazon on my machine! :^)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Editor&#39;s note: Amazon really needs to get its websites under control. Andreas also shared the following screenshot earlier:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20231220103615if_/https://cdn.discordapp.com/attachments/845200384720109639/1092101282715664484/image.png&quot; alt=&quot;Browser status bar saying &#39;www.audible.co.uk is waiting on 274 resources&#39;&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-actually-visit-rules-and-media-queries-in-imported-style-sheets&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-actually-visit-rules-and-media-queries-in-imported-style-sheets&quot;&gt;LibWeb: Actually visit rules and media queries in imported style sheets&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18101&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18101&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This ensures that we also consider &lt;code&gt;@media&lt;/code&gt; queries in style sheets loaded via CSS &lt;code&gt;@import&lt;/code&gt;, which previously wouldn&#39;t happen due to a bogus getter:&lt;/p&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;has_import_result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;m_style_sheet&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;libweb%3A-split-calculatedstylevalue-out-of-stylevalue.%7Bh%2Ccpp%7D&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-split-calculatedstylevalue-out-of-stylevalue.%7Bh%2Ccpp%7D&quot;&gt;LibWeb: Split CalculatedStyleValue out of StyleValue.{h,cpp}&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18104&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18104&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another refactor of some CSS machinery, aimed at reducing dependency cycles and improving build times 🚀🚀🚀&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-add-fast-path-to-value%3A%3Ato_u32()-if-value-is-a-positive-i32&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libjs%3A-add-fast-path-to-value%3A%3Ato_u32()-if-value-is-a-positive-i32&quot;&gt;LibJS: Add fast path to Value::to_u32() if Value is a positive i32&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18105&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18105&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This change nicely demonstrates the kind of low hanging performance fruit we still have in LibJS!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;6.6% speed-up on Kraken&#39;s stanford-crypto-aes subtest. :^)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is the entire diff:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/Userland/Libraries/LibJS/Runtime/Value.cpp&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/Userland/Libraries/LibJS/Runtime/Value.cpp&lt;/span&gt;
@@ -947,6 +947,10 @@ ThrowCompletionOr&amp;lt;i32&gt; Value::to_i32(VM&amp;amp; vm) const
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;// 7.1.7 ToUint32 ( argument ), https://tc39.es/ecma262/#sec-touint32
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;ThrowCompletionOr&amp;lt;u32&gt; Value::to_u32(VM&amp;amp; vm) const
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;{
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    // OPTIMIZATION: If this value is encoded as a positive i32, return it directly.
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    if (is_int32() &amp;amp;&amp;amp; as_i32() &gt;= 0)
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        return as_i32();
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    // 1. Let number be ? ToNumber(argument).
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    double number = TRY(to_number(vm)).as_double();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;libweb%3A-begin-implementing-the-streams-api&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-begin-implementing-the-streams-api&quot;&gt;LibWeb: Begin implementing the Streams API&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18109&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18109&lt;/a&gt; by &lt;a href=&quot;https://github.com/mattco98&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@mattco98&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;With the exception of an &lt;a href=&quot;https://github.com/SerenityOS/serenity/commit/87654f5b513165c9faf8863d3a9332aa2da7c0ac&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;empty &lt;code&gt;ReadableStream&lt;/code&gt; interface&lt;/a&gt;, no one had done any work on implementing the Streams API yet — until now!&lt;/p&gt;&lt;p&gt;This implements the following APIs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ReadableStream&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStream()&lt;/code&gt;&lt;/a&gt; constructor&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/locked&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStream.locked&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/cancel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStream.cancel()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/getReader&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStream.getReader()&lt;/code&gt;&lt;/a&gt;`&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ReadableStreamDefaultController&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController/desiredSize&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultController.desiredSize&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController/close&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultController.close()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController/enqueue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultController.enqueue()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController/error&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultController.error()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ReadableStreamDefaultReader&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/ReadableStreamDefaultReader&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultReader()&lt;/code&gt;&lt;/a&gt; constructor&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/read&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultReader.read()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/releaseLock&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultReader.releaseLock()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/cancel&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ReadableStreamDefaultReader.cancel()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With those we can already run the &lt;a href=&quot;https://mdn.github.io/dom-examples/streams/simple-random-stream/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;Simple random stream&lt;/em&gt;&lt;/a&gt; example from MDN!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20231220103623if_/https://cdn.discordapp.com/attachments/830525031720943627/1092143613892370482/image.png&quot; alt=&quot;Simple random stream demo side by side in Firefox and Ladybird&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;ladybird%2Bbrowser%3A-add-tooltip-to-reset-zoom-level-button&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#ladybird%2Bbrowser%3A-add-tooltip-to-reset-zoom-level-button&quot;&gt;Ladybird+Browser: Add tooltip to reset zoom level button&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18119&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18119&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;A follow-up to the previous UI changes around zooming to make the new button&#39;s purpose more clear.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-churn-html%3A%3Aeventloop-while-in-microtask-checkpoint&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-churn-html%3A%3Aeventloop-while-in-microtask-checkpoint&quot;&gt;LibWeb: Don&#39;t churn HTML::EventLoop while in microtask checkpoint&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18125&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18125&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This was the reason my Serenity VM&#39;s CPU was spinning at 100% for the entirety of the March update video (caused by having two GitHub tabs open) 😅&lt;/p&gt;&lt;p&gt;Luckily Andreas looked into it and found the cause: the event loop would constantly re-schedule itself even while we&#39;re &lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;performing a microtask checkpoint&lt;/a&gt;, except queued tasks wouldn&#39;t get processed in that state!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-fix-application-of-intrinsic-aspect-ratio-to-flex-column-items&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-application-of-intrinsic-aspect-ratio-to-flex-column-items&quot;&gt;LibWeb: Fix application of intrinsic aspect ratio to flex column items&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18130&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18130&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Only when using &lt;code&gt;flex-direction: row{,-reverse}&lt;/code&gt; the intrinsic aspect ratio was calculated correctly, not when using a &lt;code&gt;column{,-reverse}&lt;/code&gt; layout.&lt;/p&gt;&lt;p&gt;As many layout-related things this is somewhat vaguely defined in the &lt;a href=&quot;https://www.w3.org/TR/css-flexbox-1/#algo-main-item&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;spec&lt;/a&gt;, so it&#39;s easy to miss:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;…then the &lt;a href=&quot;https://www.w3.org/TR/css-flexbox-1/#flex-base-size&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;flex base size&lt;/a&gt; is calculated from its inner &lt;a href=&quot;https://www.w3.org/TR/css-flexbox-1/#cross-size&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;cross size&lt;/a&gt; and the &lt;a href=&quot;https://www.w3.org/TR/css-flexbox-1/#flex-item&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;flex item&lt;/a&gt;’s intrinsic aspect ratio.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;libweb%2Btests%3A-allow-subdirectories-in-layout_test.sh-and-add-grid-tests&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%2Btests%3A-allow-subdirectories-in-layout_test.sh-and-add-grid-tests&quot;&gt;LibWeb+Tests: Allow subdirectories in layout_test.sh and add grid tests&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18134&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18134&lt;/a&gt; by &lt;a href=&quot;https://github.com/martinfalisse&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@martinfalisse&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;An initial set of 12 layout tests for CSS grid, which we use to ensure that changes don&#39;t regress layout — a rather complex part of the engine — by accident.&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-fix-parsing-of-mixed-coalesceexpression-and-logical%7Band%2Cor%7Dexpression&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libjs%3A-fix-parsing-of-mixed-coalesceexpression-and-logical%7Band%2Cor%7Dexpression&quot;&gt;LibJS: Fix parsing of mixed &lt;code&gt;CoalesceExpression&lt;/code&gt; and &lt;code&gt;Logical{AND,OR}Expression&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18136&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18136&lt;/a&gt; by &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@linusg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Bugs in the JS parser have mostly been flushed out over the years, but still occur at times; especially when we throw megabytes of JS from the web at it :^)&lt;/p&gt;&lt;p&gt;This specific bug prevented us from parsing an expression like this, which occurred on &lt;code&gt;vscode.dev&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be clear, I&#39;m neither an expert on our JS parser nor do I really enjoy working on it, but I promised Andreas to fix the bug if he&#39;d reduce it, which he did, so here we are. No &lt;a href=&quot;https://github.com/tc39/test262&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test262&lt;/a&gt; tests fixed, sadly 😄&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-fix-a-few-font-scaling-issues&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-fix-a-few-font-scaling-issues&quot;&gt;LibWeb: Fix a few font scaling issues&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18137&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18137&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Even now that fonts scale properly with the CSS pixel mechanism we still occasionally find cases where device pixels are used. This fix corrected the scaling of text shadows and the markers of ordered lists (&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;).&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-don&#39;t-apply-element-inline-style-%26-presentational-hints-to-associated-pseudo-elements&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#libweb%3A-don&#39;t-apply-element-inline-style-%26-presentational-hints-to-associated-pseudo-elements&quot;&gt;LibWeb: Don&#39;t apply element inline style &amp;amp; presentational hints to associated pseudo elements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18144&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18144&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes a bug where inline styles (&lt;code&gt;&amp;lt;foo style=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt;) and presentational hints (e.g. &lt;code&gt;&amp;lt;foo width=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt;) would leak into the element&#39;s pseudo elements (&lt;code&gt;::before&lt;/code&gt;, &lt;code&gt;::after&lt;/code&gt;) by accident — they should only apply to the element itself.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&quot;screenshots&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-2/#screenshots&quot;&gt;Screenshots&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I didn&#39;t do this last week, but the browser engineer in me always gets excited when people share progress in form of a screenshot showing a mildly broken website so I think a section of random screenshots people shared is worth having 🥹&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20230402145045if_/https://pbs.twimg.com/media/FsPl44OWAAAk-La.png&quot; alt=&quot;&#39;Car talk: 1000 YouTube videos!&#39; in Ladybird, with the video player being non-functional&quot; title=&quot;Shared by @awesomekling on Twitter&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230402145128if_/https://pbs.twimg.com/media/FsJcBt7XwAEbefp.png&quot; alt=&quot;Basic version of Gmail in Ladybird&quot; title=&quot;Shared by @awesomekling on Twitter&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230402145434if_/https://pbs.twimg.com/media/FsdlsbNWAAEkHEF?format=png&quot; alt=&quot;Kraken JavaScript Benchmark (In Progress...)&quot; title=&quot;Shared by @awesomekling on Twitter&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20230402145233if_/https://pbs.twimg.com/media/FseciUxacAEMg0Q?format=png&quot; alt=&quot;Kraken JavaScript Benchmark Results&quot; title=&quot;Shared by @awesomekling on Twitter&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20231220103620if_/https://cdn.discordapp.com/attachments/830525031720943627/1090362544616652901/image.png&quot; alt=&quot;SerenityOS Changelog in Ladybird&quot; title=&quot;Shared by @awesomekling on Discord&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20231220103620if_/https://cdn.discordapp.com/attachments/830525031720943627/1090416199902429214/image.png&quot; alt=&quot;steamstat.us in Ladybird&quot; title=&quot;Shared by @Lubrsi on Discord&quot;&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;And that concludes the second post of the &lt;em&gt;This Week in Ladybird&lt;/em&gt; series!&lt;/p&gt;&lt;p&gt;Also check out the &lt;em&gt;SerenityOS update (March 2023)&lt;/em&gt; video if you haven&#39;t done that yet:&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;Ns0oInrkO8w&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>This Week in Ladybird #1</title>
    <link href="https://linus.dev/posts/this-week-in-ladybird-1/" />
    <updated>2023-03-26T00:00:00Z</updated>
    <id>https://linus.dev/posts/this-week-in-ladybird-1/</id>
    <content type="html">&lt;h2 id=&quot;preface&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#preface&quot;&gt;Preface&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Well hello friends! I&#39;ve decided to start a &lt;em&gt;This Week in Ladybird&lt;/em&gt; blog post series, a format you may already know from various other open source projects.&lt;/p&gt;&lt;p&gt;This will be in addition to my (almost-)monthly LibWeb/LibJS/Browser segment in our update videos, where I often have to exclude changes that are a little bit more technical or cannot be demo&#39;d on camera very well.&lt;/p&gt;&lt;p&gt;The &lt;em&gt;plan&lt;/em&gt; is to include all of these here once a week, both as a summarized list and briefly going into detail where I think it&#39;s warranted.&lt;/p&gt;&lt;p&gt;I&#39;ll of course be posting a link to each new post on &lt;a href=&quot;https://serenityos.social/@linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Mastodon&lt;/a&gt; so follow me there if you want to get notified, or subscribe to this site&#39;s &lt;a href=&quot;https://linus.dev/feed.xml&quot;&gt;atom feed&lt;/a&gt; if that&#39;s your thing!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Overview of changes across the Ladybird Browser project in the week from March 20 to March 26 :^)&lt;/p&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In chronological order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-more-length-types-in-svg-width%2Fheight-attributes&quot;&gt;LibWeb: Support more length types in SVG width/height attributes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%2Blibgfx%3A-add-support-for-scalable-and-theme-able-checkboxes-using-sdfs&quot;&gt;LibWeb+LibGfx: Add support for scalable and theme-able checkboxes using SDFs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-implement-indeterminate-checkboxes&quot;&gt;LibWeb: Implement indeterminate checkboxes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-update-spec-numbers-%2F-text-for-the-change-array-by-copy-proposal&quot;&gt;LibJS: Update spec numbers / text for the Change Array by Copy proposal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-the-%3Ascope-pseudo-class&quot;&gt;LibWeb: Support the :scope pseudo class&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-make-the-element.style-setter-work&quot;&gt;LibWeb: Make the Element.style setter work&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-convert-all-tokenized-features-to-a-named-enum&quot;&gt;LibWeb: Convert all tokenized features to a named enum&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-parse-css-identifiers-case-insensitively&quot;&gt;LibWeb: Parse CSS identifiers case-insensitively&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-correct-bugs-in-parsing-of-css-unicode-ranges&quot;&gt;LibWeb: Correct bugs in parsing of CSS unicode-ranges&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-loading-file%3A%2F%2F-urls-via-fetch-(through-resourceloader)&quot;&gt;LibWeb: Support loading file:// URLs via fetch (through ResourceLoader)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-add-workaround-to-restore-cross-origin-stylesheet-functionality&quot;&gt;LibWeb: Add workaround to restore cross-origin stylesheet functionality&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-support-the-yy%7B%2F%2C-%7Dmm%7B%2F%2C-%7Ddd-hh%3Amm-format-for-date&quot;&gt;LibJS: Support the yy{/,-}mm{/,-}dd hh:mm format for Date&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-parsing-%5Bhtmlconstructor%5D-idl-attribute-on-constructors&quot;&gt;LibWeb: Support parsing [HTMLConstructor] IDL attribute on constructors&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-couple-of-small-intl-editorials&quot;&gt;LibJS: Couple of small Intl editorials&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-implement-performance.%7Bmark%2Cclearmarks%2Cgetentries%2Cgetentriesbytype%2Cgetentriesbyname%7D&quot;&gt;LibWeb: Implement performance.{mark,clearMarks,getEntries,getEntriesByType,getEntriesByName}&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-create-the-correct-error-objects-in-xhr%3A%3Ahandle_errors&quot;&gt;LibWeb: Create the correct error objects in XHR::handle_errors&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-run-xml-parser-input-through-encoding-decoder&quot;&gt;LibWeb: Run XML parser input through encoding decoder&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-add-scalable-radio-buttons-(with-theme%2Faccent-color-support)&quot;&gt;LibWeb: Add scalable radio buttons (with theme/accent-color support)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-split-stylevalue.%7Bh%2Ccpp%7D-into-one-set-of-files-per-stylevalue-class&quot;&gt;LibWeb: Split StyleValue.{h,cpp} into one set of files per StyleValue class&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-check-all-conditions-of-radio-button-groups&quot;&gt;LibWeb: Check all conditions of radio button groups&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;libweb%3A-support-more-length-types-in-svg-width%2Fheight-attributes&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-more-length-types-in-svg-width%2Fheight-attributes&quot;&gt;LibWeb: Support more length types in SVG width/height attributes&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17922&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17922&lt;/a&gt; by &lt;a href=&quot;https://github.com/MichielVrinsMichielVrins&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MichielVrins&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;You can now specify the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; of an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/length&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;any of the CSS &lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt; units&lt;/a&gt;, not just pixels.&lt;/p&gt;&lt;h2 id=&quot;libweb%2Blibgfx%3A-add-support-for-scalable-and-theme-able-checkboxes-using-sdfs&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%2Blibgfx%3A-add-support-for-scalable-and-theme-able-checkboxes-using-sdfs&quot;&gt;LibWeb+LibGfx: Add support for scalable and theme-able checkboxes using SDFs&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17933&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17933&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;After &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17444&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;I recently changed checkbox painting&lt;/a&gt; from a bespoke implementation to a few lines of CSS in the user agent stylesheet, it&#39;s now changed back to a C++ implementation using &lt;abbr title=&quot;Signed Distance Field&quot;&gt;SDF&lt;/abbr&gt;s to support seamless scaling and color theming, including via the &lt;code&gt;accent-color&lt;/code&gt; CSS property!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/227041962-776ce7eb-cf4c-4553-95ca-bcf59f50730f.png&quot; alt=&quot;Screenshot of checkboxes in various states, sizes, and colors&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-indeterminate-checkboxes&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-implement-indeterminate-checkboxes&quot;&gt;LibWeb: Implement indeterminate checkboxes&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17940&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17940&lt;/a&gt; by &lt;a href=&quot;https://github.com/srikavin&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@srikavin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Previously we only supported the two obvious checkbox states, now the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;indeterminate&lt;/code&gt; checkbox state&lt;/a&gt; can be rendered as well! This was first implemented on top of the old checkbox style and later adopted by MacDue to use the new style (see above).&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/9142257/226290785-c10760c7-4789-445d-b737-7f0a54877a5e.png&quot; alt=&quot;Screenshot of checkboxes in various states and sizes&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-update-spec-numbers-%2F-text-for-the-change-array-by-copy-proposal&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-update-spec-numbers-%2F-text-for-the-change-array-by-copy-proposal&quot;&gt;LibJS: Update spec numbers / text for the Change Array by Copy proposal&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17943&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17943&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The &lt;em&gt;Change Array by Copy&lt;/em&gt; proposal has been included in the main ECMA-262 spec, so we had to update various spec numbers/links and adopt a few adjustments from the final PR.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-support-the-%3Ascope-pseudo-class&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-the-%3Ascope-pseudo-class&quot;&gt;LibWeb: Support the :scope pseudo class&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17947&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17947&lt;/a&gt; by &lt;a href=&quot;https://github.com/skyrising&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@skyrising&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This PR adds support for the &lt;code&gt;:scope&lt;/code&gt; CSS pseudo class selector, which matches the element on which &lt;code&gt;matches()&lt;/code&gt;, &lt;code&gt;closest()&lt;/code&gt;, &lt;code&gt;querySelector()&lt;/code&gt; or &lt;code&gt;querySelectorAll()&lt;/code&gt; is called. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:scope&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;MDN&lt;/a&gt; has some nice examples.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-make-the-element.style-setter-work&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-make-the-element.style-setter-work&quot;&gt;LibWeb: Make the Element.style setter work&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17949&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17949&lt;/a&gt; by &lt;a href=&quot;https://github.com/skyrising&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@skyrising&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;Element.style&lt;/code&gt; was previously implemented as a readonly property, we now support using it as a setter as well (which is a shorthand for setting &lt;code&gt;Element.style.cssText&lt;/code&gt;):&lt;/p&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;color: rebeccapurple;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cssText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;libweb%3A-convert-all-tokenized-features-to-a-named-enum&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-convert-all-tokenized-features-to-a-named-enum&quot;&gt;LibWeb: Convert all tokenized features to a named enum&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17956&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17956&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Tokenized features&lt;/em&gt; refers to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/open#windowfeatures&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;third parameter of &lt;code&gt;window.open()&lt;/code&gt;&lt;/a&gt;, a comma-separated string of &lt;code&gt;key=value&lt;/code&gt; pairs.&lt;/p&gt;&lt;p&gt;C++ lacks named arguments, so we prefer enums over plain booleans in many cases — I briefly noted this while reviewing a PR and Tim promptly fixed it :^)&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;choose_a_browsing_context(&quot;_blank&quot;sv, true, Web::HTML::ActivateTab::No);
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;choose_a_browsing_context(&quot;_blank&quot;sv, Web::HTML::TokenizedFeature::NoOpener::Yes, Web::HTML::ActivateTab::No);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;libweb%3A-parse-css-identifiers-case-insensitively&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-parse-css-identifiers-case-insensitively&quot;&gt;LibWeb: Parse CSS identifiers case-insensitively&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17958&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17958&lt;/a&gt; by &lt;a href=&quot;https://github.com/implicitfield&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@implicitfield&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now parse CSS tag name selectors so that &lt;code&gt;HTML {}&lt;/code&gt;, &lt;code&gt;Html {}&lt;/code&gt; and &lt;code&gt;html {}&lt;/code&gt; are considered identical and match any casing of &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-correct-bugs-in-parsing-of-css-unicode-ranges&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-correct-bugs-in-parsing-of-css-unicode-ranges&quot;&gt;LibWeb: Correct bugs in parsing of CSS unicode-ranges&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17989&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17989&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;CSS numbers in scientific notation, as well as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;unicode-range&lt;/code&gt;&lt;/a&gt; values, are now parsed &lt;em&gt;even more&lt;/em&gt; correctly than before!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-support-loading-file%3A%2F%2F-urls-via-fetch-(through-resourceloader)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-loading-file%3A%2F%2F-urls-via-fetch-(through-resourceloader)&quot;&gt;LibWeb: Support loading file:// URLs via fetch (through ResourceLoader)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17996&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17996&lt;/a&gt; by &lt;a href=&quot;https://github.com/awesomekling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@awesomekling&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;ResourceLoader&lt;/code&gt; is a (non-standard) layer for making individual HTTP requests, and sits at the very end of our Fetch API implementation. While &lt;a href=&quot;https://fetch.spec.whatwg.org/#scheme-fetch&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Fetch itself does not specify how to load &lt;code&gt;file://&lt;/code&gt; URLs&lt;/a&gt;, &lt;code&gt;ResourceLoader&lt;/code&gt; already supported it so only a little bit of plumbing was needed to make it work for Fetch as well.&lt;/p&gt;&lt;p&gt;This is important as &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17016&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;stylesheet loading was recently implemented on top of Fetch&lt;/a&gt;, thus temporarily breaking local (&lt;code&gt;file://&lt;/code&gt;) stylesheets.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-add-workaround-to-restore-cross-origin-stylesheet-functionality&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-add-workaround-to-restore-cross-origin-stylesheet-functionality&quot;&gt;LibWeb: Add workaround to restore cross-origin stylesheet functionality&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/17997&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#17997&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Another unexpected breakage by the stylesheet loading refactor, this time caused by a spec bug! Big thanks to Luke for &lt;a href=&quot;https://github.com/whatwg/html/issues/9066&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;filing an issue upstream&lt;/a&gt; and providing a workaround :^)&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-support-the-yy%7B%2F%2C-%7Dmm%7B%2F%2C-%7Ddd-hh%3Amm-format-for-date&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-support-the-yy%7B%2F%2C-%7Dmm%7B%2F%2C-%7Ddd-hh%3Amm-format-for-date&quot;&gt;LibJS: Support the yy{/,-}mm{/,-}dd hh:mm format for Date&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18003&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18003&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt; with a &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18011&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;test fix&lt;/a&gt; by &lt;a href=&quot;https://github.com/Hendiadyoin1&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Hendiadyoin1&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;It is not recommended to use any format other than subsets of the &lt;a href=&quot;https://tc39.es/ecma262/#sec-date-time-string-format&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;officially supported &lt;code&gt;YYYY-MM-DDTHH:mm:ss.sssZ&lt;/code&gt;&lt;/a&gt;, but of course the web is gonna web and browsers have to follow suit. In a nutshell, every time we find a website feeding another previously unknown format to &lt;code&gt;Date.parse()&lt;/code&gt;, we &lt;em&gt;have&lt;/em&gt; to support that one as well.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-support-parsing-%5Bhtmlconstructor%5D-idl-attribute-on-constructors&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-support-parsing-%5Bhtmlconstructor%5D-idl-attribute-on-constructors&quot;&gt;LibWeb: Support parsing [HTMLConstructor] IDL attribute on constructors&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18004&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18004&lt;/a&gt; by &lt;a href=&quot;https://github.com/srikavin&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@srikavin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We now can parse the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;[HTMLConstructor]&lt;/code&gt; IDL extended attribute&lt;/a&gt;, but still throw an error when calling the generated function.&lt;/p&gt;&lt;h2 id=&quot;libjs%3A-couple-of-small-intl-editorials&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libjs%3A-couple-of-small-intl-editorials&quot;&gt;LibJS: Couple of small Intl editorials&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18007&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18007&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@trflynn89&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Preparation for a few bigger upstream changes.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-implement-performance.%7Bmark%2Cclearmarks%2Cgetentries%2Cgetentriesbytype%2Cgetentriesbyname%7D&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-implement-performance.%7Bmark%2Cclearmarks%2Cgetentries%2Cgetentriesbytype%2Cgetentriesbyname%7D&quot;&gt;LibWeb: Implement performance.{mark,clearMarks,getEntries,getEntriesByType,getEntriesByName}&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18013&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18013&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Does exactly what it says on the tin! We now support the following functions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;performance.mark()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/clearMarks&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;performance.clearMarks()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntries&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;performance.getEntries()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntriesByType&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;performance.getEntriesByType()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntriesByName&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;performance.getEntriesByName()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is used on YouTube, for example:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/227283354-9bb21f69-4c94-43ba-8ebd-2127f610e24b.png&quot; alt=&quot;Demo of getEntriesByName(), clearMarks(), and getEntries()&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-create-the-correct-error-objects-in-xhr%3A%3Ahandle_errors&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-create-the-correct-error-objects-in-xhr%3A%3Ahandle_errors&quot;&gt;LibWeb: Create the correct error objects in XHR::handle_errors&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18016&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18016&lt;/a&gt; by &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@Lubrsi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes two typos that caused &lt;code&gt;XMLHttpRequest&lt;/code&gt; to return the wrong types of error objects.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-run-xml-parser-input-through-encoding-decoder&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-run-xml-parser-input-through-encoding-decoder&quot;&gt;LibWeb: Run XML parser input through encoding decoder&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18021&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18021&lt;/a&gt; by &lt;a href=&quot;https://github.com/kalenikaliaksandr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@kalenikaliaksandr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This fixes parsing of XML sources that start with a &lt;abbr title=&quot;Byte Order Mark&quot;&gt;BOM&lt;/abbr&gt;, and generally allows us to handle non-UTF-8 encoded XML as well.&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-add-scalable-radio-buttons-(with-theme%2Faccent-color-support)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-add-scalable-radio-buttons-(with-theme%2Faccent-color-support)&quot;&gt;LibWeb: Add scalable radio buttons (with theme/accent-color support)&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18022&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18022&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;In typical MacDue fashion, one painting PR is followed up by a few more! Radio buttons got the same visual treatment as checkboxes:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/227383315-69abc8bf-28b1-4221-afed-d3b4bf946e42.png&quot; alt=&quot;Screenshot of radio buttons before they were customizable&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/11597044/227382893-8230bd02-99b4-4104-b335-57c5d2429ab6.png&quot; alt=&quot;Screenshot of checkboxes and radio buttons in various states and sizes&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-split-stylevalue.%7Bh%2Ccpp%7D-into-one-set-of-files-per-stylevalue-class&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-split-stylevalue.%7Bh%2Ccpp%7D-into-one-set-of-files-per-stylevalue-class&quot;&gt;LibWeb: Split StyleValue.{h,cpp} into one set of files per StyleValue class&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18040&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18040&lt;/a&gt; by &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@AtkinsSJ&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Splitting one massive file into 34 smaller files? What&#39;s not to like!&lt;/p&gt;&lt;h2 id=&quot;libweb%3A-check-all-conditions-of-radio-button-groups&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/this-week-in-ladybird-1/#libweb%3A-check-all-conditions-of-radio-button-groups&quot;&gt;LibWeb: Check all conditions of radio button groups&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/18046&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#18046&lt;/a&gt; by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;@MacDue&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This implements radio button grouping &lt;a href=&quot;https://html.spec.whatwg.org/multipage/input.html#radio-button-group&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;according to the HTML spec&lt;/a&gt;, fixing some issues we previously had: radio buttons unchecking checkboxes with the same &lt;code&gt;name&lt;/code&gt; attribute, unchecking inputs across different forms, and treating no &lt;code&gt;name&lt;/code&gt; attribute as a group.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;And that&#39;s everything from this past week! Let me know if you enjoy this format or have any suggestions, and if you have a relevant PR open that hasn&#39;t been merged yet keep an eye out for next week&#39;s changelog 👀&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Office Hours with Guest: Linus Groh</title>
    <link href="https://www.youtube.com/watch?v=iaj-fLgGcqA" />
    <updated>2023-01-27T00:00:00Z</updated>
    <id>https://www.youtube.com/watch?v=iaj-fLgGcqA</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Road to Working on SerenityOS &amp; Ladybird Full-Time</title>
    <link href="https://linus.dev/posts/road-to-working-on-serenityos-and-ladybird-full-time/" />
    <updated>2023-01-13T00:00:00Z</updated>
    <id>https://linus.dev/posts/road-to-working-on-serenityos-and-ladybird-full-time/</id>
    <content type="html">&lt;p&gt;When I submitted my first pull request to the SerenityOS repo I had no idea where this journey would take me. Since then, almost three years and 3,200+ commits later, I have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Learned C++, and a ton about systems programming in general&lt;/li&gt;&lt;li&gt;Helped building LibJS, our from-scratch JavaScript engine, from the ground up&lt;/li&gt;&lt;li&gt;Become the first appointed project maintainer&lt;/li&gt;&lt;li&gt;Joined TC39 as an Invited Expert to represent LibJS&lt;/li&gt;&lt;li&gt;Made a bunch of friends in our community :^)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can read more about my history in the SerenityOS project in a &lt;a href=&quot;https://linus.dev/posts/my-journey-with-serenityos/&quot;&gt;previous blog post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;All of this I have done on the side, as a hobby project. I truly enjoy leading LibJS development, working on LibWeb, and everything else around it so I didn&#39;t mind spending a bunch of my spare time on it. The only issue here is that spare time is, well, limited. You might see where this is going… :^)&lt;/p&gt;&lt;h2 id=&quot;a-new-chapter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/road-to-working-on-serenityos-and-ladybird-full-time/#a-new-chapter&quot;&gt;A New Chapter&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It was clear this needed to happen eventually so I could keep up with the expectations I set for myself, but today it becomes official: &lt;strong&gt;I have now made it my goal to transition to working on &lt;a href=&quot;https://github.com/SerenityOS/serenity/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;SerenityOS&lt;/a&gt;, &lt;a href=&quot;https://awesomekling.github.io/Ladybird-a-new-cross-platform-browser-project/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Ladybird&lt;/a&gt;, and open source in general full time!&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;As a first step I have reduced the hours at my regular job — beginning with one day a week for open source work, every week :^)&lt;/p&gt;&lt;p&gt;I will still do contributions in my spare time of course, but having dedicated full days allows me to tackle bigger projects and have more uninterrupted time.&lt;/p&gt;&lt;p&gt;It&#39;s not just doing more work for the sake of it, primarily I want to get Ladybird to a point where you &amp;amp; I can use it as an everyday web browser. This will take a lot of time and effort, but I think it&#39;s totally feasible!&lt;/p&gt;&lt;h2 id=&quot;sponsorship&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/road-to-working-on-serenityos-and-ladybird-full-time/#sponsorship&quot;&gt;Sponsorship&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Of course I will need to find other sources of income before I can even think about fully committing, and GitHub sponsors is a big part of that! I joined back in June 2021 so people could support me financially if they wanted to, and while it definitely helped with covering domain costs and purchasing better hardware for making videos, I didn&#39;t want to advertise it much without a larger goal in mind.&lt;/p&gt;&lt;p&gt;Now I have such a goal, and the best way to help me achieving it is sponsorship on &lt;a href=&quot;https://github.com/sponsors/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;GitHub&lt;/a&gt; or &lt;a href=&quot;https://liberapay.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Liberapay&lt;/a&gt;! A huge thanks to everyone already supporting me this way, or considering it now — I couldn&#39;t do the next part of this journey without you ❤️&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Ladybird Browser Year in Review: What We Did in 2022!</title>
    <link href="https://linus.dev/posts/ladybird-browser-year-in-review-2022/" />
    <updated>2022-12-31T00:00:00Z</updated>
    <id>https://linus.dev/posts/ladybird-browser-year-in-review-2022/</id>
    <content type="html">&lt;p&gt;2022 is rapidly coming to an end, and of course we&#39;ve been busy all year round in the SerenityOS project! Especially LibJS and LibWeb — also known as the &lt;em&gt;Ladybird Browser Project&lt;/em&gt; — received a huge amount of work from dozens of contributors; and since they are what I work on and am most familiar with as well, let&#39;s look at some Ladybird highlights from the past year!&lt;/p&gt;&lt;p&gt;If you want to get a summary of the last 12 months of development for the whole SerenityOS project, take a look at the &lt;a href=&quot;https://www.youtube.com/playlist?list=PLMOpZvQB55bfp6ykOLayLqLrjcpv_Sw3P&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;monthly update videos&lt;/a&gt; (30-40 minutes each).&lt;/p&gt;&lt;h2 id=&quot;es-modules-support-in-libjs&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#es-modules-support-in-libjs&quot;&gt;ES Modules support in LibJS&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is from January, but easily one of my favourite LibJS changes of the entire year: &lt;a href=&quot;https://github.com/davidot&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;davidot&lt;/a&gt; single-handedly added support for parsing, loading, and executing ECMAScript modules to LibJS! Actually, not just that, but some additional goodies like top-level &lt;code&gt;await&lt;/code&gt; and dynamic &lt;code&gt;import()&lt;/code&gt; as well — check out &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/11957&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#11957&lt;/a&gt; for a full list :^)&lt;/p&gt;&lt;p&gt;Initially limited to our standalone &lt;code&gt;js&lt;/code&gt; interpreter, this was later &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15275&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;followed up with a PR&lt;/a&gt; from &lt;a href=&quot;https://github.com/networkException&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;networkException&lt;/a&gt;, who implemented support for &lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt; in LibWeb.&lt;/p&gt;&lt;h2 id=&quot;100%2F100-on-acid3&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#100%2F100-on-acid3&quot;&gt;100/100 on Acid3&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Acid3&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Acid3&lt;/a&gt; is a popular, albeit a bit dated, browser test checking the compliance with features from a variety of web standards. I don&#39;t remember how we started chasing the full score, but a lot of people chipped in and we quickly went from 50/100 in February to 100/100 at the end of March, with &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/13310&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Ali going as far as making an entire XML parser&lt;/a&gt; for XHTML support to get us that last point :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20220221214414if_/https://pbs.twimg.com/media/FMJpXDLWUAIWUFj.png&quot; alt=&quot;Acid3 in the SerenityOS Browser, showing a score of 50/100 and rendering quite poorly&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20220831173352if_/https://pbs.twimg.com/media/FPDhEYjWQAUzMfz.png&quot; alt=&quot;Acid3 in the SerenityOS Browser, showing a score of 100/100 and rendering perfectly&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;shadows%2C-gradients%2C-filters&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#shadows%2C-gradients%2C-filters&quot;&gt;Shadows, Gradients, Filters&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I think I still haven&#39;t fully grasped the amount of fancy CSS features that we started supporting in the last year, it&#39;s truly impressive! There&#39;s everything from &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14257&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;antialiased rendering&lt;/a&gt;, to improved &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/12359&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;box&lt;/a&gt; &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14343&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;shadows&lt;/a&gt;, support for &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14564&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;all&lt;/a&gt; &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15870&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;kinds&lt;/a&gt; &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/16043&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/16292&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;gradients&lt;/a&gt;, &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15123&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;various &lt;code&gt;backdrop-filter&lt;/code&gt;s&lt;/a&gt;, and more. Most of this was done by &lt;a href=&quot;https://github.com/MacDue&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;MacDue&lt;/a&gt;!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/174594895-55062788-e2a4-4d22-9053-410c9978a380.png&quot; alt=&quot;Shadow border radius demo rendered by LibWeb&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/11597044/178378834-beaff650-807e-4021-ae99-4b0bc62b9dbf.png&quot; alt=&quot;Linear gradient demo page in the SerenityOS browser&quot;&gt;&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/11597044/188284264-576d68a5-fae8-4300-bc87-153a189ae388.png&quot; alt=&quot;backdrop-filter demo rendered by LibWeb&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;the-beginnings-of-webgl&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#the-beginnings-of-webgl&quot;&gt;The beginnings of WebGL&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Usually we get to watch people when they work on cool projects in SerenityOS, but this one came out of nowhere:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;Lubrsi&amp;gt; my secret project worked out
&amp;lt;Lubrsi&amp;gt; everyone, I present to you, WebGL!
&amp;lt;awesomekling&amp;gt; bruh :poggie:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yeah, &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Luke&lt;/a&gt; just did that. &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14184&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;1.5k line PR&lt;/a&gt;, approval from the folks working on LibGL, and done :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/171981974-9cf9b8b6-deeb-44db-af5b-d8755c43a06f.gif&quot; alt=&quot;WebGL demo running in the SerenityOS Browser, showing two canvas elements with WebGL contexts and LibGL debug output on them&quot;&gt;&lt;/p&gt;&lt;p&gt;Not much has happened since then, but I hear the recent work on adding support for shaders to LibGL might be beneficial 👀&lt;/p&gt;&lt;h2 id=&quot;gc-all-the-things&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#gc-all-the-things&quot;&gt;GC all the things&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This effort was &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14816&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;led by Andreas&lt;/a&gt; who &lt;a href=&quot;https://twitter.com/awesomekling/status/1566922305346977792&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;wanted to do this back when he was working on WebKit&lt;/a&gt;, and now managed to do it in LibWeb instead :^)&lt;/p&gt;&lt;p&gt;It&#39;s all architectural and there&#39;s no screenshot for this one, but it&#39;s one of the nicest fundamental LibWeb changes we&#39;ve had in years so it deserves a mention: basically we got to really embrace LibJS&#39;s garbage collector and replaced a whole bunch of ref-counted implementation classes, each having a GC&#39;d wrapper object, with a single GC&#39;d object where the implementation &lt;em&gt;is&lt;/em&gt; the JS object itself and doesn&#39;t need a wrapper.&lt;/p&gt;&lt;p&gt;This also works for things that aren&#39;t directly exposed to user code, so we can fully avoid the ref-counting cycles there as well.&lt;/p&gt;&lt;h2 id=&quot;css-grid&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#css-grid&quot;&gt;CSS Grid&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Flexbox should be in every web developer&#39;s toolbox by now and we&#39;ve had partial support for a while. Next up on the list is CSS Grid, which &lt;a href=&quot;https://github.com/martinfalisse&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Martin&lt;/a&gt; started &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14996&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;implementing&lt;/a&gt; in August and has continued to work on in the months since, making steady progress!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45781926/198879857-659c9982-fc83-4047-aca2-fde555fdfa77.png&quot; alt=&quot;CSS Grid demo in the SerenityOS Browser&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/45781926/200173458-2b8af82f-4231-433b-b588-f6980ae97f5d.png&quot; alt=&quot;CSS Grid demo in the SerenityOS Browser&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;ladybird%3A-a-new-cross-platform-browser-project&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#ladybird%3A-a-new-cross-platform-browser-project&quot;&gt;Ladybird: A new cross-platform browser project&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;…was the title of &lt;a href=&quot;https://awesomekling.github.io/Ladybird-a-new-cross-platform-browser-project/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Andreas&#39; blog post&lt;/a&gt; officially announcing the Ladybird browser project in September, but the journey started earlier! Initially in mid-2019 with LibHTML, the earliest iteration of what is now LibWeb; but also the &lt;em&gt;cross-browser&lt;/em&gt; aspect of it, which is all thanks to &lt;a href=&quot;https://github.com/Dexesttp&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Dexesttp&lt;/a&gt; who started porting LibWeb to Linux (and, by extension, other Unix-like platforms — still no Windows port) in March, resulting in a &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/13473&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;cross-platform, LibWeb-powered &lt;code&gt;headless-browser&lt;/code&gt; utility&lt;/a&gt; that would later &lt;a href=&quot;https://www.youtube.com/watch?v=X38MTKHt3_I&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;serve as the baseline for the Ladybird Qt GUI&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/5400651/161438150-8038e232-9a35-4bba-9bb7-10a199de96fa.png&quot; alt=&quot;Acid3 web page screenshot created by the headless-browser program running on Linux&quot;&gt;&lt;/p&gt;&lt;p&gt;Since then, development of Ladybird as a cross-platform browser has of course led to better abstractions of platform-specific code in LibWeb, and even though it was a standalone project for a while, &lt;a href=&quot;https://github.com/ADKaster&quot; target=&quot;_blank&quot; class=&quot;avatar-link&quot; style=&quot;--avatar-url: url(https://avatars.githubusercontent.com/ADKaster);&quot;&gt;Andrew&lt;/a&gt; went ahead and &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/16583&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;imported it back into the mono repo&lt;/a&gt; after we broke the build with upstream API changes a few dozen times.&lt;/p&gt;&lt;h2 id=&quot;webdriver-and-steps-towards-running-wpt&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#webdriver-and-steps-towards-running-wpt&quot;&gt;WebDriver and steps towards running WPT&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://w3c.github.io/webdriver&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;WebDriver&lt;/a&gt; is an HTTP-driven interface for automated control of web browsers, and integral to running and processing results of test suites such as the &lt;a href=&quot;https://web-platform-tests.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Web Platform Tests&lt;/a&gt; in an automated fashion.&lt;/p&gt;&lt;p&gt;Implementing WebDriver support in LibWeb and both the LibGUI and Qt browser GUIs involved a whole bunch of people: we had an &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/13515&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;initial PR&lt;/a&gt; from &lt;a href=&quot;https://github.com/Orphis&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Orphis&lt;/a&gt; back in May, which &lt;a href=&quot;https://github.com/AtkinsSJ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Sam&lt;/a&gt; then &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15504&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;revived and finished&lt;/a&gt; in October, followed by &lt;a href=&quot;https://github.com/SerenityOS/serenity/issues/15551&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;implementing most of the endpoints&lt;/a&gt; with lots of help from &lt;a href=&quot;https://github.com/TobyAsE&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;TobyAsE&lt;/a&gt;, and finally &lt;a href=&quot;https://github.com/SerenityOS/ladybird/pull/126&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;integrating it with Ladybird&lt;/a&gt; by &lt;a href=&quot;https://github.com/trflynn89&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Tim&lt;/a&gt;, as well as lots of tweaks in the initial architecture and code structure to make things fully work and allow sharing most code between different embedders of LibWeb.&lt;/p&gt;&lt;p&gt;There&#39;s still a few things missing, but overall this progressed nicely and allows any WebDriver client to leverage this new functionality!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/19366641/200094044-c8f2cce3-7e83-465d-9c3b-24ebc32aa6fe.png&quot; alt=&quot;Two windows of the SerenityOS Browser, one showing a &#39;This Browser window is controlled by WebDriver&#39; banner&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/5600524/201725158-7b27a883-f7c5-400e-9a7f-910fc416590d.webm&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;With lots and lots of hacks &lt;a href=&quot;https://twitter.linus.dev/1582644753661038592&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;I already made the Browser run a single test via WPT&lt;/a&gt;, but running the entire suite needs more work. Next year!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://twitter.linus.dev/img/GcvQERem6A.jpeg&quot; alt=&quot;Math.max WPT tests loaded in the SerenityOS Browser via the wpt utility&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;libjs-representation-at-tc39&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#libjs-representation-at-tc39&quot;&gt;LibJS representation at TC39&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Back in April &lt;a href=&quot;https://github.com/littledan&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;littledan&lt;/a&gt; reached out and asked if we&#39;re interested in participating in &lt;a href=&quot;https://tc39.es&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;TC39&lt;/a&gt;, and of course we were! Fast forward to November and &lt;a href=&quot;https://twitter.linus.dev/1588534430385471491&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;I became an Invited Expert&lt;/a&gt; to represent the LibJS engine, and even managed to travel to A Coruña for their most recent in-person meeting.&lt;/p&gt;&lt;p&gt;Attending for the first time definitely had something surreal to it as I didn&#39;t know anything about JS engines before starting to work on ours less than three years ago, but I&#39;m super proud that we got to this point!&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20221228161152if_/https://pbs.twimg.com/media/Fi1-ICJXgAYwXzr?format=jpg&amp;name=large&quot; alt=&quot;TC39 delegates at two tables in a restaurant&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;bring-up-work-on-complex-web-apps&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#bring-up-work-on-complex-web-apps&quot;&gt;Bring-up work on complex web apps&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Developing unrelated features in isolation only gets you so far, trying to get some real websites to load is when you truly see how far along the browser has (or hasn&#39;t) come. We&#39;ve seen support for many major websites mature over the year (source: Andreas&#39; Twitter feed 🐦), but what&#39;s even more impressive is when web &lt;em&gt;apps&lt;/em&gt; start to load as those are usually vastly more complex in every category imaginable!&lt;/p&gt;&lt;p&gt;One of those was improving support for Discord, our main communication hub, initially done by Andreas as a video series earlier in the year. More recently, Luke has done a few as well, and I think we could call it a series at this point!&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15835&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;LibWeb: Implement enough to make YouTube load :^)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15964&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;LibWeb: Implement enough features to load and type into a Google Doc :^)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/16400&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;LibJS+LibWeb: Implement enough to load Discord messages and send GIFs :^)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/198657867-2b0c7401-94c0-4c97-a6f0-02f9543a7a3b.png&quot; alt=&quot;The YouTube home page loaded in the Ladybird browser&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/25595356/200202657-13c61721-e8d8-4f58-9478-2d03fd8d458a.mp4&quot;&gt;&lt;/video&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/25595356/206780719-8fe44941-3f89-45b3-8bd2-3132e3777bd7.png&quot; alt=&quot;The #browser channel of the SerenityOS Discord server loaded in the Ladybird browser&quot;&gt;&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/25595356/206780759-22ce8f16-198a-4884-9af9-8067c61f5f25.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;Obviously all of these are far from fully working, but it&#39;s exciting to see that bad performance is &lt;em&gt;slowly&lt;/em&gt; (&lt;img src=&quot;https://cdn.discordapp.com/emojis/933441581258768394.png&quot; width=&quot;22&quot; style=&quot;position: relative; top: 2px;&quot; title=&quot;:yakkie:&quot; alt=&quot;:yakkie: emote from the SerenityOS Discord server&quot;&gt;) replacing missing APIs as one of our biggest issues!&lt;/p&gt;&lt;h2 id=&quot;fetch-api&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#fetch-api&quot;&gt;Fetch API&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I implemented &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1706&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;support for &lt;code&gt;Promise&lt;/code&gt; in LibJS&lt;/a&gt; back in 2020-2021, which is the foundation for many modern Web APIs, such as &lt;code&gt;fetch()&lt;/code&gt;. Funnily enough I also &lt;a href=&quot;https://twitter.linus.dev/1247993638795259905&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;predicted that I would eventually implement that&lt;/a&gt;, but for a while nothing happened beyond a few failed &lt;img src=&quot;https://cdn.discordapp.com/emojis/873672505309679758.png&quot; width=&quot;22&quot; style=&quot;position: relative; top: 2px;&quot; title=&quot;:yakbait:&quot; alt=&quot;:yakbait: emote from the SerenityOS Discord server&quot;&gt; attempts.&lt;/p&gt;&lt;p&gt;My website was (and still is) using fetch for dynamically getting information from the GitHub API though, and the only way of getting that to work in Ladybird was to implement &lt;code&gt;fetch()&lt;/code&gt; so I &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14568&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;started doing that&lt;/a&gt; (yes, this is how a surprising number of things get implemented). It took a few months working on infrastructure and the JS API on and off, but eventually I &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/15795&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;arrived at a working &lt;code&gt;fetch()&lt;/code&gt; method&lt;/a&gt; :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://web.archive.org/web/20221231160415if_/https://media.discordapp.net/attachments/830525031720943627/1026767329213820968/unknown.png&quot; alt=&quot;Fetch demo in the Ladybird Browser JS console&quot;&gt; &lt;img src=&quot;https://web.archive.org/web/20221231160415if_/https://media.discordapp.net/attachments/830525031720943627/1034571445994541086/Screenshot_from_2022-10-25_21-50-13.png&quot; alt=&quot;Fetch scheme-about WPT page in the Ladybird Browser, 7/7 passing&quot;&gt;&lt;/p&gt;&lt;p&gt;It&#39;s not &lt;em&gt;completely&lt;/em&gt; to spec as we don&#39;t have the &lt;a href=&quot;https://streams.spec.whatwg.org/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Streams&lt;/a&gt; standard implemented, but the foundations are solid and already used in other places (navigation, XHR) as well, as intended by those specs. I&#39;m super happy that I managed to implement such a fundamental part of the modern web in LibWeb at last, but I also have to give some credit to Luke for their thorough testing and bug fixes!&lt;/p&gt;&lt;h2 id=&quot;css-pixels&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#css-pixels&quot;&gt;CSS Pixels&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This one &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/16514&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;is still in progress&lt;/a&gt;, but I&#39;m eagerly looking forward to it as I&#39;m writing this on a HiDPI laptop which makes websites look tiny compared to the window frame with our old assumption of &lt;em&gt;&lt;code&gt;1px&lt;/code&gt; in CSS is one pixel on the display&lt;/em&gt;:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/ladybird-browser-year-in-review-2022/ladybird-hidpi-1x.png&quot; alt=&quot;Screenshot of example.com in the Ladybird Browser on a HiDPI display without adjusted scaling&quot;&gt; &lt;img src=&quot;https://linus.dev/images/posts/ladybird-browser-year-in-review-2022/ladybird-hidpi-2x.png&quot; alt=&quot;Screenshot of example.com in the Ladybird Browser on a HiDPI display with plain 2x scaling&quot;&gt;&lt;/p&gt;&lt;p&gt;Sam, our local CSS expert, has gone and converted the entire LibWeb CSS layer to a new &lt;em&gt;CSS Pixel&lt;/em&gt; unit, which then converts to physical pixels only when rendering. This leads to much nicer results than just 2x scaling a website rendered with the old approach, and has probably been supported by the browser you&#39;re reading this in for years. Soon also coming to a Ladybird Browser near you :^)&lt;/p&gt;&lt;h2 id=&quot;2023&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/ladybird-browser-year-in-review-2022/#2023&quot;&gt;2023&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Of course we&#39;ll continue working on new Web APIs, performance, layout, rendering, and everything else there is to making a browser! As usual there&#39;s no roadmap so I can&#39;t tell you what exactly will and won&#39;t be implemented, but I&#39;m excited to see what everyone decides to hack on next year :^)&lt;/p&gt;&lt;p&gt;If you want to help out with development definitely join the &lt;a href=&quot;https://discord.gg/serenityos&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Discord server&lt;/a&gt;, and for semi-regular updates follow me on &lt;a href=&quot;https://serenityos.social/@linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Mastodon&lt;/a&gt;. You can also &lt;a href=&quot;https://github.com/sponsors/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;sponsor me on GitHub&lt;/a&gt; so that I can do more of this!&lt;/p&gt;&lt;p&gt;Happy new year! 🎉&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>SerenityOS Office Hours with Guest: Linus Groh</title>
    <link href="https://www.youtube.com/watch?v=8dDHV9DqZh4" />
    <updated>2022-09-02T00:00:00Z</updated>
    <id>https://www.youtube.com/watch?v=8dDHV9DqZh4</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Trying Real™ Websites in the SerenityOS Browser</title>
    <link href="https://linus.dev/posts/trying-real-websites-in-the-serenityos-browser/" />
    <updated>2022-07-07T00:00:00Z</updated>
    <id>https://linus.dev/posts/trying-real-websites-in-the-serenityos-browser/</id>
    <content type="html">&lt;p&gt;Well hello friends! After the recent release of &lt;a href=&quot;https://github.com/awesomekling/ladybird/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;em&gt;Ladybird&lt;/em&gt;&lt;/a&gt;, a prototype browser GUI written in C++/Qt using the LibWeb engine, SerenityOS and specifically its browser engine subproject got quite a bit of attention on the internet again — everything from the usual &amp;quot;nooo you can&#39;t build a web browser from scratch&amp;quot; and &amp;quot;y tho&amp;quot;, to excitement, to &lt;em&gt;heise online&lt;/em&gt; &lt;a href=&quot;https://www.heise.de/news/Browser-Nachwuchs-Freie-Engine-tritt-gegen-Googles-De-facto-Monopol-an-7163170.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;suggesting we&#39;re &amp;quot;competing against Google&amp;quot; now&lt;/a&gt;, and while this is not a goal we set for ourselves, it&#39;s a noble one! :^)&lt;/p&gt;&lt;p&gt;I know better than to engage with the negative comments, but it did spark my interest in seeing where we&#39;re actually at right now in terms of usability. Now, being one of the developers, I obviously test websites all the time; but on the other hand browser devs like artifical test cases — colorful boxes, Lorem Ipsum, or the infamous &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/14343&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;border-radius-shadow-egg&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Getting these individual building blocks to work nicely on their own is a lot easier than the result of combining them into a modern website. To make things even more difficult, I&#39;m only going to try landing/login pages, which are usually much more complex than your average &amp;quot;text in a box&amp;quot; pages. Custom fonts, plenty of images, complex layouts, you name it.&lt;/p&gt;&lt;h2 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/trying-real-websites-in-the-serenityos-browser/#setup&quot;&gt;Setup&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I did not prepare for this in any way, I&#39;m simply using a regular build of Serenity, at commit &lt;a href=&quot;https://github.com/SerenityOS/serenity/tree/b3deec0&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;b3deec0&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To compare each result to the expected rendering in a mature browser, I&#39;m running Firefox 101 on Fedora, i.e. the Gecko browser engine 🦎. I did change the user agent string to Serenity&#39;s (&lt;code&gt;Mozilla/5.0 (SerenityOS; x86_64) LibWeb+LibJS/1.0 Browser/1.0&lt;/code&gt;) to make sure I&#39;m being served the same content. Google for example falls back to their old/legacy/simple version of search (not sure what &lt;em&gt;they&lt;/em&gt; call it) when using an unrecognized UA string.&lt;/p&gt;&lt;p&gt;All pages were visited via a VPN connection into the US.&lt;/p&gt;&lt;h2 id=&quot;results&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/trying-real-websites-in-the-serenityos-browser/#results&quot;&gt;Results&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Below are the screenshots of websites I tried: some of them are the most visited websites worldwide, and some are random ones of things I like &amp;amp; use. Enough talking, let&#39;s have a look!&lt;/p&gt;&lt;ul&gt;&lt;/ul&gt;&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/trying-real-websites-in-the-serenityos-browser/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As you might have noticed, I&#39;m rating quite favorably here, always keeping in mind that this is a from-scratch engine that &lt;a href=&quot;https://github.com/SerenityOS/serenity/commit/a67e823&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;just turned three years old&lt;/a&gt;, mostly developed by hobbyists in their spare time; and Andreas :^)&lt;/p&gt;&lt;p&gt;The top two poorly performing features are clearly images not appearing consistently (which could be anything from using methods to load/apply them that we don&#39;t support to using formats we can&#39;t decode) and custom fonts (same reasons). And then of course there&#39;s a plethora of layout issues everywhere.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Of course&lt;/em&gt; most of these are far from perfect, and (hopefully) no one is claiming otherwise. However, just a year ago or so many of these would have crashed the browser or rendered a blank page (especially when JS language support was much weaker), so personally I find the progress we have made so far convincing enough to believe that we&#39;ll have results in a couple of years that are worthy of saying: &amp;quot;they&#39;re the same picture&amp;quot;.&lt;/p&gt;&lt;p&gt;And even before then, I&#39;m looking forward to doing this again in a few months to see what&#39;s changed!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>SerenityOS Office Hours / Q&amp;A with Linus Groh</title>
    <link href="https://www.youtube.com/watch?v=-Unnb55G8RA" />
    <updated>2022-06-03T00:00:00Z</updated>
    <id>https://www.youtube.com/watch?v=-Unnb55G8RA</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>How To Build a Standalone GUI Application for SerenityOS</title>
    <link href="https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/" />
    <updated>2022-05-11T00:00:00Z</updated>
    <id>https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/</id>
    <content type="html">&lt;p&gt;Building a GUI application &lt;em&gt;as part of&lt;/em&gt; SerenityOS is easy, and has been done plenty of times. Just take a look at &lt;a href=&quot;https://github.com/SerenityOS/serenity/tree/master/Userland/Applications&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;any of the existing ones&lt;/a&gt;, or read the (slightly outdated!) &lt;a href=&quot;https://awesomekling.github.io/Introduction-to-SerenityOS-GUI-programming/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Introduction to SerenityOS GUI programming&lt;/a&gt; on Andreas&#39; blog.&lt;/p&gt;&lt;p&gt;But what if you wanted to build one that exists as a &lt;em&gt;standalone&lt;/em&gt; project and is not part of the SerenityOS repository, and instead installed as a port, or not open source in the first place? That&#39;s also possible, but not entirely straightforward — especially when using GML, &lt;a href=&quot;https://man.serenityos.org/man5/GML-Introduction.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Serenity&#39;s very own &lt;em&gt;GUI Markup Language&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;creating-an-example-application&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/#creating-an-example-application&quot;&gt;Creating an Example Application&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The file structure for our example application looks like this:&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;example
├── CMakeLists.txt
└── src
    ├── ExampleWindow.gml
    └── main.cpp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With &lt;code&gt;src/main.cpp&lt;/code&gt; being a very simple Serenity C++ program using LibGUI and LibMain:&lt;/p&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;LibGUI/Application.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;LibGUI/Window.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;LibMain/Main.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;ExampleWindowGML.h&gt;&lt;/span&gt;&lt;/span&gt;

ErrorOr&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;serenity_main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Main&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Arguments arguments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GUI&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;try_create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arguments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GUI&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;try_create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; widget &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;try_set_main_widget&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;GUI&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Widget&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    widget&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load_from_gml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;example_window_gml&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    window&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Example&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    window&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    window&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; app&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the GML in &lt;code&gt;src/ExampleWindow.gml&lt;/code&gt; used to define layouts and widgets:&lt;/p&gt;&lt;pre class=&quot;language-clike&quot;&gt;&lt;code class=&quot;language-clike&quot;&gt;@GUI&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Widget &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fill_with_background_color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    layout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; @GUI&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;VerticalBoxLayout &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        margins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    @GUI&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Button &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Well Hello Friends!&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The result looks like this:&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/how-to-build-a-standalone-gui-application-for-serenityos/libgui-example.png&quot; alt=&quot;Screenshot of the example application&quot; style=&quot;image-rendering: pixelated;&quot;&gt;&lt;/p&gt;&lt;p&gt;Now, how do we turn this into an executable compiled for SerenityOS?&lt;/p&gt;&lt;h2 id=&quot;cmake-shenanigans&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/#cmake-shenanigans&quot;&gt;CMake Shenanigans&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You might have noticed that we include a file in our program which I didn&#39;t mention yet, &lt;code&gt;ExampleWindowGML.h&lt;/code&gt;, which is where the &lt;code&gt;example_window_gml&lt;/code&gt; variable is defined. Generally speaking, the &lt;code&gt;load_from_gml()&lt;/code&gt; function just takes a &lt;code&gt;StringView&lt;/code&gt;, so we could define our GML source in the C++ file and pass it to that function directly.&lt;/p&gt;&lt;p&gt;That&#39;s ugly though and kind of defeats the point of trying to separate the GUI declaration from the program logic. So instead, we let CMake generate a header file from our GML source file!&lt;/p&gt;&lt;p&gt;This is all handled by the SerenityOS build system, which defines the &lt;code&gt;compile_gml()&lt;/code&gt; CMake function, which in turn invokes &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Meta/text-to-cpp-string.sh&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;a simple shell script&lt;/a&gt; outputting code. The result isn&#39;t very exciting, and yet this is a useful and commonly used piece of glue code:&lt;/p&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt; example_window_gml&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt; example_window_gml&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token raw-string string&quot;&gt;R&quot;~~~(@GUI::Widget {
    ...
}
)~~~&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A regular &lt;code&gt;CMakeLists.txt&lt;/code&gt; for a SerenityOS GUI application including this GML-to-C++ transform looks like this:&lt;/p&gt;&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;cmake_minimum_required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_STANDARD&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_STANDARD_REQUIRED&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_EXTENSIONS&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;OFF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;compile_gml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src/ExampleWindow.gml ExampleWindowGML.h example_window_gml&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;SOURCES&lt;/span&gt;
    src/main.cpp
    ExampleWindowGML.h
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;add_executable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example &lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;SOURCES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;target_link_libraries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example gui main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TARGETS Example RUNTIME DESTINATION bin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;In an actual application you&#39;ll also want to add some compiler flags, such as &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/b8c0ebccfd6f1a879e3cf3012b30b51dde31387b/Meta/Lagom/CMakeLists.txt#L64-L74&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;these in the Lagom &lt;code&gt;CMakeLists.txt&lt;/code&gt;&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We set up the project using C++20 as required by the SerenityOS libraries, &amp;quot;compile&amp;quot; the GML into a generated header, specify that in &lt;code&gt;SOURCES&lt;/code&gt; along with &lt;code&gt;main.cpp&lt;/code&gt;, define an executable built from those files, link it against LibGUI and LibMain, and finally install it.&lt;/p&gt;&lt;p&gt;All of this is run by a handful of standard CMake/Ninja build commands, pointing it at Serenity&#39;s generated &lt;code&gt;CMakeToolchain.txt&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; Build
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; Build
cmake &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-GNinja&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-DCMAKE_TOOLCHAIN_FILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${SERENITY_SOURCE_DIR}&lt;/span&gt;/Build/&lt;span class=&quot;token variable&quot;&gt;${SERENITY_ARCH}&lt;/span&gt;/CMakeToolchain.txt&quot;&lt;/span&gt;
ninja
ninja &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far, so good — right? Unfortunately not:&lt;/p&gt;&lt;pre class=&quot;language-error&quot;&gt;&lt;code class=&quot;language-error&quot;&gt;CMake Error at CMakeLists.txt:9 (compile_gml):
  Unknown CMake command &quot;compile_gml&quot;.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;more-cmake-shenanigans&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/#more-cmake-shenanigans&quot;&gt;More CMake Shenanigans&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Within SerenityOS everything is set up so that you don&#39;t have to deal with where and how to make these extra functions available in any regular &lt;code&gt;CMakeLists.txt&lt;/code&gt;, but here we&#39;re on our own.&lt;/p&gt;&lt;p&gt;&lt;code&gt;compile_gml()&lt;/code&gt; is from &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Meta/CMake/code_generators.cmake&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Meta/CMake/code_generators.cmake&lt;/code&gt;&lt;/a&gt;, so let&#39;s &lt;code&gt;include()&lt;/code&gt; that (using the &lt;code&gt;SERENITY_SOURCE_DIR&lt;/code&gt; environment variable found in various other build scripts):&lt;/p&gt;&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;SerenityOS_SOURCE_DIR&lt;/span&gt; $&lt;span class=&quot;token variable&quot;&gt;ENV&lt;/span&gt;{&lt;span class=&quot;token variable&quot;&gt;SERENITY_SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;SerenityOS_SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/Meta/CMake/code_generators.cmake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;SerenityOS_SOURCE_DIR&lt;/code&gt; is a variable referenced by the function itself and is usually &lt;a href=&quot;https://cmake.org/cmake/help/latest/command/project.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;set by CMake based on the &lt;code&gt;project()&lt;/code&gt; name&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Trying again:&lt;/p&gt;&lt;pre class=&quot;language-error&quot;&gt;&lt;code class=&quot;language-error&quot;&gt;CMake Error at /path/to/serenity/Meta/CMake/code_generators.cmake:18 (add_dependencies):
  Cannot add target-level dependencies to non-existent target
  &quot;all_generated&quot;.

  The add_dependencies works for top-level logical targets created by the
  add_executable, add_library, or add_custom_target commands.  If you want to
  add file-level dependencies see the DEPENDS option of the add_custom_target
  and add_custom_command commands.
Call Stack (most recent call first):
  CMakeLists.txt:12 (compile_gml)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is an implementation detail of the build system: all generated files are added to a &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/b8c0ebccfd6f1a879e3cf3012b30b51dde31387b/CMakeLists.txt#L59-L60&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;custom &lt;code&gt;all_generated&lt;/code&gt; target&lt;/a&gt;. Let&#39;s simply define that as well, even though we&#39;re not going to use it:&lt;/p&gt;&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;add_custom_target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;all_generated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we get to a point where the compiler gets involved, but fails with:&lt;/p&gt;&lt;pre class=&quot;language-error&quot;&gt;&lt;code class=&quot;language-error&quot;&gt;FAILED: CMakeFiles/Example.dir/src/main.cpp.o
/path/to/serenity/Toolchain/Local/x86_64/bin/x86_64-pc-serenity-g++ --sysroot=/path/to/serenity/Build/superbuild-x86_64/../x86_64/Root   -std=c++20 -MD -MT CMakeFiles/Example.dir/src/main.cpp.o -MF CMakeFiles/Example.dir/src/main.cpp.o.d -o CMakeFiles/Example.dir/src/main.cpp.o -c /path/to/example/src/main.cpp
/path/to/example/src/main.cpp:4:10: fatal error: ExampleWindowGML.h: No such file or directory
    4 | #include &amp;lt;ExampleWindowGML.h&gt;
      |          ^~~~~~~~~~~~~~~~~~~~
compilation terminated.
ninja: build stopped: subcommand failed.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&#39;s our generated GML header! Let&#39;s check if it actually exists:&lt;/p&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; Build/ExampleWindowGML.h
Build/ExampleWindowGML.h: C source, ASCII text&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So we got that working, but the compiler doesn&#39;t know where to find it. Since the file simply ends up in the build directory, we can add &lt;a href=&quot;https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;CMAKE_CURRENT_BINARY_DIR&lt;/code&gt;&lt;/a&gt; to the include directories (which is also what Serenity&#39;s root &lt;code&gt;CMakeLists.txt&lt;/code&gt; does):&lt;/p&gt;&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include_directories&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CURRENT_BINARY_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And with that it finally builds and installs the executable into Serenity&#39;s &lt;code&gt;Root/usr/local/bin&lt;/code&gt; directory! Let&#39;s confirm:&lt;/p&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${SERENITY_SOURCE_DIR}&lt;/span&gt;/Build/&lt;span class=&quot;token variable&quot;&gt;${SERENITY_ARCH}&lt;/span&gt;/Root/usr/local/bin&quot;&lt;/span&gt;
$ &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; Example
Example: ELF &lt;span class=&quot;token number&quot;&gt;64&lt;/span&gt;-bit LSB pie executable, x86-64, version &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SYSV&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, dynamically linked, interpreter /usr/lib/Loader.so, with debug_info, not stripped
$ objdump &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; Example &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; NEEDED
  NEEDED               libgui.so.serenity
  NEEDED               libm.so
  NEEDED               libgcc_s.so
  NEEDED               libc.so&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;NOTE: LibMain is statically linked.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Once you rebuild the QEMU &lt;code&gt;_disk_image&lt;/code&gt;, the externally built example application will be available within the VM.&lt;/p&gt;&lt;p&gt;For completeness, here is the full working &lt;code&gt;CMakeLists.txt&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;cmake_minimum_required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_STANDARD&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_STANDARD_REQUIRED&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CXX_EXTENSIONS&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;OFF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;SerenityOS_SOURCE_DIR&lt;/span&gt; $&lt;span class=&quot;token variable&quot;&gt;ENV&lt;/span&gt;{&lt;span class=&quot;token variable&quot;&gt;SERENITY_SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;SerenityOS_SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/Meta/CMake/code_generators.cmake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;add_custom_target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;all_generated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;include_directories&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_CURRENT_BINARY_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;compile_gml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src/ExampleWindow.gml ExampleWindowGML.h example_window_gml&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;SOURCES&lt;/span&gt;
    src/main.cpp
    ExampleWindowGML.h
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;add_executable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example &lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;SOURCES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;target_link_libraries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Example gui main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TARGETS Example RUNTIME DESTINATION bin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;By replicating a few things from Serenity&#39;s build system in our own &lt;code&gt;CMakeLists.txt&lt;/code&gt;, we can get a code generator working that was never explicitly intended to be used outside of the project :^)&lt;/p&gt;&lt;p&gt;I haven&#39;t tried, but this should also be applicable to other kinds of code generators in case you need that. And, of course, if you saw anything that could be streamlined here, please let me know!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Running SerenityOS on Bare Metal</title>
    <link href="https://linus.dev/posts/running-serenityos-on-bare-metal/" />
    <updated>2022-02-19T00:00:00Z</updated>
    <id>https://linus.dev/posts/running-serenityos-on-bare-metal/</id>
    <content type="html">&lt;p&gt;In a &lt;a href=&quot;https://linus.dev/posts/my-journey-with-serenityos#the-future&quot;&gt;previous post&lt;/a&gt;, I mentioned:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I haven&#39;t managed to do a full successful boot on bare metal yet though, so that will definitely have to join this list one day.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Well, five months later I finally did it! :^)&lt;/p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;em&gt;View tweet: &lt;a href=&quot;https://twitter.com/linusgroh/status/1486111320181149697&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://twitter.com/linusgroh/status/1486111320181149697&lt;/a&gt;&lt;/em&gt; &lt;a href=&quot;https://twitter.com/linusgroh/status/1486111320181149697&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;This is a post explaining what it took to get there, both as documentation for myself and hopefully to help someone else to get Serenity up and running on their hardware!&lt;/p&gt;&lt;h2 id=&quot;hardware&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#hardware&quot;&gt;Hardware&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;While I also tried running SerenityOS on my laptop in the past (ThinkPad T450S), all serious attempts were on my desktop machine, as the availability of a serial port is highly important for debugging — especially when the kernel panics before even setting up a framebuffer and console for graphical output, and you only end up with a blank screen after booting. The alternative to this is beeping the PC speaker…&lt;/p&gt;&lt;p&gt;This is also the machine I do recording and some development on, so I didn&#39;t want to mess with the Linux partitions and ended up just giving Serenity its own SSD.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Motherboard: &lt;a href=&quot;https://www.msi.com/Motherboard/B350-PC-MATE&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;MSI B350 PC MATE&lt;/a&gt;&lt;/li&gt;&lt;li&gt;CPU: &lt;a href=&quot;https://www.amd.com/en/products/cpu/amd-ryzen-5-1600x&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;AMD Ryzen 5 1600x&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Graphics card: &lt;a href=&quot;https://www.nvidia.com/en-me/geforce/graphics-cards/rtx-2060/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;NVIDIA GeForce RTX 2060&lt;/a&gt;&lt;/li&gt;&lt;li&gt;RAM: 4 x 8 GB DDR4 (Crucial)&lt;/li&gt;&lt;li&gt;Disk: Serenity/Linux each on their own SSD (also Crucial)&lt;/li&gt;&lt;li&gt;Monitor: 27 inch QHD (Dell), connected to the GPU&#39;s HDMI output (CPU has no integrated graphics!)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;setup&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#setup&quot;&gt;Setup&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Note that I won&#39;t explain the general bare metal installation process here, that&#39;s all outlined in &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Documentation/BareMetalInstallation.md&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Documentation/BareMetalInstallation.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I usually keep my local checkout up to date, to get the latest kernel and userspace changes. This can mean that things that used to work suddenly stop working, and another investigation is required. That&#39;s ok though, as things improve for the most part. :^)&lt;/p&gt;&lt;p&gt;A recent example of this is an update to the i8042 initialisation process that would fail on my machine (&lt;code&gt;I8042: Trying to probe for existence of controller failed&lt;/code&gt;), causing the mouse and keyboard to not work. Fixed in &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/12641&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#12641&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the record though: I&#39;m using commit &lt;code&gt;eca8208&lt;/code&gt; as of writing this. I also tested i686 and x86_64, and they both boot.&lt;/p&gt;&lt;h3 id=&quot;building-%26-creating-a-bootable-disk&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#building-%26-creating-a-bootable-disk&quot;&gt;Building &amp;amp; Creating a Bootable Disk&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Basically: apply patches (discussed later), do a regular build, create GRUB disk image, &lt;code&gt;dd&lt;/code&gt; to disk, reboot.&lt;/p&gt;&lt;h3 id=&quot;serial-console&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#serial-console&quot;&gt;Serial Console&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I use an adapter cable that&#39;s permanently connected to the 9-pin &lt;code&gt;JCOM1&lt;/code&gt; connector on the motherboard and has an RS-232 connector on the other end. Then I plug in an RS-232 to USB cable whenever I need to and connect from my laptop by running &lt;code&gt;cu -s 57600 -l /dev/ttyUSB0&lt;/code&gt; in a terminal, where I will then see exactly what QEMU would show on the debug console.&lt;/p&gt;&lt;h3 id=&quot;input-devices&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#input-devices&quot;&gt;Input Devices&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;While trying to get Serenity to boot I already knew I wouldn&#39;t be able to actually use it once successful — USB HID devices aren&#39;t working in the kernel yet. The recommended first step is checking if the BIOS has an option to emulate PS/2 input devices; mine doesn&#39;t, but luckily the computer has a PS/2 port, so I ordered a cheap PS/2 mouse and keyboard. In an ironic twist, both the USB mouse and keyboard start working on Serenity once their PS/2 counterparts are plugged in, so the BIOS &lt;em&gt;does&lt;/em&gt; have some sort of emulation, just doesn&#39;t seem to expose an option for it. I haven&#39;t investigated further.&lt;/p&gt;&lt;h2 id=&quot;required-tweaks&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#required-tweaks&quot;&gt;Required Tweaks&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As mentioned before, I still have to do some small modifications for Serenity to actually boot.&lt;/p&gt;&lt;h3 id=&quot;additional-kernel-options&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#additional-kernel-options&quot;&gt;Additional Kernel Options&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;While all of these could be provided in GRUB on demand, I think it makes sense to hardcode them if the boot is known to fail without them.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;serial_debug&lt;/code&gt;: This should be self explanatory. I boot with serial debug enabled by default for now, but disabling it greatly decreases boot time, so I won&#39;t keep it forever. Can still be enabled manually in GRUB of course.&lt;/li&gt;&lt;li&gt;&lt;code&gt;enable_ioapic=off&lt;/code&gt;: Without this option, I used to get the following panic:&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;0.000 [Kernel]: APICTimer: Using HPET as calibration source
[Kernel]: KERNEL PANIC! :^(
[Kernel]: Unhandled IRQ
[Kernel]: at ./Kernel/Arch/x86/common/Interrupts.cpp:532 in void Kernel::unimp_trap()&lt;/code&gt;&lt;/pre&gt;This was fixed in &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/12182&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#12182&lt;/a&gt;, but still causes the boot to hang indefinitely when &lt;code&gt;on&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;ahci_reset_mode=aggressive&lt;/code&gt;: I wish I tried this option much earlier — many of my previous attempts failed at the stage where it was trying to locate the root file system, with the following panic:&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[init_stage2(1:1)]: ASSERTION FAILED: !m_storage_devices.is_empty()
[init_stage2(1:1)]: ./Kernel/Storage/StorageManagement.cpp:153 in void Kernel::StorageManagement::enumerate_disk_partitions()
[init_stage2(1:1)]: KERNEL PANIC! :^(
[init_stage2(1:1)]: Aborted
[init_stage2(1:1)]: at ./Kernel/Arch/x86/common/CPU.cpp:35 in void abort()
[init_stage2(1:1)]: Kernel + 0x00c39df1  Kernel::__panic(char const*, unsigned int, char const*) +0xf1
[init_stage2(1:1)]: Kernel + 0x00fbb2d9  abort.localalias +0x245
[init_stage2(1:1)]: Kernel + 0x00fbb094  abort.localalias +0x0
[init_stage2(1:1)]: Kernel + 0x011c37f9  Kernel::StorageManagement::enumerate_disk_partitions() [clone .localalias] +0xdf9
[init_stage2(1:1)]: Kernel + 0x011c92c5  Kernel::StorageManagement::initialize(AK::StringView, bool, bool) +0xb5
[init_stage2(1:1)]: Kernel + 0x00f9d4ff  Kernel::init_stage2(void*) +0x11af
[init_stage2(1:1)]: Kernel + 0x00fc3a50  exit_kernel_thread +0x0&lt;/code&gt;&lt;/pre&gt;The different reset mode fixes that!&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So the patch to add all of them to the first GRUB menu entry looks as follows:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/Meta/grub-mbr.cfg&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/Meta/grub-mbr.cfg&lt;/span&gt;
@@ -2,7 +2,7 @@ timeout=1

&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;menuentry &#39;SerenityOS (normal)&#39; {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  root=hd0,1
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;  multiboot /boot/Prekernel root=/dev/hda1
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  multiboot /boot/Prekernel root=/dev/hda1 serial_debug enable_ioapic=off ahci_reset_mode=aggressive
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  module /boot/Kernel
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that in many circumstances you will also need to specify a different value for the &lt;code&gt;root&lt;/code&gt; option, especially in a dual boot environment or when having multiple disks. I simply reordered the connected disks so that Serenity is connected to &lt;code&gt;SATA1&lt;/code&gt; and the default option (&lt;code&gt;root=/dev/hda1&lt;/code&gt;) just works. :^)&lt;/p&gt;&lt;h3 id=&quot;increasing-initial_kmalloc_memory_size&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#increasing-initial_kmalloc_memory_size&quot;&gt;Increasing &lt;code&gt;INITIAL_KMALLOC_MEMORY_SIZE&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;With the default value of 2 MiB, the kernel panics:&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[Kernel]: ASSERTION FAILED: m_has_value
[Kernel]: ././AK/Optional.h:157 in T&amp; AK::Optional&lt;t&gt;::value() &amp; [with T = KmallocGlobalData::ExpansionData]
[Kernel]: KERNEL PANIC! :^(
[Kernel]: Aborted
[Kernel]: at ./Kernel/Arch/x86/common/CPU.cpp:35 in void abort()&lt;/t&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Various kernel changes have been made over time to reduce the amount of initial kmalloc memory that&#39;s needed, but it&#39;s still not enough in my case.&lt;/p&gt;&lt;p&gt;Increasing it to 8 MiB fixes the panic:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/Kernel/Heap/kmalloc.cpp&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/Kernel/Heap/kmalloc.cpp&lt;/span&gt;
@@ -23,7 +23,7 @@ static constexpr size_t CHUNK_SIZE = 32;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;static constexpr size_t CHUNK_SIZE = 64;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;#endif
&lt;/span&gt;
&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;static constexpr size_t INITIAL_KMALLOC_MEMORY_SIZE = 2 * MiB;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;static constexpr size_t INITIAL_KMALLOC_MEMORY_SIZE = 8 * MiB;
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;// Treat the heap as logically separate from .bss
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;__attribute__((section(&quot;.heap&quot;))) static u8 initial_kmalloc_memory[INITIAL_KMALLOC_MEMORY_SIZE];
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;enabling-the-multiboot_video_mode-flag&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#enabling-the-multiboot_video_mode-flag&quot;&gt;Enabling the &lt;code&gt;MULTIBOOT_VIDEO_MODE&lt;/code&gt; Flag&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is needed as I rely on Multiboot (which we use when booting in combination with GRUB) to prepare a framebuffer for the kernel. As far as I understand, QEMU &amp;amp; co. use a different approach, so it&#39;s not enabled by default.&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/Kernel/Prekernel/Arch/x86/multiboot.S&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/Kernel/Prekernel/Arch/x86/multiboot.S&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;@@ -3,7 +3,7 @@&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;.set MULTIBOOT_PAGE_ALIGN,    0x1
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;.set MULTIBOOT_MEMORY_INFO,   0x2
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;.set MULTIBOOT_VIDEO_MODE,    0x4
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;.set multiboot_flags,         MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;.set multiboot_flags,         MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_VIDEO_MODE
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;.set multiboot_checksum,      -(MULTIBOOT_MAGIC + multiboot_flags)
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;.section .multiboot, &quot;a&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;changing-the-windowserver-screen-resolution&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#changing-the-windowserver-screen-resolution&quot;&gt;Changing the WindowServer Screen Resolution&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The default WindowServer screen resolution is 1024x768. However, the framebuffer we ask Multiboot to set up for us has a resolution of 1280x1024 by default:&lt;/p&gt;&lt;pre class=&quot;language-nasm&quot;&gt;&lt;code class=&quot;language-nasm&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; for MULTIBOOT_VIDEO_MODE &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
.long &lt;span class=&quot;token number&quot;&gt;0x00000000&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; mode_type &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
.long &lt;span class=&quot;token number&quot;&gt;1280&lt;/span&gt;          &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
.long &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;          &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; height &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
.long &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;            &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; depth &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While it&#39;s possible to change these values (which would be great, as the monitor resolution is much larger), any attempts to do so resulted in a 640x480 framebuffer. Checking &lt;code&gt;vbeinfo&lt;/code&gt; in a GRUB console revealed that no larger resolution is supported by this particular VBE setup:&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;grub&gt; vbeinfo
List of supported video modes:
Legend: mask/position=red/green/blue/reserved
Adapter `VESA BIOS Extension Video Driver&#39;:
  VBE info:   version: 3.0  OEM software rev: 144.6
              total memory: 16384 KiB
  Ox101  640 x  480 x  8 ( 640)  Paletted
  0x103  800 x  600 x  8 (1024)  Paletted
  Ox105 1024 x  768 x  8 (1024)  Paletted
  Ox107 1280 x 1024 x  8 (1280)  Paletted
  Ox111  640 x  480 x 16 (1280)  Direct color, mask: 5/6/5/0  pos: 11/5/0/0
  Ox112  640 x  480 x 32 (2560)  Direct color, mask: 8/8/8/8  pos: 16/8/0/24
  Ox114  800 x  600 x 16 (2048)  Direct color, mask: 5/6/5/0  pos: 11/5/0/0
  0x115  800 x  600 x 32 (4096)  Direct color, mask: 8/8/8/8  pos: 16/8/0/24
  Ox117 1024 x  768 x 16 (2048)  Direct color, mask: 5/6/5/0  pos: 11/5/0/0
  Ox118 1024 x  768 x 32 (4096)  Direct color, mask: 8/8/8/8  pos: 16/8/0/24
  Ox11a 1280 x 1024 x 16 (2560)  Direct color, mask: 5/6/5/0  pos: 11/5/0/0
  Ox11b 1280 x 1024 x 32 (5120)  Direct color, mask: 8/8/8/8  pos: 16/8/0/24
grub&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Changing the resolution in DisplaySettings doesn&#39;t do anything either (see &lt;a href=&quot;https://github.com/SerenityOS/serenity/issues/4414&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;#4414&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;This situation will likely improve in the future with more native graphics drivers (&lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;we already have one for Intel graphics&lt;/a&gt;), but for now I set the monitor to a 5:4 aspect ratio and apply this patch to make use of the full available resolution:&lt;/p&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/Base/etc/WindowServer.ini&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/Base/etc/WindowServer.ini&lt;/span&gt;
@@ -5,8 +5,8 @@ MainScreen=0
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;Device=/dev/fb0
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;Left=0
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;Top=0
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;Width=1024
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;Height=768
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;Width=1280
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;Height=1024
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;ScaleFactor=1
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;[Fonts]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;demo&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#demo&quot;&gt;Demo&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;With all of these changes, it finally boots! The only major issue I found is that time advances way too quickly — we&#39;d have to adjust it via NTP every now and then. Everything else seems to work just fine, including networking :^)&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;_RL4t6IbWD0&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;h2 id=&quot;thanks!&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/running-serenityos-on-bare-metal/#thanks!&quot;&gt;Thanks!&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;None of this would&#39;ve been possible without Idan, Liav, Luke &amp;amp; all the other folks in the &lt;code&gt;#bare-metal&lt;/code&gt; channel on our Discord server. I needed quite a bit of hand holding to understand the various panics and find workarounds or fixes for them!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Implementing the Temporal Proposal in LibJS</title>
    <link href="https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/" />
    <updated>2021-11-29T00:00:00Z</updated>
    <id>https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/</id>
    <content type="html">&lt;p&gt;Over the past five months we&#39;ve implemented the &lt;a href=&quot;https://github.com/tc39/proposal-temporal/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Temporal stage 3 proposal&lt;/a&gt; in LibJS, the &lt;a href=&quot;https://serenityos.org&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;SerenityOS&lt;/a&gt; JavaScript engine. In this case, &lt;em&gt;we&lt;/em&gt; is &lt;a href=&quot;https://github.com/IdanHo&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Idan&lt;/a&gt;, &lt;a href=&quot;https://github.com/Lubrsi&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Luke&lt;/a&gt;, and &lt;a href=&quot;https://github.com/linusg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;yours truly&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We&#39;re just &lt;a href=&quot;https://github.com/SerenityOS/serenity/issues/8982&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;three functions&lt;/a&gt; (&amp;amp; a bunch of bugfixes 🐛) short of having the minimum implementation completed, so I figured I&#39;d write down some thoughts about the whole process :^)&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://linus.dev/images/posts/implementing-the-temporal-proposal-in-libjs/libjs-temporal.png&quot; alt=&quot;Screenshot of Temporal functionality in LibJS&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;motivation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#motivation&quot;&gt;Motivation&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After finishing my &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/8262&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;rewrite of the &lt;code&gt;Object&lt;/code&gt; implementation&lt;/a&gt; back in June, I was looking for another project to work on. I first discovered the Temporal proposal around the same time; and since we had already experimented with implementing proposals and not just the stage 4 ECMA-262 spec before, I decided that was going to be it.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;commit 8269921212875b75c918bde5d318f0c5de152dac
Author: Linus Groh &amp;lt;mail@linusgroh.de&amp;gt;
Date:   Tue Jul 6 19:14:47 2021 +0100

    LibJS: Add the Temporal namespace object :^)

    Currently empty, but we gotta start somewhere! This is the start of
    implementing the Temporal proposal (currently stage 3).

    I have decided to start a new subdirectory (Runtime/Temporal/) as well
    as a new C++ namespace (JS::Temporal) for this so we don&#39;t have to
    prefix all the files and classes with &amp;quot;Temporal&amp;quot; - there will be a lot.

    https://tc39.es/proposal-temporal/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can&#39;t speak for others, but Idan and Luke joined me shortly after; and it&#39;s been a team effort ever since :^)&lt;/p&gt;&lt;h2 id=&quot;ok%2C-how-much-code%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#ok%2C-how-much-code%3F&quot;&gt;OK, How Much Code?&lt;/a&gt;&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;$ git rev-parse --short HEAD
7a27ecc135
$ scc Userland/Libraries/LibJS/Runtime/Temporal
───────────────────────────────────────────────────────────────────────────────
Language                 Files     Lines   Blanks  Comments     Code Complexity
───────────────────────────────────────────────────────────────────────────────
C Header                    34      2049      343       215     1491          6
C++                         34     17693     3700      5467     8526       1695
───────────────────────────────────────────────────────────────────────────────
Total                       68     19742     4043      5682    10017       1701
───────────────────────────────────────────────────────────────────────────────
Estimated Cost to Develop (organic) $303,647
Estimated Schedule Effort (organic) 8.744090 months
Estimated People Required (organic) 3.085114
───────────────────────────────────────────────────────────────────────────────
Processed 996444 bytes, 0.996 megabytes (SI)
───────────────────────────────────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;what&#39;s-special&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#what&#39;s-special&quot;&gt;What&#39;s Special&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When we started working on Temporal, LibJS was just a little over a year old, and in that year we learned a lot about what works well for us, and what doesn&#39;t.&lt;/p&gt;&lt;p&gt;Here are a couple of things that didn&#39;t follow the status quo:&lt;/p&gt;&lt;h3 id=&quot;code-structure&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#code-structure&quot;&gt;Code Structure&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In LibJS all of the ECMAScript built-in functions and objects reside within the &lt;a href=&quot;https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS/Runtime&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;Runtime/&lt;/code&gt;&lt;/a&gt; directory — &lt;a href=&quot;https://github.com/WebKit/WebKit/tree/main/Source/JavaScriptCore/runtime&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;clearly inspired by JavaScriptCore&lt;/a&gt;, as Andreas worked on WebKit for years. As mentioned in the commit message above, I decided to make a new subdirectory for Temporal specifically, and we ended up doing the same for Intl. In both cases it nicely matches the JS API, as both of them have namespace objects, and avoids polluting the top-level directory with even more files.&lt;/p&gt;&lt;p&gt;JSC did not end up doing the same, FWIW :^)&lt;/p&gt;&lt;p&gt;Additionally, everything is enclosed in a new C++ namespace, &lt;code&gt;JS::Temporal&lt;/code&gt; (we only had &lt;code&gt;JS&lt;/code&gt; so far: &lt;code&gt;JS::Object&lt;/code&gt;, &lt;code&gt;JS::Value&lt;/code&gt;, etc.):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Within the &lt;code&gt;Temporal&lt;/code&gt; namespace, objects are referred to by their name only, e.g. &lt;code&gt;PlainDateTime&lt;/code&gt;. This is clear enough in all cases.&lt;/li&gt;&lt;li&gt;Outside of that but within LibJS, we need to qualify the namespace, e.g. &lt;code&gt;Temporal::PlainDateTime&lt;/code&gt; — just like &lt;code&gt;Temporal.PlainDate&lt;/code&gt; in JS.&lt;/li&gt;&lt;li&gt;Outside of LibJS, e.g. in the &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Userland/Utilities/js.cpp&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;js(1)&lt;/code&gt; REPL&lt;/a&gt;, the full namespace is required, e.g. &lt;code&gt;JS::Temporal::PlainDateTime&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Again, we also did the same for Intl, and would do it for any other large namespace objects added to the language specification.&lt;/p&gt;&lt;p&gt;Finally, we strictly put all functions in their respective files split up by spec sections. No special reason other than consistency and zero need for bikeshedding.&lt;/p&gt;&lt;h3 id=&quot;spec-comments&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#spec-comments&quot;&gt;Spec comments&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Shortly before all of this I really started to embrace using spec comments in the respective implementation code, i.e. &lt;em&gt;literally copying the whole spec text into the implementation&lt;/em&gt;. Yes, seriously.&lt;/p&gt;&lt;p&gt;Looks like &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/ddce053f6c0312782611d8a2b8aa2263d0ec6a50/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp#L193-L217&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;this&lt;/a&gt;:&lt;/p&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 2.3.4 SystemDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemdatetime&lt;/span&gt;
ThrowCompletionOr&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PlainDateTime&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;system_date_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GlobalObject&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; global_object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Value temporal_time_zone_like&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Value calendar_like&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Object&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; time_zone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 1. If temporalTimeZoneLike is undefined, then&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;temporal_time_zone_like&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// a. Let timeZone be ! SystemTimeZone().&lt;/span&gt;
        time_zone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;system_time_zone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;global_object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 2. Else,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).&lt;/span&gt;
        time_zone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_temporal_time_zone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;global_object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; temporal_time_zone_like&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 3. Let calendar be ? ToTemporalCalendar(calendarLike).&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; calendar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_temporal_calendar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;global_object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calendar_like&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 4. Let instant be ! SystemInstant().&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; instant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;system_instant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;global_object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;builtin_time_zone_get_plain_date_time_for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;global_object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; time_zone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;instant&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;calendar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We didn&#39;t always do that. In fact, the majority of LibJS&#39;s code is still without any spec annotations. It&#39;s useful for a number of reasons though, so I wanted to take it to the next level in Temporal — 100% of its code is annotated like this, and I&#39;m not going back.&lt;/p&gt;&lt;p&gt;While the usefulness of &lt;code&gt;// 9. Else,&lt;/code&gt; specifically remains a source of debate within the SerenityOS community, we have started to also use this approach in parts of LibWeb, for example.&lt;/p&gt;&lt;h3 id=&quot;initial-lack-of-tests-in-test262&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#initial-lack-of-tests-in-test262&quot;&gt;Initial Lack of Tests in Test262&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;For a while now &lt;a href=&quot;https://libjs.dev/test262&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;we&#39;ve used test262&lt;/a&gt; to aid with testing our own implementation. Despite that, we still use and continue to expand &lt;a href=&quot;https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS/Tests&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;our own test suite&lt;/a&gt;, which became especially important again for Temporal: a considerable amount of tests initially written for the polyfill only got &lt;a href=&quot;https://github.com/tc39/test262/pull/3223&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;merged on October 1&lt;/a&gt;. Before that, test coverage was &lt;em&gt;really&lt;/em&gt; scarce, so we wrote plenty of tests ourselves:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ git ls-files Userland/Libraries/LibJS/Tests/builtins/Temporal | wc -l
298
$ git grep &#39;test(&#39; Userland/Libraries/LibJS/Tests/builtins/Temporal | wc -l
995
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Test262 coverage of Temporal will be expanded further in the future, and currently contains &lt;a href=&quot;https://github.com/tc39/test262/pull/3311&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;a number of broken tests&lt;/a&gt;, so take this with a grain of salt: at the time of writing, we pass ~85% of their tests.&lt;/p&gt;&lt;h3 id=&quot;exposure-to-the-ecmascript-proposal-process-%26-tc39&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#exposure-to-the-ecmascript-proposal-process-%26-tc39&quot;&gt;Exposure to the ECMAScript Proposal Process &amp;amp; TC39&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In the past we&#39;ve only ever taken finished, mostly stable specs &amp;amp; proposals for our implementation. Temporal still being Stage 3 meant that we eventually found, reported, and in some cases fixed more than a dozen issues with the specification itself, which we encountered during development (as well as relying on other implementors doing the same).&lt;/p&gt;&lt;p&gt;While most of the actual decision making for any normative changes happens within TC39 meetings, it was nice to get involved a little bit and see who is making the specs and how they do it :^)&lt;/p&gt;&lt;h2 id=&quot;what&#39;s-not-special&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#what&#39;s-not-special&quot;&gt;What&#39;s not Special&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;What absolutely did not change was how we approached this project. As you might know, the only plan in the SerenityOS project is &lt;em&gt;not having a plan&lt;/em&gt;, so while one of the spec&#39;s champions kindly &lt;a href=&quot;https://github.com/SerenityOS/serenity/issues/8982#issuecomment-887907673&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;pointed us to a dependency graph&lt;/a&gt; he made for the JSC implementation, we still ended up picking functions at random and implementing the missing Abstract Operations (AOs) on the fly.&lt;/p&gt;&lt;p&gt;Rinse, repeat, stop once all functions, and — by necessity — AOs are implemented.&lt;/p&gt;&lt;p&gt;Other than perhaps being slightly more consistent than other parts of LibJS, we still used the same language (C++), coding style, patterns (e.g. &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/AK/Try.h&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;TRY()&lt;/code&gt;&lt;/a&gt;), existing engine mechanisms (&lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibJS/Runtime/PrototypeObject.h&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;PrototypeObject&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibJS/Runtime/Completion.h&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;&lt;code&gt;ThrowCompletionOr&lt;/code&gt;&lt;/a&gt;, &amp;amp; co.). It will all feel very familiar if you already know the rest of the codebase.&lt;/p&gt;&lt;h2 id=&quot;what&#39;s-next&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/implementing-the-temporal-proposal-in-libjs/#what&#39;s-next&quot;&gt;What&#39;s Next&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There&#39;s still a lot of work to do! The spec requires support for a single named timezone (&lt;code&gt;UTC&lt;/code&gt;, numeric timezone offsets like &lt;code&gt;+01:30&lt;/code&gt; are a different story but part of the core functionality as well) and calendar (&lt;code&gt;iso8601&lt;/code&gt;). To be more more widely useful, we&#39;ll need support for other named timezones (e.g. &lt;code&gt;Europe/London&lt;/code&gt;) as well as calendars (e.g. &lt;code&gt;gregory&lt;/code&gt;, the full list can be found &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#unicode_calendar_keys&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;on MDN&lt;/a&gt;). These would be sourced from the &lt;a href=&quot;https://en.wikipedia.org/wiki/Tz_database&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;IANA Time Zone Database&lt;/a&gt; and the &lt;a href=&quot;https://cldr.unicode.org/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Unicode Common Locale Data Repository (CLDR)&lt;/a&gt; respectively, we already heavily utilize the latter for various functionality in ECMA-402 (Intl).&lt;/p&gt;&lt;p&gt;Speaking of Intl, there&#39;s &lt;a href=&quot;https://tc39.es/proposal-temporal/#sec-temporal-intl&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;a bunch of changes to be made&lt;/a&gt; to integrate Temporal with Intl objects, e.g. &lt;code&gt;Intl.DateTimeFormat&lt;/code&gt;. &lt;s&gt;Since we don&#39;t have that implemented yet, we didn&#39;t make those changes either.&lt;/s&gt; Just a day later, &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/11128&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Tim opened a PR&lt;/a&gt; starting to implement exactly that. It&#39;s still based on the vanilla Intl spec, though.&lt;/p&gt;&lt;p&gt;Lastly, it&#39;s still a stage 3 proposal and will probably receive further small editorial and normative changes for several more months, which we&#39;re obviously keeping up to date with.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;SpiderMonkey, JavaScriptCore, and V8 are all working on their implementations, and I imagine Temporal will eventually ship unflagged in the major browser engines sometime in 2022 — it&#39;s gonna be great. Until then, you can already use it in the SerenityOS Browser today :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Talking to SerenityOS Contributors About a Scratch-built C++ Developer’s Playground in Modern C++</title>
    <link href="https://blog.jetbrains.com/clion/2021/11/talking-to-serenityos-contributors/" />
    <updated>2021-11-12T00:00:00Z</updated>
    <id>https://blog.jetbrains.com/clion/2021/11/talking-to-serenityos-contributors/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Learning C++ With Serenity</title>
    <link href="https://cppcast.com/learning-cpp-serenity/" />
    <updated>2021-09-02T00:00:00Z</updated>
    <id>https://cppcast.com/learning-cpp-serenity/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>My Journey With SerenityOS, So Far 🐞</title>
    <link href="https://linus.dev/posts/my-journey-with-serenityos/" />
    <updated>2021-08-22T00:00:00Z</updated>
    <id>https://linus.dev/posts/my-journey-with-serenityos/</id>
    <content type="html">&lt;p&gt;This is a timeline of personal highlights of my involvement in the &lt;a href=&quot;https://github.com/SerenityOS/serenity&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;SerenityOS&lt;/a&gt; project — from learning C++ to becoming a maintainer and beyond.&lt;/p&gt;&lt;p&gt;If you have any questions, feel free to get in touch, for example on the &lt;a href=&quot;https://discord.gg/serenityos&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Discord Server&lt;/a&gt;. :^)&lt;/p&gt;&lt;h2 id=&quot;2019-%E2%80%94-discovery&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2019-%E2%80%94-discovery&quot;&gt;2019 — Discovery&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I first heard about SerenityOS in late 2019, but cannot remember where and when exactly — probably Reddit, Twitter, or a YouTube recommendation. I started watching Andreas&#39; &lt;a href=&quot;https://youtube.com/andreaskling&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;hacking videos&lt;/a&gt; occasionally and found them very enjoyable.&lt;/p&gt;&lt;h2 id=&quot;2020-01-26-%E2%80%94-first-pr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2020-01-26-%E2%80%94-first-pr&quot;&gt;2020-01-26 — First PR&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;My first ever contribution was &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1137&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PR #1137&lt;/a&gt;, titled &lt;code&gt;LibVT: Rename escape functions&lt;/code&gt;. 12 commits, +57 −35 changeset.&lt;/p&gt;&lt;p&gt;This was inspired by a remark in one of the videos and subsequent commit. It seemed easy enough for me to attempt, so I did. It was a one-off though, and I went back to just watching.&lt;/p&gt;&lt;h2 id=&quot;2020-03-13-%E2%80%94-first-libjs-pr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2020-03-13-%E2%80%94-first-libjs-pr&quot;&gt;2020-03-13 — First LibJS PR&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1439&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PR #1439&lt;/a&gt; was my first ever contribution to LibJS, or to any JavaScript engine for that matter. A simple change resolving a FIXME.&lt;/p&gt;&lt;p&gt;A long time before that, I — like so many others — believed that &lt;em&gt;you can&#39;t build a browser from scratch anymore, they&#39;re too complex&lt;/em&gt;. Sure, SerenityOS already had a simple browser, but as far as I was concerned that already existed when I first saw it, like all the other browsers. When Andreas uploaded a YouTube video titled &lt;a href=&quot;https://www.youtube.com/watch?v=byNwCHc_IIM&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;Browser hacking: Let&#39;s build a JavaScript engine for SerenityOS!&lt;/a&gt; it clicked for me. I had been wrong, I just watched a guy doing what I considered to be impossible, &lt;em&gt;starting from nothing&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I still didn&#39;t know any C++ though, but after a week the urge to take on this challenge/chance became strong enough to dive into it. I could work on a brand new JS engine, where literally &lt;em&gt;all the things&lt;/em&gt; still had to be implemented. I&#39;m a web developer professionally, so I was &lt;em&gt;excited&lt;/em&gt; — and that provided enough motivation to get started. Needless to say, I&#39;m glad I did, otherwise I&#39;d probably still be watching this project from the outside.&lt;/p&gt;&lt;h2 id=&quot;2020-04-08-%E2%80%94-starting-the-promises-pr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2020-04-08-%E2%80%94-starting-the-promises-pr&quot;&gt;2020-04-08 — Starting the Promises PR&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A few weeks into the first lockdown in the UK and with more free time on my hands than ever before, I needed something to work on. That something was implementing JavaScript Promises in LibJS.&lt;/p&gt;&lt;p&gt;At that point I still barely knew what I was doing when it comes to C++, so I spent countless hours trying to understand compiler errors and debugging silly mistakes. I got pretty far and opened &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1706&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PR #1706&lt;/a&gt; after a few days, earning lots excitement, but then got stuck, and eventually closed the PR after having it sit around for a while, thinking I couldn&#39;t finish it (I probably &lt;em&gt;could&lt;/em&gt; have, with some help).&lt;/p&gt;&lt;p&gt;Luckily this story will have a happy ending!&lt;/p&gt;&lt;h2 id=&quot;2020-05-17-%E2%80%94-100-commits&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2020-05-17-%E2%80%94-100-commits&quot;&gt;2020-05-17 — 100 Commits&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;About two months after starting to contribute regularly, I reached 100 commits in the project, and became the fifth person to &lt;a href=&quot;https://github.com/SerenityOS/serenity/commit/2949c3e5e1d49373f4684f3cce6a9f56920e4240&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;join the list of contributors&lt;/a&gt; in the README.&lt;/p&gt;&lt;p&gt;70 of these commits are work on LibJS and the js REPL :^)&lt;/p&gt;&lt;p&gt;At this point things calmed down for a little while, as I went on vacation in July/August and was quite busy with work in September/October.&lt;/p&gt;&lt;h2 id=&quot;2020-12-29-%E2%80%94-crash-reporter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2020-12-29-%E2%80%94-crash-reporter&quot;&gt;2020-12-29 — Crash Reporter&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is a fun little project I started during the last couple of days in 2020 (&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/4626&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PR #4626&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;Here&#39;s what the initial version looked like, and what it looks like now (as of August 2021):&lt;/p&gt;&lt;p class=&quot;gallery&quot;&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/19366641/103284589-ea26aa00-49db-11eb-90fc-cf4f4f535306.png&quot; alt=&quot;Screenshot of the initial version&quot;&gt; &lt;img src=&quot;https://user-images.githubusercontent.com/19366641/130292623-a991a026-e78d-4cc7-b624-14a883200b26.png&quot; alt=&quot;Screenshot as of August 2021&quot;&gt;&lt;/p&gt;&lt;p&gt;Even though I&#39;ve worked on various GUI applications in SerenityOS, the Crash Reporter remains the only one that I wrote myself.&lt;/p&gt;&lt;h2 id=&quot;2021-01-18-%E2%80%94-python-3.9-port&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-01-18-%E2%80%94-python-3.9-port&quot;&gt;2021-01-18 — Python 3.9 Port&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For a long time I was a bit uncomfortable with contributing to Serenity&#39;s ports, as I had never ported software to a new system before. After working on a couple of small ports (&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1809&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;gnuplot&lt;/a&gt;, &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/1989&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;neofetch&lt;/a&gt;, as well as fixes for other existing ones), I attempted a big one: Python 3.9.&lt;/p&gt;&lt;p&gt;At the time we already had a Python 3.6 port, but it was several releases behind upstream, experimental, and didn&#39;t compile with threading support enabled. So, I started over, and it was a success! The initial port was done in &lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/5001&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;PR #5001&lt;/a&gt;, with several follow-up PRs afterwards.&lt;/p&gt;&lt;p&gt;Since then I&#39;ve been keeping the port up to date with the latest Python releases and gradually enabled more features (which mostly boils down to getting as many modules as possible to compile). Python is the first programming language I learned and still the one I know the best, and as such it&#39;s my favourite non-core part of the system. 🐍&lt;/p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;em&gt;View tweet: &lt;a href=&quot;https://twitter.com/linusgroh/status/1351251082601787396&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://twitter.com/linusgroh/status/1351251082601787396&lt;/a&gt;&lt;/em&gt; &lt;a href=&quot;https://twitter.com/linusgroh/status/1351251082601787396&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;2021-02-13-%E2%80%94-developer-interview&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-02-13-%E2%80%94-developer-interview&quot;&gt;2021-02-13 — Developer Interview&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Andreas asked me if I&#39;d be interested in doing an interview in early January, and without much hesitation I agreed. I had never done anything of this sort before, so I was slightly nervous, but it was a great experience :^)&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;oG8RSX1hyCg&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;p&gt;Other developer interviews followed, you can watch those &lt;a href=&quot;https://www.youtube.com/playlist?list=PLMOpZvQB55bdDy0Lgn9fG2c7NEK_rkkkQ&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;in this playlist&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;2021-04-02-%E2%80%94-finishing-the-promises-pr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-04-02-%E2%80%94-finishing-the-promises-pr&quot;&gt;2021-04-02 — Finishing the Promises PR&lt;/a&gt;&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;Mar 26 09:40:04 &amp;lt;kling&amp;gt; linusg: I used a JS promise at work the other day and it made me remember you had a LibJS promise thing going a while back
Mar 26 09:41:21 &amp;lt;linusg&amp;gt; I did never finish it, the &amp;quot;schedule this asap but not immediately&amp;quot; part threw me off IIRC
Mar 26 09:42:53 &amp;lt;linusg&amp;gt; I&#39;ll rebase it and then we can figure that out :)
...
Mar 29 20:02:04 &amp;lt;linusg&amp;gt; kling: thanks for nudging me towards picking up the promises PR again, it&#39;s been a fun couple of days :D
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I rewrote some parts of it, fixed some other parts, posted an up-to-date screenshot — and then it got merged, almost exactly one year after starting it. I was &lt;em&gt;stoked&lt;/em&gt; :^)&lt;/p&gt;&lt;p&gt;During this one year, the question of when LibJS would have Promise support came up several times, and the answer always was &amp;quot;when someone picks it up (again)&amp;quot;. I still &lt;em&gt;wanted&lt;/em&gt; to be that person.&lt;/p&gt;&lt;p&gt;This PR remains one of my all time favourites, not only because it&#39;s a really cool JS feature and basic requirement for many modern Web APIs, but also because of what it represents for me personally: going from poking at a new JS engine and trying to somehow make it work, to later turning that initial failure into a success and realising &lt;em&gt;I can actually do this&lt;/em&gt;.&lt;/p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;em&gt;View tweet: &lt;a href=&quot;https://twitter.com/awesomekling/status/1377724079696924676&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://twitter.com/awesomekling/status/1377724079696924676&lt;/a&gt;&lt;/em&gt; &lt;a href=&quot;https://twitter.com/awesomekling/status/1377724079696924676&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;2021-04-05-%E2%80%94-1000-commits&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-04-05-%E2%80%94-1000-commits&quot;&gt;2021-04-05 — 1000 Commits&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A little over a year after starting to contribute regularly, I reached 1000 commits in the project. Andreas tweeted this screenshot of a DM I had sent him, and I couldn&#39;t be happier about proving myself wrong!&lt;/p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;em&gt;View tweet: &lt;a href=&quot;https://twitter.com/awesomekling/status/1379127438823817220&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://twitter.com/awesomekling/status/1379127438823817220&lt;/a&gt;&lt;/em&gt; &lt;a href=&quot;https://twitter.com/awesomekling/status/1379127438823817220&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;2021-04-10-%E2%80%94-discord&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-04-10-%E2%80%94-discord&quot;&gt;2021-04-10 — Discord&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For a long time all the SerenityOS developers were only on IRC — and it was great. Any requests to start a Discord server were dismissed, until we tried it one day after a good experience with a Discord server of the Zig community, and it turns out: we were &lt;em&gt;so&lt;/em&gt; wrong.&lt;/p&gt;&lt;p&gt;There was a concern that Discord would have more memes and nonsense than productive discussion, and to some extent that appeared to be true — there was a lot of noise and excitement in the beginning. Now the Discord server has grown to almost 4000 people and about two dozen channels, and it&#39;s more active and productive than IRC ever was (it had less than 200 people on it). Oh, and it also has more yak emotes than IRC. &lt;img src=&quot;https://raw.githubusercontent.com/SerenityOS/artwork/master/images/emotes/yaksplode.png&quot; width=&quot;22&quot; style=&quot;position: relative; top: 2px;&quot; title=&quot;:yaksplode:&quot; alt=&quot;:yaksplode: emote from the SerenityOS Discord server&quot;&gt;&lt;/p&gt;&lt;p&gt;My ZNC is still running, but I haven&#39;t connected to it in a long time, especially since the freenode drama. It was fun while it lasted :^)&lt;/p&gt;&lt;h2 id=&quot;2021-04-16-%E2%80%94-becoming-a-reviewer&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-04-16-%E2%80%94-becoming-a-reviewer&quot;&gt;2021-04-16 — Becoming a Reviewer&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Andreas did an &lt;a href=&quot;https://www.youtube.com/watch?v=5h8bo9OxCwI&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;interview&lt;/a&gt; with Jonathan Turner (Systems With JT), and the topic of dealing with the ever growing list list of pull requests came up. Later that day I Andreas asked me if I wanted to be a reviewer. Of course I did!&lt;/p&gt;&lt;p&gt;It took me a couple of days to get comfortable with this new position as well as overcoming the initial fear of somehow messing up. Over the next couple of weeks my code contributions plummeted as I spent more time on code reviews instead.&lt;/p&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;em&gt;View tweet: &lt;a href=&quot;https://twitter.com/awesomekling/status/1383081852857155592&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;https://twitter.com/awesomekling/status/1383081852857155592&lt;/a&gt;&lt;/em&gt; &lt;a href=&quot;https://twitter.com/awesomekling/status/1383081852857155592&quot;&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;If one person doesn&#39;t scale, two won&#39;t either, so we were soon after joined by Ali, and then Gunnar.&lt;/p&gt;&lt;h2 id=&quot;2021-06-30-%E2%80%94-montly-update-video&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-06-30-%E2%80%94-montly-update-video&quot;&gt;2021-06-30 — Montly Update Video&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Much to my surprise Andreas asked me if I wanted to do a part of his monthly SerenityOS update video, specifically to talk about LibJS. It already was the last day of the month though, so there was very little preparation time 😅&lt;/p&gt;&lt;p&gt;It worked out nonetheless!&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;QI3o2G8MPbQ&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;h2 id=&quot;2021-07-31-%E2%80%94-montly-update-video%2C-again&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#2021-07-31-%E2%80%94-montly-update-video%2C-again&quot;&gt;2021-07-31 — Montly Update Video, Again&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This time with way more preparation, a proper microphone, and covering both LibJS and LibWeb.&lt;/p&gt;&lt;p&gt;&lt;lite-youtube videoid=&quot;nUCpt6F5q-s&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;&lt;h2 id=&quot;the-future&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://linus.dev/posts/my-journey-with-serenityos/#the-future&quot;&gt;The Future&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As you know, there is no plan!&lt;/p&gt;&lt;p&gt;Well, that&#39;s not entirely true: I do plan to use Serenity as my primary OS eventually. I haven&#39;t managed to do a full successful boot on bare metal yet though, so that will definitely have to join this list one day.&lt;/p&gt;&lt;p&gt;This is the most fun and ambitious project I have ever worked on, and I&#39;m proud to be a part of it. It&#39;s been keeping me busy during lockdown in 2020, and has been growing closer to my heart ever since. Being exposed to the exceptional can-do attitude has been invaluable. In other words: I&#39;m here to stay :^)&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>SerenityOS developer interview: Linus Groh</title>
    <link href="https://www.youtube.com/watch?v=oG8RSX1hyCg" />
    <updated>2021-02-13T00:00:00Z</updated>
    <id>https://www.youtube.com/watch?v=oG8RSX1hyCg</id>
    <content type="html"></content>
  </entry>
</feed>