diff --git a/src/value.ts b/src/value.ts index be65dea..e52115a 100644 --- a/src/value.ts +++ b/src/value.ts @@ -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 '' + return getOriginalFunction(v.fn) } } diff --git a/tests/value.test.ts b/tests/value.test.ts index 46c1838..22f42f5 100644 --- a/tests/value.test.ts +++ b/tests/value.test.ts @@ -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) +})