From 151dd4835c241dc68214e395157447e994f20074 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 4 May 2026 18:24:33 +0800 Subject: [PATCH] implement __functor --- fix-builtins/src/lib.rs | 2 ++ fix-vm/src/dispatch_tailcall.rs | 2 +- fix-vm/src/instructions/calls.rs | 24 +++++++++++++++++- fix-vm/src/lib.rs | 4 +++ fix-vm/src/primops/control.rs | 42 +++++++++++++++++++++++++++++++- fix-vm/src/primops/list.rs | 3 ++- fix-vm/src/primops/mod.rs | 10 +++++--- 7 files changed, 79 insertions(+), 8 deletions(-) diff --git a/fix-builtins/src/lib.rs b/fix-builtins/src/lib.rs index 5496693..c9bef75 100644 --- a/fix-builtins/src/lib.rs +++ b/fix-builtins/src/lib.rs @@ -244,6 +244,8 @@ pub enum PrimOpPhase { // TODO: split into separate enums CallPattern, + CallFunctor1, + CallFunctor2, Illegal, } diff --git a/fix-vm/src/dispatch_tailcall.rs b/fix-vm/src/dispatch_tailcall.rs index eb7ebb9..ff60bc4 100644 --- a/fix-vm/src/dispatch_tailcall.rs +++ b/fix-vm/src/dispatch_tailcall.rs @@ -271,7 +271,7 @@ table! { JumpIfTrue => op_jump_if_true, Jump => op_jump, - CoerceToString => op_coerce_to_string, + CoerceToString => op_coerce_to_string, ConcatStrings => op_concat_strings, ResolvePath => op_resolve_path, diff --git a/fix-vm/src/instructions/calls.rs b/fix-vm/src/instructions/calls.rs index a8a8784..2111c23 100644 --- a/fix-vm/src/instructions/calls.rs +++ b/fix-vm/src/instructions/calls.rs @@ -83,8 +83,30 @@ impl<'gc> crate::Vm<'gc> { }; self.push(Value::new_gc(Gc::new(mc, new_app))) } + } else if let Some(attrs) = func.as_gc::() + && let Some(functor) = attrs.lookup(self.functor_sym) + { + // f arg => (f.__functor f) arg + // + // Stage the work for `CallFunctor1` so retries during force are + // safe: the stack invariant `[..., orig_arg, self, functor]` + // holds every time control re-enters phase 1. + self.call_depth -= 1; + self.call_stack.push(CallFrame { + pc: resume_pc, + thunk: None, + env: self.env, + }); + self.push(arg); + self.push(func.relax()); + self.push(functor); + reader.set_pc(PrimOpPhase::CallFunctor1.ip() as usize); + return Step::Continue(()); } else { - todo!("call other types: {func:?}") + return self.finish_err(Error::eval_error(format!( + "attempt to call something which is not a function but {}", + func.ty() + ))); } Step::Continue(()) } diff --git a/fix-vm/src/lib.rs b/fix-vm/src/lib.rs index 62a870d..61b4b4c 100644 --- a/fix-vm/src/lib.rs +++ b/fix-vm/src/lib.rs @@ -223,6 +223,8 @@ pub struct Vm<'gc> { #[collect(require_static)] result: Option>, + + functor_sym: StringId, } enum OperandData { @@ -340,6 +342,8 @@ impl<'gc> Vm<'gc> { force_mode, result: None, + + functor_sym: ctx.intern_string("__functor"), } } diff --git a/fix-vm/src/primops/control.rs b/fix-vm/src/primops/control.rs index ae19951..f3b33d1 100644 --- a/fix-vm/src/primops/control.rs +++ b/fix-vm/src/primops/control.rs @@ -4,11 +4,12 @@ use gc_arena::{Gc, Mutation, RefLock}; use smallvec::SmallVec; use crate::value::*; -use crate::{BytecodeReader, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt}; +use crate::{BytecodeReader, NixNum, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt}; impl<'gc> Vm<'gc> { pub(crate) fn primop_seq( &mut self, + ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { @@ -36,6 +37,7 @@ impl<'gc> Vm<'gc> { pub(crate) fn primop_deep_seq_force_top( &mut self, + ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { @@ -84,6 +86,7 @@ impl<'gc> Vm<'gc> { pub(crate) fn primop_deep_seq_push( &mut self, + ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { @@ -271,6 +274,43 @@ impl<'gc> Vm<'gc> { } } + pub(crate) fn primop_call_functor_1( + &mut self, + reader: &mut BytecodeReader<'_>, + mc: &Mutation<'gc>, + ) -> Step { + // Stack invariant on every (re-)entry: [..., orig_arg, self, functor] + // where `functor` is TOS. Retries during force land back here safely. + let functor = self.force_and_retry::(reader, mc)?; + // Stack now: [..., orig_arg, self] + let self_val = self.pop(); + self.push(functor.relax()); + // Stack: [..., orig_arg, functor] + // Call 1: functor(self). Resume into CallFunctor2 once it returns. + self.call( + reader, + mc, + self_val, + PrimOpPhase::CallFunctor2.ip() as usize, + ) + } + + pub(crate) fn primop_call_functor_2( + &mut self, + reader: &mut BytecodeReader<'_>, + mc: &Mutation<'gc>, + ) -> Step { + // Stack on entry: [..., orig_arg, intermediate] + // call_stack top: synthetic frame with caller's resume_pc. + let intermediate = self.pop(); + let orig_arg = self.pop(); + let saved = self.call_stack.pop().expect("functor outer frame missing"); + self.env = saved.env; + self.push(intermediate); + // Call 2: intermediate(orig_arg). Resume to caller. + self.call(reader, mc, orig_arg, saved.pc) + } + pub(crate) fn primop_call_pattern( &mut self, ctx: &mut impl VmRuntimeCtx, diff --git a/fix-vm/src/primops/list.rs b/fix-vm/src/primops/list.rs index ce1eb45..a594c92 100644 --- a/fix-vm/src/primops/list.rs +++ b/fix-vm/src/primops/list.rs @@ -3,7 +3,7 @@ use gc_arena::Mutation; use crate::bytecode_reader::BytecodeReader; use crate::value::*; -use crate::{Step, Vm}; +use crate::{Step, Vm, VmRuntimeCtx}; impl<'gc> Vm<'gc> { pub(crate) fn primop_filter_force_list( @@ -44,6 +44,7 @@ impl<'gc> Vm<'gc> { pub(crate) fn primop_filter_check( &mut self, + ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { diff --git a/fix-vm/src/primops/mod.rs b/fix-vm/src/primops/mod.rs index e1109ba..1f3effb 100644 --- a/fix-vm/src/primops/mod.rs +++ b/fix-vm/src/primops/mod.rs @@ -32,14 +32,14 @@ impl<'gc> Vm<'gc> { match phase { Abort => self.primop_abort(ctx, reader, mc), - DeepSeq => self.primop_deep_seq_force_top(reader, mc), - DeepSeqPush => self.primop_deep_seq_push(reader, mc), + DeepSeq => self.primop_deep_seq_force_top(ctx, reader, mc), + DeepSeqPush => self.primop_deep_seq_push(ctx, reader, mc), DeepSeqLoop => self.primop_deep_seq_loop(reader, mc), - Seq => self.primop_seq(reader, mc), + Seq => self.primop_seq(ctx, reader, mc), FilterForceList => self.primop_filter_force_list(reader, mc), FilterCallPred => self.primop_filter_call_pred(reader, mc), - FilterCheck => self.primop_filter_check(reader, mc), + FilterCheck => self.primop_filter_check(ctx, reader, mc), ForceResultShallow => self.primop_force_result_shallow(ctx, reader, mc), ForceResultShallowPush => self.primop_force_result_shallow_push(ctx, reader, mc), @@ -47,6 +47,8 @@ impl<'gc> Vm<'gc> { ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc), CallPattern => self.primop_call_pattern(ctx, reader, mc), + CallFunctor1 => self.primop_call_functor_1(reader, mc), + CallFunctor2 => self.primop_call_functor_2(reader, mc), phase => todo!("primop phase {phase:?}"), }