Skip to content

Latest commit

 

History

History
419 lines (312 loc) · 11 KB

File metadata and controls

419 lines (312 loc) · 11 KB

Runtime Codegen Library (runtime-codegen)

Overview

libruntime-codegen provides runtime code templates for bundled Oak modules, including the module system implementation for both Oak native and JavaScript targets.

Import

runtimeCodegen := import('runtime-codegen')
{ OakNativeRuntime: OakNativeRuntime, OakJSRuntime: OakJSRuntime } := import('runtime-codegen')
{ OakNativeRuntime: OakNativeRuntime, OakJSRuntime: OakJSRuntime
  encodeWATString: encodeWATString, renderWasmBundle: renderWasmBundle } := import('runtime-codegen')

Constants

OakNativeRuntime

Oak source code for the module system runtime (native Oak target).

Provides:

  • __Oak_Modules - Module registry
  • __Oak_Import_Aliases - Import alias mapping
  • __oak_modularize(name, module) - Register module
  • __oak_module_import(name) - Load module
{ OakNativeRuntime: OakNativeRuntime } := import('runtime-codegen')

// Used in bundled Oak code
bundleSource := OakNativeRuntime + '\n' + moduleCode

OakJSRuntime

JavaScript code for the module system runtime (web target).

Provides:

  • Module system (same functions as Oak runtime)
  • Language primitives:
    • __oak_eq(a, b) - Deep equality comparison
    • __oak_resolve_trampoline(fn, ...args) - Tail call optimization
    • __oak_trampoline(fn, ...args) - Trampoline wrapper
  • Helper functions for Oak semantics in JavaScript
{ OakJSRuntime: OakJSRuntime } := import('runtime-codegen')

// Used in web builds
jsBundleSource := OakJSRuntime + '\n' + transpiled JSCode

Oak Native Runtime

Module Registry

__Oak_Modules := {}              // Stores all modules
__Oak_Import_Aliases := ?        // Import path aliases

Module Registration

fn __oak_modularize(name, module) {
    __Oak_Modules.(name) := module
}

// Usage in bundle:
__oak_modularize('utils.oak', fn { /* module code */ })

Module Import

fn __oak_module_import(name) if ___runtime_lib?(name) {
    true -> import(name)  // Built-in library
    _ -> if type(module := __Oak_Modules.(name)) {
        :null -> if module := __Oak_Modules.(__Oak_Import_Aliases.(name)) {
            ? -> import(name)
            _ -> {
                mod := module()
                __Oak_Modules.(name) := mod  // Cache
                mod
            }
        }
        :function -> {
            m := module()
            __Oak_Modules.(name) := m  // Cache
            m
        }
        _ -> module  // Already cached
    }
}

JavaScript Runtime

Module System

const __Oak_Modules = {};
let __Oak_Import_Aliases;

function __oak_modularize(name, fn) {
    __Oak_Modules[name] = fn;
}

function __oak_module_import(name) {
    if (typeof __Oak_Modules[name] === 'object') {
        return __Oak_Modules[name];  // Cached
    }
    
    const module = __Oak_Modules[name] || 
                   __Oak_Modules[__Oak_Import_Aliases[name]];
    
    if (module) {
        __Oak_Modules[name] = {};  // Break circular imports
        return __Oak_Modules[name] = module();
    } else {
        throw new Error(`Could not import Oak module "${name}" at runtime`);
    }
}

Deep Equality

function __oak_eq(a, b) {
    // Handle Oak's empty value (_)
    if (a === __Oak_Empty || b === __Oak_Empty) return true;
    
    // Null handling
    if (a == null && b == null) return true;
    if (a == null || b == null) return false;
    
    // Primitives
    if (typeof a === 'boolean' || typeof a === 'number' ||
        typeof a === 'symbol' || typeof a === 'function') {
        return a === b;
    }
    
    // Strings (Oak strings need special handling)
    a = __as_oak_string(a);
    b = __as_oak_string(b);
    if (typeof a !== typeof b) return false;
    if (__is_oak_string(a) && __is_oak_string(b)) {
        return a.valueOf() === b.valueOf();
    }
    
    // Collections (recursive comparison)
    if (len(a) !== len(b)) return false;
    for (const key of keys(a)) {
        if (!__oak_eq(a[key], b[key])) return false;
    }
    return true;
}

Tail Call Optimization (Trampolining)

// Resolve trampoline to final value
function __oak_resolve_trampoline(fn, ...args) {
    let result = fn(...args);
    while (typeof result === 'object' && 
           typeof result[Symbol.iterator] === 'function') {
        result = result[Symbol.iterator]().next().value(...args);
    }
    return result;
}

// Create trampoline for tail recursion
function __oak_trampoline(fn, ...args) {
    return function*() {
        yield function() {
            return fn(...args);
        };
    };
}

Usage Examples

Oak Native Bundle

{ OakNativeRuntime: OakNativeRuntime } := import('runtime-codegen')

bundleCode := OakNativeRuntime + '

__oak_modularize("main.oak", fn {
    println("Hello from main!")
})

__oak_modularize("utils.oak", fn {
    { add: fn(a, b) { a + b } }
})

__oak_module_import("main.oak")
'

writeFile('bundle.oak', bundleCode)

JavaScript Web Bundle

{ OakJSRuntime: OakJSRuntime } := import('runtime-codegen')

jsBundleCode := OakJSRuntime + '

__oak_modularize("app", function() {
    console.log("App loaded");
    return { version: "1.0" };
});

__oak_module_import("app");
'

writeFile('bundle.js', jsBundleCode)

Build System Integration

{ OakNativeRuntime: OakNativeRuntime, OakJSRuntime: OakJSRuntime } := import('runtime-codegen')

