# QuickJS JavaScript Engine Integration Guide

- QuickJS: <https://bellard.org/quickjs/>, <https://bellard.org/quickjs/quickjs.html>
- QuickJS New Generation: <https://github.com/quickjs-ng/quickjs>
- Add Polyfills for Intl, Locale and others: <https://polyfill-fastly.io/>

## Unsupported JavaScript Features

### Explicitly Unsupported

| Feature | Status |
|---------|--------|
| **Tail calls** | Not supported |
| **ECMA402 (Internationalization API)** | Not supported - includes `Intl`, `Locale`, `DateTimeFormat`, `NumberFormat`, `Collator` |
| **Nested workers** | `os.Workolder()` does not support nested workers |
| **Map/Set in worker postMessage** | Structured cloning does not support Map/Set |

### Missing Standard APIs

QuickJS has a minimal standard library. Common browser/Node.js APIs that are **absent**:

| API | QuickJS Alternative |
|-----|---------------------|
| `fetch` | `std.urlGet()` (requires curl) |
| `URL`, `URLSearchParams` | Not available |
| `globalThis.setTimeout` | `os.setTimeout()` |
| `TextEncoder`, `TextDecoder` | Not available |
| `Performance` API | `os.now()` |
| `QueueMicrotask` | Not available |
| `structuredClone` | Limited support in workers |
| `WeakRef`, `FinalizationRegistry` | Not available |
| `Atomics` | Requires CONFIG_ATOMICS build flag |
| `Temporal` API | Not available (ES2024+) |

### What IS Supported

- ES2023: modules, async generators, proxies, BigInt, Promise
- RegExp with Unicode properties
- Classes, arrow functions, destructuring
- TypedArray, `for...of`, async/await

---

- QuickJS GitHub: <https://github.com/quickjs-ng/quickjs>
- LuaJIT FFI: <http://luajit.org/ext_ffi.html>

## Overview

QuickJS is a small and embeddable JavaScript engine supporting the ES2023 specification. This integration provides a Lua FFI interface to evaluate JavaScript code from Lua.

## Features

- **ES2023 Support**: Full ES2023 compliance including modules, async generators, proxies, and BigInt
- **Fast Interpreter**: Low startup time, runs 78,000 ECMAScript tests in ~2 minutes
- **Small Footprint**: 367 KiB x86 code for simple hello world
- **Reference Counting GC**: Deterministic behavior with cycle removal
- **Zero-copy**: Direct FFI calls to QuickJS C API

## Architecture

```text
Lua Code
   |
   v
quick-js.lua (FFI wrapper)
   |
   v
libquick-js.dylib (QuickJS shared library)
   |
   v
QuickJS Engine (JavaScript evaluation)
```

## Files

### Source (Input)

- **`libquickjs.js`** - JavaScript test functions
- **`xmake.lua`** - Build configuration

### Build (Output)

- **`libquickjs.dylib`** - Native shared library (located in `/Volumes/nc/bin/bin_osx_arm64/`)

### Usage

- **`lib/quick-js.lua`** - Lua module for QuickJS interaction
- **`quick-js-test.lua`** - Unit tests

## Building

```bash
cd /Volumes/nc/nc-server/lib/javascript/quick-js
xmake -vr
```

This:

1. Compiles QuickJS source files to object files
2. Links into shared library `libquickjs.dylib`
3. Copies to `/Volumes/nc/bin/bin_osx_arm64/libquickjs.dylib`

## Current Status

### Working Features ✅

- **Number evaluation**: `2 + 2` → `"4"`, `42` → `"42"`
- **Boolean evaluation**: `true` → `"true"`, `false` → `"false"`
- **Integer evaluation**: `100 + 23` → `123`
- **Complex expressions**: `(10 * 5) + 3` → `"53"`
- **Function definition and calling**: `add(5, 3)` → `"8"`
- **Global variables**: `setGlobal("x", 42)`, `multiEvalReuse()`
- **Runtime management**: `newRuntime()`, `freeRuntime()`

### Known Limitations ⚠️

- **String literals**: `'hello world'` returns pointer value instead of actual string
- **Object/Array literals**: Objects and arrays return pointer values instead of JSON
- **Error handling**: Syntax/runtime errors may not be properly caught

