better force eval ergonomic
This commit is contained in:
@@ -0,0 +1,255 @@
|
|||||||
|
use fix_common::StringId;
|
||||||
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
|
use crate::value::*;
|
||||||
|
use crate::{Break, BytecodeReader, NixNum, Step, Vm};
|
||||||
|
|
||||||
|
pub(crate) trait Forced<'gc>: Sized {
|
||||||
|
const WIDTH: usize;
|
||||||
|
|
||||||
|
/// Force and type-check the `WIDTH` slots starting at `base_depth` from
|
||||||
|
/// TOS, deepest-first. If a slot holds a thunk, enter it and return
|
||||||
|
/// `Break::Force`. If a slot holds a value of the wrong type, call
|
||||||
|
/// `finish_type_err` and return `Break::Done`.
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step;
|
||||||
|
|
||||||
|
/// After `force_and_check` returned `Continue`, pop `WIDTH` slots
|
||||||
|
/// (TOS first) and convert. Type assertions are infallible because
|
||||||
|
/// `force_and_check` already validated every slot.
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Forced<'gc> for StrictValue<'gc> {
|
||||||
|
const WIDTH: usize = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step {
|
||||||
|
vm.force_slot(base_depth, reader, mc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
vm.pop_forced()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_forced_inline {
|
||||||
|
($($ty:ty => $nix_ty:expr),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
impl<'gc> Forced<'gc> for $ty {
|
||||||
|
const WIDTH: usize = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step {
|
||||||
|
vm.force_slot(base_depth, reader, mc)?;
|
||||||
|
let v = vm.peek_forced(base_depth);
|
||||||
|
if v.as_inline::<$ty>().is_none() {
|
||||||
|
let _: Step = vm.finish_type_err($nix_ty, v.ty());
|
||||||
|
return Step::Break(Break::Done);
|
||||||
|
}
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
vm.pop_forced()
|
||||||
|
.as_inline::<$ty>()
|
||||||
|
.expect("type checked in force_and_check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_forced_gc {
|
||||||
|
($($ty:ty => $nix_ty:expr),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
impl<'gc> Forced<'gc> for Gc<'gc, $ty> {
|
||||||
|
const WIDTH: usize = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step {
|
||||||
|
vm.force_slot(base_depth, reader, mc)?;
|
||||||
|
let v = vm.peek_forced(base_depth);
|
||||||
|
if v.as_gc::<$ty>().is_none() {
|
||||||
|
let _: Step = vm.finish_type_err($nix_ty, v.ty());
|
||||||
|
return Step::Break(Break::Done);
|
||||||
|
}
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
vm.pop_forced()
|
||||||
|
.as_gc::<$ty>()
|
||||||
|
.expect("type checked in force_and_check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_forced_inline! {
|
||||||
|
i32 => NixType::Int,
|
||||||
|
bool => NixType::Bool,
|
||||||
|
Null => NixType::Null,
|
||||||
|
StringId => NixType::String,
|
||||||
|
PrimOp => NixType::PrimOp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_forced_gc! {
|
||||||
|
i64 => NixType::Int,
|
||||||
|
NixString => NixType::String,
|
||||||
|
AttrSet<'gc> => NixType::AttrSet,
|
||||||
|
List<'gc> => NixType::List,
|
||||||
|
Closure<'gc> => NixType::Closure,
|
||||||
|
PrimOpApp<'gc> => NixType::PrimOpApp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Forced<'gc> for NixNum {
|
||||||
|
const WIDTH: usize = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step {
|
||||||
|
vm.force_slot(base_depth, reader, mc)?;
|
||||||
|
let v = vm.peek_forced(base_depth);
|
||||||
|
if v.as_num().is_none() {
|
||||||
|
let _: Step = vm.finish_type_err(NixType::Int, v.ty());
|
||||||
|
return Step::Break(Break::Done);
|
||||||
|
}
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
vm.pop_forced()
|
||||||
|
.as_num()
|
||||||
|
.expect("type checked in force_and_check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Forced<'gc> for f64 {
|
||||||
|
const WIDTH: usize = 1;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base_depth: usize,
|
||||||
|
) -> Step {
|
||||||
|
vm.force_slot(base_depth, reader, mc)?;
|
||||||
|
let v = vm.peek_forced(base_depth);
|
||||||
|
if v.as_float().is_none() {
|
||||||
|
let _: Step = vm.finish_type_err(NixType::Float, v.ty());
|
||||||
|
return Step::Break(Break::Done);
|
||||||
|
}
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
vm.pop_forced()
|
||||||
|
.as_float()
|
||||||
|
.expect("type checked in force_and_check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc, A: Forced<'gc>, B: Forced<'gc>> Forced<'gc> for (A, B) {
|
||||||
|
const WIDTH: usize = A::WIDTH + B::WIDTH;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base: usize,
|
||||||
|
) -> Step {
|
||||||
|
A::force_and_check(vm, reader, mc, base + B::WIDTH)?;
|
||||||
|
B::force_and_check(vm, reader, mc, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
let b = B::pop_converted(vm);
|
||||||
|
let a = A::pop_converted(vm);
|
||||||
|
(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc, A: Forced<'gc>, B: Forced<'gc>, C: Forced<'gc>> Forced<'gc> for (A, B, C) {
|
||||||
|
const WIDTH: usize = A::WIDTH + B::WIDTH + C::WIDTH;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base: usize,
|
||||||
|
) -> Step {
|
||||||
|
A::force_and_check(vm, reader, mc, base + B::WIDTH + C::WIDTH)?;
|
||||||
|
B::force_and_check(vm, reader, mc, base + C::WIDTH)?;
|
||||||
|
C::force_and_check(vm, reader, mc, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
let c = C::pop_converted(vm);
|
||||||
|
let b = B::pop_converted(vm);
|
||||||
|
let a = A::pop_converted(vm);
|
||||||
|
(a, b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc, A: Forced<'gc>, B: Forced<'gc>, C: Forced<'gc>, D: Forced<'gc>> Forced<'gc>
|
||||||
|
for (A, B, C, D)
|
||||||
|
{
|
||||||
|
const WIDTH: usize = A::WIDTH + B::WIDTH + C::WIDTH + D::WIDTH;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn force_and_check(
|
||||||
|
vm: &mut Vm<'gc>,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
base: usize,
|
||||||
|
) -> Step {
|
||||||
|
A::force_and_check(vm, reader, mc, base + B::WIDTH + C::WIDTH + D::WIDTH)?;
|
||||||
|
B::force_and_check(vm, reader, mc, base + C::WIDTH + D::WIDTH)?;
|
||||||
|
C::force_and_check(vm, reader, mc, base + D::WIDTH)?;
|
||||||
|
D::force_and_check(vm, reader, mc, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_converted(vm: &mut Vm<'gc>) -> Self {
|
||||||
|
let d = D::pop_converted(vm);
|
||||||
|
let c = C::pop_converted(vm);
|
||||||
|
let b = B::pop_converted(vm);
|
||||||
|
let a = A::pop_converted(vm);
|
||||||
|
(a, b, c, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{BytecodeReader, NixNum, Step, VmContextExt, VmError};
|
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
|
use crate::{BytecodeReader, NixNum, Step, VmContextExt, VmError};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -13,10 +13,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
|
||||||
let rhs = self.pop_forced();
|
|
||||||
let lhs = self.pop_forced();
|
|
||||||
if let (Some(ls), Some(rs)) = (
|
if let (Some(ls), Some(rs)) = (
|
||||||
VmContextExt::get_string(ctx, lhs),
|
VmContextExt::get_string(ctx, lhs),
|
||||||
VmContextExt::get_string(ctx, rhs),
|
VmContextExt::get_string(ctx, rhs),
|
||||||
@@ -61,10 +58,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
int_op: fn(i64, i64) -> i64,
|
int_op: fn(i64, i64) -> i64,
|
||||||
float_op: fn(f64, f64) -> f64,
|
float_op: fn(f64, f64) -> f64,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
|
||||||
let rhs = self.pop_forced();
|
|
||||||
let lhs = self.pop_forced();
|
|
||||||
let res = numeric_binop(lhs, rhs, mc, int_op, float_op);
|
let res = numeric_binop(lhs, rhs, mc, int_op, float_op);
|
||||||
match res {
|
match res {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
@@ -81,10 +75,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
|
||||||
let rhs = self.pop_forced();
|
|
||||||
let lhs = self.pop_forced();
|
|
||||||
match (get_num(rhs), get_num(lhs)) {
|
match (get_num(rhs), get_num(lhs)) {
|
||||||
(_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => {
|
(_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => {
|
||||||
return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
||||||
@@ -110,9 +101,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
let eq = match self.values_equal(ctx) {
|
|
||||||
Ok(eq) => eq,
|
Ok(eq) => eq,
|
||||||
Err(e) => return self.finish_vm_err(e),
|
Err(e) => return self.finish_vm_err(e),
|
||||||
};
|
};
|
||||||
@@ -127,9 +117,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
let eq = match self.values_equal(ctx) {
|
|
||||||
Ok(eq) => eq,
|
Ok(eq) => eq,
|
||||||
Err(e) => return self.finish_vm_err(e),
|
Err(e) => return self.finish_vm_err(e),
|
||||||
};
|
};
|
||||||
@@ -184,9 +173,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
match self.compare_values_inner(ctx, pred, lhs, rhs) {
|
||||||
match self.compare_values_inner(ctx, pred) {
|
|
||||||
Ok(()) => Step::Continue(()),
|
Ok(()) => Step::Continue(()),
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
@@ -198,16 +186,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (l, r) = self.try_force::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
|
||||||
let r = match self.pop_forced_expect_gc::<List>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(got) => return self.finish_type_err(NixType::List, got)
|
|
||||||
};
|
|
||||||
let l = match self.pop_forced_expect_gc::<List>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(got) => return self.finish_type_err(NixType::List, got)
|
|
||||||
};
|
|
||||||
let mut items = smallvec::SmallVec::new();
|
let mut items = smallvec::SmallVec::new();
|
||||||
items.extend_from_slice(&l);
|
items.extend_from_slice(&l);
|
||||||
items.extend_from_slice(&r);
|
items.extend_from_slice(&r);
|
||||||
@@ -221,16 +200,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(1, reader, mc)?;
|
let (l, r) = self.try_force::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
||||||
self.try_force(0, reader, mc)?;
|
|
||||||
let r = match self.pop_forced_expect_gc::<AttrSet>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(got) => return self.finish_type_err(NixType::AttrSet, got)
|
|
||||||
};
|
|
||||||
let l = match self.pop_forced_expect_gc::<AttrSet>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(got) => return self.finish_type_err(NixType::AttrSet, got)
|
|
||||||
};
|
|
||||||
self.push(Value::new_gc(l.merge(&r, mc)));
|
self.push(Value::new_gc(l.merge(&r, mc)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
@@ -245,10 +215,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
todo!("implement unary operation");
|
todo!("implement unary operation");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn values_equal(&mut self, ctx: &impl crate::VmContext) -> crate::VmResult<bool> {
|
pub(crate) fn values_equal(
|
||||||
let rhs = self.pop_forced();
|
&mut self,
|
||||||
let lhs = self.pop_forced();
|
ctx: &impl crate::VmContext,
|
||||||
|
lhs: StrictValue<'gc>,
|
||||||
|
rhs: StrictValue<'gc>,
|
||||||
|
) -> crate::VmResult<bool> {
|
||||||
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
||||||
return Ok(match (a, b) {
|
return Ok(match (a, b) {
|
||||||
(NixNum::Int(a), NixNum::Int(b)) => a == b,
|
(NixNum::Int(a), NixNum::Int(b)) => a == b,
|
||||||
@@ -273,16 +245,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
if a.inner.len() != b.inner.len() {
|
if a.inner.len() != b.inner.len() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let len = a.inner.len();
|
for (x, y) in a.inner.iter().zip(b.inner.iter()) {
|
||||||
for (x, y) in a.inner.iter().zip(b.inner.iter()).rev() {
|
let lx = x.restrict().expect("forced");
|
||||||
self.push(*x);
|
let ly = y.restrict().expect("forced");
|
||||||
self.push(*y);
|
if !self.values_equal(ctx, lx, ly)? {
|
||||||
}
|
|
||||||
for i in 0..len {
|
|
||||||
let eq = self.values_equal(ctx)?;
|
|
||||||
if !eq {
|
|
||||||
let rem = len - 1 - i;
|
|
||||||
self.stack.truncate(self.stack.len() - rem * 2);
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,19 +258,13 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
if a.len() != b.len() {
|
if a.len() != b.len() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let len = a.len();
|
for ((k1, v1), (k2, v2)) in a.iter().zip(b.iter()) {
|
||||||
for ((k1, v1), (k2, v2)) in a.iter().zip(b.iter()).rev() {
|
|
||||||
if k1 != k2 {
|
if k1 != k2 {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
self.push(*v1);
|
let lv1 = v1.restrict().expect("forced");
|
||||||
self.push(*v2);
|
let lv2 = v2.restrict().expect("forced");
|
||||||
}
|
if !self.values_equal(ctx, lv1, lv2)? {
|
||||||
for i in 0..len {
|
|
||||||
let eq = self.values_equal(ctx)?;
|
|
||||||
if !eq {
|
|
||||||
let rem = len - 1 - i;
|
|
||||||
self.stack.truncate(self.stack.len() - rem * 2);
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,10 +277,9 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
ctx: &impl crate::VmContext,
|
ctx: &impl crate::VmContext,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
|
lhs: StrictValue<'gc>,
|
||||||
|
rhs: StrictValue<'gc>,
|
||||||
) -> crate::VmResult<()> {
|
) -> crate::VmResult<()> {
|
||||||
let rhs = self.pop_forced();
|
|
||||||
let lhs = self.pop_forced();
|
|
||||||
|
|
||||||
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
||||||
let ord = match (a, b) {
|
let ord = match (a, b) {
|
||||||
(NixNum::Int(a), NixNum::Int(b)) => a.cmp(&b),
|
(NixNum::Int(a), NixNum::Int(b)) => a.cmp(&b),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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::{BytecodeReader, CallFrame, Closure, Env, Step, ThunkState, VmContextExt};
|
use crate::{BytecodeReader, CallFrame, Closure, Env, Step, ThunkState, VmContextExt};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -11,12 +12,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.try_force(0, reader, mc)?;
|
let func = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
if self.call_depth > 10000 {
|
if self.call_depth > 10000 {
|
||||||
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
||||||
}
|
}
|
||||||
self.call_depth += 1;
|
self.call_depth += 1;
|
||||||
let func = self.pop();
|
|
||||||
let arg = reader.read_operand_data(ctx).resolve(mc, self);
|
let arg = reader.read_operand_data(ctx).resolve(mc, self);
|
||||||
if let Some(closure) = func.as_gc::<Closure>() {
|
if let Some(closure) = func.as_gc::<Closure>() {
|
||||||
let ip = closure.ip;
|
let ip = closure.ip;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use gc_arena::Gc;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, Step, Value,
|
AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, Step, StrictValue, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -57,18 +57,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
self.try_force(0, reader, mc)?;
|
let attrset = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||||
|
|
||||||
let attrs = self.peek(0).restrict().expect("forced");
|
|
||||||
let Some(attrset) = attrs.as_gc::<AttrSet>() else {
|
|
||||||
return self.finish_err(Error::eval_error(
|
|
||||||
"value is not a set while a set was expected",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
match attrset.lookup(key) {
|
match attrset.lookup(key) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.replace(0, v);
|
self.push(v);
|
||||||
}
|
}
|
||||||
None => loop {
|
None => loop {
|
||||||
let byte = reader.bytecode()[reader.pc()];
|
let byte = reader.bytecode()[reader.pc()];
|
||||||
@@ -78,7 +71,6 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader.set_pc(reader.pc() + 1 + 4);
|
reader.set_pc(reader.pc() + 1 + 4);
|
||||||
} else if byte == fix_codegen::Op::JumpIfSelectSucceeded as u8 {
|
} else if byte == fix_codegen::Op::JumpIfSelectSucceeded as u8 {
|
||||||
reader.set_pc(reader.pc() + 1 + 4);
|
reader.set_pc(reader.pc() + 1 + 4);
|
||||||
let _ = self.pop();
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
let name = ctx.resolve_string(key);
|
let name = ctx.resolve_string(key);
|
||||||
@@ -99,12 +91,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
|
|
||||||
self.try_force(0, reader, mc)?;
|
let (attrset, key_val) = self.try_force::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||||
self.try_force(1, reader, mc)?;
|
|
||||||
|
|
||||||
let key_val = self.stack[self.stack.len() - 1]
|
|
||||||
.restrict()
|
|
||||||
.expect("dynamic key must be forced");
|
|
||||||
let key_sid = if let Some(sid) = key_val.as_inline::<crate::StringId>() {
|
let key_sid = if let Some(sid) = key_val.as_inline::<crate::StringId>() {
|
||||||
sid
|
sid
|
||||||
} else if let Some(ns) = key_val.as_gc::<NixString>() {
|
} else if let Some(ns) = key_val.as_gc::<NixString>() {
|
||||||
@@ -113,16 +101,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
return self.finish_err(Error::eval_error("dynamic select key must be a string"));
|
return self.finish_err(Error::eval_error("dynamic select key must be a string"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let attrset_val = self.stack[self.stack.len() - 2].restrict().expect("forced");
|
|
||||||
let Some(attrset) = attrset_val.as_gc::<AttrSet>() else {
|
|
||||||
return self.finish_err(Error::eval_error(
|
|
||||||
"value is not a set while a set was expected",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
match attrset.lookup(key_sid) {
|
match attrset.lookup(key_sid) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.stack.truncate(self.stack.len() - 2);
|
|
||||||
self.push(v);
|
self.push(v);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::value::*;
|
||||||
use crate::{BytecodeReader, Step};
|
use crate::{BytecodeReader, Step};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -8,8 +9,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
self.try_force(0, reader, mc)?;
|
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
let cond = self.pop();
|
|
||||||
if cond.as_inline::<bool>() == Some(false) {
|
if cond.as_inline::<bool>() == Some(false) {
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
self.try_force(0, reader, mc)?;
|
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
let cond = self.pop();
|
|
||||||
if cond.as_inline::<bool>() == Some(true) {
|
if cond.as_inline::<bool>() == Some(true) {
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,12 +66,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
self.push(env);
|
self.push(env);
|
||||||
self.try_force(0, reader, mc)?;
|
let env = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||||
|
|
||||||
let env = match self.pop_forced_expect_gc::<AttrSet>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(got) => return self.finish_type_err(NixType::List, got)
|
|
||||||
};
|
|
||||||
let Some(val) = env.lookup(name) else {
|
let Some(val) = env.lookup(name) else {
|
||||||
reader.set_pc(reader.inst_start_pc());
|
reader.set_pc(reader.inst_start_pc());
|
||||||
self.with_env = prev;
|
self.with_env = prev;
|
||||||
|
|||||||
+10
-18
@@ -21,12 +21,14 @@ mod boxing;
|
|||||||
mod bytecode_reader;
|
mod bytecode_reader;
|
||||||
#[cfg(feature = "tailcall")]
|
#[cfg(feature = "tailcall")]
|
||||||
mod dispatch_tailcall;
|
mod dispatch_tailcall;
|
||||||
|
mod forced;
|
||||||
mod value;
|
mod value;
|
||||||
pub use value::StaticValue;
|
pub use value::StaticValue;
|
||||||
use value::*;
|
use value::*;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub(crate) mod instructions;
|
pub(crate) mod instructions;
|
||||||
pub(crate) use bytecode_reader::BytecodeReader;
|
pub(crate) use bytecode_reader::BytecodeReader;
|
||||||
|
pub(crate) use forced::Forced;
|
||||||
use helpers::*;
|
use helpers::*;
|
||||||
|
|
||||||
type VmResult<T> = std::result::Result<T, VmError>;
|
type VmResult<T> = std::result::Result<T, VmError>;
|
||||||
@@ -346,27 +348,17 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn pop_forced_expect_inline<T: InlineStorable>(&mut self) -> std::result::Result<T, NixType> {
|
pub(crate) fn try_force<T: Forced<'gc>>(
|
||||||
self.pop_forced().expect_inline::<T>()
|
&mut self,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> std::ops::ControlFlow<Break, T> {
|
||||||
|
T::force_and_check(self, reader, mc, 0)?;
|
||||||
|
std::ops::ControlFlow::Continue(T::pop_converted(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn pop_forced_expect_gc<T: GcStorable>(&mut self) -> std::result::Result<Gc<'gc, T>, NixType> {
|
pub(crate) fn force_slot(
|
||||||
self.pop_forced().expect_gc::<T>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn pop_forced_expect_num(&mut self) -> std::result::Result<NixNum, NixType> {
|
|
||||||
self.pop_forced().expect_num()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn pop_forced_expect_bool(&mut self) -> std::result::Result<bool, NixType> {
|
|
||||||
self.pop_forced().expect_bool()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn try_force(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
|||||||
Reference in New Issue
Block a user