Compare commits
5 Commits
98b07f00e4
...
e469d1b819
| Author | SHA1 | Date | |
|---|---|---|---|
|
e469d1b819
|
|||
|
b31c2a4906
|
|||
|
11b0b8a78e
|
|||
|
581c333070
|
|||
|
520bb7d75e
|
+1
-1
@@ -10,4 +10,4 @@ prof.json
|
|||||||
*.cpuprofile
|
*.cpuprofile
|
||||||
*.cpuprofile.gz
|
*.cpuprofile.gz
|
||||||
*v8.log*
|
*v8.log*
|
||||||
callgrind.out.*
|
callgrind.*
|
||||||
|
|||||||
+24
-8
@@ -160,6 +160,22 @@ enum TagVal {
|
|||||||
pub(crate) struct RawTag(TagVal);
|
pub(crate) struct RawTag(TagVal);
|
||||||
|
|
||||||
impl RawTag {
|
impl RawTag {
|
||||||
|
pub(crate) const P1: RawTag = RawTag(TagVal::_P1);
|
||||||
|
pub(crate) const P2: RawTag = RawTag(TagVal::_P2);
|
||||||
|
pub(crate) const P3: RawTag = RawTag(TagVal::_P3);
|
||||||
|
pub(crate) const P4: RawTag = RawTag(TagVal::_P4);
|
||||||
|
pub(crate) const P5: RawTag = RawTag(TagVal::_P5);
|
||||||
|
pub(crate) const P6: RawTag = RawTag(TagVal::_P6);
|
||||||
|
pub(crate) const P7: RawTag = RawTag(TagVal::_P7);
|
||||||
|
|
||||||
|
pub(crate) const N1: RawTag = RawTag(TagVal::_N1);
|
||||||
|
pub(crate) const N2: RawTag = RawTag(TagVal::_N2);
|
||||||
|
pub(crate) const N3: RawTag = RawTag(TagVal::_N3);
|
||||||
|
pub(crate) const N4: RawTag = RawTag(TagVal::_N4);
|
||||||
|
pub(crate) const N5: RawTag = RawTag(TagVal::_N5);
|
||||||
|
pub(crate) const N6: RawTag = RawTag(TagVal::_N6);
|
||||||
|
pub(crate) const N7: RawTag = RawTag(TagVal::_N7);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new(neg: bool, val: NonZeroU8) -> RawTag {
|
pub(crate) fn new(neg: bool, val: NonZeroU8) -> RawTag {
|
||||||
@@ -195,7 +211,7 @@ impl RawTag {
|
|||||||
/// `val` must be in the range `1..8`
|
/// `val` must be in the range `1..8`
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag {
|
pub(crate) const unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag {
|
||||||
RawTag(match (neg, val) {
|
RawTag(match (neg, val) {
|
||||||
(false, 1) => TagVal::_P1,
|
(false, 1) => TagVal::_P1,
|
||||||
(false, 2) => TagVal::_P2,
|
(false, 2) => TagVal::_P2,
|
||||||
@@ -244,7 +260,7 @@ impl RawTag {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn neg_val(self) -> (bool, u8) {
|
pub(crate) const fn neg_val(self) -> (bool, u8) {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
TagVal::_P1 => (false, 1),
|
TagVal::_P1 => (false, 1),
|
||||||
TagVal::_P2 => (false, 2),
|
TagVal::_P2 => (false, 2),
|
||||||
@@ -285,17 +301,17 @@ impl Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag(self) -> RawTag {
|
const fn tag(self) -> RawTag {
|
||||||
unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) }
|
unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_sign(self) -> bool {
|
const fn get_sign(self) -> bool {
|
||||||
self.0 & 0x8000 != 0
|
self.0 & 0x8000 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_tag(self) -> u8 {
|
const fn get_tag(self) -> u8 {
|
||||||
(self.0 & 0x0007) as u8
|
(self.0 & 0x0007) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +357,7 @@ impl Value {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn tag(&self) -> RawTag {
|
pub(crate) const fn tag(&self) -> RawTag {
|
||||||
self.header.tag()
|
self.header.tag()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +433,7 @@ impl RawBox {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn tag(&self) -> Option<RawTag> {
|
pub(crate) const fn tag(&self) -> Option<RawTag> {
|
||||||
if self.is_value() {
|
if self.is_value() {
|
||||||
Some(unsafe { self.value.tag() })
|
Some(unsafe { self.value.tag() })
|
||||||
} else {
|
} else {
|
||||||
@@ -433,7 +449,7 @@ impl RawBox {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn is_value(&self) -> bool {
|
pub(crate) const fn is_value(&self) -> bool {
|
||||||
(unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN })
|
(unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
use crate::{BytecodeReader, ForceInfo, StepResult, Vm, VmContext};
|
use crate::{Break, BytecodeReader, Step, Vm, VmContext};
|
||||||
|
|
||||||
pub(crate) enum TailResult<'gc> {
|
pub(crate) enum TailResult {
|
||||||
YieldFuel(u32),
|
YieldFuel(u32),
|
||||||
ForceThunk(ForceInfo<'gc>),
|
|
||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ pub(crate) type OpFn<'gc, C> = extern "rust-preserve-none" fn(
|
|||||||
&DispatchTable<'gc, C>,
|
&DispatchTable<'gc, C>,
|
||||||
u32,
|
u32,
|
||||||
u32,
|
u32,
|
||||||
) -> TailResult<'gc>;
|
) -> TailResult;
|
||||||
|
|
||||||
pub(crate) struct DispatchTable<'gc, C: VmContext>(pub(crate) [OpFn<'gc, C>; 256]);
|
pub(crate) struct DispatchTable<'gc, C: VmContext>(pub(crate) [OpFn<'gc, C>; 256]);
|
||||||
|
|
||||||
@@ -30,16 +29,15 @@ extern "rust-preserve-none" fn op_illegal<'gc, C: VmContext>(
|
|||||||
_table: &DispatchTable<'gc, C>,
|
_table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
_fuel: u32,
|
_fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
panic!("illegal opcode at pc = {pc}");
|
panic!("illegal opcode at pc = {pc}");
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! tail_dispatch_after {
|
macro_rules! tail_dispatch_after {
|
||||||
($result:expr, $new_pc:expr, $vm:ident, $mc:ident, $ctx:ident, $bc:ident, $table:ident, $fuel:ident) => {{
|
($result:expr, $new_pc:expr, $vm:ident, $mc:ident, $ctx:ident, $bc:ident, $table:ident, $fuel:ident) => {{
|
||||||
match $result {
|
match $result {
|
||||||
StepResult::Continue => {}
|
Step::Continue(()) | Step::Break(Break::Force) => {}
|
||||||
StepResult::ForceThunk(info) => return TailResult::ForceThunk(info),
|
Step::Break(Break::Done) => return TailResult::Done,
|
||||||
StepResult::Done => return TailResult::Done,
|
|
||||||
}
|
}
|
||||||
let new_pc: u32 = $new_pc;
|
let new_pc: u32 = $new_pc;
|
||||||
if $fuel == 0 {
|
if $fuel == 0 {
|
||||||
@@ -60,7 +58,7 @@ macro_rules! tail_fn {
|
|||||||
table: &DispatchTable<'gc, C>,
|
table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
let result = vm.$name();
|
let result = vm.$name();
|
||||||
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
||||||
}
|
}
|
||||||
@@ -74,7 +72,7 @@ macro_rules! tail_fn {
|
|||||||
table: &DispatchTable<'gc, C>,
|
table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
||||||
let result = vm.$name(&mut reader);
|
let result = vm.$name(&mut reader);
|
||||||
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
||||||
@@ -89,7 +87,7 @@ macro_rules! tail_fn {
|
|||||||
table: &DispatchTable<'gc, C>,
|
table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
||||||
let result = vm.$name(&mut reader, mc);
|
let result = vm.$name(&mut reader, mc);
|
||||||
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
||||||
@@ -104,26 +102,12 @@ macro_rules! tail_fn {
|
|||||||
table: &DispatchTable<'gc, C>,
|
table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
let mut reader = BytecodeReader::from_after_op(bc, pc as usize);
|
||||||
let result = vm.$name(ctx, &mut reader, mc);
|
let result = vm.$name(ctx, &mut reader, mc);
|
||||||
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, reader.pc() as u32, vm, mc, ctx, bc, table, fuel)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:ident, (mc, inst_start_pc)) => {
|
|
||||||
extern "rust-preserve-none" fn $name<'gc, C: VmContext>(
|
|
||||||
vm: &mut Vm<'gc>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
ctx: &mut C,
|
|
||||||
bc: &[u8],
|
|
||||||
table: &DispatchTable<'gc, C>,
|
|
||||||
pc: u32,
|
|
||||||
fuel: u32,
|
|
||||||
) -> TailResult<'gc> {
|
|
||||||
let result = vm.$name(mc, pc as usize);
|
|
||||||
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($name:ident, (ctx)) => {
|
($name:ident, (ctx)) => {
|
||||||
extern "rust-preserve-none" fn $name<'gc, C: VmContext>(
|
extern "rust-preserve-none" fn $name<'gc, C: VmContext>(
|
||||||
vm: &mut Vm<'gc>,
|
vm: &mut Vm<'gc>,
|
||||||
@@ -133,7 +117,7 @@ macro_rules! tail_fn {
|
|||||||
table: &DispatchTable<'gc, C>,
|
table: &DispatchTable<'gc, C>,
|
||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
let result = vm.$name(ctx);
|
let result = vm.$name(ctx);
|
||||||
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
||||||
}
|
}
|
||||||
@@ -181,7 +165,7 @@ tail_fn!(op_gt, (ctx, reader, mc));
|
|||||||
tail_fn!(op_leq, (ctx, reader, mc));
|
tail_fn!(op_leq, (ctx, reader, mc));
|
||||||
tail_fn!(op_geq, (ctx, reader, mc));
|
tail_fn!(op_geq, (ctx, reader, mc));
|
||||||
tail_fn!(op_concat, (reader, mc));
|
tail_fn!(op_concat, (reader, mc));
|
||||||
tail_fn!(op_update, (mc, inst_start_pc));
|
tail_fn!(op_update, (reader, mc));
|
||||||
|
|
||||||
tail_fn!(op_neg, ());
|
tail_fn!(op_neg, ());
|
||||||
tail_fn!(op_not, ());
|
tail_fn!(op_not, ());
|
||||||
@@ -303,9 +287,8 @@ pub(crate) fn run_tailcall<'gc, C: VmContext>(
|
|||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
bc: &[u8],
|
bc: &[u8],
|
||||||
pc: u32,
|
pc: u32,
|
||||||
) -> TailResult<'gc> {
|
) -> TailResult {
|
||||||
const FUEL: u32 = 1024;
|
|
||||||
let table = &DispatchTable::<'gc, C>::NEW;
|
let table = &DispatchTable::<'gc, C>::NEW;
|
||||||
let op = bc[pc as usize] as usize;
|
let op = bc[pc as usize] as usize;
|
||||||
table.0[op](vm, mc, ctx, bc, table, pc, FUEL)
|
table.0[op](vm, mc, ctx, bc, table, pc, Vm::DEFAULT_FUEL_AMOUNT)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +2,8 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{BytecodeReader, NixNum, StepResult, StrictValue, Value, VmContextExt, VmError};
|
use crate::value::*;
|
||||||
|
use crate::{BytecodeReader, NixNum, Step, VmContextExt, VmError};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -11,28 +12,21 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let rhs = self.pop_stack_forced();
|
|
||||||
let lhs = self.pop_stack_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),
|
||||||
) {
|
) {
|
||||||
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
||||||
self.push_stack(Value::new_gc(ns));
|
self.push(Value::new_gc(ns));
|
||||||
return StepResult::Continue;
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
let res = numeric_binop(lhs, rhs, mc, i64::wrapping_add, |a, b| a + b);
|
let res = numeric_binop(lhs, rhs, mc, i64::wrapping_add, |a, b| a + b);
|
||||||
match res {
|
match res {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
self.push_stack(val);
|
self.push(val);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
@@ -43,8 +37,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.op_arith(mc, i64::wrapping_sub, |a, b| a - b, reader.inst_start_pc())
|
self.op_arith(reader, mc, i64::wrapping_sub, |a, b| a - b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -52,31 +46,24 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.op_arith(mc, i64::wrapping_mul, |a, b| a * b, reader.inst_start_pc())
|
self.op_arith(reader, mc, i64::wrapping_mul, |a, b| a * b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn op_arith(
|
fn op_arith(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'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,
|
||||||
inst_start_pc: usize,
|
) -> Step {
|
||||||
) -> StepResult<'gc> {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
if let Some(step) = self.try_force_resolved(1, inst_start_pc, mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, inst_start_pc, mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let rhs = self.pop_stack_forced();
|
|
||||||
let lhs = self.pop_stack_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) => {
|
||||||
self.push_stack(val);
|
self.push(val);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
@@ -87,15 +74,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let rhs = self.pop_stack_forced();
|
|
||||||
let lhs = self.pop_stack_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(
|
||||||
@@ -107,8 +87,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let res = numeric_binop(lhs, rhs, mc, |a, b| a / b, |a, b| a / b);
|
let res = numeric_binop(lhs, rhs, mc, |a, b| a / b, |a, b| a / b);
|
||||||
match res {
|
match res {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
self.push_stack(val);
|
self.push(val);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
@@ -120,19 +100,14 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
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),
|
||||||
};
|
};
|
||||||
self.push_stack(Value::new_inline(eq));
|
self.push(Value::new_inline(eq));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -141,19 +116,14 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
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),
|
||||||
};
|
};
|
||||||
self.push_stack(Value::new_inline(!eq));
|
self.push(Value::new_inline(!eq));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -162,7 +132,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_lt)
|
self.compare_values(ctx, reader, mc, Ordering::is_lt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +142,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_gt)
|
self.compare_values(ctx, reader, mc, Ordering::is_gt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +152,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_le)
|
self.compare_values(ctx, reader, mc, Ordering::is_le)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +162,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_ge)
|
self.compare_values(ctx, reader, mc, Ordering::is_ge)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,15 +172,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
match self.compare_values_inner(ctx, pred, lhs, rhs) {
|
||||||
}
|
Ok(()) => Step::Continue(()),
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
match self.compare_values_inner(ctx, pred) {
|
|
||||||
Ok(()) => StepResult::Continue,
|
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,74 +185,42 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
let (l, r) = self.try_force::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let rhs = self.pop_stack_forced();
|
|
||||||
let lhs = self.pop_stack_forced();
|
|
||||||
let Some(l) = lhs.as_gc::<crate::List>() else {
|
|
||||||
return self.finish_err(fix_error::Error::eval_error(
|
|
||||||
"cannot concatenate: left operand is not a list",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let Some(r) = rhs.as_gc::<crate::List>() else {
|
|
||||||
return self.finish_err(fix_error::Error::eval_error(
|
|
||||||
"cannot concatenate: right operand is not a list",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
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);
|
||||||
self.push_stack(Value::new_gc(Gc::new(mc, crate::List { inner: items })));
|
self.push(Value::new_gc(Gc::new(mc, crate::List { inner: items })));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_update(
|
pub(crate) fn op_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
inst_start_pc: usize,
|
) -> Step {
|
||||||
) -> StepResult<'gc> {
|
let (l, r) = self.try_force::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
||||||
if let Some(step) = self.try_force_resolved(1, inst_start_pc, mc) {
|
self.push(Value::new_gc(l.merge(&r, mc)));
|
||||||
return step;
|
Step::Continue(())
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(0, inst_start_pc, mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let rhs = self.pop_stack_forced();
|
|
||||||
let lhs = self.pop_stack_forced();
|
|
||||||
let Some(l) = lhs.as_gc::<crate::AttrSet>() else {
|
|
||||||
return self.finish_err(fix_error::Error::eval_error(
|
|
||||||
"cannot update: left operand is not a set",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let Some(r) = rhs.as_gc::<crate::AttrSet>() else {
|
|
||||||
return self.finish_err(fix_error::Error::eval_error(
|
|
||||||
"cannot update: right operand is not a set",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
self.push_stack(Value::new_gc(l.merge(&r, mc)));
|
|
||||||
StepResult::Continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_neg(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_neg(&mut self) -> Step {
|
||||||
todo!("implement unary operation");
|
todo!("implement unary operation");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_not(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_not(&mut self) -> Step {
|
||||||
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_stack_forced();
|
&mut self,
|
||||||
let lhs = self.pop_stack_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,
|
||||||
@@ -312,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_stack(*x);
|
let ly = y.restrict().expect("forced");
|
||||||
self.push_stack(*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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,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_stack(*v1);
|
let lv1 = v1.restrict().expect("forced");
|
||||||
self.push_stack(*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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,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_stack_forced();
|
|
||||||
let lhs = self.pop_stack_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),
|
||||||
@@ -371,16 +291,17 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
a.partial_cmp(&(b as f64)).unwrap_or(Ordering::Less)
|
a.partial_cmp(&(b as f64)).unwrap_or(Ordering::Less)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.push_stack(Value::new_inline(pred(ord)));
|
self.push(Value::new_inline(pred(ord)));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let (Some(a), Some(b)) = (
|
if let (Some(a), Some(b)) = (
|
||||||
VmContextExt::get_string(ctx, lhs),
|
VmContextExt::get_string(ctx, lhs),
|
||||||
VmContextExt::get_string(ctx, rhs),
|
VmContextExt::get_string(ctx, rhs),
|
||||||
) {
|
) {
|
||||||
self.push_stack(Value::new_inline(pred(a.cmp(b))));
|
self.push(Value::new_inline(pred(a.cmp(b))));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
// TODO: compare other types
|
||||||
Err(crate::vm_err("cannot compare these types"))
|
Err(crate::vm_err("cannot compare these types"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
use fix_builtins::BuiltinId;
|
use fix_builtins::BuiltinId;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::{BytecodeReader, PrimOp, StepResult, Value};
|
use crate::{BytecodeReader, PrimOp, Step, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_builtins(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_load_builtins(&mut self) -> Step {
|
||||||
self.push_stack(self.builtins);
|
self.push(self.builtins);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_builtin(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_load_builtin(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let Ok(id) = BuiltinId::try_from_primitive(reader.read_u8())
|
let Ok(id) = BuiltinId::try_from_primitive(reader.read_u8())
|
||||||
.map_err(|err| panic!("unknown builtin id: {}", err.number));
|
.map_err(|err| panic!("unknown builtin id: {}", err.number));
|
||||||
self.push_stack(Value::new_inline(PrimOp {
|
self.push(Value::new_inline(PrimOp {
|
||||||
id,
|
id,
|
||||||
arity: fix_builtins::BUILTINS[id as usize].1,
|
arity: fix_builtins::BUILTINS[id as usize].1,
|
||||||
}));
|
}));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_mk_pos(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_mk_pos(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
todo!("MkPos");
|
todo!("MkPos");
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
pub(crate) fn op_load_repl_binding(
|
pub(crate) fn op_load_repl_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let _name = reader.read_string_id();
|
let _name = reader.read_string_id();
|
||||||
todo!("LoadReplBinding");
|
todo!("LoadReplBinding");
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
pub(crate) fn op_load_scoped_binding(
|
pub(crate) fn op_load_scoped_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let _name = reader.read_string_id();
|
let _name = reader.read_string_id();
|
||||||
todo!("LoadScopedBinding");
|
todo!("LoadScopedBinding");
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
_mc: &gc_arena::Mutation<'gc>,
|
_mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let _parts_count = reader.read_u16() as usize;
|
let _parts_count = reader.read_u16() as usize;
|
||||||
let _force_string = reader.read_u8() != 0;
|
let _force_string = reader.read_u8() != 0;
|
||||||
let mut _operands: smallvec::SmallVec<[crate::OperandData; 4]> =
|
let mut _operands: smallvec::SmallVec<[crate::OperandData; 4]> =
|
||||||
@@ -63,7 +63,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_resolve_path(&mut self, _ctx: &mut impl crate::VmContext) -> StepResult<'gc> {
|
pub(crate) fn op_resolve_path(&mut self, _ctx: &mut impl crate::VmContext) -> Step {
|
||||||
todo!("implement ResolvePath");
|
todo!("implement ResolvePath");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{BytecodeReader, CallFrame, Closure, Env, StepResult, ThunkState, VmContextExt};
|
use crate::value::*;
|
||||||
|
use crate::{BytecodeReader, CallFrame, Closure, Env, Step, ThunkState, VmContextExt};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -10,15 +11,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let func = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
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_stack();
|
|
||||||
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;
|
||||||
@@ -41,7 +39,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
} else {
|
} else {
|
||||||
todo!("call other types: {func:?}")
|
todo!("call other types: {func:?}")
|
||||||
}
|
}
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -50,7 +48,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
self.handle_return(reader, ctx, mc)
|
self.handle_return(reader, ctx, mc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +57,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
ctx: &C,
|
ctx: &C,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let ret_inst_pc = reader.pc() - 1;
|
let ret_inst_pc = reader.pc() - 1;
|
||||||
let Some(CallFrame {
|
let Some(CallFrame {
|
||||||
pc: ret_pc,
|
pc: ret_pc,
|
||||||
@@ -69,18 +67,18 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
with_env,
|
with_env,
|
||||||
}) = self.call_stack.pop()
|
}) = self.call_stack.pop()
|
||||||
else {
|
else {
|
||||||
let val = self.pop_stack();
|
let val = self.pop();
|
||||||
return self.finish_ok(ctx.convert_value(val));
|
return self.finish_ok(ctx.convert_value(val));
|
||||||
};
|
};
|
||||||
reader.set_pc(ret_pc);
|
reader.set_pc(ret_pc);
|
||||||
if let Some(outer_thunk) = thunk {
|
if let Some(outer_thunk) = thunk {
|
||||||
let val = self.pop_stack();
|
let val = self.pop();
|
||||||
match val.restrict() {
|
match val.restrict() {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
||||||
if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8)
|
if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8)
|
||||||
{
|
{
|
||||||
self.push_stack(val.relax());
|
self.push(val.relax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(inner_thunk) => {
|
Err(inner_thunk) => {
|
||||||
@@ -109,14 +107,14 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader.set_pc(inner_ip);
|
reader.set_pc(inner_ip);
|
||||||
self.env = inner_env;
|
self.env = inner_env;
|
||||||
self.with_env = inner_with_env;
|
self.with_env = inner_with_env;
|
||||||
return StepResult::Continue;
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
ThunkState::Evaluated(val) => {
|
ThunkState::Evaluated(val) => {
|
||||||
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
||||||
if reader.bytecode().get(ret_pc).copied()
|
if reader.bytecode().get(ret_pc).copied()
|
||||||
== Some(fix_codegen::Op::Return as u8)
|
== Some(fix_codegen::Op::Return as u8)
|
||||||
{
|
{
|
||||||
self.push_stack(val.relax());
|
self.push(val.relax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"),
|
ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"),
|
||||||
@@ -132,6 +130,6 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
self.env = env;
|
self.env = env;
|
||||||
self.with_env = with_env;
|
self.with_env = with_env;
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{BytecodeReader, StepResult, ThunkState, Value};
|
use crate::{BytecodeReader, Step, ThunkState, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -8,7 +8,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let thunk = Gc::new(
|
let thunk = Gc::new(
|
||||||
mc,
|
mc,
|
||||||
@@ -18,8 +18,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
with_env: self.with_env,
|
with_env: self.with_env,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
self.push_stack(Value::new_gc(thunk));
|
self.push(Value::new_gc(thunk));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -27,7 +27,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let n_locals = reader.read_u32();
|
let n_locals = reader.read_u32();
|
||||||
let closure = Gc::new(
|
let closure = Gc::new(
|
||||||
@@ -39,8 +39,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
pattern: None,
|
pattern: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.push_stack(Value::new_gc(closure));
|
self.push(Value::new_gc(closure));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -48,7 +48,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let n_locals = reader.read_u32();
|
let n_locals = reader.read_u32();
|
||||||
let req_count = reader.read_u16() as usize;
|
let req_count = reader.read_u16() as usize;
|
||||||
@@ -89,7 +89,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
pattern: Some(pattern),
|
pattern: Some(pattern),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.push_stack(Value::new_gc(closure));
|
self.push(Value::new_gc(closure));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use gc_arena::Gc;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, StepResult, Value,
|
AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, Step, StrictValue, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
@@ -13,7 +13,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
let mut entries: SmallVec<[AttrEntry; 4]> = SmallVec::with_capacity(count);
|
let mut entries: SmallVec<[AttrEntry; 4]> = SmallVec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
@@ -37,14 +37,14 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
kv.sort_by_key(|(k, _)| *k);
|
kv.sort_by_key(|(k, _)| *k);
|
||||||
let attrs = Gc::new(mc, AttrSet::from_sorted_unchecked(kv));
|
let attrs = Gc::new(mc, AttrSet::from_sorted_unchecked(kv));
|
||||||
self.push_stack(Value::new_gc(attrs));
|
self.push(Value::new_gc(attrs));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_empty_attrs(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_make_empty_attrs(&mut self) -> Step {
|
||||||
self.push_stack(self.empty_attrs);
|
self.push(self.empty_attrs);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -53,24 +53,15 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let attrset = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
|
|
||||||
let attrs = self.peek_stack(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_stack(0, v);
|
self.push(v);
|
||||||
}
|
}
|
||||||
None => loop {
|
None => loop {
|
||||||
let byte = reader.bytecode()[reader.pc()];
|
let byte = reader.bytecode()[reader.pc()];
|
||||||
@@ -80,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_stack();
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
let name = ctx.resolve_string(key);
|
let name = ctx.resolve_string(key);
|
||||||
@@ -89,7 +79,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -98,19 +88,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
|
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let (attrset, key_val) = self.try_force::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
if let Some(step) = self.try_force_resolved(1, reader.inst_start_pc(), mc) {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
|
|
||||||
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>() {
|
||||||
@@ -119,38 +101,30 @@ 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_stack(v);
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let name = ctx.resolve_string(key_sid);
|
let name = ctx.resolve_string(key_sid);
|
||||||
return self.finish_err(Error::eval_error(format!("attribute '{name}' missing")));
|
return self.finish_err(Error::eval_error(format!("attribute '{name}' missing")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump_if_select_succeeded(
|
pub(crate) fn op_jump_if_select_succeeded(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_has_attr(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let _n = reader.read_u16() as usize;
|
let _n = reader.read_u16() as usize;
|
||||||
todo!("HasAttr");
|
todo!("HasAttr");
|
||||||
}
|
}
|
||||||
@@ -161,21 +135,21 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
items.push(reader.read_operand_data(ctx).resolve(mc, self));
|
items.push(reader.read_operand_data(ctx).resolve(mc, self));
|
||||||
}
|
}
|
||||||
let list = Gc::new(mc, List { inner: items });
|
let list = Gc::new(mc, List { inner: items });
|
||||||
self.push_stack(Value::new_gc(list));
|
self.push(Value::new_gc(list));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_empty_list(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_make_empty_list(&mut self) -> Step {
|
||||||
self.push_stack(self.empty_list);
|
self.push(self.empty_list);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{BytecodeReader, StepResult};
|
use crate::value::*;
|
||||||
|
use crate::{BytecodeReader, Step};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -6,16 +7,13 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let cond = self.pop_stack();
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -23,27 +21,24 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
let cond = self.pop_stack();
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_jump(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_assert(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_assert(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let _raw_idx = reader.read_u32();
|
let _raw_idx = reader.read_u32();
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
todo!("implement Assert (force TOS)");
|
todo!("implement Assert (force TOS)");
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{BytecodeReader, StepResult, Value};
|
use crate::{BytecodeReader, Step, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let val = reader.read_i32();
|
let val = reader.read_i32();
|
||||||
self.push_stack(Value::new_inline(val));
|
self.push(Value::new_inline(val));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -15,41 +15,41 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let val = reader.read_i64();
|
let val = reader.read_i64();
|
||||||
self.push_stack(Value::new_gc(Gc::new(mc, val)));
|
self.push(Value::new_gc(Gc::new(mc, val)));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let val = reader.read_f64();
|
let val = reader.read_f64();
|
||||||
self.push_stack(Value::new_float(val));
|
self.push(Value::new_float(val));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let sid = reader.read_string_id();
|
let sid = reader.read_string_id();
|
||||||
self.push_stack(Value::new_inline(sid));
|
self.push(Value::new_inline(sid));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_null(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_push_null(&mut self) -> Step {
|
||||||
self.push_stack(Value::new_inline(crate::Null));
|
self.push(Value::new_inline(crate::Null));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_true(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_push_true(&mut self) -> Step {
|
||||||
self.push_stack(Value::new_inline(true));
|
self.push(Value::new_inline(true));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_false(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_push_false(&mut self) -> Step {
|
||||||
self.push_stack(Value::new_inline(false));
|
self.push(Value::new_inline(false));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use crate::{BytecodeReader, Mutation, StepResult, Value};
|
use crate::{BytecodeReader, Mutation, Step, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let idx = reader.read_u32() as usize;
|
let idx = reader.read_u32() as usize;
|
||||||
self.push_stack(self.env.borrow().locals[idx]);
|
self.push(self.env.borrow().locals[idx]);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_outer(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult<'gc> {
|
pub(crate) fn op_load_outer(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let layer = reader.read_u8();
|
let layer = reader.read_u8();
|
||||||
let idx = reader.read_u32() as usize;
|
let idx = reader.read_u32() as usize;
|
||||||
let mut cur = self.env;
|
let mut cur = self.env;
|
||||||
@@ -18,8 +18,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
cur = prev;
|
cur = prev;
|
||||||
}
|
}
|
||||||
let val = cur.borrow().locals[idx];
|
let val = cur.borrow().locals[idx];
|
||||||
self.push_stack(val);
|
self.push(val);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -27,11 +27,11 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let idx = reader.read_u32() as usize;
|
let idx = reader.read_u32() as usize;
|
||||||
let val = self.pop_stack();
|
let val = self.pop();
|
||||||
self.env.borrow_mut(mc).locals[idx] = val;
|
self.env.borrow_mut(mc).locals[idx] = val;
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -39,12 +39,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
self.env
|
self.env
|
||||||
.borrow_mut(mc)
|
.borrow_mut(mc)
|
||||||
.locals
|
.locals
|
||||||
.extend(std::iter::repeat_n(Value::default(), count));
|
.extend(std::iter::repeat_n(Value::default(), count));
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use fix_common::Symbol;
|
|||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use gc_arena::Gc;
|
use gc_arena::Gc;
|
||||||
|
|
||||||
use crate::{BytecodeReader, CallFrame, StepResult, WithEnv};
|
use crate::{BytecodeReader, CallFrame, Step, WithEnv};
|
||||||
|
use crate::value::*;
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
impl<'gc> crate::Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -11,7 +12,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let env = reader.read_operand_data(ctx).resolve(mc, self);
|
let env = reader.read_operand_data(ctx).resolve(mc, self);
|
||||||
let scope = Gc::new(
|
let scope = Gc::new(
|
||||||
mc,
|
mc,
|
||||||
@@ -21,20 +22,20 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.with_env = Some(scope);
|
self.with_env = Some(scope);
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_pop_with(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_pop_with(&mut self) -> Step {
|
||||||
let Some(scope) = self.with_env else {
|
let Some(scope) = self.with_env else {
|
||||||
unreachable!("no with_scope to pop");
|
unreachable!("no with_scope to pop");
|
||||||
};
|
};
|
||||||
self.with_env = scope.prev;
|
self.with_env = scope.prev;
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_prepare_with(&mut self) -> StepResult<'gc> {
|
pub(crate) fn op_prepare_with(&mut self) -> Step {
|
||||||
self.call_stack.push(CallFrame {
|
self.call_stack.push(CallFrame {
|
||||||
pc: usize::MAX,
|
pc: usize::MAX,
|
||||||
stack_depth: 0,
|
stack_depth: 0,
|
||||||
@@ -42,7 +43,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
env: self.env,
|
env: self.env,
|
||||||
with_env: self.with_env,
|
with_env: self.with_env,
|
||||||
});
|
});
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -51,7 +52,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
ctx: &mut impl crate::VmContext,
|
ctx: &mut impl crate::VmContext,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let name = reader.read_string_id();
|
let name = reader.read_string_id();
|
||||||
|
|
||||||
let Some(&WithEnv { env, prev }) = self.with_env.as_deref() else {
|
let Some(&WithEnv { env, prev }) = self.with_env.as_deref() else {
|
||||||
@@ -64,23 +65,19 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
Symbol::from(ctx.resolve_string(name))
|
Symbol::from(ctx.resolve_string(name))
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
self.push_stack(env);
|
self.push(env);
|
||||||
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
let env = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||||
return step;
|
|
||||||
}
|
|
||||||
|
|
||||||
let env = self.pop_stack().as_gc::<crate::AttrSet>().unwrap();
|
|
||||||
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;
|
||||||
return StepResult::Continue;
|
return Step::Continue(());
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push_stack(val);
|
self.push(val);
|
||||||
let Some(CallFrame { with_env, .. }) = self.call_stack.pop() else {
|
let Some(CallFrame { with_env, .. }) = self.call_stack.pop() else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
self.with_env = with_env;
|
self.with_env = with_env;
|
||||||
StepResult::Continue
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+76
-84
@@ -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>;
|
||||||
@@ -131,20 +133,13 @@ impl<T: VmContext> VmContextExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum StepResult<'gc> {
|
#[repr(u8)]
|
||||||
Continue,
|
pub(crate) enum Break {
|
||||||
ForceThunk(ForceInfo<'gc>),
|
Force,
|
||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ForceInfo<'gc> {
|
pub(crate) type Step = std::ops::ControlFlow<Break>;
|
||||||
pub(crate) thunk: Gc<'gc, Thunk<'gc>>,
|
|
||||||
pub(crate) stack_depth: usize,
|
|
||||||
pub(crate) inst_start_pc: usize,
|
|
||||||
pub(crate) ip: usize,
|
|
||||||
pub(crate) env: GcEnv<'gc>,
|
|
||||||
pub(crate) with_env: Option<GcWithEnv<'gc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Collect)]
|
#[derive(Collect)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
@@ -152,6 +147,7 @@ pub struct Vm<'gc> {
|
|||||||
pub(crate) stack: Vec<Value<'gc>>,
|
pub(crate) stack: Vec<Value<'gc>>,
|
||||||
pub(crate) call_stack: Vec<CallFrame<'gc>>,
|
pub(crate) call_stack: Vec<CallFrame<'gc>>,
|
||||||
pub(crate) call_depth: usize,
|
pub(crate) call_depth: usize,
|
||||||
|
#[allow(dead_code)]
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
pub(crate) error_context: Vec<ErrorFrame>,
|
pub(crate) error_context: Vec<ErrorFrame>,
|
||||||
|
|
||||||
@@ -279,36 +275,42 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn finish_ok(&mut self, val: fix_common::Value) -> StepResult<'gc> {
|
pub(crate) fn finish_ok(&mut self, val: fix_common::Value) -> Step {
|
||||||
self.result = Some(Ok(val));
|
self.result = Some(Ok(val));
|
||||||
StepResult::Done
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn finish_err(&mut self, err: Box<Error>) -> StepResult<'gc> {
|
pub(crate) fn finish_err(&mut self, err: Box<Error>) -> Step {
|
||||||
self.result = Some(Err(err));
|
self.result = Some(Err(err));
|
||||||
StepResult::Done
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn finish_vm_err(&mut self, err: VmError) -> StepResult<'gc> {
|
pub(crate) fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step {
|
||||||
|
self.result = Some(Err(Error::eval_error(format!("expected {expected}, got {got}"))));
|
||||||
|
Step::Break(Break::Done)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn finish_vm_err(&mut self, err: VmError) -> Step {
|
||||||
self.finish_err(err.into_error())
|
self.finish_err(err.into_error())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_stack(&mut self, val: Value<'gc>) {
|
pub(crate) fn push(&mut self, val: Value<'gc>) {
|
||||||
self.stack.push(val);
|
self.stack.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn pop_stack(&mut self) -> Value<'gc> {
|
pub(crate) fn pop(&mut self) -> Value<'gc> {
|
||||||
self.stack.pop().expect("stack underflow")
|
self.stack.pop().expect("stack underflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn peek_stack(&mut self, depth: usize) -> Value<'gc> {
|
pub(crate) fn peek(&mut self, depth: usize) -> Value<'gc> {
|
||||||
*self
|
*self
|
||||||
.stack
|
.stack
|
||||||
.get(self.stack.len() - depth - 1)
|
.get(self.stack.len() - depth - 1)
|
||||||
@@ -316,7 +318,18 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn replace_stack(&mut self, depth: usize, val: Value<'gc>) {
|
#[must_use]
|
||||||
|
pub(crate) fn peek_forced(&mut self, depth: usize) -> StrictValue<'gc> {
|
||||||
|
self
|
||||||
|
.stack
|
||||||
|
.get(self.stack.len() - depth - 1)
|
||||||
|
.expect("stack underflow")
|
||||||
|
.restrict()
|
||||||
|
.expect("forced")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
||||||
let len = self.stack.len();
|
let len = self.stack.len();
|
||||||
*self
|
*self
|
||||||
.stack
|
.stack
|
||||||
@@ -326,7 +339,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg_attr(debug_assertions, track_caller)]
|
#[cfg_attr(debug_assertions, track_caller)]
|
||||||
pub(crate) fn pop_stack_forced(&mut self) -> StrictValue<'gc> {
|
pub(crate) fn pop_forced(&mut self) -> StrictValue<'gc> {
|
||||||
self.stack
|
self.stack
|
||||||
.pop()
|
.pop()
|
||||||
.expect("stack underflow")
|
.expect("stack underflow")
|
||||||
@@ -335,67 +348,53 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_force(
|
pub(crate) fn try_force<T: Forced<'gc>>(
|
||||||
|
&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)]
|
||||||
|
pub(crate) fn force_slot(
|
||||||
&mut self,
|
&mut self,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
inst_start_pc: usize,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> StepResult<'gc> {
|
) -> Step {
|
||||||
let val = self.peek_stack(depth);
|
let Some(thunk) = self.peek(depth).as_gc::<Thunk>() else {
|
||||||
if let Some(thunk) = val.as_gc::<Thunk>() {
|
return Step::Continue(());
|
||||||
|
};
|
||||||
let mut state = thunk.borrow_mut(mc);
|
let mut state = thunk.borrow_mut(mc);
|
||||||
match *state {
|
match *state {
|
||||||
ThunkState::Pending { ip, env, with_env } => {
|
ThunkState::Pending { ip, env, with_env } => {
|
||||||
*state = ThunkState::Blackhole;
|
*state = ThunkState::Blackhole;
|
||||||
drop(state);
|
drop(state);
|
||||||
StepResult::ForceThunk(ForceInfo {
|
|
||||||
thunk,
|
|
||||||
stack_depth: depth,
|
|
||||||
inst_start_pc,
|
|
||||||
ip,
|
|
||||||
env,
|
|
||||||
with_env,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ThunkState::Evaluated(v) => {
|
|
||||||
self.replace_stack(depth, v.relax());
|
|
||||||
StepResult::Continue
|
|
||||||
}
|
|
||||||
ThunkState::Apply { .. } => todo!("force apply"),
|
|
||||||
ThunkState::Blackhole => {
|
|
||||||
self.finish_err(Error::eval_error("infinite recursion encountered"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
StepResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn try_force_resolved(
|
|
||||||
&mut self,
|
|
||||||
depth: usize,
|
|
||||||
inst_start_pc: usize,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
) -> Option<StepResult<'gc>> {
|
|
||||||
match self.try_force(depth, inst_start_pc, mc) {
|
|
||||||
StepResult::Continue => None,
|
|
||||||
other => Some(other),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn apply_force_thunk(&mut self, info: ForceInfo<'gc>) -> usize {
|
|
||||||
self.call_stack.push(CallFrame {
|
self.call_stack.push(CallFrame {
|
||||||
thunk: Some(info.thunk),
|
thunk: Some(thunk),
|
||||||
stack_depth: info.stack_depth,
|
stack_depth: depth,
|
||||||
pc: info.inst_start_pc,
|
pc: reader.inst_start_pc(),
|
||||||
env: self.env,
|
env: self.env,
|
||||||
with_env: self.with_env,
|
with_env: self.with_env,
|
||||||
});
|
});
|
||||||
self.env = info.env;
|
self.env = env;
|
||||||
self.with_env = info.with_env;
|
self.with_env = with_env;
|
||||||
info.ip
|
reader.set_pc(ip);
|
||||||
|
Step::Break(Break::Force)
|
||||||
|
}
|
||||||
|
ThunkState::Evaluated(v) => {
|
||||||
|
drop(state);
|
||||||
|
self.replace(depth, v.relax());
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
ThunkState::Apply { .. } => todo!("force apply"),
|
||||||
|
ThunkState::Blackhole => {
|
||||||
|
drop(state);
|
||||||
|
self.finish_err(Error::eval_error("infinite recursion encountered"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,6 +456,8 @@ impl Vm<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Vm<'gc> {
|
impl<'gc> Vm<'gc> {
|
||||||
|
const DEFAULT_FUEL_AMOUNT: u32 = 1024;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn dispatch_batch<C: VmContext>(
|
fn dispatch_batch<C: VmContext>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -476,10 +477,6 @@ impl<'gc> Vm<'gc> {
|
|||||||
TailResult::YieldFuel(new_pc) => Action::Continue {
|
TailResult::YieldFuel(new_pc) => Action::Continue {
|
||||||
pc: new_pc as usize,
|
pc: new_pc as usize,
|
||||||
},
|
},
|
||||||
TailResult::ForceThunk(info) => {
|
|
||||||
let new_pc = self.apply_force_thunk(info);
|
|
||||||
Action::Continue { pc: new_pc }
|
|
||||||
}
|
|
||||||
TailResult::Done => {
|
TailResult::Done => {
|
||||||
Action::Done(self.result.take().expect("TailResult::Done without result"))
|
Action::Done(self.result.take().expect("TailResult::Done without result"))
|
||||||
}
|
}
|
||||||
@@ -497,10 +494,9 @@ impl<'gc> Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Action {
|
) -> Action {
|
||||||
use fix_codegen::Op::*;
|
use fix_codegen::Op::*;
|
||||||
const DEFAULT_FUEL_AMOUNT: usize = 1024;
|
|
||||||
|
|
||||||
let mut reader = BytecodeReader::new(bytecode, pc);
|
let mut reader = BytecodeReader::new(bytecode, pc);
|
||||||
let mut fuel = DEFAULT_FUEL_AMOUNT;
|
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if fuel == 0 {
|
if fuel == 0 {
|
||||||
@@ -552,7 +548,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
OpLeq => self.op_leq(ctx, &mut reader, mc),
|
OpLeq => self.op_leq(ctx, &mut reader, mc),
|
||||||
OpGeq => self.op_geq(ctx, &mut reader, mc),
|
OpGeq => self.op_geq(ctx, &mut reader, mc),
|
||||||
OpConcat => self.op_concat(&mut reader, mc),
|
OpConcat => self.op_concat(&mut reader, mc),
|
||||||
OpUpdate => self.op_update(mc, reader.inst_start_pc()),
|
OpUpdate => self.op_update(&mut reader, mc),
|
||||||
|
|
||||||
OpNeg => self.op_neg(),
|
OpNeg => self.op_neg(),
|
||||||
OpNot => self.op_not(),
|
OpNot => self.op_not(),
|
||||||
@@ -582,14 +578,10 @@ impl<'gc> Vm<'gc> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
StepResult::Continue => {}
|
Step::Continue(()) | Step::Break(Break::Force) => {}
|
||||||
StepResult::ForceThunk(info) => {
|
Step::Break(Break::Done) => {
|
||||||
let new_pc = self.apply_force_thunk(info);
|
|
||||||
reader.set_pc(new_pc);
|
|
||||||
}
|
|
||||||
StepResult::Done => {
|
|
||||||
return Action::Done(
|
return Action::Done(
|
||||||
self.result.take().expect("StepResult::Done without result"),
|
self.result.take().expect("Break::Done without result"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+136
-45
@@ -23,11 +23,12 @@ mod private {
|
|||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// 1. TAG must be unique among all implementors.
|
/// TAG must be unique among all implementors.
|
||||||
/// 2. TAG must be within 1..=7
|
#[allow(private_interfaces)]
|
||||||
pub unsafe trait Storable: private::Cealed {
|
pub unsafe trait Storable: private::Cealed {
|
||||||
const TAG: (bool, u8);
|
const TAG: RawTag;
|
||||||
}
|
}
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub trait InlineStorable: Storable + RawStore {}
|
pub trait InlineStorable: Storable + RawStore {}
|
||||||
pub trait GcStorable: Storable {}
|
pub trait GcStorable: Storable {}
|
||||||
|
|
||||||
@@ -37,15 +38,17 @@ macro_rules! define_value_types {
|
|||||||
gc { $($gtype:ty => $gtag:expr, $gname:literal;)* }
|
gc { $($gtype:ty => $gtag:expr, $gname:literal;)* }
|
||||||
) => {
|
) => {
|
||||||
$(
|
$(
|
||||||
|
#[allow(private_interfaces)]
|
||||||
unsafe impl Storable for $itype {
|
unsafe impl Storable for $itype {
|
||||||
const TAG: (bool, u8) = $itag;
|
const TAG: RawTag = $itag;
|
||||||
}
|
}
|
||||||
impl InlineStorable for $itype {}
|
impl InlineStorable for $itype {}
|
||||||
impl private::Cealed for $itype {}
|
impl private::Cealed for $itype {}
|
||||||
)*
|
)*
|
||||||
$(
|
$(
|
||||||
|
#[allow(private_interfaces)]
|
||||||
unsafe impl Storable for $gtype {
|
unsafe impl Storable for $gtype {
|
||||||
const TAG: (bool, u8) = $gtag;
|
const TAG: RawTag = $gtag;
|
||||||
}
|
}
|
||||||
impl GcStorable for $gtype {}
|
impl GcStorable for $gtype {}
|
||||||
impl private::Cealed for $gtype {}
|
impl private::Cealed for $gtype {}
|
||||||
@@ -53,11 +56,9 @@ macro_rules! define_value_types {
|
|||||||
|
|
||||||
const _: () = assert!(size_of::<Value<'static>>() == 8);
|
const _: () = assert!(size_of::<Value<'static>>() == 8);
|
||||||
$(const _: () = assert!(size_of::<$itype>() <= 6);)*
|
$(const _: () = assert!(size_of::<$itype>() <= 6);)*
|
||||||
$(const _: () = { let (_, val) = $itag; assert!(val >= 1 && val <= 7); };)*
|
|
||||||
$(const _: () = { let (_, val) = $gtag; assert!(val >= 1 && val <= 7); };)*
|
|
||||||
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
let tags: &[(bool, u8)] = &[$($itag),*, $($gtag),*];
|
let tags: &[(bool, u8)] = &[$(RawTag::neg_val($itag)),*, $(RawTag::neg_val($gtag)),*];
|
||||||
let mut mask_false: u8 = 0;
|
let mut mask_false: u8 = 0;
|
||||||
let mut mask_true: u8 = 0;
|
let mut mask_true: u8 = 0;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -79,12 +80,12 @@ macro_rules! define_value_types {
|
|||||||
const NEEDS_TRACE: bool = true;
|
const NEEDS_TRACE: bool = true;
|
||||||
fn trace<T: Trace<'gc>>(&self, cc: &mut T) {
|
fn trace<T: Trace<'gc>>(&self, cc: &mut T) {
|
||||||
let Some(tag) = self.raw.tag() else { return };
|
let Some(tag) = self.raw.tag() else { return };
|
||||||
match tag.neg_val() {
|
match tag {
|
||||||
$(<$gtype as Storable>::TAG => unsafe {
|
$(<$gtype as Storable>::TAG => unsafe {
|
||||||
self.load_gc::<$gtype>().trace(cc)
|
self.load_gc::<$gtype>().trace(cc)
|
||||||
},)*
|
},)*
|
||||||
$(<$itype as Storable>::TAG => (),)*
|
$(<$itype as Storable>::TAG => (),)*
|
||||||
(neg, val) => unreachable!("invalid tag: neg={neg}, val={val}"),
|
_ => unreachable!("invalid value tag"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +101,7 @@ macro_rules! define_value_types {
|
|||||||
}),)*
|
}),)*
|
||||||
$(Some(<$gtype as Storable>::TAG) =>
|
$(Some(<$gtype as Storable>::TAG) =>
|
||||||
write!(f, "{}(..)", $gname),)*
|
write!(f, "{}(..)", $gname),)*
|
||||||
Some((neg, val)) => write!(f, "Unknown(neg={neg}, val={val})"),
|
_ => unreachable!("invalid value tag"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,20 +110,20 @@ macro_rules! define_value_types {
|
|||||||
|
|
||||||
define_value_types! {
|
define_value_types! {
|
||||||
inline {
|
inline {
|
||||||
i32 => (false, 1), "SmallInt";
|
i32 => RawTag::P1, "SmallInt";
|
||||||
bool => (false, 2), "Bool";
|
bool => RawTag::P2, "Bool";
|
||||||
Null => (false, 3), "Null";
|
Null => RawTag::P3, "Null";
|
||||||
StringId => (false, 4), "SmallString";
|
StringId => RawTag::P4, "SmallString";
|
||||||
PrimOp => (false, 5), "PrimOp";
|
PrimOp => RawTag::P5, "PrimOp";
|
||||||
}
|
}
|
||||||
gc {
|
gc {
|
||||||
i64 => (false, 6), "BigInt";
|
i64 => RawTag::P6, "BigInt";
|
||||||
NixString => (false, 7), "String";
|
NixString => RawTag::P7, "String";
|
||||||
AttrSet<'_> => (true, 1), "AttrSet";
|
AttrSet<'_> => RawTag::N1, "AttrSet";
|
||||||
List<'_> => (true, 2), "List";
|
List<'_> => RawTag::N2, "List";
|
||||||
Thunk<'_> => (true, 3), "Thunk";
|
Thunk<'_> => RawTag::N3, "Thunk";
|
||||||
Closure<'_> => (true, 4), "Closure";
|
Closure<'_> => RawTag::N4, "Closure";
|
||||||
PrimOpApp<'_> => (true, 5), "PrimOpApp";
|
PrimOpApp<'_> => RawTag::N5, "PrimOpApp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ define_value_types! {
|
|||||||
/// NaN-boxed value fitting in 8 bytes.
|
/// NaN-boxed value fitting in 8 bytes.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub(crate) struct Value<'gc> {
|
pub struct Value<'gc> {
|
||||||
raw: RawBox,
|
raw: RawBox,
|
||||||
_marker: PhantomData<Gc<'gc, ()>>,
|
_marker: PhantomData<Gc<'gc, ()>>,
|
||||||
}
|
}
|
||||||
@@ -166,16 +167,15 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `(negative, val)` tag, or `None` for a float.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn tag(self) -> Option<(bool, u8)> {
|
const fn tag(self) -> Option<RawTag> {
|
||||||
self.raw.tag().map(|t| t.neg_val())
|
self.raw.tag()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Value<'gc> {
|
impl<'gc> Value<'gc> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new_float(val: f64) -> Self {
|
pub fn new_float(val: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: RawBox::from_float(val),
|
raw: RawBox::from_float(val),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
@@ -183,24 +183,24 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new_inline<T: InlineStorable>(val: T) -> Self {
|
pub fn new_inline<T: InlineStorable>(val: T) -> Self {
|
||||||
Self::from_raw_value(RawValue::store(
|
Self::from_raw_value(RawValue::store(
|
||||||
unsafe { RawTag::new_unchecked(T::TAG.0, T::TAG.1) },
|
T::TAG,
|
||||||
val,
|
val,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new_gc<T: GcStorable>(gc: Gc<'gc, T>) -> Self {
|
pub fn new_gc<T: GcStorable>(gc: Gc<'gc, T>) -> Self {
|
||||||
let ptr = Gc::as_ptr(gc);
|
let ptr = Gc::as_ptr(gc);
|
||||||
Self::from_raw_value(RawValue::store(
|
Self::from_raw_value(RawValue::store(
|
||||||
unsafe { RawTag::new_unchecked(T::TAG.0, T::TAG.1) },
|
T::TAG,
|
||||||
ptr,
|
ptr,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn make_int(val: i64, mc: &Mutation<'gc>) -> Self {
|
pub fn make_int(val: i64, mc: &Mutation<'gc>) -> Self {
|
||||||
if val >= i32::MIN as i64 && val <= i32::MAX as i64 {
|
if val >= i32::MIN as i64 && val <= i32::MAX as i64 {
|
||||||
Value::new_inline(val as i32)
|
Value::new_inline(val as i32)
|
||||||
} else {
|
} else {
|
||||||
@@ -211,24 +211,24 @@ impl<'gc> Value<'gc> {
|
|||||||
|
|
||||||
impl<'gc> Value<'gc> {
|
impl<'gc> Value<'gc> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn is_float(self) -> bool {
|
pub fn is_float(self) -> bool {
|
||||||
self.raw.is_float()
|
self.raw.is_float()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn is<T: Storable>(self) -> bool {
|
pub fn is<T: Storable>(self) -> bool {
|
||||||
self.tag() == Some(T::TAG)
|
self.tag() == Some(T::TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Value<'gc> {
|
impl<'gc> Value<'gc> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn as_float(self) -> Option<f64> {
|
pub fn as_float(self) -> Option<f64> {
|
||||||
self.raw.float().copied()
|
self.raw.float().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
pub fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
Some(unsafe {
|
Some(unsafe {
|
||||||
let rv = self.raw.value().unwrap_unchecked();
|
let rv = self.raw.value().unwrap_unchecked();
|
||||||
@@ -240,7 +240,7 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn as_gc<T: GcStorable>(self) -> Option<Gc<'gc, T>> {
|
pub fn as_gc<T: GcStorable>(self) -> Option<Gc<'gc, T>> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
Some(unsafe {
|
Some(unsafe {
|
||||||
let rv = self.raw.value().unwrap_unchecked();
|
let rv = self.raw.value().unwrap_unchecked();
|
||||||
@@ -253,7 +253,7 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn as_num(self) -> Option<NixNum> {
|
pub fn as_num(self) -> Option<NixNum> {
|
||||||
if let Some(i) = self.as_inline::<i32>() {
|
if let Some(i) = self.as_inline::<i32>() {
|
||||||
Some(NixNum::Int(i as i64))
|
Some(NixNum::Int(i as i64))
|
||||||
} else if let Some(gc_i) = self.as_gc::<i64>() {
|
} else if let Some(gc_i) = self.as_gc::<i64>() {
|
||||||
@@ -264,13 +264,69 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn restrict(self) -> Result<StrictValue<'gc>, Gc<'gc, Thunk<'gc>>> {
|
pub fn restrict(self) -> Result<StrictValue<'gc>, Gc<'gc, Thunk<'gc>>> {
|
||||||
if let Some(thunk) = self.as_gc::<Thunk<'gc>>() {
|
if let Some(thunk) = self.as_gc::<Thunk<'gc>>() {
|
||||||
Err(thunk)
|
Err(thunk)
|
||||||
} else {
|
} else {
|
||||||
Ok(StrictValue(self))
|
Ok(StrictValue(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ty(self) -> NixType {
|
||||||
|
if self.is_float() {
|
||||||
|
NixType::Float
|
||||||
|
} else if self.is::<i32>() || self.is::<i64>() {
|
||||||
|
NixType::Int
|
||||||
|
} else if self.is::<bool>() {
|
||||||
|
NixType::Bool
|
||||||
|
} else if self.is::<Null>() {
|
||||||
|
NixType::Null
|
||||||
|
} else if self.is::<StringId>() {
|
||||||
|
NixType::String
|
||||||
|
} else if self.is::<PrimOp>() {
|
||||||
|
NixType::PrimOp
|
||||||
|
} else if self.is::<NixString>() {
|
||||||
|
NixType::String
|
||||||
|
} else if self.is::<AttrSet>() {
|
||||||
|
NixType::AttrSet
|
||||||
|
} else if self.is::<List>() {
|
||||||
|
NixType::List
|
||||||
|
} else if self.is::<Thunk>() {
|
||||||
|
NixType::Thunk
|
||||||
|
} else if self.is::<Closure>() {
|
||||||
|
NixType::Closure
|
||||||
|
} else if self.is::<PrimOpApp>() {
|
||||||
|
NixType::PrimOpApp
|
||||||
|
} else {
|
||||||
|
unreachable!("value has no recognized type tag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_inline<T: InlineStorable>(self) -> Result<T, NixType> {
|
||||||
|
self.as_inline::<T>().ok_or_else(|| self.ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_gc<T: GcStorable>(self) -> Result<Gc<'gc, T>, NixType> {
|
||||||
|
self.as_gc::<T>().ok_or_else(|| self.ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_num(self) -> Result<NixNum, NixType> {
|
||||||
|
self.as_num().ok_or_else(|| self.ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_bool(self) -> Result<bool, NixType> {
|
||||||
|
self.as_inline::<bool>().ok_or_else(|| self.ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn expect_float(self) -> Result<f64, NixType> {
|
||||||
|
self.as_float().ok_or_else(|| self.ty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
@@ -549,8 +605,9 @@ pub(crate) struct PrimOpApp<'gc> {
|
|||||||
pub(crate) args: SmallVec<[Value<'gc>; 2]>,
|
pub(crate) args: SmallVec<[Value<'gc>; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default, Collect)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[collect(no_drop)]
|
||||||
pub(crate) struct StrictValue<'gc>(Value<'gc>);
|
pub(crate) struct StrictValue<'gc>(Value<'gc>);
|
||||||
|
|
||||||
impl<'gc> StrictValue<'gc> {
|
impl<'gc> StrictValue<'gc> {
|
||||||
@@ -574,9 +631,43 @@ impl fmt::Debug for StrictValue<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> Collect<'gc> for StrictValue<'gc> {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Collect)]
|
||||||
const NEEDS_TRACE: bool = true;
|
#[collect(require_static)]
|
||||||
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, cc: &mut T) {
|
pub(crate) enum NixType {
|
||||||
self.0.trace(cc);
|
Int,
|
||||||
|
Float,
|
||||||
|
Bool,
|
||||||
|
Null,
|
||||||
|
String,
|
||||||
|
AttrSet,
|
||||||
|
List,
|
||||||
|
Thunk,
|
||||||
|
Closure,
|
||||||
|
PrimOp,
|
||||||
|
PrimOpApp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NixType {
|
||||||
|
pub fn display(self) -> &'static str {
|
||||||
|
use NixType::*;
|
||||||
|
match self {
|
||||||
|
Int => "an integer",
|
||||||
|
Float => "a float",
|
||||||
|
Bool => "a boolean",
|
||||||
|
Null => "null",
|
||||||
|
String => "a string",
|
||||||
|
AttrSet => "a set",
|
||||||
|
List => "a list",
|
||||||
|
Thunk => "a thunk",
|
||||||
|
Closure => "a function",
|
||||||
|
PrimOp => "a built-in function",
|
||||||
|
PrimOpApp => "a partially applied built-in function",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for NixType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.display())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user