ForceMode
This commit is contained in:
@@ -237,6 +237,11 @@ pub enum PrimOpPhase {
|
|||||||
Warn,
|
Warn,
|
||||||
ZipAttrsWith,
|
ZipAttrsWith,
|
||||||
|
|
||||||
|
ForceResultShallow,
|
||||||
|
ForceResultShallowPush,
|
||||||
|
ForceResultShallowLoop,
|
||||||
|
ForceResultDeepFinish,
|
||||||
|
|
||||||
Illegal,
|
Illegal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ use num_enum::TryFromPrimitive as _;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use crate::{
|
use crate::{BytecodeReader, CallFrame, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
BytecodeReader, CallFrame, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<'gc> Vm<'gc> {
|
impl<'gc> Vm<'gc> {
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
@@ -34,7 +32,12 @@ impl<'gc> Vm<'gc> {
|
|||||||
FilterCallPred => self.primop_filter_call_pred(reader, mc),
|
FilterCallPred => self.primop_filter_call_pred(reader, mc),
|
||||||
FilterCheck => self.primop_filter_check(reader, mc),
|
FilterCheck => self.primop_filter_check(reader, mc),
|
||||||
|
|
||||||
_ => todo!("dispatch builtin phase"),
|
ForceResultShallow => self.primop_force_result_shallow(ctx, reader, mc),
|
||||||
|
ForceResultShallowPush => self.primop_force_result_shallow_push(ctx, reader, mc),
|
||||||
|
ForceResultShallowLoop => self.primop_force_result_shallow_loop(reader, mc),
|
||||||
|
ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc),
|
||||||
|
|
||||||
|
phase => todo!("primop phase {phase:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,9 +146,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
// stack: [msg] - force msg, then abort with it
|
// stack: [msg] - force msg, then abort with it
|
||||||
self.force_slot(0, reader, mc)?;
|
self.force_slot(0, reader, mc)?;
|
||||||
let msg_val = self.peek_forced(0);
|
let msg_val = self.peek_forced(0);
|
||||||
let msg = ctx
|
let msg = ctx.get_string(msg_val).unwrap_or("<non-string-value>");
|
||||||
.get_string(msg_val)
|
|
||||||
.unwrap_or("<non-string-value>");
|
|
||||||
self.finish_err(Error::eval_error(format!(
|
self.finish_err(Error::eval_error(format!(
|
||||||
"evaluation aborted with the following error message: '{msg}'"
|
"evaluation aborted with the following error message: '{msg}'"
|
||||||
)))
|
)))
|
||||||
@@ -222,12 +223,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
self.push(item);
|
self.push(item);
|
||||||
|
|
||||||
// force item at TOS, resume at DeepSeqLoop after force
|
// force item at TOS, resume at DeepSeqLoop after force
|
||||||
self.force_slot_to_pc(
|
self.force_slot_to_pc(0, reader, mc, PrimOpPhase::DeepSeqLoop.ip() as usize)?;
|
||||||
0,
|
|
||||||
reader,
|
|
||||||
mc,
|
|
||||||
PrimOpPhase::DeepSeqLoop.ip() as usize,
|
|
||||||
)?;
|
|
||||||
reader.set_pc(PrimOpPhase::DeepSeqLoop.ip() as usize);
|
reader.set_pc(PrimOpPhase::DeepSeqLoop.ip() as usize);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
@@ -281,11 +277,98 @@ impl<'gc> Vm<'gc> {
|
|||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_value_in_seen(
|
pub(crate) fn primop_force_result_shallow(
|
||||||
&self,
|
&mut self,
|
||||||
seen: Gc<'gc, List<'gc>>,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
val: Value<'gc>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
) -> bool {
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
self.force_slot(0, reader, mc)?;
|
||||||
|
let val = self.peek_forced(0);
|
||||||
|
|
||||||
|
let (count, has_children) = if let Some(attrs) = val.as_gc::<AttrSet>() {
|
||||||
|
let len = attrs.len();
|
||||||
|
(len, len > 0)
|
||||||
|
} else if let Some(list) = val.as_gc::<List<'gc>>() {
|
||||||
|
let len = list.inner.borrow().len();
|
||||||
|
(len, len > 0)
|
||||||
|
} else {
|
||||||
|
(0, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !has_children {
|
||||||
|
let val = self.pop();
|
||||||
|
return self.finish_ok(ctx.convert_value(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.push(Value::new_inline(0i32));
|
||||||
|
self.push(Value::new_inline(count as i32));
|
||||||
|
reader.set_pc(PrimOpPhase::ForceResultShallowPush.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_force_result_shallow_push(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let idx = self.peek(1).as_inline::<i32>().unwrap();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let len = self.peek(0).as_inline::<i32>().unwrap();
|
||||||
|
|
||||||
|
if idx == len {
|
||||||
|
let _ = self.pop(); // len
|
||||||
|
let _ = self.pop(); // idx
|
||||||
|
let val = self.pop();
|
||||||
|
return self.finish_ok(ctx.convert_value(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = self.peek_forced(2);
|
||||||
|
let child = if let Some(attrs) = val.as_gc::<AttrSet>() {
|
||||||
|
attrs.get(idx as usize).map(|&(_, v)| v)
|
||||||
|
} else if let Some(list) = val.as_gc::<List<'gc>>() {
|
||||||
|
list.inner.borrow().get(idx as usize).copied()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(child) = child {
|
||||||
|
self.replace(1, Value::new_inline(idx + 1));
|
||||||
|
self.push(child);
|
||||||
|
self.force_slot_to_pc(
|
||||||
|
0,
|
||||||
|
reader,
|
||||||
|
mc,
|
||||||
|
PrimOpPhase::ForceResultShallowLoop.ip() as usize,
|
||||||
|
)?;
|
||||||
|
reader.set_pc(PrimOpPhase::ForceResultShallowLoop.ip() as usize);
|
||||||
|
}
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_force_result_shallow_loop(
|
||||||
|
&mut self,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
_mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let _ = self.pop(); // forced child
|
||||||
|
reader.set_pc(PrimOpPhase::ForceResultShallowPush.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_force_result_deep_finish(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let val = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
|
self.finish_ok(ctx.convert_value(val.relax()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_value_in_seen(&self, seen: Gc<'gc, List<'gc>>, val: Value<'gc>) -> bool {
|
||||||
if !is_container(val) {
|
if !is_container(val) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -298,12 +381,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_value_to_seen(
|
fn add_value_to_seen(&self, seen: Gc<'gc, List<'gc>>, mc: &Mutation<'gc>, val: Value<'gc>) {
|
||||||
&self,
|
|
||||||
seen: Gc<'gc, List<'gc>>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
val: Value<'gc>,
|
|
||||||
) {
|
|
||||||
if is_container(val) {
|
if is_container(val) {
|
||||||
seen.unlock(mc).borrow_mut().push(val);
|
seen.unlock(mc).borrow_mut().push(val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use fix_builtins::PrimOpPhase;
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
BytecodeReader, CallFrame, Closure, Env, Step, ThunkState, VmRuntimeCtx, VmRuntimeCtxExt,
|
BytecodeReader, CallFrame, Closure, Env, ForceMode, Step, ThunkState, VmRuntimeCtx,
|
||||||
|
VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -114,7 +116,28 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
with_env,
|
with_env,
|
||||||
}) = self.call_stack.pop()
|
}) = self.call_stack.pop()
|
||||||
else {
|
else {
|
||||||
return self.finish_ok(ctx.convert_value(val.relax()));
|
match self.force_mode {
|
||||||
|
ForceMode::AsIs => return self.finish_ok(ctx.convert_value(val.relax())),
|
||||||
|
ForceMode::Shallow => {
|
||||||
|
self.push(val.relax());
|
||||||
|
reader.set_pc(PrimOpPhase::ForceResultShallow.ip() as usize);
|
||||||
|
return Step::Continue(());
|
||||||
|
}
|
||||||
|
ForceMode::Deep => {
|
||||||
|
self.push(val.relax());
|
||||||
|
self.push(val.relax());
|
||||||
|
self.call_stack.push(CallFrame {
|
||||||
|
pc: PrimOpPhase::ForceResultDeepFinish.ip() as usize,
|
||||||
|
stack_depth: 0,
|
||||||
|
thunk: None,
|
||||||
|
env: self.env,
|
||||||
|
with_env: self.with_env,
|
||||||
|
});
|
||||||
|
self.call_depth += 1;
|
||||||
|
reader.set_pc(PrimOpPhase::DeepSeq.ip() as usize);
|
||||||
|
return Step::Continue(());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.set_pc(ret_pc);
|
reader.set_pc(ret_pc);
|
||||||
if let Some(outer_thunk) = thunk {
|
if let Some(outer_thunk) = thunk {
|
||||||
|
|||||||
+37
-6
@@ -13,7 +13,7 @@ use fix_common::StringId;
|
|||||||
use fix_error::{Error, Result, Source};
|
use fix_error::{Error, Result, Source};
|
||||||
use gc_arena::arena::CollectionPhase;
|
use gc_arena::arena::CollectionPhase;
|
||||||
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::{HashMap, HashSet};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -115,6 +115,16 @@ impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(&self, val: Value) -> fix_common::Value {
|
fn convert_value(&self, val: Value) -> fix_common::Value {
|
||||||
|
self.convert_value_with_seen(val, &mut HashSet::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ConvertValueWithSeen: VmRuntimeCtx {
|
||||||
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
||||||
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value {
|
||||||
use fix_common::Value;
|
use fix_common::Value;
|
||||||
if let Some(i) = val.as_inline::<i32>() {
|
if let Some(i) = val.as_inline::<i32>() {
|
||||||
Value::Int(i as i64)
|
Value::Int(i as i64)
|
||||||
@@ -132,26 +142,44 @@ impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
|||||||
} else if let Some(ns) = val.as_gc::<NixString>() {
|
} else if let Some(ns) = val.as_gc::<NixString>() {
|
||||||
Value::String(ns.as_str().to_owned())
|
Value::String(ns.as_str().to_owned())
|
||||||
} else if let Some(attrs) = val.as_gc::<AttrSet>() {
|
} else if let Some(attrs) = val.as_gc::<AttrSet>() {
|
||||||
|
let bits = val.to_bits();
|
||||||
|
if attrs.is_empty() {
|
||||||
|
return Value::AttrSet(Default::default());
|
||||||
|
}
|
||||||
|
if !seen.insert(bits) {
|
||||||
|
return Value::Repeated;
|
||||||
|
}
|
||||||
let mut map = std::collections::BTreeMap::new();
|
let mut map = std::collections::BTreeMap::new();
|
||||||
for &(key, val) in attrs.iter() {
|
for &(key, val) in attrs.iter() {
|
||||||
let key = self.resolve_string(key).to_owned();
|
let key = self.resolve_string(key).to_owned();
|
||||||
let converted = self.convert_value(val);
|
let converted = self.convert_value_with_seen(val, seen);
|
||||||
map.insert(fix_common::Symbol::from(key), converted);
|
map.insert(fix_common::Symbol::from(key), converted);
|
||||||
}
|
}
|
||||||
Value::AttrSet(fix_common::AttrSet::new(map))
|
Value::AttrSet(fix_common::AttrSet::new(map))
|
||||||
} else if let Some(list) = val.as_gc::<List>() {
|
} else if let Some(list) = val.as_gc::<List>() {
|
||||||
|
let bits = val.to_bits();
|
||||||
|
if list.inner.borrow().is_empty() {
|
||||||
|
return Value::List(Default::default());
|
||||||
|
}
|
||||||
|
if !seen.insert(bits) {
|
||||||
|
return Value::Repeated;
|
||||||
|
}
|
||||||
let items: Vec<_> = list
|
let items: Vec<_> = list
|
||||||
.inner
|
.inner
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.map(|v| self.convert_value(v))
|
.map(|v| self.convert_value_with_seen(v, seen))
|
||||||
.collect();
|
.collect();
|
||||||
Value::List(fix_common::List::new(items))
|
Value::List(fix_common::List::new(items))
|
||||||
} else if val.is::<Closure>() {
|
} else if val.is::<Closure>() {
|
||||||
Value::Func
|
Value::Func
|
||||||
} else if val.is::<Thunk>() {
|
} else if let Some(thunk) = val.as_gc::<Thunk>() {
|
||||||
Value::Thunk
|
if let ThunkState::Evaluated(v) = *thunk.borrow() {
|
||||||
|
self.convert_value_with_seen(v.relax(), seen)
|
||||||
|
} else {
|
||||||
|
Value::Thunk
|
||||||
|
}
|
||||||
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
||||||
let name = fix_builtins::BUILTINS[primop.id as usize].0;
|
let name = fix_builtins::BUILTINS[primop.id as usize].0;
|
||||||
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
||||||
@@ -278,7 +306,10 @@ fn init_builtins<'gc>(mc: &Mutation<'gc>, ctx: &mut impl VmRuntimeCtx) -> Value<
|
|||||||
entries.sort_by_key(|(k, _)| *k);
|
entries.sort_by_key(|(k, _)| *k);
|
||||||
|
|
||||||
let builtins_set = Gc::new(mc, AttrSet::from_sorted_unchecked(entries));
|
let builtins_set = Gc::new(mc, AttrSet::from_sorted_unchecked(entries));
|
||||||
Value::new_gc(builtins_set)
|
let builtins_value = Value::new_gc(builtins_set);
|
||||||
|
*self_ref_thunk.borrow_mut(mc) =
|
||||||
|
ThunkState::Evaluated(builtins_value.restrict().expect("builtins is not a thunk"));
|
||||||
|
builtins_value
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Vm<'gc> {
|
impl<'gc> Vm<'gc> {
|
||||||
|
|||||||
+6
-3
@@ -506,9 +506,12 @@ pub(crate) struct List<'gc> {
|
|||||||
|
|
||||||
impl<'gc> List<'gc> {
|
impl<'gc> List<'gc> {
|
||||||
pub(crate) fn new(mc: &Mutation<'gc>, data: SmallVec<[Value<'gc>; 4]>) -> Gc<'gc, Self> {
|
pub(crate) fn new(mc: &Mutation<'gc>, data: SmallVec<[Value<'gc>; 4]>) -> Gc<'gc, Self> {
|
||||||
Gc::new(mc, Self {
|
Gc::new(
|
||||||
inner: RefLock::new(data)
|
mc,
|
||||||
})
|
Self {
|
||||||
|
inner: RefLock::new(data),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_gc(mc: &Mutation<'gc>) -> Gc<'gc, Self> {
|
pub(crate) fn new_gc(mc: &Mutation<'gc>) -> Gc<'gc, Self> {
|
||||||
|
|||||||
Reference in New Issue
Block a user