Compare commits
1 Commits
main
...
aec24493e5
| Author | SHA1 | Date | |
|---|---|---|---|
|
aec24493e5
|
+20
-15
@@ -12,6 +12,7 @@ use crate::downgrade::{Downgrade as _, DowngradeContext};
|
|||||||
use crate::error::{Error, Result, Source};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::ir::{ArgId, Ir, IrKey, IrRef, RawIrRef, StringId, ThunkId, ir_content_eq};
|
use crate::ir::{ArgId, Ir, IrKey, IrRef, RawIrRef, StringId, ThunkId, ir_content_eq};
|
||||||
use crate::runtime::builtins::new_builtins_env;
|
use crate::runtime::builtins::new_builtins_env;
|
||||||
|
use crate::runtime::vm::ForceMode;
|
||||||
use crate::store::{DaemonStore, StoreConfig};
|
use crate::store::{DaemonStore, StoreConfig};
|
||||||
use crate::value::Symbol;
|
use crate::value::Symbol;
|
||||||
|
|
||||||
@@ -56,20 +57,15 @@ impl Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&mut self, source: Source) -> Result<crate::value::Value> {
|
pub fn eval(&mut self, source: Source) -> Result<crate::value::Value> {
|
||||||
let root = self.downgrade(source, None)?;
|
self.do_eval(source, None, ForceMode::Normal)
|
||||||
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
|
||||||
self.run(ip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_shallow(&mut self, _source: Source) -> Result<crate::value::Value> {
|
pub fn eval_shallow(&mut self, source: Source) -> Result<crate::value::Value> {
|
||||||
todo!()
|
self.do_eval(source, None, ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_deep(&mut self, source: Source) -> Result<crate::value::Value> {
|
pub fn eval_deep(&mut self, source: Source) -> Result<crate::value::Value> {
|
||||||
// FIXME: deep
|
self.do_eval(source, None, ForceMode::Deep)
|
||||||
let root = self.downgrade(source, None)?;
|
|
||||||
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
|
||||||
self.run(ip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_repl(
|
pub fn eval_repl(
|
||||||
@@ -77,10 +73,18 @@ impl Runtime {
|
|||||||
source: Source,
|
source: Source,
|
||||||
scope: &HashSet<StringId>,
|
scope: &HashSet<StringId>,
|
||||||
) -> Result<crate::value::Value> {
|
) -> Result<crate::value::Value> {
|
||||||
// FIXME: shallow
|
self.do_eval(source, Some(Scope::Repl(scope)), ForceMode::Shallow)
|
||||||
let root = self.downgrade(source, Some(Scope::Repl(scope)))?;
|
}
|
||||||
|
|
||||||
|
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);
|
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
||||||
self.run(ip)
|
self.run(ip, force_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_binding(
|
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;
|
let mut pc = ip.0;
|
||||||
|
self.arena
|
||||||
|
.mutate_root(|_, vm| vm.set_force_mode(force_mode));
|
||||||
loop {
|
loop {
|
||||||
let Runtime {
|
let Runtime {
|
||||||
bytecode,
|
bytecode,
|
||||||
@@ -157,8 +163,7 @@ impl Runtime {
|
|||||||
arena,
|
arena,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
let action =
|
let action = arena.mutate_root(|mc, vm| vm.run_batch(bytecode, &mut pc, mc, strings));
|
||||||
arena.mutate_root(|mc, root| root.run_batch(bytecode, &mut pc, mc, strings));
|
|
||||||
match action {
|
match action {
|
||||||
Action::NeedGc => {
|
Action::NeedGc => {
|
||||||
if self.arena.collection_phase() == CollectionPhase::Sweeping {
|
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)]
|
#[derive(Collect)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub(super) struct VM<'gc> {
|
pub(super) struct VM<'gc> {
|
||||||
@@ -38,6 +46,7 @@ pub(super) struct VM<'gc> {
|
|||||||
pc: usize,
|
pc: usize,
|
||||||
current_env: Option<Gc<'gc, RefLock<Env<'gc>>>>,
|
current_env: Option<Gc<'gc, RefLock<Env<'gc>>>>,
|
||||||
started: bool,
|
started: bool,
|
||||||
|
force_mode: ForceMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Collect)]
|
#[derive(Collect)]
|
||||||
@@ -139,6 +148,16 @@ pub(super) enum AfterForce<'gc> {
|
|||||||
DiscardAndPush {
|
DiscardAndPush {
|
||||||
value: Value<'gc>,
|
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>),
|
Builtin(BuiltinState<'gc>),
|
||||||
BuiltinArgForce {
|
BuiltinArgForce {
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
@@ -217,6 +236,7 @@ impl<'gc> VM<'gc> {
|
|||||||
pc: 0,
|
pc: 0,
|
||||||
current_env: None,
|
current_env: None,
|
||||||
started: false,
|
started: false,
|
||||||
|
force_mode: ForceMode::Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +313,10 @@ impl<'gc> VM<'gc> {
|
|||||||
(builtins_val, builtin_lookup)
|
(builtins_val, builtin_lookup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_force_mode(&mut self, mode: ForceMode) {
|
||||||
|
self.force_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_array<const N: usize>(&mut self, bc: &[u8]) -> [u8; N] {
|
fn read_array<const N: usize>(&mut self, bc: &[u8]) -> [u8; N] {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@@ -511,7 +535,13 @@ impl<'gc> VM<'gc> {
|
|||||||
) -> Action {
|
) -> Action {
|
||||||
let Some(frame) = self.frames.pop() else {
|
let Some(frame) = self.frames.pop() else {
|
||||||
return match self.force_inline(ret_val) {
|
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) => {
|
Ok(other) => {
|
||||||
try_vm!(self, self.setup_force(other, AfterForce::TopLevelForce, mc, strings));
|
try_vm!(self, self.setup_force(other, AfterForce::TopLevelForce, mc, strings));
|
||||||
Action::Continue
|
Action::Continue
|
||||||
@@ -748,11 +778,35 @@ impl<'gc> VM<'gc> {
|
|||||||
Err(e) => self.handle_vm_error(e),
|
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 } => {
|
AfterForce::DiscardAndPush { value } => {
|
||||||
self.push_stack(value);
|
self.push_stack(value);
|
||||||
Action::Continue
|
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) => {
|
AfterForce::Builtin(state) => {
|
||||||
let ctx = PrimOpCtx {
|
let ctx = PrimOpCtx {
|
||||||
vm: self,
|
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(
|
fn process_builtin_result(
|
||||||
&mut self,
|
&mut self,
|
||||||
result: BuiltinResult<'gc>,
|
result: BuiltinResult<'gc>,
|
||||||
@@ -1488,7 +1659,7 @@ impl<'gc> VM<'gc> {
|
|||||||
|
|
||||||
fn convert_value(
|
fn convert_value(
|
||||||
&self,
|
&self,
|
||||||
val: &Value<'gc>,
|
val: Value<'gc>,
|
||||||
strings: &DefaultStringInterner,
|
strings: &DefaultStringInterner,
|
||||||
) -> crate::value::Value {
|
) -> crate::value::Value {
|
||||||
if let Some(i) = val.as_inline::<i32>() {
|
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())
|
crate::value::Value::String(ns.as_str().to_owned())
|
||||||
} else if let Some(attrs) = val.as_gc::<AttrSet<'gc>>() {
|
} else if let Some(attrs) = val.as_gc::<AttrSet<'gc>>() {
|
||||||
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_str = strings.resolve(key.0).unwrap_or("").to_owned();
|
let key_str = strings.resolve(key.0).unwrap_or("").to_owned();
|
||||||
let converted = self.convert_value(val, strings);
|
let converted = self.convert_value(val, strings);
|
||||||
map.insert(crate::value::Symbol::from(key_str), converted);
|
map.insert(crate::value::Symbol::from(key_str), converted);
|
||||||
@@ -1518,13 +1689,17 @@ impl<'gc> VM<'gc> {
|
|||||||
let items: Vec<_> = list
|
let items: Vec<_> = list
|
||||||
.inner
|
.inner
|
||||||
.iter()
|
.iter()
|
||||||
|
.copied()
|
||||||
.map(|v| self.convert_value(v, strings))
|
.map(|v| self.convert_value(v, strings))
|
||||||
.collect();
|
.collect();
|
||||||
crate::value::Value::List(crate::value::List::new(items))
|
crate::value::Value::List(crate::value::List::new(items))
|
||||||
} else if val.is::<Closure<'gc>>() {
|
} else if val.is::<Closure<'gc>>() {
|
||||||
crate::value::Value::Func
|
crate::value::Value::Func
|
||||||
} else if val.is::<Thunk<'gc>>() {
|
} 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() {
|
} else if val.as_inline::<PrimOp>().is_some() {
|
||||||
crate::value::Value::PrimOp("primop".into())
|
crate::value::Value::PrimOp("primop".into())
|
||||||
} else if val.is::<PrimOpApp<'gc>>() {
|
} else if val.is::<PrimOpApp<'gc>>() {
|
||||||
@@ -1758,6 +1933,7 @@ impl<'gc> VM<'gc> {
|
|||||||
if key.is::<Null>() {
|
if key.is::<Null>() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// FIXME: register?
|
||||||
let Some(key_sid) = Self::get_string_id(key) else {
|
let Some(key_sid) = Self::get_string_id(key) else {
|
||||||
return Action::Done(Err(Error::eval_error(
|
return Action::Done(Err(Error::eval_error(
|
||||||
"dynamic attribute name must be a string",
|
"dynamic attribute name must be a string",
|
||||||
|
|||||||
Reference in New Issue
Block a user