implement __functor

This commit is contained in:
2026-05-04 18:24:33 +08:00
parent b5246c04ae
commit 2fd034965b
7 changed files with 79 additions and 8 deletions
+1 -1
View File
@@ -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,
+23 -1
View File
@@ -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::<AttrSet>()
&& 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(())
}
+4
View File
@@ -223,6 +223,8 @@ pub struct Vm<'gc> {
#[collect(require_static)]
result: Option<Result<fix_common::Value>>,
functor_sym: StringId,
}
enum OperandData {
@@ -340,6 +342,8 @@ impl<'gc> Vm<'gc> {
force_mode,
result: None,
functor_sym: ctx.intern_string("__functor"),
}
}
+41 -1
View File
@@ -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::<StrictValue>(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,
+2 -1
View File
@@ -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 {
+6 -4
View File
@@ -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:?}"),
}