Compare commits
No commits in common. "0f39e9401eb7a0a7c906e150127f9829458a79b6" and "fa021e3f187dd94d3a580d9d93270555d3d8e2b5" have entirely different histories.
0f39e9401e
...
fa021e3f18
500
IDEAS.md
500
IDEAS.md
|
|
@ -1,500 +0,0 @@
|
||||||
# ReefVM Architectural Improvement Ideas
|
|
||||||
|
|
||||||
This document contains architectural ideas for improving ReefVM. These focus on enhancing the VM's capabilities through structural improvements rather than just adding new opcodes.
|
|
||||||
|
|
||||||
## 1. Scope Resolution Optimization
|
|
||||||
|
|
||||||
**Current Issue**: Variable lookups are O(n) through the scope chain on every `LOAD`. This becomes expensive in deeply nested closures.
|
|
||||||
|
|
||||||
**Architectural Solution**: Implement **static scope analysis** with **lexical addressing**:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Instead of: LOAD x (runtime scope chain walk)
|
|
||||||
// Compile to: LOAD_FAST 2 1 (scope depth 2, slot 1 - O(1) lookup)
|
|
||||||
|
|
||||||
class Scope {
|
|
||||||
locals: Map<string, Value>
|
|
||||||
parent?: Scope
|
|
||||||
|
|
||||||
// NEW: Add indexed slots for fast access
|
|
||||||
slots: Value[] // Direct array access
|
|
||||||
nameToSlot: Map<string, number> // Compile-time mapping
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- O(1) variable access instead of O(n)
|
|
||||||
- Critical for hot loops and deeply nested functions
|
|
||||||
- Compiler can still fall back to named lookup for dynamic cases
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Module System Architecture
|
|
||||||
|
|
||||||
**Current Gap**: No way to organize code across multiple files or create reusable libraries.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add first-class module support:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// New opcodes: IMPORT, EXPORT, MAKE_MODULE
|
|
||||||
// New bytecode structure:
|
|
||||||
type Bytecode = {
|
|
||||||
instructions: Instruction[]
|
|
||||||
constants: Constant[]
|
|
||||||
exports?: Map<string, number> // Exported symbols
|
|
||||||
imports?: Import[] // Import declarations
|
|
||||||
}
|
|
||||||
|
|
||||||
type Import = {
|
|
||||||
modulePath: string
|
|
||||||
symbols: string[] // [] means import all
|
|
||||||
alias?: string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Pattern**:
|
|
||||||
```
|
|
||||||
MAKE_MODULE .module_body
|
|
||||||
EXPORT add
|
|
||||||
EXPORT subtract
|
|
||||||
HALT
|
|
||||||
|
|
||||||
.module_body:
|
|
||||||
MAKE_FUNCTION (x y) .add_impl
|
|
||||||
RETURN
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Code organization and reusability
|
|
||||||
- Circular dependency detection at load time
|
|
||||||
- Natural namespace isolation
|
|
||||||
- Enables standard library architecture
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Source Map Integration
|
|
||||||
|
|
||||||
**Current Issue**: Runtime errors show bytecode addresses, not source locations.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add source mapping layer:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
type Bytecode = {
|
|
||||||
instructions: Instruction[]
|
|
||||||
constants: Constant[]
|
|
||||||
sourceMap?: SourceMap // NEW
|
|
||||||
}
|
|
||||||
|
|
||||||
type SourceMap = {
|
|
||||||
file?: string
|
|
||||||
mappings: SourceMapping[] // Instruction index → source location
|
|
||||||
}
|
|
||||||
|
|
||||||
type SourceMapping = {
|
|
||||||
instruction: number
|
|
||||||
line: number
|
|
||||||
column: number
|
|
||||||
source?: string // Original source text
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Meaningful error messages with line/column
|
|
||||||
- Debugger can show original source
|
|
||||||
- Stack traces map to source code
|
|
||||||
- Critical for production debugging
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Debugger Hook Architecture
|
|
||||||
|
|
||||||
**Current Gap**: No way to pause execution, inspect state, or step through code.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add debug event system:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class VM {
|
|
||||||
debugger?: Debugger
|
|
||||||
|
|
||||||
async execute(instruction: Instruction) {
|
|
||||||
// Before execution
|
|
||||||
await this.debugger?.onInstruction(this.pc, instruction, this)
|
|
||||||
|
|
||||||
// Execute
|
|
||||||
switch (instruction.op) { ... }
|
|
||||||
|
|
||||||
// After execution
|
|
||||||
await this.debugger?.afterInstruction(this.pc, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Debugger {
|
|
||||||
breakpoints: Set<number>
|
|
||||||
onInstruction(pc: number, instruction: Instruction, vm: VM): Promise<void>
|
|
||||||
afterInstruction(pc: number, vm: VM): Promise<void>
|
|
||||||
onCall(fn: Value, args: Value[]): Promise<void>
|
|
||||||
onReturn(value: Value): Promise<void>
|
|
||||||
onException(error: Value): Promise<void>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Step-through debugging
|
|
||||||
- Breakpoints at any instruction
|
|
||||||
- State inspection at any point
|
|
||||||
- Non-invasive (no bytecode modification)
|
|
||||||
- Can build IDE integrations
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Bytecode Optimization Pass Framework
|
|
||||||
|
|
||||||
**Current Gap**: Bytecode is emitted directly, no optimization.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add optimization pipeline:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
type Optimizer = (bytecode: Bytecode) => Bytecode
|
|
||||||
|
|
||||||
// Framework for composable optimization passes
|
|
||||||
class BytecodeOptimizer {
|
|
||||||
passes: Optimizer[] = []
|
|
||||||
|
|
||||||
add(pass: Optimizer): this {
|
|
||||||
this.passes.push(pass)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
optimize(bytecode: Bytecode): Bytecode {
|
|
||||||
return this.passes.reduce((bc, pass) => pass(bc), bytecode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example passes:
|
|
||||||
const optimizer = new BytecodeOptimizer()
|
|
||||||
.add(constantFolding) // PUSH 2; PUSH 3; ADD → PUSH 5
|
|
||||||
.add(deadCodeElimination) // Remove unreachable code after HALT/RETURN
|
|
||||||
.add(jumpChaining) // JUMP .a → .a: JUMP .b → JUMP .b directly
|
|
||||||
.add(peepholeOptimization) // DUP; POP → (nothing)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Faster execution without changing compiler
|
|
||||||
- Can add passes without modifying VM
|
|
||||||
- Composable and testable
|
|
||||||
- Enables aggressive optimizations (inlining, constant folding, etc.)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Value Memory Management Architecture
|
|
||||||
|
|
||||||
**Current Issue**: No tracking of memory usage, no GC hooks, unbounded growth.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add memory management layer:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class MemoryManager {
|
|
||||||
allocatedBytes: number = 0
|
|
||||||
maxBytes?: number
|
|
||||||
|
|
||||||
allocateValue(value: Value): Value {
|
|
||||||
const size = this.sizeOf(value)
|
|
||||||
if (this.maxBytes && this.allocatedBytes + size > this.maxBytes) {
|
|
||||||
throw new Error('Out of memory')
|
|
||||||
}
|
|
||||||
this.allocatedBytes += size
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
sizeOf(value: Value): number {
|
|
||||||
// Estimate memory footprint
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook for custom GC
|
|
||||||
gc?: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
class VM {
|
|
||||||
memory: MemoryManager
|
|
||||||
|
|
||||||
// All value-creating operations check memory
|
|
||||||
push(value: Value) {
|
|
||||||
this.memory.allocateValue(value)
|
|
||||||
this.stack.push(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Memory limits for sandboxing
|
|
||||||
- Memory profiling
|
|
||||||
- Custom GC strategies
|
|
||||||
- Prevents runaway memory usage
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Instruction Profiler Architecture
|
|
||||||
|
|
||||||
**Current Gap**: No way to identify performance bottlenecks in bytecode.
|
|
||||||
|
|
||||||
**Architectural Solution**: Add instrumentation layer:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class Profiler {
|
|
||||||
instructionCounts: Map<number, number> = new Map()
|
|
||||||
instructionTime: Map<number, number> = new Map()
|
|
||||||
hotFunctions: Map<number, FunctionProfile> = new Map()
|
|
||||||
|
|
||||||
recordInstruction(pc: number, duration: number) {
|
|
||||||
this.instructionCounts.set(pc, (this.instructionCounts.get(pc) || 0) + 1)
|
|
||||||
this.instructionTime.set(pc, (this.instructionTime.get(pc) || 0) + duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
getHotSpots(): HotSpot[] {
|
|
||||||
// Identify most-executed instructions
|
|
||||||
}
|
|
||||||
|
|
||||||
generateReport(): ProfileReport {
|
|
||||||
// Human-readable performance report
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VM {
|
|
||||||
profiler?: Profiler
|
|
||||||
|
|
||||||
async execute(instruction: Instruction) {
|
|
||||||
const start = performance.now()
|
|
||||||
// ... execute ...
|
|
||||||
const duration = performance.now() - start
|
|
||||||
this.profiler?.recordInstruction(this.pc, duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Identify hot loops and functions
|
|
||||||
- Guide optimization efforts
|
|
||||||
- Measure impact of changes
|
|
||||||
- Can feed into JIT compiler (future)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Standard Library Plugin Architecture
|
|
||||||
|
|
||||||
**Current Issue**: Native functions registered manually, no standard library structure.
|
|
||||||
|
|
||||||
**Architectural Solution**: Module-based native libraries:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface NativeModule {
|
|
||||||
name: string
|
|
||||||
exports: Record<string, any>
|
|
||||||
init?(vm: VM): void
|
|
||||||
}
|
|
||||||
|
|
||||||
class VM {
|
|
||||||
modules: Map<string, NativeModule> = new Map()
|
|
||||||
|
|
||||||
registerModule(module: NativeModule) {
|
|
||||||
this.modules.set(module.name, module)
|
|
||||||
module.init?.(this)
|
|
||||||
|
|
||||||
// Auto-register exports to global scope
|
|
||||||
for (const [name, value] of Object.entries(module.exports)) {
|
|
||||||
this.set(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadModule(name: string): NativeModule {
|
|
||||||
return this.modules.get(name) || throw new Error(`Module ${name} not found`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
const mathModule: NativeModule = {
|
|
||||||
name: 'math',
|
|
||||||
exports: {
|
|
||||||
sin: Math.sin,
|
|
||||||
cos: Math.cos,
|
|
||||||
sqrt: Math.sqrt,
|
|
||||||
PI: Math.PI
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.registerModule(mathModule)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Organized standard library
|
|
||||||
- Lazy loading of modules
|
|
||||||
- Third-party plugin system
|
|
||||||
- Clear namespace boundaries
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Streaming Bytecode Execution
|
|
||||||
|
|
||||||
**Current Limitation**: Must load entire bytecode before execution.
|
|
||||||
|
|
||||||
**Architectural Solution**: Incremental bytecode loading:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class StreamingBytecode {
|
|
||||||
chunks: BytecodeChunk[] = []
|
|
||||||
|
|
||||||
append(chunk: BytecodeChunk) {
|
|
||||||
// Remap addresses, merge constants
|
|
||||||
this.chunks.push(chunk)
|
|
||||||
}
|
|
||||||
|
|
||||||
getInstruction(pc: number): Instruction | undefined {
|
|
||||||
// Resolve across chunks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VM {
|
|
||||||
async runStreaming(stream: ReadableStream<BytecodeChunk>) {
|
|
||||||
for await (const chunk of stream) {
|
|
||||||
this.bytecode.append(chunk)
|
|
||||||
await this.continue() // Execute new chunk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Execute before full load (faster startup)
|
|
||||||
- Network streaming of bytecode
|
|
||||||
- Incremental compilation
|
|
||||||
- Better REPL experience
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Type Annotation System (Optional Runtime Types)
|
|
||||||
|
|
||||||
**Current Gap**: All values dynamically typed, no way to enforce types.
|
|
||||||
|
|
||||||
**Architectural Solution**: Optional type metadata:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
type TypedValue = Value & {
|
|
||||||
typeAnnotation?: TypeAnnotation
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypeAnnotation =
|
|
||||||
| { kind: 'number' }
|
|
||||||
| { kind: 'string' }
|
|
||||||
| { kind: 'array', elementType?: TypeAnnotation }
|
|
||||||
| { kind: 'dict', valueType?: TypeAnnotation }
|
|
||||||
| { kind: 'function', params: TypeAnnotation[], return: TypeAnnotation }
|
|
||||||
|
|
||||||
// New opcodes: TYPE_CHECK, TYPE_ASSERT
|
|
||||||
// Functions can declare parameter types:
|
|
||||||
MAKE_FUNCTION (x:number y:string) .body
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Catch type errors earlier
|
|
||||||
- Self-documenting code
|
|
||||||
- Enables static analysis tools
|
|
||||||
- Optional (doesn't break existing code)
|
|
||||||
- Can enable optimizations (known number type → skip toNumber())
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. VM State Serialization
|
|
||||||
|
|
||||||
**Current Gap**: Can't save/restore VM execution state.
|
|
||||||
|
|
||||||
**Architectural Solution**: Serializable VM state:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class VM {
|
|
||||||
serialize(): SerializedState {
|
|
||||||
return {
|
|
||||||
instructions: this.instructions,
|
|
||||||
constants: this.constants,
|
|
||||||
pc: this.pc,
|
|
||||||
stack: this.stack.map(serializeValue),
|
|
||||||
callStack: this.callStack.map(serializeFrame),
|
|
||||||
scope: serializeScope(this.scope),
|
|
||||||
handlers: this.handlers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(state: SerializedState): VM {
|
|
||||||
const vm = new VM(/* ... */)
|
|
||||||
vm.restore(state)
|
|
||||||
return vm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Save/restore execution state
|
|
||||||
- Distributed computing (send state to workers)
|
|
||||||
- Crash recovery
|
|
||||||
- Time-travel debugging
|
|
||||||
- Checkpoint/restart
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Async Iterator Support
|
|
||||||
|
|
||||||
**Current Gap**: Iterators work via break, but no async iteration.
|
|
||||||
|
|
||||||
**Architectural Solution**: First-class async iteration:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// New value type:
|
|
||||||
type Value = ... | { type: 'async_iterator', value: AsyncIterableIterator<Value> }
|
|
||||||
|
|
||||||
// New opcodes: MAKE_ASYNC_ITERATOR, AWAIT_NEXT, YIELD_ASYNC
|
|
||||||
|
|
||||||
// Pattern:
|
|
||||||
for_await (item in asyncIterable) {
|
|
||||||
// Compiles to AWAIT_NEXT loop
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
- Stream processing
|
|
||||||
- Async I/O without blocking
|
|
||||||
- Natural async patterns
|
|
||||||
- Matches JavaScript async iterators
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Priority Recommendations
|
|
||||||
|
|
||||||
### Tier 1 (Highest Impact):
|
|
||||||
1. **Source Map Integration** - Critical for usability
|
|
||||||
2. **Module System** - Essential for scaling beyond toy programs
|
|
||||||
3. **Scope Resolution Optimization** - Performance multiplier
|
|
||||||
|
|
||||||
### Tier 2 (High Value):
|
|
||||||
4. **Debugger Hook Architecture** - Developer experience game-changer
|
|
||||||
5. **Standard Library Plugin Architecture** - Enables ecosystem
|
|
||||||
6. **Bytecode Optimization Framework** - Performance without complexity
|
|
||||||
|
|
||||||
### Tier 3 (Nice to Have):
|
|
||||||
7. **Instruction Profiler** - Guides future optimization
|
|
||||||
8. **Memory Management** - Important for production use
|
|
||||||
9. **VM State Serialization** - Enables advanced use cases
|
|
||||||
|
|
||||||
### Tier 4 (Future/Experimental):
|
|
||||||
10. **Type Annotations** - Optional, doesn't break existing code
|
|
||||||
11. **Streaming Bytecode** - Mostly useful for large programs
|
|
||||||
12. **Async Iterators** - Specialized use case
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Design Principles
|
|
||||||
|
|
||||||
These improvements focus on:
|
|
||||||
- **Performance** (scope optimization, bytecode optimization)
|
|
||||||
- **Developer Experience** (source maps, debugger, profiler)
|
|
||||||
- **Scalability** (modules, standard library architecture)
|
|
||||||
- **Production Readiness** (memory management, serialization)
|
|
||||||
|
|
||||||
All ideas maintain ReefVM's core design philosophy of simplicity, orthogonality, and explicit behavior.
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { wrapNative, getOriginalFunction } from "./function"
|
import { wrapNative } from "./function"
|
||||||
import { OpCode } from "./opcode"
|
import { OpCode } from "./opcode"
|
||||||
import { Scope } from "./scope"
|
import { Scope } from "./scope"
|
||||||
import { VM } from "./vm"
|
import { VM } from "./vm"
|
||||||
|
|
@ -183,7 +183,7 @@ export function fromValue(v: Value, vm?: VM): any {
|
||||||
if (!vm || !(vm instanceof VM)) throw new Error('VM is required for function conversion')
|
if (!vm || !(vm instanceof VM)) throw new Error('VM is required for function conversion')
|
||||||
return fnFromValue(v, vm)
|
return fnFromValue(v, vm)
|
||||||
case 'native':
|
case 'native':
|
||||||
return getOriginalFunction(v.fn)
|
return '<function>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,74 +113,3 @@ test("toValue - converts wrapped Reef functions back to original Value", async (
|
||||||
expect(backToValue).toBe(reefFunction) // Same reference
|
expect(backToValue).toBe(reefFunction) // Same reference
|
||||||
expect(backToValue.type).toBe("function")
|
expect(backToValue.type).toBe("function")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("fromValue - converts native function back to original JS function", async () => {
|
|
||||||
const { VM, toBytecode, toValue, fromValue } = await import("#reef")
|
|
||||||
|
|
||||||
const bytecode = toBytecode([["HALT"]])
|
|
||||||
const vm = new VM(bytecode)
|
|
||||||
|
|
||||||
// Create a native JS function
|
|
||||||
const originalFn = (x: number, y: number) => x * y
|
|
||||||
|
|
||||||
// Convert to Value (wraps it as a native function)
|
|
||||||
const nativeValue = toValue(originalFn, vm)
|
|
||||||
expect(nativeValue.type).toBe("native")
|
|
||||||
|
|
||||||
// Convert back to JS - should get the original function
|
|
||||||
const convertedFn = fromValue(nativeValue, vm)
|
|
||||||
expect(typeof convertedFn).toBe("function")
|
|
||||||
|
|
||||||
// Verify it's the same function
|
|
||||||
expect(convertedFn).toBe(originalFn)
|
|
||||||
|
|
||||||
// Verify it works correctly
|
|
||||||
expect(convertedFn(3, 4)).toBe(12)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("fromValue - native function roundtrip preserves functionality", async () => {
|
|
||||||
const { VM, toBytecode, toValue, fromValue } = await import("#reef")
|
|
||||||
|
|
||||||
const bytecode = toBytecode([["HALT"]])
|
|
||||||
const vm = new VM(bytecode)
|
|
||||||
|
|
||||||
// Create a native function with state
|
|
||||||
let callCount = 0
|
|
||||||
const countingFn = (n: number) => {
|
|
||||||
callCount++
|
|
||||||
return n * callCount
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roundtrip through Value
|
|
||||||
const nativeValue = toValue(countingFn, vm)
|
|
||||||
const roundtrippedFn = fromValue(nativeValue, vm)
|
|
||||||
|
|
||||||
// Verify it maintains state across calls
|
|
||||||
expect(roundtrippedFn(10)).toBe(10) // 10 * 1
|
|
||||||
expect(roundtrippedFn(10)).toBe(20) // 10 * 2
|
|
||||||
expect(roundtrippedFn(10)).toBe(30) // 10 * 3
|
|
||||||
expect(callCount).toBe(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("fromValue - async native function roundtrip", async () => {
|
|
||||||
const { VM, toBytecode, toValue, fromValue } = await import("#reef")
|
|
||||||
|
|
||||||
const bytecode = toBytecode([["HALT"]])
|
|
||||||
const vm = new VM(bytecode)
|
|
||||||
|
|
||||||
// Create an async native function
|
|
||||||
const asyncFn = async (x: number, y: number) => {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1))
|
|
||||||
return x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roundtrip through Value
|
|
||||||
const nativeValue = toValue(asyncFn, vm)
|
|
||||||
expect(nativeValue.type).toBe("native")
|
|
||||||
|
|
||||||
const roundtrippedFn = fromValue(nativeValue, vm)
|
|
||||||
|
|
||||||
// Verify it works
|
|
||||||
const result = await roundtrippedFn(5, 7)
|
|
||||||
expect(result).toBe(12)
|
|
||||||
})
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user