**Root Cause**: LuaJIT FFI has limited precision when passing 64-bit JSValue with negative tags (used for strings and objects in QuickJS's NaN boxing). The tag information is lost during FFI marshaling, causing `JS_ToCStringLen2` to receive corrupted values.

## API Reference

### Module Functions

#### `quickJs.newRuntime()`

Create new QuickJS runtime context.

```lua
local ctx = quickJs.newRuntime()
```

**Returns**: Context table or `nil, error` on failure

#### `quickJs.freeRuntime(ctx)`

Free QuickJS runtime context.

```lua
quickJs.freeRuntime(ctx)
```

#### `quickJs.eval(code, ctx)`

Evaluate JavaScript code and return result as string.

```lua
local result = quickJs.eval("2 + 2", ctx)  -- "4"
```

**Returns**: Result string or `nil, error` on failure

**Note**: String and object values may not work correctly due to FFI 64-bit precision limitations.

#### `quickJs.evalInt(code, ctx)`

Evaluate JavaScript code and return integer result.

```lua
local result = quickJs.evalInt("10 + 5", ctx)  -- 15
```

**Returns**: Integer result

#### `quickJs.call(funcName, param, ctx)`

Call JavaScript function by name.

```lua
quickJs.eval("function add(a, b) { return a + b; }", ctx)
local result = quickJs.call("add", "5,3", ctx)  -- "8"
```

**Parameters**:

- `funcName` - Name of global function to call
- `param` - Parameters as string (e.g., "5,3" for two parameters)

**Returns**: Result string or `nil, error` on failure

#### `quickJs.setGlobal(name, value, ctx)`

Set global variable in JavaScript context.

```lua
quickJs.setGlobal("myVar", 42, ctx)
local result = quickJs.eval("myVar", ctx)  -- "42"
```

#### `quickJs.getGlobal(name, ctx)`

Get global variable from JavaScript context.

```lua
quickJs.eval("let x = 42", ctx)
local value = quickJs.getGlobal("x", ctx)  -- "42"
```

**Note**: May not work correctly for string variables due to FFI limitations.

#### `quickJs.evalFile(path, ctx)`

Evaluate JavaScript file.

```lua
local result = quickJs.evalFile("/path/to/script.js", ctx)
```

#### `quickJs.getVersion()`

Get QuickJS version string.

```lua
local version = quickJs.getVersion()  -- "QuickJS 2025-09-13"
```

## Usage Examples

### Basic Number Evaluation

```lua
local quick-js = require "quick-js"

-- Use default context
local result = quickJs.eval("1 + 1")
print(result)  -- "2"

-- Integer result
local sum = quickJs.evalInt("10 + 20 + 30")
print(sum)  -- 60
```

### Boolean Evaluation

```lua
local trueResult = quickJs.eval("true", ctx)
print(trueResult)  -- "true"

local falseResult = quickJs.eval("false", ctx)
print(falseResult)  -- "false"
```

### Custom Context

```lua
local ctx = quickJs.newRuntime()

quickJs.eval("let counter = 0", ctx)
quickJs.eval("counter += 1", ctx)
quickJs.eval("counter += 1", ctx)
local result = quickJs.eval("counter", ctx)
print(result)  -- "2"

quickJs.freeRuntime(ctx)
```

### Define and Call Functions

```lua
local ctx = quickJs.newRuntime()

-- Define function
quickJs.eval([[
	function add(a, b) {
		return a + b;
	}
]], ctx)

-- Call function
local result = quickJs.call("add", "5,3", ctx)
print(result)  -- "8"

quickJs.freeRuntime(ctx)
```

## Performance Considerations

| Operation | Time | Notes |
|-----------|------|-------|
| Runtime creation | ~100 μs | One-time cost per context |
| Simple eval | <10 μs | Basic arithmetic |
| Function call | ~20 μs | Including parameter passing |
| File eval | +I/O | Depends on file size |

## Comparison: QuickJS vs Porffor vs SpiderMonkey

| Feature | QuickJS | Porffor AOT | SpiderMonkey JIT |
|---------|---------|-------------|------------------|
| **Startup** | Fast (~300 μs) | Slow (compile) | Medium |
| **First call** | Fast | Slow (compile) | Fast |
| **Repeated calls** | Fast | Fastest (native) | Fast |
| **Memory** | Low | Medium | High |
| **ES support** | ES2023 | Limited | ES2023+ |
| **Size** | 367 KiB | Varies | Large |

## Troubleshooting

### Dylib not found

```text
error: failed to load library 'libquickjs.dylib'
```

**Solution**: Run `xmake` to build dylib

### Context creation fails

```text
failed to create QuickJS runtime
```

**Solution**: Check that QuickJS source files exist at `/Volumes/stu/Documents/installed/js/quick-js/`

### String/object evaluation returns numeric values

```lua
local result = quickJs.eval("'hello world'", ctx)
print(result)  -- "683903904" (pointer value) instead of "hello world"
```

**Cause**: FFI 64-bit precision loss with NaN-boxed JSValues that have negative tags.

**Workaround**: Use JavaScript code that returns numbers or booleans instead of strings/objects.

## Test Coverage

Run tests:

```bash
cd /Volumes/nc/nc-server/lib/javascript/quick-js
lj quick-js-test.lua
```

**Passing tests**: Number evaluation, boolean evaluation, function calls, global variables, multi-evaluation reuse.

**Known failures**: String literals, object/array literals (due to FFI limitations).
