fromValue can convert native functions now

This commit is contained in:
Chris Wanstrath 2025-11-01 23:09:11 -07:00
parent 676f53c66b
commit 0f39e9401e
2 changed files with 73 additions and 2 deletions

View File

@ -1,4 +1,4 @@
import { wrapNative } from "./function"
import { wrapNative, getOriginalFunction } from "./function"
import { OpCode } from "./opcode"
import { Scope } from "./scope"
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')
return fnFromValue(v, vm)
case 'native':
return '<function>'
return getOriginalFunction(v.fn)
}
}

View File

@ -113,3 +113,74 @@ test("toValue - converts wrapped Reef functions back to original Value", async (
expect(backToValue).toBe(reefFunction) // Same reference
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)
})