Compare commits

...

1 Commits

Author SHA1 Message Date
imxyy1soope1 aec24493e5 ForceMode (WIP) 2026-03-22 17:01:44 +08:00
2 changed files with 201 additions and 20 deletions
+20 -15
View File
@@ -12,6 +12,7 @@ use crate::downgrade::{Downgrade as _, DowngradeContext};
use crate::error::{Error, Result, Source};
use crate::ir::{ArgId, Ir, IrKey, IrRef, RawIrRef, StringId, ThunkId, ir_content_eq};
use crate::runtime::builtins::new_builtins_env;
use crate::runtime::vm::ForceMode;
use crate::store::{DaemonStore, StoreConfig};
use crate::value::Symbol;
@@ -56,20 +57,15 @@ impl Runtime {
}
pub fn eval(&mut self, source: Source) -> Result<crate::value::Value> {
let root = self.downgrade(source, None)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.do_eval(source, None, ForceMode::Normal)
}
pub fn eval_shallow(&mut self, _source: Source) -> Result<crate::value::Value> {
todo!()
pub fn eval_shallow(&mut self, source: Source) -> Result<crate::value::Value> {
self.do_eval(source, None, ForceMode::Shallow)
}
pub fn eval_deep(&mut self, source: Source) -> Result<crate::value::Value> {
// FIXME: deep
let root = self.downgrade(source, None)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.do_eval(source, None, ForceMode::Deep)
}
pub fn eval_repl(
@@ -77,10 +73,18 @@ impl Runtime {
source: Source,
scope: &HashSet<StringId>,
) -> Result<crate::value::Value> {
// FIXME: shallow
let root = self.downgrade(source, Some(Scope::Repl(scope)))?;
self.do_eval(source, Some(Scope::Repl(scope)), ForceMode::Shallow)
}
fn do_eval<'ctx>(
&'ctx mut self,
source: Source,
extra_scope: Option<Scope<'ctx>>,
force_mode: ForceMode,
) -> Result<crate::value::Value> {
let root = self.downgrade(source, extra_scope)?;
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
self.run(ip)
self.run(ip, force_mode)
}
pub fn add_binding(
@@ -148,8 +152,10 @@ impl Runtime {
})
}
fn run(&mut self, ip: InstructionPtr) -> Result<crate::value::Value> {
fn run(&mut self, ip: InstructionPtr, force_mode: ForceMode) -> Result<crate::value::Value> {
let mut pc = ip.0;
self.arena
.mutate_root(|_, vm| vm.set_force_mode(force_mode));
loop {
let Runtime {
bytecode,
@@ -157,8 +163,7 @@ impl Runtime {
arena,
..
} = self;
let action =
arena.mutate_root(|mc, root| root.run_batch(bytecode, &mut pc, mc, strings));
let action = arena.mutate_root(|mc, vm| vm.run_batch(bytecode, &mut pc, mc, strings));
match action {
Action::NeedGc => {
if self.arena.collection_phase() == CollectionPhase::Sweeping {
+181 -5
View File
@@ -26,6 +26,14 @@ impl From<Box<Error>> for VmError {
}
}
#[derive(Collect, Clone, Copy, Debug, PartialEq, Eq)]
#[collect(require_static)]
pub(super) enum ForceMode {
Normal,
Shallow,
Deep,
}
#[derive(Collect)]
#[collect(no_drop)]
pub(super) struct VM<'gc> {
@@ -38,6 +46,7 @@ pub(super) struct VM<'gc> {
pc: usize,
current_env: Option<Gc<'gc, RefLock<Env<'gc>>>>,
started: bool,
force_mode: ForceMode,
}
#[derive(Collect)]
@@ -139,6 +148,16 @@ pub(super) enum AfterForce<'gc> {
DiscardAndPush {
value: Value<'gc>,
},
ShallowForce,
ShallowForceList {
forced: SmallVec<[Value<'gc>; 4]>,
remaining: SmallVec<[Value<'gc>; 4]>,
},
ShallowForceAttrs {
keys: SmallVec<[StringId; 4]>,
forced: SmallVec<[Value<'gc>; 4]>,
remaining: SmallVec<[Value<'gc>; 4]>,
},
Builtin(BuiltinState<'gc>),
BuiltinArgForce {
#[collect(require_static)]
@@ -217,6 +236,7 @@ impl<'gc> VM<'gc> {
pc: 0,
current_env: None,
started: false,
force_mode: ForceMode::Normal,
}
}
@@ -293,6 +313,10 @@ impl<'gc> VM<'gc> {
(builtins_val, builtin_lookup)
}
pub(super) fn set_force_mode(&mut self, mode: ForceMode) {
self.force_mode = mode;
}
#[inline(always)]
fn read_array<const N: usize>(&mut self, bc: &[u8]) -> [u8; N] {
#[cfg(debug_assertions)]
@@ -511,7 +535,13 @@ impl<'gc> VM<'gc> {
) -> Action {
let Some(frame) = self.frames.pop() else {
return match self.force_inline(ret_val) {
Ok(ForceResult::Ready(v)) => Action::Done(Ok(self.convert_value(&v, strings))),
Ok(ForceResult::Ready(v)) => {
if self.force_mode == ForceMode::Normal {
Action::Done(Ok(self.convert_value(v.relax(), strings)))
} else {
self.force_children(v, mc, strings)
}
}
Ok(other) => {
try_vm!(self, self.setup_force(other, AfterForce::TopLevelForce, mc, strings));
Action::Continue
@@ -748,11 +778,35 @@ impl<'gc> VM<'gc> {
Err(e) => self.handle_vm_error(e),
}
}
AfterForce::TopLevelForce => Action::Done(Ok(self.convert_value(&val, strings))),
AfterForce::TopLevelForce => {
if self.force_mode == ForceMode::Normal {
Action::Done(Ok(self.convert_value(val.relax(), strings)))
} else {
self.force_children(val, mc, strings)
}
}
AfterForce::DiscardAndPush { value } => {
self.push_stack(value);
Action::Continue
}
AfterForce::ShallowForce => {
self.force_children(val, mc, strings)
}
AfterForce::ShallowForceList {
mut forced,
remaining,
} => {
forced.push(val.relax());
self.continue_force_list(forced, remaining, mc, strings)
}
AfterForce::ShallowForceAttrs {
keys,
mut forced,
remaining,
} => {
forced.push(val.relax());
self.continue_force_attrs(keys, forced, remaining, mc, strings)
}
AfterForce::Builtin(state) => {
let ctx = PrimOpCtx {
vm: self,
@@ -812,6 +866,123 @@ impl<'gc> VM<'gc> {
}
}
fn force_children(
&mut self,
val: StrictValue<'gc>,
mc: &Mutation<'gc>,
strings: &DefaultStringInterner,
) -> Action {
if let Some(list) = val.as_gc::<List<'gc>>() {
if list.inner.is_empty() {
return Action::Done(Ok(self.convert_value(val.relax(), strings)));
}
let remaining: SmallVec<[Value<'gc>; 4]> = list.inner.iter().copied().collect();
let forced = SmallVec::new();
self.continue_force_list(forced, remaining, mc, strings)
} else if let Some(attrs) = val.as_gc::<AttrSet<'gc>>() {
if attrs.is_empty() {
return Action::Done(Ok(self.convert_value(val.relax(), strings)));
}
let keys: SmallVec<[StringId; 4]> = attrs.iter().map(|(k, _)| *k).collect();
let remaining: SmallVec<[Value<'gc>; 4]> = attrs.iter().map(|(_, v)| *v).collect();
let forced = SmallVec::new();
self.continue_force_attrs(keys, forced, remaining, mc, strings)
} else {
Action::Done(Ok(self.convert_value(val.relax(), strings)))
}
}
fn continue_force_list(
&mut self,
mut forced: SmallVec<[Value<'gc>; 4]>,
mut remaining: SmallVec<[Value<'gc>; 4]>,
mc: &Mutation<'gc>,
strings: &DefaultStringInterner,
) -> Action {
while let Some(item) = remaining.pop() {
match self.force_inline(item) {
Ok(ForceResult::Ready(v)) => {
forced.push(v.relax());
}
Ok(other) => {
try_vm!(
self,
self.setup_force(
other,
AfterForce::ShallowForceList {
forced,
remaining,
},
mc,
strings,
)
);
return Action::Continue;
}
Err(e) => return self.handle_vm_error(e),
}
}
forced.reverse();
let list = Gc::new(mc, List { inner: forced });
let val = Value::new_gc(list);
Action::Done(Ok(self.convert_value(val, strings)))
}
fn continue_force_attrs(
&mut self,
keys: SmallVec<[StringId; 4]>,
mut forced: SmallVec<[Value<'gc>; 4]>,
mut remaining: SmallVec<[Value<'gc>; 4]>,
mc: &Mutation<'gc>,
strings: &DefaultStringInterner,
) -> Action {
while !remaining.is_empty() {
let item = remaining.remove(0);
match self.force_inline(item) {
Ok(ForceResult::Ready(v)) => {
forced.push(v.relax());
}
Ok(other) => {
try_vm!(
self,
self.setup_force(
other,
AfterForce::ShallowForceAttrs {
keys,
forced,
remaining,
},
mc,
strings,
)
);
return Action::Continue;
}
Err(e) => return self.handle_vm_error(e),
}
}
let entries: SmallVec<[(StringId, Value<'gc>); 4]> = keys
.iter()
.zip(forced.iter())
.map(|(k, v)| (*k, *v))
.collect();
let attrs = Gc::new(mc, unsafe { AttrSet::from_sorted_unchecked(entries) });
let val = Value::new_gc(attrs);
Action::Done(Ok(self.convert_value(val, strings)))
}
fn convert_top_of_stack_list(
&mut self,
strings: &DefaultStringInterner,
) -> crate::value::Value {
let mut items = Vec::new();
while let Some(val) = self.stack.pop() {
items.push(self.convert_value(val, strings));
}
items.reverse();
crate::value::Value::List(crate::value::List::new(items))
}
fn process_builtin_result(
&mut self,
result: BuiltinResult<'gc>,
@@ -1488,7 +1659,7 @@ impl<'gc> VM<'gc> {
fn convert_value(
&self,
val: &Value<'gc>,
val: Value<'gc>,
strings: &DefaultStringInterner,
) -> crate::value::Value {
if let Some(i) = val.as_inline::<i32>() {
@@ -1508,7 +1679,7 @@ impl<'gc> VM<'gc> {
crate::value::Value::String(ns.as_str().to_owned())
} else if let Some(attrs) = val.as_gc::<AttrSet<'gc>>() {
let mut map = std::collections::BTreeMap::new();
for (key, val) in attrs.iter() {
for &(key, val) in attrs.iter() {
let key_str = strings.resolve(key.0).unwrap_or("").to_owned();
let converted = self.convert_value(val, strings);
map.insert(crate::value::Symbol::from(key_str), converted);
@@ -1518,13 +1689,17 @@ impl<'gc> VM<'gc> {
let items: Vec<_> = list
.inner
.iter()
.copied()
.map(|v| self.convert_value(v, strings))
.collect();
crate::value::Value::List(crate::value::List::new(items))
} else if val.is::<Closure<'gc>>() {
crate::value::Value::Func
} else if val.is::<Thunk<'gc>>() {
crate::value::Value::Thunk
match self.force_inline(val) {
Ok(ForceResult::Ready(forced)) => self.convert_value(forced.relax(), strings),
_ => crate::value::Value::Thunk,
}
} else if val.as_inline::<PrimOp>().is_some() {
crate::value::Value::PrimOp("primop".into())
} else if val.is::<PrimOpApp<'gc>>() {
@@ -1758,6 +1933,7 @@ impl<'gc> VM<'gc> {
if key.is::<Null>() {
continue;
}
// FIXME: register?
let Some(key_sid) = Self::get_string_id(key) else {
return Action::Done(Err(Error::eval_error(
"dynamic attribute name must be a string",