fn generateBundle(modules, target) {
    runtime := if target {
        :web -> OakJSRuntime
        :native -> OakNativeRuntime
        _ -> OakNativeRuntime
    }
    
    moduleCode := modules |> map(fn([name, code]) {
        '__oak_modularize("' + name + '", function() {\n' +
        code + '\n});'
    }) |> join('\n\n')
    
    entryCall := '__oak_module_import("' + modules.0.0 + '");'
    
    runtime + '\n\n' + moduleCode + '\n\n' + entryCall
}

WebAssembly WASM Bundle

{ renderWasmBundle: renderWasmBundle } := import('runtime-codegen')
buildRender := import('build-render')

// renderWasmBundle produces WAT (WebAssembly Text) source embedding the Oak
// bundle in linear memory. Requires `wat2wasm` to produce binary .wasm.
fn generateWasmBundle(bundleNode, vfsFiles, encodeJSON, oakNativeRuntime, formatIdent, abort) {
    renderOak := fn(node) buildRender.renderOakBundle(
        node, vfsFiles, encodeJSON, oakNativeRuntime, formatIdent, abort
    )
    watSource := renderWasmBundle(bundleNode, renderOak)
    writeFile('program.wat', watSource)
    // Then convert: wat2wasm program.wat -o program.wasm
}

Custom WAT Data Segment

{ encodeWATString: encodeWATString } := import('runtime-codegen')

text := 'Hello, 世界!'
encoded := encodeWATString(text)
// encoded.byteLen => UTF-8 byte count (useful for sizing WASM memory)
// encoded.escaped => hex-escaped string for WAT data element

watData := '(data (i32.const 1024) "' << encoded.escaped << '")'
memoryPages := int((1024 + encoded.byteLen) / 65536) + 1

Circular Import Handling

// JavaScript runtime breaks circular imports
__Oak_Modules[name] = {};  // Placeholder
return __Oak_Modules[name] = module();  // Replace with actual

Example:

// a.js imports b.js, b.js imports a.js

__oak_modularize("a", function() {
    const b = __oak_module_import("b");  // Gets placeholder {}
    return { name: "A", b: b };
});

__oak_modularize("b", function() {
    const a = __oak_module_import("a");  // Placeholder prevents infinite loop
    return { name: "B", a: a };
});

Module Caching

Both runtimes cache module results:

// First call: executes module function
utils := __oak_module_import('utils')

// Second call: returns cached result
utils2 := __oak_module_import('utils')

// utils === utils2 (same object)

Functions

renderWasmBundle(bundleNode, renderOakBundle)

Renders Oak AST nodes to WebAssembly Text (WAT) format. The resulting WAT module embeds the bundled Oak source code in linear memory and exports functions for a host Oak interpreter to execute.

Parameters:

  • bundleNode — Root AST node of the bundled program
  • renderOakBundle — Callback fn(node) that renders an AST node to Oak source text

Returns: A WAT source string ready for conversion to binary WASM via wat2wasm.

Exports from the generated WAT module:

  • memory — Linear memory (sized to fit the bundle source)
  • __oak_bundle_ptr (global) — Memory offset of the embedded Oak source
  • __oak_bundle_len (global) — Byte length of the embedded Oak source
  • run() — Calls oak.run with the bundle pointer and length
  • bundle_ptr() — Returns __oak_bundle_ptr
  • bundle_len() — Returns __oak_bundle_len
  • main() — Entry point; delegates to run()

Imports required from host:

  • oak.run(ptr: i32, len: i32) → i32 — Executes the Oak source at the given memory location
{ renderWasmBundle: renderWasmBundle } := import('runtime-codegen')
{ renderOakBundle: renderOakBundle } := import('build-render')

watSource := renderWasmBundle(bundleNode, renderOakBundle)
writeFile('program.wat', watSource)

encodeWATString(s)

Encodes an Oak string as a WAT data segment value. Returns the UTF-8 byte sequence as a hex-escaped string safe for use inside a WAT (data ...) element, along with the byte length.

Parameters:

  • s — Oak string to encode

Returns: An object { escaped: string, byteLen: int } where:

  • escaped — The string with non-printable and non-ASCII characters hex-escaped (\xx)
  • byteLen — The total number of UTF-8 encoded bytes (used to size WASM memory)
{ encodeWATString: encodeWATString } := import('runtime-codegen')

encoded := encodeWATString('Hello, 世界')
// => { escaped: 'Hello, \e4\b8\96\e7\95\8c', byteLen: 13 }

wat := '(data (i32.const 1024) "' << encoded.escaped << '")'

Runtime Functions

___runtime_lib?(name)

Built-in function (Oak native only) that checks if a name is a runtime library.

___runtime_lib?('std')   // => true
___runtime_lib?('http')  // => true
___runtime_lib?('myapp') // => false

Implementation Notes

  • Module functions are called only once
  • Results are cached in __Oak_Modules
  • Circular imports return placeholder object
  • JavaScript runtime includes polyfills for Oak semantics
  • Trampoline optimization prevents stack overflow
  • Deep equality handles Oak's value semantics

Limitations

  • Circular imports may have incomplete exports
  • Dynamic imports not supported
  • Module names must be string literals
  • No lazy loading
  • No module hot-reloading
  • Trampoline has overhead compared to native tail calls

Use Cases

  • Bundling: Embed in bundled code
  • Code generation: Template for module system
  • Web deployment: JavaScript runtime for browsers
  • Testing: Standalone module execution

Security Considerations

  • No sandboxing of module code
  • All modules share global scope
  • No module permission system
  • Errors in one module can crash all

See Also