lupyuen.org/articles/quickjs.html
Lup Yuen Lee 6e2f142414
Some checks are pending
Build Articles / build (push) Waiting to run
Commit from GitHub Actions
2025-01-04 06:09:02 +00:00

1092 lines
No EOL
59 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)"
data-rh="true">
<meta property="og:description"
content="Can we run QuickJS JavaScript Engine on Apache NuttX RTOS? And Blink the LED on Ox64 BL808 RISC-V SBC... In 4 lines of JavaScript? Lets do it!"
data-rh="true">
<meta name="description"
content="Can we run QuickJS JavaScript Engine on Apache NuttX RTOS? And Blink the LED on Ox64 BL808 RISC-V SBC... In 4 lines of JavaScript? Lets do it!">
<meta property="og:image"
content="https://lupyuen.github.io/images/quickjs-title.png">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical"
href="https://lupyuen.org/articles/quickjs.html" />
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)</h1>
<nav id="rustdoc"><ul>
<li><a href="#quickjs-on-nuttx-emulator" title="QuickJS on NuttX Emulator">1 QuickJS on NuttX Emulator</a><ul></ul></li>
<li><a href="#build-quickjs-for-nuttx" title="Build QuickJS for NuttX">2 Build QuickJS for NuttX</a><ul></ul></li>
<li><a href="#nuttx-stack-is-full-of-quickjs" title="NuttX Stack is Full of QuickJS">3 NuttX Stack is Full of QuickJS</a><ul></ul></li>
<li><a href="#add-ioctl-to-quickjs" title="Add ioctl() to QuickJS">4 Add ioctl() to QuickJS</a><ul></ul></li>
<li><a href="#quickjs-blinks-the-led-on-ox64-sbc" title="QuickJS Blinks the LED on Ox64 SBC">5 QuickJS Blinks the LED on Ox64 SBC</a><ul></ul></li>
<li><a href="#how-small-is-quickjs" title="How Small is QuickJS">6 How Small is QuickJS</a><ul></ul></li>
<li><a href="#simulate-the-led-on-ox64-emulator" title="Simulate the LED on Ox64 Emulator">7 Simulate the LED on Ox64 Emulator</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">8 Whats Next</a><ul></ul></li>
<li><a href="#appendix-build-quickjs-for-nuttx" title="Appendix: Build QuickJS for NuttX">9 Appendix: Build QuickJS for NuttX</a><ul></ul></li>
<li><a href="#appendix-build-nuttx-for-qemu" title="Appendix: Build NuttX for QEMU">10 Appendix: Build NuttX for QEMU</a><ul></ul></li>
<li><a href="#appendix-build-nuttx-for-ox64" title="Appendix: Build NuttX for Ox64">11 Appendix: Build NuttX for Ox64</a><ul></ul></li></ul></nav><p>📝 <em>18 Feb 2024</em></p>
<p><img src="https://lupyuen.github.io/images/quickjs-title.webp" alt="QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)" /></p>
<p><a href="https://lupyuen.github.io/nuttx-tinyemu/quickjs/"><em>(Try the Online Demo)</em></a></p>
<p><a href="https://youtu.be/AFDVceqQNRs"><em>(Watch the Demo on YouTube)</em></a></p>
<p><a href="https://github.com/bellard/quickjs"><strong>QuickJS</strong></a> is a small <strong>JavaScript Engine</strong> that supports <a href="https://bellard.org/quickjs/quickjs.html#os-module"><strong>POSIX Functions</strong></a>.</p>
<p><a href="https://nuttx.apache.org/docs/latest/index.html"><strong>Apache NuttX RTOS</strong></a> is a tiny <strong>Real-Time Operating System</strong> (for all kinds of devices) thats compatible with POSIX.</p>
<p><em>Can we run QuickJS on NuttX? And Blink the LED in 4 lines of JavaScript?</em></p>
<div class="example-wrap"><pre class="language-javascript"><code>// Blink the NuttX LED, on then off
const ULEDIOC_SETALL = 0x1d03;
const fd = os.open(&quot;/dev/userleds&quot;, os.O_WRONLY);
os.ioctl(fd, ULEDIOC_SETALL, 1);
os.ioctl(fd, ULEDIOC_SETALL, 0);</code></pre></div>
<p>Lets do it! In this article we…</p>
<ul>
<li>
<p>Run QuickJS on <strong>Ox64 BL808 RISC-V SBC</strong></p>
</li>
<li>
<p>Blink the LED by adding the <strong>ioctl() function</strong></p>
</li>
<li>
<p>Reconfigure the <strong>NuttX App Stack</strong> (because its too tiny)</p>
</li>
<li>
<p>Analyse the <strong>Memory Footprint</strong> of QuickJS (Code + Data + Heap Size)</p>
</li>
<li>
<p>Test QuickJS in the Web Browser with <strong>NuttX Emulator</strong> (and a Simulated LED)</p>
</li>
</ul>
<p>QuickJS is perfect for Iterative, Interactive Experiments on NuttX! We go hands-on (fingers too)…</p>
<p><img src="https://lupyuen.github.io/images/quickjs-title2.png" alt="QuickJS JavaScript Engine in Ox64 NuttX Emulator" /></p>
<h1 id="quickjs-on-nuttx-emulator"><a class="doc-anchor" href="#quickjs-on-nuttx-emulator">§</a>1 QuickJS on NuttX Emulator</h1>
<p>Click here to try <strong>QuickJS JavaScript Engine</strong> in NuttX Emulator (pic above)…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/nuttx-tinyemu/quickjs"><strong>QuickJS on Ox64 NuttX Emulator</strong></a></p>
<p><a href="https://youtu.be/AFDVceqQNRs">(Watch the <strong>Demo on YouTube</strong>)</a></p>
</li>
</ul>
<p>Now we do some Finger Exercises (sorry <strong>copy-pasta wont work</strong> in the Emulator)</p>
<ol>
<li>
<p>To start QuickJS: Enter this at the <strong>NSH Prompt</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>qjs</code></pre></div></li>
<li>
<p>At the QuickJS Prompt: We define the <strong>NuttX LED Command</strong></p>
<div class="example-wrap"><pre class="language-javascript"><code>ULEDIOC_SETALL = 0x1d03</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/nim#blink-an-led">(About <strong>ULEDIOC_SETALL</strong>)</a></p>
</li>
<li>
<p>Next we open the <strong>NuttX LED Device</strong> (write-only)…</p>
<div class="example-wrap"><pre class="language-javascript"><code>fd = os.open(&quot;/dev/userleds&quot;, os.O_WRONLY)</code></pre></div></li>
<li>
<p>Watch what happens when we <strong>Flip On the LED</strong></p>
<div class="example-wrap"><pre class="language-javascript"><code>os.ioctl(fd, ULEDIOC_SETALL, 1)</code></pre></div>
<p><strong>GPIO 29</strong> turns Green! (Pic above)</p>
</li>
<li>
<p>When we <strong>Flip Off the LED</strong></p>
<div class="example-wrap"><pre class="language-javascript"><code>os.ioctl(fd, ULEDIOC_SETALL, 0)</code></pre></div>
<p><strong>GPIO 29</strong> goes back to Normal!</p>
</li>
<li>
<p>Our Demo does this…</p>
<div class="example-wrap"><pre class="language-bash"><code>NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs
QuickJS - Type &quot;\h&quot; for help
## Define the NuttX LED Command
qjs &gt; ULEDIOC_SETALL = 0x1d03
7427
## Open the NuttX LED Device (write-only)
qjs &gt; fd = os.open(&quot;/dev/userleds&quot;, os.O_WRONLY)
3
## Flip LED On: GPIO 29 turns Green
qjs &gt; os.ioctl(fd, ULEDIOC_SETALL, 1)
## Flip LED Off: GPIO 29 goes back to normal
qjs &gt; os.ioctl(fd, ULEDIOC_SETALL, 0)</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#quickjs-blinks-the-led-on-ox64-emulator">(See the <strong>Complete Log</strong>)</a></p>
<p><a href="https://youtu.be/AFDVceqQNRs">(Watch the <strong>Demo on YouTube</strong>)</a></p>
</li>
</ol>
<p><em>Erm our fingers are hurting?</em></p>
<p>Try this <strong>Non-Interactive JavaScript</strong> with QuickJS…</p>
<div class="example-wrap"><pre class="language-bash"><code>nsh&gt; qjs --std /system/bin/blink.js</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/blink.js">(See the <strong>Blinky JavaScript</strong>)</a></p>
<p><a href="https://bellard.org/quickjs/quickjs.html#qjs-interpreter">(Option “<strong><code>--std</code></strong>” will import the <strong>POSIX Functions</strong>)</a></p>
<p><em>Wow… A Blinky in JavaScript?</em></p>
<p>Yep we flipped this <a href="https://github.com/lupyuen/quickjs-nuttx#quickjs-calls-nuttx-led-driver"><strong>NuttX Blinky App</strong></a> from C to <strong>Interactive JavaScript</strong>!</p>
<p><em>Does it work on Real Hardware?</em></p>
<p>The exact same QuickJS blinks a Real LED on <a href="https://www.hackster.io/lupyuen/8-risc-v-sbc-on-a-real-time-operating-system-ox64-nuttx-474358"><strong>Ox64 BL808 SBC</strong></a>, based on 64-bit RISC-V. (Well come back to this)</p>
<p>How did we make this happen? Read on to find out…</p>
<p><img src="https://lupyuen.github.io/images/quickjs-expect.png" alt="Auto-Test QuickJS with Expect Scripting" /></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qemu.exp"><em>Auto-Test QuickJS with Expect Scripting</em></a></p>
<h1 id="build-quickjs-for-nuttx"><a class="doc-anchor" href="#build-quickjs-for-nuttx">§</a>2 Build QuickJS for NuttX</h1>
<p><em>QuickJS compiles OK for NuttX?</em></p>
<p>Mostly. QuickJS compiles for NuttX <strong>with no code changes</strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx"><strong>“Build QuickJS for NuttX”</strong></a></li>
</ul>
<p>Then we hit some <strong>Missing Functions</strong></p>
<ol>
<li>
<p><strong>POSIX Functions:</strong> <em>popen, pclose, pipe2, symlink, …</em></p>
</li>
<li>
<p><strong>Dynamic Linking:</strong> <em>dlopen, dlsym, dlclose</em></p>
</li>
<li>
<p><strong>Math Functions:</strong> <em>pow, floor, trunc, …</em></p>
</li>
<li>
<p><strong>Atomic Functions:</strong> <em>atomic_fetch_add_2, atomic_fetch_or_1, …</em></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#fix-the-missing-functions">(See the <strong>Missing Functions</strong>)</a></p>
</li>
</ol>
<p><em>Can we fill in the missing functions?</em></p>
<ol>
<li>
<p><strong>POSIX Functions:</strong> The typical POSIX Functions are OK. The special ones are probably available if we tweak the <strong>Build Options</strong> for NuttX.</p>
<p>For now, we stick with the Basic NuttX Config and stub out the <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/stub.c#L10-L14"><strong>Advanced POSIX Functions</strong></a>.</p>
</li>
<li>
<p><strong>Dynamic Linking:</strong> We wont support Dynamic Linking for NuttX. We <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/stub.c#L3-L5"><strong>stubbed the missing functions</strong></a>.</p>
</li>
<li>
<p><strong>Math Functions:</strong> We linked them with GCC Option “<strong><code>-lm</code></strong>”. The last few stragglers: We <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/stub.c#L7-L9"><strong>stubbed the functions</strong></a>.</p>
</li>
<li>
<p><strong>Atomic Functions:</strong> We filled in the <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/arch_atomic.c#L32-L743"><strong>Missing Atomic Functions</strong></a>.</p>
<p><a href="https://github.com/apache/nuttx/issues/10642">(About <strong>NuttX Atomic Functions</strong>)</a></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/quickjs.c#L67-L73">(We might <strong>Disable Atomic Functions</strong>)</a></p>
</li>
</ol>
<p>After these fixes, QuickJS builds OK for NuttX!</p>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx">(How to build <strong>QuickJS for NuttX</strong>)</a></p>
<p><em>Thats plenty of stubbing. Will it break QuickJS?</em></p>
<p>Thankfully we have <strong>Automated Testing</strong> with an Expect Script (pic above): <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qemu.exp">qemu.exp</a></p>
<div class="example-wrap"><pre class="language-bash"><code>#!/usr/bin/expect
## Expect Script for Testing QuickJS with QEMU Emulator
## For every 1 character sent, wait 0.001 milliseconds
set send_slow {1 0.001}
## Start NuttX on QEMU Emulator
## Remove `-bios none` for newer versions of NuttX
spawn qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-bios none \
-kernel nuttx \
-nographic
## Wait for the prompt and enter this command
expect &quot;nsh&gt; &quot;
send -s &quot;qjs -e console.log(123) \r&quot;
## Check the response...
expect {
## If we see this message, exit normally
&quot;nsh&gt;&quot; { exit 0 }
## If timeout, exit with an error
timeout { exit 1 }
}</code></pre></div>
<p>Before the Auto-Test, we solve the Auto-Crash…</p>
<p><img src="https://lupyuen.github.io/images/quickjs-stack.webp" alt="Loopy Stack Trace probably means Stack Full" /></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/0aafbb7572d4d0a1f7ac48d0b6a5ac0ba8374cfc/nuttx/qemu.log#L5385-L5478"><em>Loopy Stack Trace probably means Stack Full</em></a></p>
<h1 id="nuttx-stack-is-full-of-quickjs"><a class="doc-anchor" href="#nuttx-stack-is-full-of-quickjs">§</a>3 NuttX Stack is Full of QuickJS</h1>
<p><em>QuickJS builds OK for NuttX. Does it run?</em></p>
<p>Sorry nope! QuickJS ran into <a href="https://github.com/lupyuen/quickjs-nuttx#quickjs-crashes-on-nuttx"><strong>Bizarre Crashes</strong></a> on NuttX (with looping Stack Traces, pic above)…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#atom-sentinel-becomes-0xffff_ffff"><strong>Strange Pointers</strong></a> (<code>0xFFFF_FFFF</code>) while reading the JavaScript Atoms</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#unexpected-character-in-quickjs"><strong>Unexpected Characters</strong></a> (<code>0xFF</code>) appeared in our JavaScript Strings</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#malloc-problems-in-nuttx"><strong>Malloc was Erasing</strong></a> our JavaScript Strings</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#heap-errors-and-stdio-weirdness"><strong>Heap Memory</strong></a> got weirdly corrupted (even <strong>printf()</strong> failed)</p>
</li>
</ul>
<p>After plenty of headscratching troubleshooting, this <a href="https://github.com/lupyuen/quickjs-nuttx#nuttx-stack-is-full-of-quickjs"><strong>Vital Clue</strong></a> suddenly pops up…</p>
<div class="example-wrap"><pre class="language-yaml"><code>riscv_exception: EXCEPTION: Load page fault. MCAUSE: 000000000000000d, EPC: 00000000c0006d52, MTVAL: ffffffffffffffff
PID GRP PRI POLICY TYPE NPX STATE EVENT STACKBASE SIZE USED FILLED COMMAND
0x802002b0 2048 2040 99.6%! irq
0 0 0 FIFO Kthread - Ready 0x80206010 3056 1856 60.7% Idle_Task
1 1 100 RR Kthread - Waiting Semaphore 0x8020a050 1968 704 35.7% lpwork 0x802015f0 0x80201618
2 2 100 RR Task - Waiting Semaphore 0xc0202040 3008 744 24.7% /system/bin/init
3 3 100 RR Task - Running 0xc0202050 1968 1968 100.0%! qjs }¼uq¦ü®઄²äÅ</code></pre></div>
<p>The last line shows that the <strong>QuickJS Stack</strong> (2 KB) is <strong>100% Full</strong>! (With the Command Line incorrigibly corrupted)</p>
<p>We follow these steps to <a href="https://github.com/lupyuen/nuttx-star64#increase-stack-size"><strong>increase the App Stack Size</strong></a></p>
<ol>
<li>
<p>Enter “<strong><code>make menuconfig</code></strong></p>
</li>
<li>
<p>Select <em>“Library Routines &gt; Program Execution Options”</em></p>
</li>
<li>
<p>Set <em>“Default task_spawn Stack Size”</em> to <strong>65536</strong></p>
<p>(Thats 64 KB)</p>
</li>
<li>
<p>Select <em>“Library Routines &gt; Thread Local Storage (TLS)”</em></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#fix-quickjs-interactive-mode-on-nuttx">(Why we set <strong>Thread Local Storage</strong>)</a></p>
</li>
<li>
<p>Set <em>“Maximum Stack Size (Log2)”</em> to <strong>16</strong></p>
<p>(Because 2^16 = 64 KB)</p>
</li>
</ol>
<p>Which becomes this in our <strong>NuttX Build Config</strong>: <a href="https://github.com/lupyuen2/wip-nuttx/commit/904b95534298378d64b99c1f9e649f8bc27a8048#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078">ox64/nsh/defconfig</a></p>
<div class="example-wrap"><pre class="language-bash"><code>## Upsize the App Stack to 64 KB
CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=65536
CONFIG_TLS_LOG2_MAXSTACK=16</code></pre></div>
<p>QuickJS crashes no more!</p>
<p><strong>Lesson Learnt:</strong> If the NuttX Stack Dump loops forever, were probably <strong>Out Of Stack Space</strong>.</p>
<p><img src="https://lupyuen.github.io/images/quickjs-posix.png" alt="POSIX Functions in QuickJS" /></p>
<p><a href="https://bellard.org/quickjs/quickjs.html#os-module"><em>POSIX Functions in QuickJS</em></a></p>
<h1 id="add-ioctl-to-quickjs"><a class="doc-anchor" href="#add-ioctl-to-quickjs">§</a>4 Add ioctl() to QuickJS</h1>
<p><em>ioctl() doesnt appear in the QuickJS Docs? (Pic above)</em></p>
<div class="example-wrap"><pre class="language-javascript"><code>// Flip On the NuttX LED
const ULEDIOC_SETALL = 0x1d03;
const fd = os.open(&quot;/dev/userleds&quot;, os.O_WRONLY);
os.ioctl(fd, ULEDIOC_SETALL, 1);</code></pre></div>
<p>Thats because we added <strong>ioctl()</strong> to QuickJS: <a href="https://github.com/lupyuen/quickjs-nuttx/commit/91aaf4257992c08b01590f0d61fa37a386933a4b#diff-95fe784bea3e0fbdf30ba834b1a74b538090f4d70f4f8770ef397ef68ec37aa3">quickjs-libc.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// List of JavaScript Functions in `os` Module
static const JSCFunctionListEntry js_os_funcs[] = {
...
// Declare our ioctl() function...
JS_CFUNC_DEF(
&quot;ioctl&quot;, // Function Name
3, // Parameters
js_os_ioctl // Implemented here
),
};
// Define our ioctl() function
static JSValue js_os_ioctl(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
int fd, req; // ioctl() File Descriptor and Request Number
int64_t arg, ret; // ioctl() Parameter and Return Value
BOOL is_bigint; // True if we&#39;re using BigInt
// First Arg is ioctl() File Descriptor (int32)
if (JS_ToInt32(ctx, &amp;fd, argv[0]))
return JS_EXCEPTION;
// Second Arg is ioctl() Request Number (int32)
if (JS_ToInt32(ctx, &amp;req, argv[1]))
return JS_EXCEPTION;
// Third Arg is ioctl() Parameter (int64)
// TODO: What if it&#39;s int32? What about passing a Pointer to a C Struct?
is_bigint = JS_IsBigInt(ctx, argv[2]);
if (JS_ToInt64Ext(ctx, &amp;arg, argv[2]))
return JS_EXCEPTION;
// Call NuttX ioctl()
ret = ioctl(fd, req, arg);
if (ret == -1)
ret = -errno;
// Return the Result as BigInt or Normal Integer
if (is_bigint)
return JS_NewBigInt64(ctx, ret);
else
return JS_NewInt64(ctx, ret);
}</code></pre></div>
<p>After adding this code to QuickJS, <strong>ioctl()</strong> comes to life…</p>
<div class="example-wrap"><pre class="language-bash"><code>NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs
QuickJS - Type &quot;\h&quot; for help
qjs &gt; os.ioctl
function ioctl()
qjs &gt; os.ioctl(1,2,3)
-25
qjs &gt; os.ioctl(100,2,3)
-9</code></pre></div>
<p>We test <strong>ioctl()</strong> on real hardware…</p>
<p><img src="https://lupyuen.github.io/images/nim-wiring.webp" alt="Connect an LED to Ox64 SBC at GPIO 29, Pin 21" /></p>
<h1 id="quickjs-blinks-the-led-on-ox64-sbc"><a class="doc-anchor" href="#quickjs-blinks-the-led-on-ox64-sbc">§</a>5 QuickJS Blinks the LED on Ox64 SBC</h1>
<p><em>We added ioctl() to QuickJS. Does it work?</em></p>
<p>We test <strong>ioctl()</strong> on a Real Device with a Real LED: <strong>Ox64 BL808 RISC-V SBC</strong>. Right after these tweaks…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/8f75f3744f3964bd3ed0596421a93e59fb39cdd8"><strong>Add the GPIO Driver</strong></a> for Ox64 BL808</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/4f3996959132ca0d35874b7be3eef89d6bf7f351"><strong>Add the LED Driver</strong></a> for Ox64 BL808</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/904b95534298378d64b99c1f9e649f8bc27a8048"><strong>Increase the App Stack Size</strong></a> from 2 KB to 64 KB</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/28453790d06c0282b85e5df98624f8fa1c0b2226"><strong>Increase the RAM Disk Region</strong></a> from 16 MB to 40 MB</p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#add-led-driver-to-nuttx-ox64-bl808-sbc">(Why we enlarge the <strong>RAM Disk Region</strong>)</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx-apps/commit/66f1389c8d17eecdc5ef7baa62d13435bd053ee3"><strong>Patch the <code>leds</code> app</strong></a> for testing LED Driver</p>
<p>(Because <strong>task_create()</strong> is missing from Kernel Mode)</p>
</li>
</ul>
<p>Follow these steps to download (or build) <strong>NuttX and QuickJS</strong></p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-nuttx-for-ox64"><strong>“Build NuttX for Ox64”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx"><strong>“Build QuickJS for NuttX Ox64”</strong></a></p>
</li>
</ul>
<p>Connect an LED to Ox64 SBC at <strong>GPIO 29, Pin 21</strong> (pic above)…</p>
<div><table><thead><tr><th style="text-align: left">Connect</th><th style="text-align: left">To</th><th style="text-align: left">Wire</th></tr></thead><tbody>
<tr><td style="text-align: left"><strong>Ox64 Pin 21</strong> <br><em>(GPIO 29)</em></td><td style="text-align: left"><strong>Resistor</strong> <br><em>(47 Ohm)</em></td><td style="text-align: left">Red</td></tr>
<tr><td style="text-align: left"><strong>Resistor</strong> <br><em>(47 Ohm)</em></td><td style="text-align: left"><strong>LED +</strong> <br><em>(Curved Edge)</em></td><td style="text-align: left">Breadboard</td></tr>
<tr><td style="text-align: left"><strong>LED -</strong> <br><em>(Flat Edge)</em></td><td style="text-align: left"><strong>Ox64 Pin 23</strong> <br><em>(GND)</em></td><td style="text-align: left">Black</td></tr>
</tbody></table>
</div>
<p><a href="https://wiki.pine64.org/wiki/File:Ox64_pinout.png">(See the <strong>Ox64 Pinout</strong>)</a></p>
<p>(Resistor is <strong>47 Ohm</strong>, yellow-purple-black-gold, almost Karma Chameleon)</p>
<p>Boot NuttX on Ox64. Enter these commands…</p>
<div class="example-wrap"><pre class="language-bash"><code>NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs
QuickJS - Type &quot;\h&quot; for help
## Define the NuttX LED Command
qjs &gt; ULEDIOC_SETALL = 0x1d03
7427
## Open the NuttX LED Device (write-only)
qjs &gt; fd = os.open(&quot;/dev/userleds&quot;, os.O_WRONLY)
3
## Flip LED to On
qjs &gt; os.ioctl(fd, ULEDIOC_SETALL, 1)
bl808_gpiowrite: regaddr=0x20000938, set=0x1000000
0
## Flip LED to Off
qjs &gt; os.ioctl(fd, ULEDIOC_SETALL, 0)
bl808_gpiowrite: regaddr=0x20000938, clear=0x1000000
0</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/aeb74f047dc81be08e812458232ef92f#file-nuttx-quickjs-ox64-log-L165-L247">(Or run the <strong>Blinky JavaScript</strong>)</a></p>
<p>Yep <strong>ioctl()</strong> works great on a Real Device, with a Real LED!</p>
<p><a href="https://youtu.be/jv29M16CFJQ">(Watch the <strong>Demo on YouTube</strong>)</a></p>
<p><a href="https://gist.github.com/lupyuen/aeb74f047dc81be08e812458232ef92f#file-nuttx-quickjs-ox64-log-L112-L247">(See the <strong>NuttX Log</strong>)</a></p>
<p><img src="https://lupyuen.github.io/images/nim-blink2.webp" alt="Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: QuickJS blinks our LED" /></p>
<p><em>If we dont have an Ox64 SBC?</em></p>
<p>No worries, the exact same steps will work for <strong>QEMU Emulator</strong> (64-bit RISC-V)…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/1037eda906f11aef44f7670f8cc5a1c1d2141911"><strong>Add the LED Driver</strong></a> for QEMU</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/3b662696aff4b89e2b873a6b75d0006860fc9f7b"><strong>Increase the App Stack Size</strong></a> from 2 KB to 64 KB</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx-apps/commit/45dbe5ce07239e7ca7dcb50cb0e55da151052429"><strong>Patch the <code>leds</code> app</strong></a> for testing LED Driver</p>
</li>
</ul>
<p>When we download (or build) <strong>NuttX and QuickJS</strong></p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-nuttx-for-qemu"><strong>“Build NuttX for QEMU”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx"><strong>“Build QuickJS for NuttX QEMU”</strong></a></p>
</li>
</ul>
<p>QuickJS blinks a <strong>Simulated LED</strong> on NuttX QEMU…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Start NuttX on QEMU
## Remove `-bios none` for newer versions of NuttX
$ qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -bios none -kernel nuttx -nographic
## Run our Blinky JavaScript with QuickJS
NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs --std /system/bin/blink.js
led=0, val=1
led=0, val=0
led=0, val=1</code></pre></div>
<p>To Exit QEMU: Press <strong><code>Ctrl-A</code></strong> then <strong><code>x</code></strong></p>
<p><a href="https://gist.github.com/lupyuen/a3d2a491112eaf5810edc1fa355606db">(See the <strong>Complete Log</strong>)</a></p>
<p><img src="https://lupyuen.github.io/images/quickjs-text.webp" alt="QuickJS Code Size rendered with linkermapviz" /></p>
<p><a href="https://lupyuen.github.io/nuttx-tinyemu/quickjs/linkermap"><em>QuickJS Code Size rendered with linkermapviz</em></a></p>
<h1 id="how-small-is-quickjs"><a class="doc-anchor" href="#how-small-is-quickjs">§</a>6 How Small is QuickJS</h1>
<p><em>Will QuickJS run on all kinds of NuttX Devices?</em></p>
<div class="example-wrap"><pre class="language-bash"><code>$ riscv64-unknown-elf-size apps/bin/qjs
text data bss dec
554847 260 94 555201</code></pre></div>
<p>Probably not ALL devices? JavaScript needs <strong>a fair bit of RAM</strong> to run comfortably.</p>
<p><em>Whats the Memory Footprint like?</em></p>
<p>We ran <a href="https://github.com/PromyLOPh/linkermapviz"><strong>linkermapviz</strong></a> on the <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qjs-riscv.map"><strong>QuickJS Linker Map</strong></a> for NuttX QEMU…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Visualise the QuickJS Linker Map for NuttX QEMU.
## Produces linkermap.html: https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/linkermap.html
git clone https://github.com/PromyLOPh/linkermapviz
cd linkermapviz
pip3 install .
linkermapviz &lt; quickjs-nuttx/nuttx/qjs-riscv.map</code></pre></div>
<p>Which produces the <a href="https://lupyuen.github.io/nuttx-tinyemu/quickjs/linkermap"><strong>Visualised Linker Map</strong></a> for QuickJS. (Pics above and below)</p>
<p>Here are the sizes of QuickJS and its options…</p>
<div><table><thead><tr><th style="text-align: left">Size of Code + Data (Read-Only)</th><th style="text-align: center"></th></tr></thead><tbody>
<tr><td style="text-align: left">QuickJS with All The Toppings</td><td style="text-align: center"><strong>554 KB</strong></td></tr>
<tr><td style="text-align: left">Without REPL</td><td style="text-align: center"><strong>538 KB</strong></td></tr>
<tr><td style="text-align: left">Without BigInt</td><td style="text-align: center"><strong>522 KB</strong></td></tr>
<tr><td style="text-align: left">Without BigInt, REPL</td><td style="text-align: center"><strong>506 KB</strong></td></tr>
</tbody></table>
</div><span style="font-size:90%">
<p><a href="https://bellard.org/quickjs/quickjs.html#Quick-start">(<strong>REPL</strong> is for Interactive Commands)</a></p>
<p><a href="https://bellard.org/quickjs/quickjs.html#BigInt_002c-BigFloat_002c-BigDecimal">(<strong>BigInt</strong> is for Big Numbers and Calculator)</a></p>
</span>
<p><img src="https://lupyuen.github.io/images/quickjs-data.jpg" alt="QuickJS Data Size" /></p>
<p><em>What about Heap Memory Size?</em></p>
<p>Based on the NuttX Logs with <a href="https://github.com/lupyuen2/wip-nuttx/blob/master/Kconfig#L963-L988"><strong>Heap Logging Enabled</strong></a>: <em>“Build Setup &gt; Debug Options &gt; Enable Debug Features &gt; Memory Manager Debug Features &gt; Info Output”</em></p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/d2dbef1afef26ae4cc76719d7cac3740da5f3387/nuttx/qemu.log#L74-L2905"><strong>Heap Log: Without REPL</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/38e004e6eb643932f6957e03828ad25242cf803a/nuttx/qemu.log#L74-L18308"><strong>Heap Log: With REPL</strong></a></p>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx">(<strong>REPL</strong> runs extra <strong>JavaScript Bytecode</strong>)</a></p>
</li>
</ul>
<p><img src="https://lupyuen.github.io/images/quickjs-sheet.webp" alt="Computing the QuickJS Heap Usage with a Spreadsheet" /></p>
<p>We compute the <strong>Heap Usage</strong> with a Spreadsheet (pic above)…</p>
<ul>
<li>
<p><a href="https://docs.google.com/spreadsheets/d/1EpdktueHxfAR4VR80d1XSZRwdO2UvNGf_sPetHHzAGQ/edit?usp=sharing"><strong>Heap Usage: Without REPL</strong> (Google Sheets)</a></p>
</li>
<li>
<p><a href="https://docs.google.com/spreadsheets/d/1g0-O2qdgjwNfSIxfayNzpUN8mmMyWFmRf2dMyQ9a8JI/edit?usp=sharing"><strong>Heap Usage: With REPL</strong> (Google Sheets)</a></p>
</li>
</ul>
<p>(<strong>“Free Size”</strong> might be inaccurate because it uses <strong>VLOOKUP</strong> for Top-Down Lookup, though we actually need Down-Top Lookup)</p>
<p>And we derive the <strong>QuickJS Heap Usage</strong> (pic below)…</p>
<div><table><thead><tr><th style="text-align: left">Max Heap Usage</th><th style="text-align: center"></th></tr></thead><tbody>
<tr><td style="text-align: left">QuickJS without REPL</td><td style="text-align: center"><strong>276 KB</strong></td></tr>
<tr><td style="text-align: left">QuickJS with REPL</td><td style="text-align: center"><strong>371 KB</strong></td></tr>
</tbody></table>
</div>
<p><img src="https://lupyuen.github.io/images/quickjs-heap.jpg" alt="QuickJS Heap Usage" /></p>
<p>Which totals <strong>782 KB</strong> for Barebones QuickJS. And a whopping <strong>925 KB</strong> for Turducken QuickJS. (Nearly 1 MB for Code + Data + Heap!)</p>
<p>For newer <strong>Upsized NuttX Gadgets</strong> that are <strong>Extra Roomy</strong> (and Vroomy), theres a high chance that we can run QuickJS…</p>
<p>And experiment with all kinds of <strong>NuttX Drivers</strong> via ioctl(). The Interactive JavaScript Way!</p>
<p><img src="https://lupyuen.github.io/images/quickjs-size.png" alt="QEMU vs Ox64 QuickJS: 4 MB vs 22 MB" /></p>
<p><em>QEMU vs Ox64 QuickJS: Any diff? (Pic above)</em></p>
<p>QuickJS for NuttX QEMU is more Memory-Efficient because it uses <a href="https://github.com/lupyuen/quickjs-nuttx#full-linking-for-nuttx-apps"><strong>Full Linking</strong></a>.</p>
<p>(Instead of ELF Loader patching the <a href="https://lupyuen.github.io/articles/app#inside-a-nuttx-app"><strong>Relocatable Symbols</strong></a> at runtime)</p>
<p>Ox64 QuickJS was slower and <strong>multi-deca-mega-chonky</strong>: 22 MB! So we switched to <a href="https://github.com/lupyuen/quickjs-nuttx#switch-ox64-quickjs-to-full-linking"><strong>QuickJS with Full Linking</strong></a>, QuickJS is now 4 MB on Ox64. (Similar to QEMU)</p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx#full-linking-for-nuttx-apps">(About <strong>NuttX Full Linking</strong>)</a></p>
<p><img src="https://lupyuen.github.io/images/quickjs-title.webp" alt="QuickJS JavaScript Engine to Apache NuttX RTOS" /></p>
<p><a href="https://youtu.be/AFDVceqQNRs"><em>Watch the Demo on YouTube</em></a></p>
<h1 id="simulate-the-led-on-ox64-emulator"><a class="doc-anchor" href="#simulate-the-led-on-ox64-emulator">§</a>7 Simulate the LED on Ox64 Emulator</h1>
<p><em>NuttX Emulator blinks a Simulated LED…</em></p>
<p><em>How does it work? (Pic above, lower right)</em></p>
<p>We modded <a href="https://lupyuen.github.io/articles/tinyemu3"><strong>NuttX Emulator</strong></a> (in WebAssembly) to…</p>
<ol>
<li>
<p>Watch for updates to <a href="https://lupyuen.github.io/articles/nim#led-driver-for-ox64"><strong>GPIO Registers</strong></a></p>
<p>(Like <code>0x2000_0938</code> for GPIO 29)</p>
</li>
<li>
<p>Notify our <a href="https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/term.js#L487-L497"><strong>Web Browser JavaScript</strong></a> of any updates</p>
<p>(Like <em>{“nuttxemu”:{“gpio29”:1}}</em>)</p>
</li>
<li>
<p>And our Web Browser JavaScript <a href="https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/term.js#L497-L507"><strong>Flips the Simulated LED</strong></a></p>
<p>(On or Off)</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/quickjs-led.jpg" alt="Simulate the LED on Ox64 Emulator" /></p>
<p>Heres our <a href="https://lupyuen.github.io/articles/tinyemu3"><strong>NuttX Emulator</strong></a> (WebAssembly) intercepting all <strong>Writes to GPIO 29</strong>: <a href="https://github.com/lupyuen/ox64-tinyemu/blob/main/riscv_cpu.c#L486-L553">riscv_cpu.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// WebAssembly called by TinyEmu to emulate
// Writes to RISC-V Addresses
int target_write_slow(...) {
...
// Intercept Writes to Memory-Mapped I/O
switch(paddr) {
// If we&#39;re writing to BL808 GPIO 29 (0x2000_0938)...
case 0x20000938: {
// Send an Emulator Notification to the Console:
// {&quot;nuttxemu&quot;:{&quot;gpio29&quot;:1}}
// Check if the Output Bit is Off or On
#define reg_gpio_xx_o 24
const char b =
((val &amp; (1 &lt;&lt; reg_gpio_xx_o)) == 0)
? &#39;0&#39; : &#39;1&#39;;
// Send the Notification to Console
char notify[] = &quot;{\&quot;nuttxemu\&quot;:{\&quot;gpio29\&quot;:0}}\r\n&quot;;
notify[strlen(notify) - 5] = b;
print_console(NULL, notify, strlen(notify));
}</code></pre></div>
<p>Which sends a Notification to the Web Browser (JavaScript), saying that the <strong>GPIO Output has changed</strong></p>
<div class="example-wrap"><pre class="language-json"><code>{&quot;nuttxemu&quot;:
{&quot;gpio29&quot;: 1}
}</code></pre></div>
<p>Our Web Browser (JavaScript) receives the Notification and <strong>Flips the Simulated LED</strong>: <a href="https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/term.js#L487-L507">term.js</a></p>
<div class="example-wrap"><pre class="language-javascript"><code>// JavaScript called by our WebAssembly
// to print something to Console
Term.prototype.write = function(str) {
// If this is a Notification JSON from Emulator WebAssembly:
// {&quot;nuttxemu&quot;:{&quot;gpio29&quot;:1}}
if (str.indexOf(`{&quot;nuttxemu&quot;:`) == 0) {
// Get the GPIO Number and GPIO Value from JSON
const notify = JSON.parse(str).nuttxemu; // {gpio29:1}
const gpio = Object.keys(notify)[0]; // &quot;gpio29&quot;
const val = notify[gpio]; // 0 or 1
// Render the GPIO in HTML:
// &lt;td id=&quot;gpio29&quot; class=&quot;gpio_on&quot;&gt;GPIO29&lt;/td&gt;
document.getElementById(&quot;status&quot;).style.width = document.getElementById(&quot;term_wrap&quot;).style.width; // Space out the GPIO Status
const gpio_status = document.getElementById(gpio);
gpio_status.style.display = &quot;block&quot;;
// GPIO Off or On
gpio_status.className = (val == 0)
? &quot;gpio_off&quot; // Normal CSS Style
: &quot;gpio_on&quot;; // Green CSS Style
return; // Don&#39;t show in Console Output
}</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/index.html#L21-L29">(<strong>status</strong> and <strong>gpio29</strong> are in HTML)</a></p>
<p><a href="https://github.com/lupyuen/nuttx-tinyemu/blob/main/docs/quickjs/style.css#L106-L117">(<strong>gpio_off</strong> and <strong>gpio_on</strong> are in CSS)</a></p>
<p><strong>Emulator Notifications</strong> wont appear in the Emulator Console Output. (Because we suppressed them)</p>
<p>Well see the Notifications in the <strong>JavaScript Console</strong>. (Pic below)</p>
<p><img src="https://lupyuen.github.io/images/quickjs-notify.png" alt="Notifications from NuttX Emulator appear in JavaScript Console" /></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>8 Whats Next</h1>
<p>Thanks to the <a href="https://github.com/bellard/quickjs"><strong>QuickJS Team</strong></a>, we have a fun new way to Experiment Interactively with NuttX Gadgets!</p>
<ul>
<li>
<p>We ran QuickJS on <strong>Ox64 BL808 RISC-V SBC</strong> and <strong>NuttX Emulator</strong></p>
</li>
<li>
<p>Blinking the LED works great after we added the <strong>ioctl() function</strong></p>
</li>
<li>
<p><strong>Memory Footprint</strong> of QuickJS might reach 1 MB (if were using all the features)</p>
</li>
<li>
<p>How will you use <strong>QuickJS on NuttX</strong>? Lemme know!</p>
</li>
</ul>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> (and the awesome NuttX Community) for supporting my work! This article wouldnt have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor"><strong>Sponsor me a coffee</strong></a></p>
</li>
<li>
<p><a href="https://news.ycombinator.com/item?id=39414388"><strong>Discuss this article on Hacker News</strong></a></p>
</li>
<li>
<p><a href="https://forum.pine64.org/showthread.php?tid=19083"><strong>Discuss this article on Pine64 Forum</strong></a></p>
</li>
<li>
<p><a href="https://bbs.bouffalolab.com/d/283-article-quickjs-javascript-on-a-real-time-operating-system-ox64-nuttx"><strong>Discuss this article on Bouffalo Lab Forum</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-ox64"><strong>My Current Project: “Apache NuttX RTOS for Ox64 BL808”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-star64"><strong>My Other Project: “NuttX for Star64 JH7110”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>Older Project: “NuttX for PinePhone”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io"><strong>Check out my articles</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml"><strong>RSS Feed</strong></a></p>
</li>
</ul>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/quickjs.md"><strong>lupyuen.github.io/src/quickjs.md</strong></a></p>
<p><img src="https://lupyuen.github.io/images/quickjs-expect.png" alt="Auto-Test QuickJS with Expect Scripting" /></p>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qemu.exp"><em>Auto-Test QuickJS with Expect Scripting</em></a></p>
<h1 id="appendix-build-quickjs-for-nuttx"><a class="doc-anchor" href="#appendix-build-quickjs-for-nuttx">§</a>9 Appendix: Build QuickJS for NuttX</h1>
<p>Before building QuickJS: Build NuttX for <strong>QEMU or Ox64</strong></p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-nuttx-for-qemu"><strong>Build NuttX for QEMU</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-nuttx-for-ox64"><strong>Build NuttX for Ox64</strong></a></p>
</li>
</ul>
<p>(Or download the NuttX and QuickJS Binaries from these links)</p>
<p>Then follow these steps to build <strong>QuickJS for NuttX</strong> (QEMU or Ox64)…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Download and build QuickJS for NuttX
git clone https://github.com/lupyuen/quickjs-nuttx
cd quickjs-nutttx/nuttx
./build.sh</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh">(See the <strong>Build Script</strong>)</a></p>
<p>Remember to…</p>
<ul>
<li>
<p>Set the <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh#L4-L8"><strong>Toolchain Path</strong></a></p>
</li>
<li>
<p>Select <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh#L8-L14"><strong>QuickJS for NuttX QEMU</strong></a></p>
</li>
<li>
<p>Or <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh#L14-L25"><strong>QuickJS for NuttX Ox64</strong></a></p>
</li>
<li>
<p>Do <a href="https://github.com/lupyuen/quickjs-nuttx#switch-ox64-quickjs-to-full-linking"><strong>Full Linking</strong></a> for Smaller Ox64 Binaries</p>
</li>
</ul>
<p><em>How did we figure out the steps to build QuickJS for NuttX?</em></p>
<p>We ran “<strong><code>make</code> <code>--trace</code></strong>” to observe the <strong>QuickJS Build</strong>: <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/make.log">make.log</a></p>
<div class="example-wrap"><pre class="language-bash"><code>## Build QuickJS for Debian x64 and observe the build
$ make --trace
## Build qjs.o
gcc \
-g \
-Wall \
-MMD \
-MF .obj/qjs.o.d \
-Wno-array-bounds \
-Wno-format-truncation \
-fwrapv \
-D_GNU_SOURCE \
-DCONFIG_VERSION=\&quot;2024-01-13\&quot; \
-DCONFIG_BIGNUM \
-O2 \
-c \
-o .obj/qjs.o \
qjs.c
## Omitted: Build a bunch of other binaries
...
## Link them together
gcc \
-g \
-rdynamic \
-o qjs \
.obj/qjs.o \
.obj/repl.o \
.obj/quickjs.o \
.obj/libregexp.o \
.obj/libunicode.o \
.obj/cutils.o \
.obj/quickjs-libc.o \
.obj/libbf.o \
.obj/qjscalc.o \
-lm \
-ldl \
-lpthread</code></pre></div>
<p>And we know that NuttX builds <a href="https://github.com/lupyuen/tcc-riscv32-wasm#how-nuttx-build-links-a-nuttx-app"><strong>NuttX Apps</strong></a> like this…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Build NuttX Apps for QEMU and observe the build
$ cd ../apps
$ make --trace import
## Compile hello app
## For riscv-none-elf-gcc: &quot;-march=rv64imafdc_zicsr_zifencei&quot;
## For riscv64-unknown-elf-gcc: &quot;-march=rv64imafdc&quot;
riscv-none-elf-gcc \
-c \
-fno-common \
-Wall \
-Wstrict-prototypes \
-Wshadow \
-Wundef \
-Wno-attributes \
-Wno-unknown-pragmas \
-Wno-psabi \
-fno-common \
-pipe \
-Os \
-fno-strict-aliasing \
-fomit-frame-pointer \
-ffunction-sections \
-fdata-sections \
-g \
-mcmodel=medany \
-march=rv64imafdc_zicsr_zifencei \
-mabi=lp64d \
-isystem apps/import/include \
-isystem apps/import/include \
-D__NuttX__ \
-I &quot;apps/include&quot; \
hello_main.c \
-o hello_main.c.workspaces.bookworm.apps.examples.hello.o
## Link hello app
## For riscv-none-elf-ld: &quot;rv64imafdc_zicsr/lp64d&quot;
## For riscv64-unknown-elf-ld: &quot;rv64imafdc/lp64d
riscv-none-elf-ld \
--oformat elf64-littleriscv \
-e _start \
-Bstatic \
-Tapps/import/scripts/gnu-elf.ld \
-Lapps/import/libs \
-L &quot;xpack-riscv-none-elf-gcc-13.2.0-2/lib/gcc/riscv-none-elf/13.2.0/rv64imafdc_zicsr/lp64d&quot; \
apps/import/startup/crt0.o \
hello_main.c.workspaces.bookworm.apps.examples.hello.o \
--start-group \
-lmm \
-lc \
-lproxies \
-lgcc apps/libapps.a \
xpack-riscv-none-elf-gcc-13.2.0-2/lib/gcc/riscv-none-elf/13.2.0/rv64imafdc_zicsr/lp64d/libgcc.a \
--end-group \
-o apps/bin/hello</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh#L19-L25">(<strong>Ox64 Build</strong> is a little different)</a></p>
<p>Thus we…</p>
<ul>
<li>
<p>Combine everything above into our <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/build.sh"><strong>QuickJS Build Script</strong></a></p>
<p>(Later well merge into the NuttX Makefiles)</p>
</li>
<li>
<p>Add an <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qemu.exp"><strong>Expect Script</strong></a> for auto-testing QuickJS on QEMU</p>
<p>(Pic above)</p>
</li>
</ul>
<p>Everything builds OK without changing any code in QuickJS! Though we <a href="https://lupyuen.github.io/articles/quickjs#build-quickjs-for-nuttx"><strong>stubbed out some functions</strong></a> because NuttX works a little differently.</p>
<p><em>repl.c and qjscalc.c are missing?</em></p>
<p>Theyre generated by the <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/make.log#L28-L32"><strong>QuickJS Compiler</strong></a></p>
<div class="example-wrap"><pre class="language-bash"><code>## Compile the REPL from JavaScript to C
./qjsc -c -o repl.c -m repl.js
## Compile the BigNum Calculator from JavaScript to C
./qjsc -fbignum -c -o qjscalc.c qjscalc.js</code></pre></div>
<p>So we <strong>borrow the output</strong> from another QuickJS Build (Debian x64) and add to NuttX…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/repl.c"><strong>nuttx/repl.c</strong></a>: Interactive-Mode REPL for QuickJS</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/qjscalc.c"><strong>nuttx/qjscalc.c</strong></a>: BigNum Calculator for QuickJS</p>
</li>
</ul>
<p><em>Whats inside repl.c and qjscalc.c?</em></p>
<p>They contain plenty of <a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/repl.c"><strong>JavaScript Bytecode</strong></a> for REPL and BigNum Calculator. Brilliant!</p>
<div class="example-wrap"><pre class="language-c"><code>/* File generated automatically by the QuickJS compiler. */
#include &lt;inttypes.h&gt;
const uint32_t qjsc_repl_size = 16280;
const uint8_t qjsc_repl[16280] = {
0x02, 0xa5, 0x03, 0x0e, 0x72, 0x65, 0x70, 0x6c,
0x2e, 0x6a, 0x73, 0x06, 0x73, 0x74, 0x64, 0x04, ...</code></pre></div>
<p>Thats why REPL and BigNum will require more Heap Memory, to execute the extra JavaScript Bytecode.</p>
<p><img src="https://lupyuen.github.io/images/quickjs-qemu.png" alt="QuickJS on NuttX QEMU" /></p>
<p><a href="https://gist.github.com/lupyuen/a3d2a491112eaf5810edc1fa355606db"><em>QuickJS on NuttX QEMU</em></a></p>
<h1 id="appendix-build-nuttx-for-qemu"><a class="doc-anchor" href="#appendix-build-nuttx-for-qemu">§</a>10 Appendix: Build NuttX for QEMU</h1>
<p>In this article, we compiled a Work-In-Progress Version of <strong>Apache NuttX RTOS for QEMU RISC-V (64-bit Kernel Mode)</strong> that has these updates…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/1037eda906f11aef44f7670f8cc5a1c1d2141911"><strong>Add the LED Driver</strong></a> for QEMU</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/3b662696aff4b89e2b873a6b75d0006860fc9f7b"><strong>Increase the App Stack Size</strong></a> from 2 KB to 64 KB</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx-apps/commit/45dbe5ce07239e7ca7dcb50cb0e55da151052429"><strong>Patch the <code>leds</code> app</strong></a> for testing LED Driver</p>
</li>
</ul>
<p>We may download the <a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/qemuled-1"><strong>NuttX Binaries for QEMU</strong></a></p>
<ol>
<li>
<p>Download the NuttX Kernel: <a href="https://github.com/lupyuen2/wip-nuttx/releases/download/qemuled-1/nuttx"><strong><code>nuttx</code></strong></a></p>
<p>Copy to <strong><code>$HOME/nuttx/</code></strong></p>
</li>
<li>
<p>Download the NuttX Apps: <a href="https://github.com/lupyuen2/wip-nuttx/releases/download/qemuled-1/apps-bin.zip"><strong><code>apps-bin.zip</code></strong></a></p>
<p>Unzip and copy the files inside (not the folder) into <strong><code>$HOME/apps/bin/</code></strong></p>
<p>(We should see <strong><code>$HOME/apps/bin/qjs</code></strong> and <strong><code>blink.js</code></strong>)</p>
</li>
<li>
<p>Then run…</p>
<div class="example-wrap"><pre class="language-bash"><code>$ cd $HOME/nuttx/
$ qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-bios none \
-kernel nuttx \
-nographic</code></pre></div>
<p>(Remove <strong><code>-bios none</code></strong> for newer versions of NuttX)</p>
<p><a href="https://gist.github.com/lupyuen/a3d2a491112eaf5810edc1fa355606db">(See the <strong>NuttX Log</strong>)</a></p>
</li>
</ol>
<p>Or if we prefer to <strong>build NuttX ourselves</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Download the WIP NuttX Source Code
git clone \
--branch qemuled \
https://github.com/lupyuen2/wip-nuttx \
nuttx
git clone \
--branch qemuled \
https://github.com/lupyuen2/wip-nuttx-apps \
apps
## Configure NuttX for QEMU RISC-V (64-bit Kernel Mode)
cd nuttx
tools/configure.sh rv-virt:knsh64
## Build NuttX
make
## Dump the disassembly to nuttx.S
riscv64-unknown-elf-objdump \
--syms --source --reloc --demangle --line-numbers --wide \
--debugging \
nuttx \
&gt;nuttx.S \
2&gt;&amp;1
## Build the Apps Filesystem
make -j 8 export
pushd ../apps
./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz
make -j 8 import
popd</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/release#build-nuttx-for-star64">(Remember to install the <strong>Build Prerequisites and Toolchain</strong>)</a></p>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/qemuled-1">(See the <strong>Build Script</strong>)</a></p>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/qemuled-1">(See the <strong>Build Outputs</strong>)</a></p>
<p>This produces the NuttX ELF Image <strong><code>nuttx</code></strong> that well boot on QEMU RISC-V Emulator in a while.</p>
<p>But first: We build <strong>QuickJS for QEMU</strong>, which will produce <code>qjs</code> and <code>blink.sh</code> in the <code>apps/bin</code> folder…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx"><strong>“Build QuickJS for NuttX QEMU”</strong></a></li>
</ul>
<p>Now we boot <strong><code>nuttx</code></strong> on QEMU RISC-V Emulator…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Start the QEMU RISC-V Emulator (64-bit) with NuttX RTOS
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-bios none \
-kernel nuttx \
-nographic</code></pre></div>
<p>(Remove <strong><code>-bios none</code></strong> for newer versions of NuttX)</p>
<p>At the NuttX Prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>qjs --std /system/bin/blink.js</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/blink.js">(See the <strong>Blinky JavaScript</strong>)</a></p>
<p>QuickJS runs our Blinky JavaScript and blinks a <strong>Simulated LED</strong></p>
<div class="example-wrap"><pre class="language-text"><code>## Remove `-bios none` for newer versions of NuttX
$ qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -bios none -kernel nuttx -nographic
NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs --std /system/bin/blink.js
led=0, val=1
led=0, val=0
led=0, val=1</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/a3d2a491112eaf5810edc1fa355606db">(See the <strong>Complete Log</strong>)</a></p>
<p>To Exit QEMU: Press <strong><code>Ctrl-A</code></strong> then <strong><code>x</code></strong></p>
<p><img src="https://lupyuen.github.io/images/quickjs-title2.png" alt="QuickJS on NuttX Ox64" /></p>
<p><a href="https://gist.github.com/lupyuen/f879aa3378aa1b0170a1d3ea2b0b9d67"><em>QuickJS on NuttX Ox64</em></a></p>
<h1 id="appendix-build-nuttx-for-ox64"><a class="doc-anchor" href="#appendix-build-nuttx-for-ox64">§</a>11 Appendix: Build NuttX for Ox64</h1>
<p>In this article, we compiled a Work-In-Progress Version of <strong>Apache NuttX RTOS for Ox64</strong> that has these updates…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/8f75f3744f3964bd3ed0596421a93e59fb39cdd8"><strong>Add the GPIO Driver</strong></a> for Ox64 BL808</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/4f3996959132ca0d35874b7be3eef89d6bf7f351"><strong>Add the LED Driver</strong></a> for Ox64 BL808</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/904b95534298378d64b99c1f9e649f8bc27a8048"><strong>Increase the App Stack Size</strong></a> from 2 KB to 64 KB</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/commit/28453790d06c0282b85e5df98624f8fa1c0b2226"><strong>Increase the RAM Disk Region</strong></a> from 16 MB to 40 MB</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx-apps/commit/66f1389c8d17eecdc5ef7baa62d13435bd053ee3"><strong>Patch the <code>leds</code> app</strong></a> for testing LED Driver</p>
</li>
</ul>
<p>We may download the <a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/gpio2-1"><strong>NuttX Binaries for Ox64</strong></a></p>
<ol>
<li>
<p>Download the NuttX Image: <a href="https://github.com/lupyuen2/wip-nuttx/releases/download/gpio2-1/Image"><strong><code>Image</code></strong></a></p>
</li>
<li>
<p>Prepare a <strong>Linux microSD</strong> for Ox64 as described <a href="https://lupyuen.github.io/articles/ox64"><strong>in the previous article</strong></a>.</p>
<p><a href="https://lupyuen.github.io/articles/ox64#flash-opensbi-and-u-boot">(Remember to flash <strong>OpenSBI and U-Boot Bootloader</strong>)</a></p>
</li>
<li>
<p>Copy the <strong><code>Image</code></strong> file (from above) and overwrite the <strong><code>Image</code></strong> in the Linux microSD…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Overwrite the Linux Image
## on Ox64 microSD
cp Image \
&quot;/Volumes/NO NAME/Image&quot;
diskutil unmountDisk /dev/disk2</code></pre></div></li>
<li>
<p>Insert the <a href="https://lupyuen.github.io/images/ox64-sd.jpg"><strong>microSD into Ox64</strong></a> and power up Ox64.</p>
</li>
<li>
<p>Ox64 boots <a href="https://lupyuen.github.io/articles/sbi"><strong>OpenSBI</strong></a>, which starts <a href="https://lupyuen.github.io/articles/linux#u-boot-bootloader-for-star64"><strong>U-Boot Bootloader</strong></a>, which starts <strong>NuttX Kernel</strong> and the NuttX Shell (NSH).</p>
</li>
<li>
<p>At the NuttX Prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>qjs --std /system/bin/blink.js</code></pre></div>
<p><a href="https://github.com/lupyuen/quickjs-nuttx/blob/master/nuttx/blink.js">(See the <strong>Blinky JavaScript</strong>)</a></p>
</li>
<li>
<p>QuickJS executes our Blinky JavaScript and blinks our <strong>LED on GPIO 29</strong></p>
<div class="example-wrap"><pre class="language-yaml"><code>NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt; qjs --std /system/bin/blink.js
bl808_gpiowrite: regaddr=0x20000938, set=0x1000000
bl808_gpiowrite: regaddr=0x20000938, clear=0x1000000
bl808_gpiowrite: regaddr=0x20000938, set=0x1000000
bl808_gpiowrite: regaddr=0x20000938, clear=0x1000000</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/aeb74f047dc81be08e812458232ef92f#file-nuttx-quickjs-ox64-log-L112-L247">(See the <strong>Complete Log</strong>)</a></p>
</li>
</ol>
<p>Or if we prefer to <strong>build NuttX ourselves</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Download the WIP NuttX Source Code
git clone \
--branch gpio2 \
https://github.com/lupyuen2/wip-nuttx \
nuttx
git clone \
--branch gpio2 \
https://github.com/lupyuen2/wip-nuttx-apps \
apps
## Configure NuttX for Ox64 BL808 RISC-V SBC
cd nuttx
tools/configure.sh ox64:nsh
## Build NuttX
make
## Export the NuttX Kernel
## to `nuttx.bin`
riscv64-unknown-elf-objcopy \
-O binary \
nuttx \
nuttx.bin
## Dump the disassembly to nuttx.S
riscv64-unknown-elf-objdump \
--syms --source --reloc --demangle --line-numbers --wide \
--debugging \
nuttx \
&gt;nuttx.S \
2&gt;&amp;1
## Dump the hello_nim disassembly to hello_nim.S
riscv64-unknown-elf-objdump \
--syms --source --reloc --demangle --line-numbers --wide \
--debugging \
../apps/bin/hello_nim \
&gt;hello_nim.S \
2&gt;&amp;1</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/release#build-nuttx-for-star64">(Remember to install the <strong>Build Prerequisites and Toolchain</strong>)</a></p>
<p>We build the <strong>NuttX Apps Filesystem</strong> that contains NuttX Shell and NuttX Apps…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Build the Apps Filesystem
make -j 8 export
pushd ../apps
./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz
make -j 8 import
popd</code></pre></div>
<p>Next we build <strong>QuickJS for Ox64</strong>, which will produce <code>qjs</code> and <code>blink.sh</code> in the <code>apps/bin</code> folder…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/quickjs#appendix-build-quickjs-for-nuttx"><strong>“Build QuickJS for NuttX Ox64”</strong></a></li>
</ul>
<p>We bundle QuickJS into the <strong>Initial RAM Disk</strong> and append it to the NuttX Image…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Generate the Initial RAM Disk `initrd`
## in ROMFS Filesystem Format
## from the Apps Filesystem `../apps/bin`
## and label it `NuttXBootVol`
genromfs \
-f initrd \
-d ../apps/bin \
-V &quot;NuttXBootVol&quot;
## Prepare a Padding with 64 KB of zeroes
head -c 65536 /dev/zero &gt;/tmp/nuttx.pad
## Append Padding and Initial RAM Disk to NuttX Kernel
cat nuttx.bin /tmp/nuttx.pad initrd \
&gt;Image</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/gpio2-1">(See the <strong>Build Script</strong>)</a></p>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/gpio2-1">(See the <strong>Build Outputs</strong>)</a></p>
<p>This produces the NuttX Image for Ox64: <strong><code>Image</code></strong></p>
<p>Follow the <a href="https://lupyuen.github.io/articles/quickjs#appendix-build-nuttx-for-ox64"><strong>earlier instructions</strong></a> to copy <strong><code>Image</code></strong> to a Linux microSD, boot it on Ox64 and run QuickJS. (Pic below)</p>
<p><a href="https://youtu.be/jv29M16CFJQ">(Watch the <strong>Demo on YouTube</strong>)</a></p>
<p><a href="https://gist.github.com/lupyuen/aeb74f047dc81be08e812458232ef92f#file-nuttx-quickjs-ox64-log-L112-L247">(See the <strong>NuttX Log</strong>)</a></p>
<p><em>What about the NuttX Image for Ox64 Emulator? (Pic above)</em></p>
<p>The exact same Build Outputs from above, we copied to the <a href="https://github.com/lupyuen/nuttx-tinyemu/tree/main/docs/quickjs"><strong>NuttX Emulator for Ox64</strong></a>.</p>
<p><img src="https://lupyuen.github.io/images/quickjs-ox64.webp" alt="QuickJS blinks an LED on Ox64 BL808 SBC" /></p>
<p><a href="https://youtu.be/jv29M16CFJQ"><em>QuickJS blinks an LED on Ox64 BL808 SBC</em></a></p>
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker and Prism Theme -->
<script src="../theme.js"></script>
<script src="../prism.js"></script>
<!-- Theme Picker and Prism Theme -->
<!-- End scripts/rustdoc-after.html -->
</body>
</html>