ForceMode (WIP)
This commit is contained in:
+20
-15
@@ -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
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user