Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
f0e3f1eeca
|
|||
|
afbc471e40
|
+17
-14
@@ -10,31 +10,34 @@ members = [
|
|||||||
"fix-vm",
|
"fix-vm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.profiling]
|
|
||||||
inherits = "release"
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[profile.lto]
|
|
||||||
inherits = "release"
|
|
||||||
lto = true
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bumpalo = { version = "3.20", features = [
|
bumpalo = {
|
||||||
|
version = "3.20",
|
||||||
|
features = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"boxed",
|
"boxed",
|
||||||
"collections",
|
"collections",
|
||||||
] }
|
]
|
||||||
|
}
|
||||||
|
ere = "0.2"
|
||||||
ghost-cell = "0.2"
|
ghost-cell = "0.2"
|
||||||
hashbrown = "0.16"
|
hashbrown = "0.16"
|
||||||
|
likely_stable = "0.1"
|
||||||
num_enum = "0.7.5"
|
num_enum = "0.7.5"
|
||||||
smallvec = { version = "1.15", features = ["const_new", "const_generics"] }
|
|
||||||
ere = "0.2"
|
|
||||||
string-interner = "0.19"
|
|
||||||
rnix = "0.14"
|
rnix = "0.14"
|
||||||
rowan = "0.16"
|
rowan = "0.16"
|
||||||
likely_stable = "0.1"
|
smallvec = { version = "1.15", features = ["const_generics", "const_new"] }
|
||||||
|
string-interner = "0.19"
|
||||||
|
|
||||||
[workspace.dependencies.gc-arena]
|
[workspace.dependencies.gc-arena]
|
||||||
git = "https://github.com/kyren/gc-arena"
|
git = "https://github.com/kyren/gc-arena"
|
||||||
rev = "75671ae03f53718357b741ed4027560f14e90836"
|
rev = "75671ae03f53718357b741ed4027560f14e90836"
|
||||||
features = ["allocator-api2", "hashbrown", "smallvec"]
|
features = ["allocator-api2", "hashbrown", "smallvec"]
|
||||||
|
|
||||||
|
[profile.lto]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
|
||||||
|
[profile.profiling]
|
||||||
|
inherits = "release"
|
||||||
|
debug = true
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bumpalo = { workspace = true }
|
bumpalo = { workspace = true }
|
||||||
|
colored = "3.1.1"
|
||||||
ghost-cell = { workspace = true }
|
ghost-cell = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
num_enum = { workspace = true }
|
num_enum = { workspace = true }
|
||||||
rnix = { workspace = true }
|
rnix = { workspace = true }
|
||||||
rowan = { workspace = true }
|
rowan = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
colored = "3.1.1"
|
|
||||||
|
|
||||||
fix-bytecode = { path = "../fix-bytecode" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
miette = { version = "7.6", features = ["fancy"] }
|
miette = { version = "7.6", features = ["fancy"] }
|
||||||
thiserror = "2.0"
|
|
||||||
rnix = { workspace = true }
|
rnix = { workspace = true }
|
||||||
|
thiserror = "2.0"
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ere = { workspace = true }
|
||||||
gc-arena = { workspace = true }
|
gc-arena = { workspace = true }
|
||||||
num_enum = { workspace = true }
|
num_enum = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
ere = { workspace = true }
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
gc-arena = { workspace = true }
|
gc-arena = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
|
likely_stable = { workspace = true }
|
||||||
num_enum = { workspace = true }
|
num_enum = { workspace = true }
|
||||||
smallvec = { workspace = true }
|
smallvec = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
|
||||||
likely_stable = { workspace = true }
|
|
||||||
sptr = "0.3"
|
sptr = "0.3"
|
||||||
|
string-interner = { workspace = true }
|
||||||
|
|
||||||
fix-bytecode = { path = "../fix-bytecode" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-lang = { path = "../fix-lang" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
|
|||||||
@@ -26,11 +26,15 @@ use crate::{
|
|||||||
/// - Imports and scope slots (`import_cache_*` / `scope_slot*` / `set_pending_load`)
|
/// - Imports and scope slots (`import_cache_*` / `scope_slot*` / `set_pending_load`)
|
||||||
pub trait Machine<'gc> {
|
pub trait Machine<'gc> {
|
||||||
fn push(&mut self, val: Value<'gc>);
|
fn push(&mut self, val: Value<'gc>);
|
||||||
|
#[must_use]
|
||||||
fn pop(&mut self) -> Value<'gc>;
|
fn pop(&mut self) -> Value<'gc>;
|
||||||
|
#[must_use]
|
||||||
fn peek(&self, depth: usize) -> Value<'gc>;
|
fn peek(&self, depth: usize) -> Value<'gc>;
|
||||||
|
#[must_use]
|
||||||
fn peek_forced(&self, depth: usize) -> StrictValue<'gc>;
|
fn peek_forced(&self, depth: usize) -> StrictValue<'gc>;
|
||||||
fn pop_forced(&mut self) -> StrictValue<'gc>;
|
fn pop_forced(&mut self) -> StrictValue<'gc>;
|
||||||
fn replace(&mut self, depth: usize, val: Value<'gc>);
|
fn replace(&mut self, depth: usize, val: Value<'gc>);
|
||||||
|
fn drop_n(&mut self, depth: usize);
|
||||||
fn stack_len(&self) -> usize;
|
fn stack_len(&self) -> usize;
|
||||||
|
|
||||||
fn force_slot_to_pc(
|
fn force_slot_to_pc(
|
||||||
|
|||||||
+5
-5
@@ -3,20 +3,20 @@ name = "fix-vm"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
|
||||||
tailcall = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gc-arena = { workspace = true }
|
gc-arena = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
|
likely_stable = { workspace = true }
|
||||||
num_enum = { workspace = true }
|
num_enum = { workspace = true }
|
||||||
smallvec = { workspace = true }
|
smallvec = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
|
||||||
likely_stable = { workspace = true }
|
|
||||||
sptr = "0.3"
|
sptr = "0.3"
|
||||||
|
string-interner = { workspace = true }
|
||||||
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
||||||
|
|
||||||
fix-bytecode = { path = "../fix-bytecode" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
fix-lang = { path = "../fix-lang" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
fix-runtime = { path = "../fix-runtime" }
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tailcall = []
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ macro_rules! tail_fn {
|
|||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult {
|
) -> TailResult {
|
||||||
let result = vm.$name();
|
let result = crate::instructions::$name(vm);
|
||||||
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
tail_dispatch_after!(result, pc + 1, vm, mc, ctx, bc, table, fuel)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -76,7 +76,7 @@ macro_rules! tail_fn {
|
|||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult {
|
) -> 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 = crate::instructions::$name(vm, &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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -91,7 +91,7 @@ macro_rules! tail_fn {
|
|||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult {
|
) -> 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 = crate::instructions::$name(vm, &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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -106,7 +106,7 @@ macro_rules! tail_fn {
|
|||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult {
|
) -> 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 = crate::instructions::$name(vm, 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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -120,7 +120,7 @@ macro_rules! tail_fn {
|
|||||||
pc: u32,
|
pc: u32,
|
||||||
fuel: u32,
|
fuel: u32,
|
||||||
) -> TailResult {
|
) -> TailResult {
|
||||||
let result = vm.$name(ctx);
|
let result = crate::instructions::$name(vm, 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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,15 +5,14 @@ use gc_arena::{Gc, Mutation, RefLock};
|
|||||||
|
|
||||||
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx};
|
use crate::{BytecodeReader, NixNum, Step, VmError, VmRuntimeCtx};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_add(
|
pub(crate) fn op_add<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
// if the LHS is a path, the result is a path obtained by
|
// if the LHS is a path, the result is a path obtained by
|
||||||
// canonicalizing the concatenated string. RHS may be a path or a
|
// canonicalizing the concatenated string. RHS may be a path or a
|
||||||
// string. (A `string + path` keeps the string-typed result, handled
|
// string. (A `string + path` keeps the string-typed result, handled
|
||||||
@@ -21,7 +20,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
if lhs.is::<Path>() {
|
if lhs.is::<Path>() {
|
||||||
let (Some(ls), Some(rs)) = (ctx.get_string_or_path(lhs), ctx.get_string_or_path(rhs))
|
let (Some(ls), Some(rs)) = (ctx.get_string_or_path(lhs), ctx.get_string_or_path(rhs))
|
||||||
else {
|
else {
|
||||||
return self.finish_err(fix_error::Error::eval_error(format!(
|
return m.finish_err(fix_error::Error::eval_error(format!(
|
||||||
"cannot append {} to a path",
|
"cannot append {} to a path",
|
||||||
rhs.ty()
|
rhs.ty()
|
||||||
)));
|
)));
|
||||||
@@ -29,7 +28,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let combined = format!("{ls}{rs}");
|
let combined = format!("{ls}{rs}");
|
||||||
let canon = canon_path_str(&combined);
|
let canon = canon_path_str(&combined);
|
||||||
let sid = ctx.intern_string(canon);
|
let sid = ctx.intern_string(canon);
|
||||||
self.push(Value::new_inline(fix_runtime::Path(sid)));
|
m.push(Value::new_inline(fix_runtime::Path(sid)));
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string_or_path(rhs)) {
|
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string_or_path(rhs)) {
|
||||||
@@ -40,54 +39,66 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc,
|
mc,
|
||||||
crate::NixString::with_context(format!("{ls}{rs}"), merged),
|
crate::NixString::with_context(format!("{ls}{rs}"), merged),
|
||||||
);
|
);
|
||||||
self.push(Value::new_gc(ns));
|
m.push(Value::new_gc(ns));
|
||||||
return Step::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(val);
|
m.push(val);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => m.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_sub(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
pub(crate) fn op_sub<'gc, M: Machine<'gc>>(
|
||||||
self.op_arith(reader, mc, i64::wrapping_sub, |a, b| a - b)
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
op_arith(m, reader, mc, i64::wrapping_sub, |a, b| a - b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_mul(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
pub(crate) fn op_mul<'gc, M: Machine<'gc>>(
|
||||||
self.op_arith(reader, mc, i64::wrapping_mul, |a, b| a * b)
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
op_arith(m, reader, mc, i64::wrapping_mul, |a, b| a * b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn op_arith(
|
fn op_arith<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
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,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
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(val);
|
m.push(val);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => m.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_div(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
pub(crate) fn op_div<'gc, M: Machine<'gc>>(
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
match (get_num(lhs), get_num(rhs)) {
|
match (get_num(lhs), get_num(rhs)) {
|
||||||
(_, 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 m.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
||||||
"division by zero",
|
"division by zero",
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -96,100 +107,100 @@ 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(val);
|
m.push(val);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => m.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_eq(
|
pub(crate) fn op_eq<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
crate::primops::start_eq(self, ctx, reader, mc, lhs, rhs, false)
|
crate::primops::start_eq(m, ctx, reader, mc, lhs, rhs, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_neq(
|
pub(crate) fn op_neq<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
crate::primops::start_eq(self, ctx, reader, mc, lhs, rhs, true)
|
crate::primops::start_eq(m, ctx, reader, mc, lhs, rhs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_lt(
|
pub(crate) fn op_lt<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_lt)
|
compare_values(m, ctx, reader, mc, Ordering::is_lt)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_gt(
|
pub(crate) fn op_gt<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_gt)
|
compare_values(m, ctx, reader, mc, Ordering::is_gt)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_leq(
|
pub(crate) fn op_leq<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_le)
|
compare_values(m, ctx, reader, mc, Ordering::is_le)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_geq(
|
pub(crate) fn op_geq<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.compare_values(ctx, reader, mc, Ordering::is_ge)
|
compare_values(m, ctx, reader, mc, Ordering::is_ge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_values(
|
fn compare_values<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &impl VmRuntimeCtx,
|
ctx: &impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (lhs, rhs) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
match self.compare_values_inner(ctx, pred, lhs, rhs) {
|
match compare_values_inner(m, ctx, pred, lhs, rhs) {
|
||||||
Ok(()) => Step::Continue(()),
|
Ok(()) => Step::Continue(()),
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => m.finish_vm_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_concat(
|
pub(crate) fn op_concat<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (l, r) = self.force_and_retry::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
let (l, r) = m.force_and_retry::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
||||||
let mut items = smallvec::SmallVec::new();
|
let mut items = smallvec::SmallVec::new();
|
||||||
items.extend_from_slice(&l.inner.borrow());
|
items.extend_from_slice(&l.inner.borrow());
|
||||||
items.extend_from_slice(&r.inner.borrow());
|
items.extend_from_slice(&r.inner.borrow());
|
||||||
self.push(Value::new_gc(Gc::new(
|
m.push(Value::new_gc(Gc::new(
|
||||||
mc,
|
mc,
|
||||||
crate::List {
|
crate::List {
|
||||||
inner: RefLock::new(items),
|
inner: RefLock::new(items),
|
||||||
@@ -199,35 +210,43 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_update(
|
pub(crate) fn op_update<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (l, r) = self.force_and_retry::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
let (l, r) = m.force_and_retry::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
||||||
self.push(Value::new_gc(l.merge(&r, mc)));
|
m.push(Value::new_gc(l.merge(&r, mc)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_neg(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
pub(crate) fn op_neg<'gc, M: Machine<'gc>>(
|
||||||
let rhs = self.force_and_retry::<NixNum>(reader, mc)?;
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let rhs = m.force_and_retry::<NixNum>(reader, mc)?;
|
||||||
match rhs {
|
match rhs {
|
||||||
NixNum::Int(int) => self.push(Value::make_int(-int, mc)),
|
NixNum::Int(int) => m.push(Value::make_int(-int, mc)),
|
||||||
NixNum::Float(float) => self.push(Value::new_float(-float)),
|
NixNum::Float(float) => m.push(Value::new_float(-float)),
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_not(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
pub(crate) fn op_not<'gc, M: Machine<'gc>>(
|
||||||
let rhs = self.force_and_retry::<bool>(reader, mc)?;
|
m: &mut M,
|
||||||
self.push(Value::new_inline(!rhs));
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let rhs = m.force_and_retry::<bool>(reader, mc)?;
|
||||||
|
m.push(Value::new_inline(!rhs));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_values_inner(
|
fn compare_values_inner<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &impl VmRuntimeCtx,
|
ctx: &impl VmRuntimeCtx,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
lhs: StrictValue<'gc>,
|
lhs: StrictValue<'gc>,
|
||||||
@@ -244,17 +263,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(Value::new_inline(pred(ord)));
|
m.push(Value::new_inline(pred(ord)));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let (Some(a), Some(b)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
if let (Some(a), Some(b)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
||||||
self.push(Value::new_inline(pred(a.cmp(b))));
|
m.push(Value::new_inline(pred(a.cmp(b))));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let (Some(a), Some(b)) = (lhs.as_inline::<Path>(), rhs.as_inline::<Path>()) {
|
if let (Some(a), Some(b)) = (lhs.as_inline::<Path>(), rhs.as_inline::<Path>()) {
|
||||||
let a = ctx.resolve_string(a.0);
|
let a = ctx.resolve_string(a.0);
|
||||||
let b = ctx.resolve_string(b.0);
|
let b = ctx.resolve_string(b.0);
|
||||||
self.push(Value::new_inline(pred(a.cmp(b))));
|
m.push(Value::new_inline(pred(a.cmp(b))));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// TODO: compare other types
|
// TODO: compare other types
|
||||||
@@ -264,7 +283,6 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
rhs.ty()
|
rhs.ty()
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_num(val: StrictValue<'_>) -> Option<NixNum> {
|
pub(crate) fn get_num(val: StrictValue<'_>) -> Option<NixNum> {
|
||||||
if let Some(i) = val.as_inline::<i32>() {
|
if let Some(i) = val.as_inline::<i32>() {
|
||||||
|
|||||||
@@ -8,29 +8,28 @@ use crate::{
|
|||||||
VmRuntimeCtxExt,
|
VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn call(
|
pub(crate) fn call<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
arg: Value<'gc>,
|
arg: Value<'gc>,
|
||||||
resume_pc: usize,
|
resume_pc: usize,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let func = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let func = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
if self.call_depth > 10000 {
|
if m.call_depth() > 10000 {
|
||||||
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
return m.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
||||||
}
|
}
|
||||||
self.call_depth += 1;
|
m.inc_call_depth();
|
||||||
if let Some(closure) = func.as_gc::<Closure>() {
|
if let Some(closure) = func.as_gc::<Closure>() {
|
||||||
if closure.pattern.is_some() {
|
if closure.pattern.is_some() {
|
||||||
// FIXME: better DX...
|
// FIXME: better DX...
|
||||||
self.push(func.relax());
|
m.push(func.relax());
|
||||||
self.push(arg);
|
m.push(arg);
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
reader.set_pc(PrimOpPhase::CallPattern.ip() as usize);
|
reader.set_pc(PrimOpPhase::CallPattern.ip() as usize);
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
@@ -40,20 +39,20 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let n_locals = closure.n_locals;
|
let n_locals = closure.n_locals;
|
||||||
let env = closure.env;
|
let env = closure.env;
|
||||||
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
|
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
reader.set_pc(ip as usize);
|
reader.set_pc(ip as usize);
|
||||||
self.env = new_env;
|
m.set_env(new_env);
|
||||||
} else if let Some(primop) = func.as_inline::<PrimOp>() {
|
} else if let Some(primop) = func.as_inline::<PrimOp>() {
|
||||||
if primop.arity == 1 {
|
if primop.arity == 1 {
|
||||||
self.push(arg);
|
m.push(arg);
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
reader.set_pc(primop.dispatch_ip as usize)
|
reader.set_pc(primop.dispatch_ip as usize)
|
||||||
} else {
|
} else {
|
||||||
@@ -62,18 +61,18 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
arity: primop.arity - 1,
|
arity: primop.arity - 1,
|
||||||
args: [arg, Value::default(), Value::default()],
|
args: [arg, Value::default(), Value::default()],
|
||||||
};
|
};
|
||||||
self.push(Value::new_gc(Gc::new(mc, app)));
|
m.push(Value::new_gc(Gc::new(mc, app)));
|
||||||
}
|
}
|
||||||
} else if let Some(app) = func.as_gc::<PrimOpApp>() {
|
} else if let Some(app) = func.as_gc::<PrimOpApp>() {
|
||||||
if app.arity == 1 {
|
if app.arity == 1 {
|
||||||
for i in 0..app.primop.arity - 1 {
|
for i in 0..app.primop.arity - 1 {
|
||||||
self.push(app.args[i as usize]);
|
m.push(app.args[i as usize]);
|
||||||
}
|
}
|
||||||
self.push(arg);
|
m.push(arg);
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
reader.set_pc(app.primop.dispatch_ip as usize)
|
reader.set_pc(app.primop.dispatch_ip as usize)
|
||||||
} else {
|
} else {
|
||||||
@@ -83,29 +82,29 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
..*app
|
..*app
|
||||||
};
|
};
|
||||||
new_app.args[position] = arg;
|
new_app.args[position] = arg;
|
||||||
self.push(Value::new_gc(Gc::new(mc, new_app)))
|
m.push(Value::new_gc(Gc::new(mc, new_app)))
|
||||||
}
|
}
|
||||||
} else if let Some(attrs) = func.as_gc::<AttrSet>()
|
} else if let Some(attrs) = func.as_gc::<AttrSet>()
|
||||||
&& let Some(functor) = attrs.lookup(self.functor_sym)
|
&& let Some(functor) = attrs.lookup(m.functor_sym())
|
||||||
{
|
{
|
||||||
// f arg => (f.__functor f) arg
|
// f arg => (f.__functor f) arg
|
||||||
//
|
//
|
||||||
// Stage the work for `CallFunctor1` so retries during force are
|
// Stage the work for `CallFunctor1` so retries during force are
|
||||||
// safe: the stack invariant `[..., orig_arg, self, functor]`
|
// safe: the stack invariant `[..., orig_arg, self, functor]`
|
||||||
// holds every time control re-enters phase 1.
|
// holds every time control re-enters phase 1.
|
||||||
self.call_depth -= 1;
|
m.dec_call_depth();
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
self.push(arg);
|
m.push(arg);
|
||||||
self.push(func.relax());
|
m.push(func.relax());
|
||||||
self.push(functor);
|
m.push(functor);
|
||||||
reader.set_pc(PrimOpPhase::CallFunctor1.ip() as usize);
|
reader.set_pc(PrimOpPhase::CallFunctor1.ip() as usize);
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
} else {
|
} else {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return m.finish_err(Error::eval_error(format!(
|
||||||
"attempt to call something which is not a function but {}",
|
"attempt to call something which is not a function but {}",
|
||||||
func.ty()
|
func.ty()
|
||||||
)));
|
)));
|
||||||
@@ -114,47 +113,47 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_call(
|
pub(crate) fn op_call<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &impl VmRuntimeCtx,
|
ctx: &impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let arg = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
let arg = resolve_operand(&reader.read_operand_data(), mc, ctx, m);
|
||||||
let pc = reader.pc();
|
let pc = reader.pc();
|
||||||
self.call(reader, mc, arg, pc)
|
m.call(reader, mc, arg, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_return(
|
pub(crate) fn op_return<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let val = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let Some(CallFrame {
|
let Some(CallFrame {
|
||||||
pc: ret_pc,
|
pc: ret_pc,
|
||||||
thunk,
|
thunk,
|
||||||
env,
|
env,
|
||||||
}) = self.call_stack.pop()
|
}) = m.pop_call_frame()
|
||||||
else {
|
else {
|
||||||
match self.force_mode {
|
match m.force_mode() {
|
||||||
ForceMode::AsIs => return self.finish_ok(ctx.convert_value(val.relax())),
|
ForceMode::AsIs => return m.finish_ok(ctx.convert_value(val.relax())),
|
||||||
ForceMode::Shallow => {
|
ForceMode::Shallow => {
|
||||||
self.push(val.relax());
|
m.push(val.relax());
|
||||||
reader.set_pc(PrimOpPhase::ForceResultShallow.ip() as usize);
|
reader.set_pc(PrimOpPhase::ForceResultShallow.ip() as usize);
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
ForceMode::Deep => {
|
ForceMode::Deep => {
|
||||||
self.push(val.relax());
|
m.push(val.relax());
|
||||||
self.push(val.relax());
|
m.push(val.relax());
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
pc: PrimOpPhase::ForceResultDeepFinish.ip() as usize,
|
pc: PrimOpPhase::ForceResultDeepFinish.ip() as usize,
|
||||||
thunk: None,
|
thunk: None,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
self.call_depth += 1;
|
m.inc_call_depth();
|
||||||
reader.set_pc(PrimOpPhase::DeepSeq.ip() as usize);
|
reader.set_pc(PrimOpPhase::DeepSeq.ip() as usize);
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
@@ -164,20 +163,19 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
if let Some(outer_thunk) = thunk {
|
if let Some(outer_thunk) = thunk {
|
||||||
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
||||||
} else {
|
} else {
|
||||||
self.call_depth -= 1;
|
m.dec_call_depth();
|
||||||
self.push(val.relax())
|
m.push(val.relax())
|
||||||
}
|
}
|
||||||
self.env = env;
|
m.set_env(env);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_dispatch_primop(
|
pub(crate) fn op_dispatch_primop<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
crate::primops::dispatch_primop(self, ctx, reader, mc)
|
crate::primops::dispatch_primop(m, ctx, reader, mc)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
use fix_runtime::Machine;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{BytecodeReader, Step, ThunkState, Value};
|
use crate::{BytecodeReader, Step, ThunkState, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_thunk(
|
pub(crate) fn op_make_thunk<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
@@ -14,16 +14,16 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc,
|
mc,
|
||||||
RefLock::new(ThunkState::Pending {
|
RefLock::new(ThunkState::Pending {
|
||||||
ip: entry_point as usize,
|
ip: entry_point as usize,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
self.push(Value::new_gc(thunk));
|
m.push(Value::new_gc(thunk));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_closure(
|
pub(crate) fn op_make_closure<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
@@ -34,17 +34,17 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
crate::Closure {
|
crate::Closure {
|
||||||
ip: entry_point,
|
ip: entry_point,
|
||||||
n_locals,
|
n_locals,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
pattern: None,
|
pattern: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.push(Value::new_gc(closure));
|
m.push(Value::new_gc(closure));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_pattern_closure(
|
pub(crate) fn op_make_pattern_closure<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
@@ -84,11 +84,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
crate::Closure {
|
crate::Closure {
|
||||||
ip: entry_point,
|
ip: entry_point,
|
||||||
n_locals,
|
n_locals,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
pattern: Some(pattern),
|
pattern: Some(pattern),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.push(Value::new_gc(closure));
|
m.push(Value::new_gc(closure));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use fix_lang::StringId;
|
use fix_lang::StringId;
|
||||||
use fix_runtime::{NixType, resolve_operand};
|
use fix_runtime::{Machine, MachineExt, NixType, resolve_operand};
|
||||||
use gc_arena::{Gc, RefLock};
|
use gc_arena::{Gc, RefLock};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -8,10 +8,9 @@ use crate::{
|
|||||||
AttrSet, BytecodeReader, List, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
AttrSet, BytecodeReader, List, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_attrs(
|
pub(crate) fn op_make_attrs<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
@@ -21,35 +20,35 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
|
|
||||||
for i in 0..dynamic_count {
|
for i in 0..dynamic_count {
|
||||||
let depth = dynamic_count - 1 - i;
|
let depth = dynamic_count - 1 - i;
|
||||||
self.force_slot_to_pc(depth, reader, mc, reader.inst_start_pc())?;
|
m.force_slot_to_pc(depth, reader, mc, reader.inst_start_pc())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dyn_keys: SmallVec<[_; 2]> = SmallVec::with_capacity(dynamic_count);
|
let mut dyn_keys: SmallVec<[_; 2]> = SmallVec::with_capacity(dynamic_count);
|
||||||
for i in 0..dynamic_count {
|
for i in 0..dynamic_count {
|
||||||
let depth = dynamic_count - 1 - i;
|
let depth = dynamic_count - 1 - i;
|
||||||
let key_val = self.peek_forced(depth);
|
let key_val = m.peek_forced(depth);
|
||||||
let key_sid = match ctx.get_string_id(key_val) {
|
let key_sid = match ctx.get_string_id(key_val) {
|
||||||
Ok(id) => Some(id),
|
Ok(id) => Some(id),
|
||||||
Err(NixType::Null) => None,
|
Err(NixType::Null) => None,
|
||||||
Err(got) => return self.finish_type_err(NixType::String, got),
|
Err(got) => return m.finish_type_err(NixType::String, got),
|
||||||
};
|
};
|
||||||
dyn_keys.push(key_sid);
|
dyn_keys.push(key_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.truncate(self.stack.len() - dynamic_count);
|
m.drop_n(dynamic_count);
|
||||||
|
|
||||||
let mut kv: SmallVec<[(crate::StringId, Value); 4]> =
|
let mut kv: SmallVec<[(crate::StringId, Value); 4]> =
|
||||||
SmallVec::with_capacity(static_count + dynamic_count);
|
SmallVec::with_capacity(static_count + dynamic_count);
|
||||||
|
|
||||||
for _ in 0..static_count {
|
for _ in 0..static_count {
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, m);
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
kv.push((key, val));
|
kv.push((key, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in dyn_keys {
|
for key in dyn_keys {
|
||||||
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, self);
|
let val = resolve_operand(&reader.read_operand_data(), mc, ctx, m);
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
kv.push((key, val))
|
kv.push((key, val))
|
||||||
@@ -58,19 +57,19 @@ 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(Value::new_gc(attrs));
|
m.push(Value::new_gc(attrs));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_empty_attrs(&mut self) -> Step {
|
pub(crate) fn op_make_empty_attrs<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(self.empty_attrs);
|
m.push(m.empty_attrs());
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_select_static(
|
pub(crate) fn op_select_static<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
@@ -78,38 +77,38 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
let attrset = self.force_and_retry::<Gc<AttrSet>>(reader, mc)?;
|
let attrset = m.force_and_retry::<Gc<AttrSet>>(reader, mc)?;
|
||||||
|
|
||||||
match attrset.lookup(key) {
|
match attrset.lookup(key) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.push(v);
|
m.push(v);
|
||||||
}
|
}
|
||||||
None => return self.select_skip(key, ctx, reader),
|
None => return select_skip(m, key, ctx, reader),
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_select_dynamic(
|
pub(crate) fn op_select_dynamic<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
|
|
||||||
let (attrset, key_val) = self.force_and_retry::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
let (attrset, key_val) = m.force_and_retry::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||||
|
|
||||||
let key_sid = match ctx.get_string_id(key_val) {
|
let key_sid = match ctx.get_string_id(key_val) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(got) => return self.finish_type_err(NixType::String, got),
|
Err(got) => return m.finish_type_err(NixType::String, got),
|
||||||
};
|
};
|
||||||
|
|
||||||
match attrset.lookup(key_sid) {
|
match attrset.lookup(key_sid) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.push(v);
|
m.push(v);
|
||||||
}
|
}
|
||||||
None => return self.select_skip(key_sid, ctx, reader),
|
None => return select_skip(m, key_sid, ctx, reader),
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
@@ -118,8 +117,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
/// Only recognises Select opcodes and jumps; encountering any other
|
/// Only recognises Select opcodes and jumps; encountering any other
|
||||||
/// opcode means we've reached the end of the select sequence and
|
/// opcode means we've reached the end of the select sequence and
|
||||||
/// should report the missing-attribute error.
|
/// should report the missing-attribute error.
|
||||||
fn select_skip(
|
fn select_skip<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
key: StringId,
|
key: StringId,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
@@ -143,8 +142,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let name = ctx.resolve_string(key);
|
let name = ctx.resolve_string(key);
|
||||||
return self
|
return m.finish_err(Error::eval_error(format!("attribute '{name}' missing")));
|
||||||
.finish_err(Error::eval_error(format!("attribute '{name}' missing")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +150,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
|
|
||||||
/// Skip the rest of a **HasAttr** attrpath after an intermediate
|
/// Skip the rest of a **HasAttr** attrpath after an intermediate
|
||||||
/// lookup failed. Only recognises HasAttr opcodes and jumps.
|
/// lookup failed. Only recognises HasAttr opcodes and jumps.
|
||||||
fn has_attr_skip(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
fn has_attr_skip(reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
use fix_bytecode::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
loop {
|
loop {
|
||||||
match reader.read_op() {
|
match reader.read_op() {
|
||||||
@@ -185,8 +183,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr_path_static(
|
pub(crate) fn op_has_attr_path_static<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
_ctx: &mut impl VmRuntimeCtx,
|
_ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
@@ -194,34 +192,34 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
let current = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let current = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
|
||||||
match current
|
match current
|
||||||
.as_gc::<AttrSet>()
|
.as_gc::<AttrSet>()
|
||||||
.and_then(|attrs| attrs.lookup(key))
|
.and_then(|attrs| attrs.lookup(key))
|
||||||
{
|
{
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.push(v);
|
m.push(v);
|
||||||
}
|
}
|
||||||
None => return self.has_attr_skip(reader),
|
None => return has_attr_skip(reader),
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr_path_dynamic(
|
pub(crate) fn op_has_attr_path_dynamic<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
|
|
||||||
let (current, key_val) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (current, key_val) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
|
|
||||||
let key_sid = match ctx.get_string_id(key_val) {
|
let key_sid = match ctx.get_string_id(key_val) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(got) => return self.finish_type_err(NixType::String, got),
|
Err(got) => return m.finish_type_err(NixType::String, got),
|
||||||
};
|
};
|
||||||
|
|
||||||
match current
|
match current
|
||||||
@@ -229,37 +227,43 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
.and_then(|attrs| attrs.lookup(key_sid))
|
.and_then(|attrs| attrs.lookup(key_sid))
|
||||||
{
|
{
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
self.push(v);
|
m.push(v);
|
||||||
}
|
}
|
||||||
None => return self.has_attr_skip(reader),
|
None => return has_attr_skip(reader),
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump_if_select_failed(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_jump_if_select_failed<'gc, M: Machine<'gc>>(
|
||||||
|
_m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
// No-op
|
// No-op
|
||||||
let _offset = reader.read_i32();
|
let _offset = reader.read_i32();
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump_if_select_succeeded(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_jump_if_select_succeeded<'gc, M: Machine<'gc>>(
|
||||||
|
_m: &mut M,
|
||||||
|
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);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr_static(
|
pub(crate) fn op_has_attr_static<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
let current = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let current = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
|
|
||||||
self.push(Value::new_inline(
|
m.push(Value::new_inline(
|
||||||
current
|
current
|
||||||
.as_gc::<AttrSet>()
|
.as_gc::<AttrSet>()
|
||||||
.and_then(|attrs| attrs.lookup(key))
|
.and_then(|attrs| attrs.lookup(key))
|
||||||
@@ -272,20 +276,20 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr_dynamic(
|
pub(crate) fn op_has_attr_dynamic<'gc, M: MachineExt<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let (current, dyn_key) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
let (current, dyn_key) = m.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||||
|
|
||||||
let key_sid = match ctx.get_string_id(dyn_key) {
|
let key_sid = match ctx.get_string_id(dyn_key) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(got) => return self.finish_type_err(NixType::String, got),
|
Err(got) => return m.finish_type_err(NixType::String, got),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push(Value::new_inline(
|
m.push(Value::new_inline(
|
||||||
current
|
current
|
||||||
.as_gc::<AttrSet>()
|
.as_gc::<AttrSet>()
|
||||||
.and_then(|attrs| attrs.lookup(key_sid))
|
.and_then(|attrs| attrs.lookup(key_sid))
|
||||||
@@ -298,15 +302,15 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_has_attr_resolve(&mut self) -> Step {
|
pub(crate) fn op_has_attr_resolve<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
// If we reach here, has_attr check has failed, push false (AttrSet is already popped)
|
// If we reach here, has_attr check has failed, push false (AttrSet is already popped)
|
||||||
self.push(Value::new_inline(false));
|
m.push(Value::new_inline(false));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_list(
|
pub(crate) fn op_make_list<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
@@ -314,7 +318,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
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(resolve_operand(&reader.read_operand_data(), mc, ctx, self));
|
items.push(resolve_operand(&reader.read_operand_data(), mc, ctx, m));
|
||||||
}
|
}
|
||||||
let list = Gc::new(
|
let list = Gc::new(
|
||||||
mc,
|
mc,
|
||||||
@@ -322,13 +326,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
inner: RefLock::new(items),
|
inner: RefLock::new(items),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.push(Value::new_gc(list));
|
m.push(Value::new_gc(list));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_make_empty_list(&mut self) -> Step {
|
pub(crate) fn op_make_empty_list<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(self.empty_list);
|
m.push(m.empty_list());
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use gc_arena::Mutation;
|
|||||||
|
|
||||||
use crate::{BytecodeReader, Step, VmRuntimeCtx};
|
use crate::{BytecodeReader, Step, VmRuntimeCtx};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump_if_false(
|
pub(crate) fn op_jump_if_false<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
let cond = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let cond = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -20,13 +19,13 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump_if_true(
|
pub(crate) fn op_jump_if_true<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
let offset = reader.read_i32();
|
||||||
let cond = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let cond = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -34,15 +33,15 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_jump(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_jump<'gc, M: Machine<'gc>>(_m: &mut M, 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);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_assert(
|
pub(crate) fn op_assert<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
@@ -50,11 +49,10 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let raw_id = reader.read_string_id();
|
let raw_id = reader.read_string_id();
|
||||||
let raw = ctx.resolve_string(raw_id);
|
let raw = ctx.resolve_string(raw_id);
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let assertion = self.force_and_retry::<bool>(reader, mc)?;
|
let assertion = m.force_and_retry::<bool>(reader, mc)?;
|
||||||
if !assertion {
|
if !assertion {
|
||||||
// FIXME: use catchable error
|
// FIXME: use catchable error
|
||||||
return self.finish_err(Error::eval_error(format!("assertion '{raw}' failed")));
|
return m.finish_err(Error::eval_error(format!("assertion '{raw}' failed")));
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,55 +1,63 @@
|
|||||||
|
use fix_runtime::Machine;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{BytecodeReader, Step, Value};
|
use crate::{BytecodeReader, Step, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_push_smi<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
let val = reader.read_i32();
|
let val = reader.read_i32();
|
||||||
self.push(Value::new_inline(val));
|
m.push(Value::new_inline(val));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_bigint(
|
pub(crate) fn op_push_bigint<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let val = reader.read_i64();
|
let val = reader.read_i64();
|
||||||
self.push(Value::new_gc(Gc::new(mc, val)));
|
m.push(Value::new_gc(Gc::new(mc, val)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_push_float<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
let val = reader.read_f64();
|
let val = reader.read_f64();
|
||||||
self.push(Value::new_float(val));
|
m.push(Value::new_float(val));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_push_string<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
let sid = reader.read_string_id();
|
let sid = reader.read_string_id();
|
||||||
self.push(Value::new_inline(sid));
|
m.push(Value::new_inline(sid));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_null(&mut self) -> Step {
|
pub(crate) fn op_push_null<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(Value::new_inline(crate::Null));
|
m.push(Value::new_inline(crate::Null));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_true(&mut self) -> Step {
|
pub(crate) fn op_push_true<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(Value::new_inline(true));
|
m.push(Value::new_inline(true));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_push_false(&mut self) -> Step {
|
pub(crate) fn op_push_false<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(Value::new_inline(false));
|
m.push(Value::new_inline(false));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,23 +3,27 @@ use std::path::PathBuf;
|
|||||||
use fix_bytecode::PrimOpPhase;
|
use fix_bytecode::PrimOpPhase;
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
use fix_runtime::{AttrSet, NixString, Path, StrictValue, StringContext, canon_path_str};
|
use fix_runtime::{
|
||||||
|
AttrSet, Machine, MachineExt, NixString, Path, StrictValue, StringContext, canon_path_str,
|
||||||
|
};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
use crate::{BytecodeReader, PrimOp, Step, Value, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_builtins(&mut self) -> Step {
|
pub(crate) fn op_load_builtins<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
self.push(self.builtins);
|
m.push(m.builtins());
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_builtin(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_load_builtin<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
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(Value::new_inline(PrimOp {
|
m.push(Value::new_inline(PrimOp {
|
||||||
id,
|
id,
|
||||||
arity: BUILTINS[id as usize].1,
|
arity: BUILTINS[id as usize].1,
|
||||||
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
||||||
@@ -28,37 +32,33 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_repl_binding(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_load_repl_binding<'gc, M: Machine<'gc>>(
|
||||||
|
_m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
let _name = reader.read_string_id();
|
let _name = reader.read_string_id();
|
||||||
todo!("LoadReplBinding");
|
todo!("LoadReplBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_scoped_binding(
|
pub(crate) fn op_load_scoped_binding<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &impl VmRuntimeCtx,
|
ctx: &impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
_mc: &gc_arena::Mutation<'gc>,
|
_mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let slot_id = reader.read_u32();
|
let slot_id = reader.read_u32();
|
||||||
let name = reader.read_string_id();
|
let name = reader.read_string_id();
|
||||||
let scope = match self.scope_slots.get(slot_id as usize).copied() {
|
let scope = m.scope_slot(slot_id);
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
return self.finish_err(Error::eval_error(format!(
|
|
||||||
"internal: invalid scope slot {slot_id}"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let Some(attrs) = scope.as_gc::<AttrSet>() else {
|
let Some(attrs) = scope.as_gc::<AttrSet>() else {
|
||||||
return self.finish_err(Error::eval_error("internal: scope slot is not an attrset"));
|
return m.finish_err(Error::eval_error("internal: scope slot is not an attrset"));
|
||||||
};
|
};
|
||||||
match attrs.lookup(name) {
|
match attrs.lookup(name) {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
self.push(val);
|
m.push(val);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
None => self.finish_err(Error::eval_error(format!(
|
None => m.finish_err(Error::eval_error(format!(
|
||||||
"scoped binding '{}' not found",
|
"scoped binding '{}' not found",
|
||||||
ctx.resolve_string(name)
|
ctx.resolve_string(name)
|
||||||
))),
|
))),
|
||||||
@@ -66,18 +66,18 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_coerce_to_string(
|
pub(crate) fn op_coerce_to_string<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let val = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
if val.is::<StringId>() || val.is::<NixString>() {
|
if val.is::<StringId>() || val.is::<NixString>() {
|
||||||
self.push(val.relax());
|
m.push(val.relax());
|
||||||
} else if let Some(p) = val.as_inline::<Path>() {
|
} else if let Some(p) = val.as_inline::<Path>() {
|
||||||
// Coercing a path to a string yields the canonical path text.
|
// Coercing a path to a string yields the canonical path text.
|
||||||
// FIXME: copy to store
|
// FIXME: copy to store
|
||||||
self.push(Value::new_inline(p.0));
|
m.push(Value::new_inline(p.0));
|
||||||
} else {
|
} else {
|
||||||
todo!("coerce other types to string: {:?}", val.ty());
|
todo!("coerce other types to string: {:?}", val.ty());
|
||||||
}
|
}
|
||||||
@@ -85,8 +85,8 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_concat_strings(
|
pub(crate) fn op_concat_strings<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
@@ -97,7 +97,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let mut total_len = 0;
|
let mut total_len = 0;
|
||||||
let mut has_any_context = false;
|
let mut has_any_context = false;
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
let val = self.peek_forced(count - 1 - i);
|
let val = m.peek_forced(count - 1 - i);
|
||||||
let s = ctx.get_string(val).expect("coerced");
|
let s = ctx.get_string(val).expect("coerced");
|
||||||
total_len += s.len();
|
total_len += s.len();
|
||||||
if !ctx.get_string_context(val).is_empty() {
|
if !ctx.get_string_context(val).is_empty() {
|
||||||
@@ -108,7 +108,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let mut result = String::with_capacity(total_len);
|
let mut result = String::with_capacity(total_len);
|
||||||
let mut merged = StringContext::new();
|
let mut merged = StringContext::new();
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
let val = self.peek_forced(count - 1 - i);
|
let val = m.peek_forced(count - 1 - i);
|
||||||
let s = ctx.get_string(val).expect("coerced");
|
let s = ctx.get_string(val).expect("coerced");
|
||||||
result.push_str(s);
|
result.push_str(s);
|
||||||
if has_any_context {
|
if has_any_context {
|
||||||
@@ -119,36 +119,36 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.truncate(self.stack.len() - count);
|
m.drop_n(count);
|
||||||
|
|
||||||
if merged.is_empty() {
|
if merged.is_empty() {
|
||||||
let sid = ctx.intern_string(result);
|
let sid = ctx.intern_string(result);
|
||||||
self.push(Value::new_inline(sid));
|
m.push(Value::new_inline(sid));
|
||||||
} else {
|
} else {
|
||||||
let ns = gc_arena::Gc::new(mc, NixString::with_context(result, merged));
|
let ns = gc_arena::Gc::new(mc, NixString::with_context(result, merged));
|
||||||
self.push(Value::new_gc(ns));
|
m.push(Value::new_gc(ns));
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_resolve_path(
|
pub(crate) fn op_resolve_path<'gc, M: MachineExt<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let path_val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
let path_val = m.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let dir_id = reader.read_string_id();
|
let dir_id = reader.read_string_id();
|
||||||
// Already a path: keep as-is. ResolvePath is idempotent on paths.
|
// Already a path: keep as-is. ResolvePath is idempotent on paths.
|
||||||
if let Some(p) = path_val.as_inline::<Path>() {
|
if let Some(p) = path_val.as_inline::<Path>() {
|
||||||
self.push(Value::new_inline(p));
|
m.push(Value::new_inline(p));
|
||||||
return Step::Continue(());
|
return Step::Continue(());
|
||||||
}
|
}
|
||||||
let path = match ctx.get_string(path_val) {
|
let path = match ctx.get_string(path_val) {
|
||||||
Some(s) => s.to_owned(),
|
Some(s) => s.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return m.finish_err(Error::eval_error(format!(
|
||||||
"expected a string for path, got {}",
|
"expected a string for path, got {}",
|
||||||
path_val.ty()
|
path_val.ty()
|
||||||
)));
|
)));
|
||||||
@@ -156,13 +156,12 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
};
|
};
|
||||||
let resolved = match resolve_path_str(ctx.resolve_string(dir_id), &path) {
|
let resolved = match resolve_path_str(ctx.resolve_string(dir_id), &path) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return self.finish_err(e),
|
Err(e) => return m.finish_err(e),
|
||||||
};
|
};
|
||||||
let sid = ctx.intern_string(resolved);
|
let sid = ctx.intern_string(resolved);
|
||||||
self.push(Value::new_inline(Path(sid)));
|
m.push(Value::new_inline(Path(sid)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>> {
|
fn resolve_path_str(current_dir: &str, path: &str) -> Result<String, Box<Error>> {
|
||||||
let raw = if path.starts_with('/') {
|
let raw = if path.starts_with('/') {
|
||||||
|
|||||||
@@ -7,3 +7,13 @@ pub(crate) mod literals;
|
|||||||
pub(crate) mod misc;
|
pub(crate) mod misc;
|
||||||
pub(crate) mod variables;
|
pub(crate) mod variables;
|
||||||
pub(crate) mod with_scope;
|
pub(crate) mod with_scope;
|
||||||
|
|
||||||
|
pub(crate) use arithmetic::*;
|
||||||
|
pub(crate) use calls::*;
|
||||||
|
pub(crate) use closures::*;
|
||||||
|
pub(crate) use collections::*;
|
||||||
|
pub(crate) use control::*;
|
||||||
|
pub(crate) use literals::*;
|
||||||
|
pub(crate) use misc::*;
|
||||||
|
pub(crate) use variables::*;
|
||||||
|
pub(crate) use with_scope::*;
|
||||||
|
|||||||
@@ -1,50 +1,48 @@
|
|||||||
|
use fix_runtime::Machine;
|
||||||
|
|
||||||
use crate::{BytecodeReader, Mutation, Step, Value};
|
use crate::{BytecodeReader, Mutation, Step, Value};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_load_local<'gc, M: Machine<'gc>>(m: &mut M, reader: &mut BytecodeReader<'_>) -> Step {
|
||||||
let idx = reader.read_u32() as usize;
|
let idx = reader.read_u32() as usize;
|
||||||
self.push(self.env.borrow().locals[idx]);
|
m.push(m.env().borrow().locals[idx]);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_load_outer(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
pub(crate) fn op_load_outer<'gc, M: Machine<'gc>>(m: &mut M, 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 = m.env();
|
||||||
for _ in 0..layer {
|
for _ in 0..layer {
|
||||||
let prev = cur.borrow().prev.expect("LoadOuter: env chain too short");
|
let prev = cur.borrow().prev.expect("LoadOuter: env chain too short");
|
||||||
cur = prev;
|
cur = prev;
|
||||||
}
|
}
|
||||||
let val = cur.borrow().locals[idx];
|
let val = cur.borrow().locals[idx];
|
||||||
self.push(val);
|
m.push(val);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_store_local(
|
pub(crate) fn op_store_local<'gc, M: Machine<'gc>>(m: &mut M,
|
||||||
&mut self,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let idx = reader.read_u32() as usize;
|
let idx = reader.read_u32() as usize;
|
||||||
let val = self.pop();
|
let val = m.pop();
|
||||||
self.env.borrow_mut(mc).locals[idx] = val;
|
m.env().borrow_mut(mc).locals[idx] = val;
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_alloc_locals(
|
pub(crate) fn op_alloc_locals<'gc, M: Machine<'gc>>(m: &mut M,
|
||||||
&mut self,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
self.env
|
m.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));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,22 +5,21 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
use crate::{Break, BytecodeReader, CallFrame, Step, VmRuntimeCtx};
|
use crate::{Break, BytecodeReader, CallFrame, Step, VmRuntimeCtx};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_lookup_with(
|
pub(crate) fn op_lookup_with<'gc, M: Machine<'gc>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
ctx: &mut impl VmRuntimeCtx,
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
let counter = self.peek_forced(0).as_inline::<i32>().unwrap();
|
let counter = m.peek_forced(0).as_inline::<i32>().unwrap();
|
||||||
|
|
||||||
let name = reader.read_string_id();
|
let name = reader.read_string_id();
|
||||||
let n = reader.read_u8();
|
let n = reader.read_u8();
|
||||||
let mut namespaces = SmallVec::<[_; 2]>::new();
|
let mut namespaces = SmallVec::<[_; 2]>::new();
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
namespaces.push(resolve_operand(&reader.read_operand_data(), mc, ctx, self));
|
namespaces.push(resolve_operand(&reader.read_operand_data(), mc, ctx, m));
|
||||||
}
|
}
|
||||||
|
|
||||||
let resume_pc = reader.inst_start_pc();
|
let resume_pc = reader.inst_start_pc();
|
||||||
@@ -31,28 +30,27 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
match *state {
|
match *state {
|
||||||
ThunkState::Pending { ip, env } => {
|
ThunkState::Pending { ip, env } => {
|
||||||
*state = ThunkState::Blackhole;
|
*state = ThunkState::Blackhole;
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
thunk: Some(thunk),
|
thunk: Some(thunk),
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
self.env = env;
|
m.set_env(env);
|
||||||
reader.set_pc(ip);
|
reader.set_pc(ip);
|
||||||
return Step::Break(Break::Force);
|
return Step::Break(Break::Force);
|
||||||
}
|
}
|
||||||
ThunkState::Evaluated(v) => v,
|
ThunkState::Evaluated(v) => v,
|
||||||
ThunkState::Apply { func, arg } => {
|
ThunkState::Apply { func, arg } => {
|
||||||
self.call_stack.push(CallFrame {
|
m.push_call_frame(CallFrame {
|
||||||
thunk: Some(thunk),
|
thunk: Some(thunk),
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
env: self.env,
|
env: m.env(),
|
||||||
});
|
});
|
||||||
self.push(func);
|
m.push(func);
|
||||||
return self.call(reader, mc, arg, resume_pc);
|
return m.call(reader, mc, arg, resume_pc);
|
||||||
}
|
}
|
||||||
ThunkState::Blackhole => {
|
ThunkState::Blackhole => {
|
||||||
return self
|
return m.finish_err(Error::eval_error("infinite recursion encountered"));
|
||||||
.finish_err(Error::eval_error("infinite recursion encountered"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,17 +60,16 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
.as_gc::<AttrSet>()
|
.as_gc::<AttrSet>()
|
||||||
.and_then(|attrs| attrs.lookup(name))
|
.and_then(|attrs| attrs.lookup(name))
|
||||||
{
|
{
|
||||||
self.replace(0, val);
|
m.replace(0, val);
|
||||||
} else if counter + 1 == n as i32 {
|
} else if counter + 1 == n as i32 {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return m.finish_err(Error::eval_error(format!(
|
||||||
"undefined variable '{}'",
|
"undefined variable '{}'",
|
||||||
Symbol::from(ctx.resolve_string(name))
|
Symbol::from(ctx.resolve_string(name))
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
self.replace(0, Value::new_inline(counter + 1));
|
m.replace(0, Value::new_inline(counter + 1));
|
||||||
reader.set_pc(resume_pc);
|
reader.set_pc(resume_pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
+80
-198
@@ -137,45 +137,20 @@ impl<'gc> Vm<'gc> {
|
|||||||
functor_sym: ctx.intern_string("__functor"),
|
functor_sym: ctx.intern_string("__functor"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
|
||||||
self.result = Some(Ok(val));
|
|
||||||
Step::Break(Break::Done)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn finish_err(&mut self, err: Box<Error>) -> Step {
|
|
||||||
self.result = Some(Err(err));
|
|
||||||
Step::Break(Break::Done)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
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)]
|
|
||||||
fn finish_vm_err(&mut self, err: VmError) -> Step {
|
|
||||||
self.finish_err(err.into_error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn push(&mut self, val: Value<'gc>) {
|
fn push(&mut self, val: Value<'gc>) {
|
||||||
self.stack.push(val);
|
self.stack.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn pop(&mut self) -> Value<'gc> {
|
fn pop(&mut self) -> Value<'gc> {
|
||||||
self.stack.pop().expect("stack underflow")
|
self.stack.pop().expect("stack underflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn peek(&self, depth: usize) -> Value<'gc> {
|
fn peek(&self, depth: usize) -> Value<'gc> {
|
||||||
*self
|
*self
|
||||||
.stack
|
.stack
|
||||||
@@ -184,7 +159,6 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn peek_forced(&self, depth: usize) -> StrictValue<'gc> {
|
fn peek_forced(&self, depth: usize) -> StrictValue<'gc> {
|
||||||
self.stack
|
self.stack
|
||||||
.get(self.stack.len() - depth - 1)
|
.get(self.stack.len() - depth - 1)
|
||||||
@@ -193,6 +167,15 @@ impl<'gc> Vm<'gc> {
|
|||||||
.expect("forced")
|
.expect("forced")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
||||||
|
self.stack
|
||||||
|
.pop()
|
||||||
|
.expect("stack underflow")
|
||||||
|
.restrict()
|
||||||
|
.expect("forced")
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
||||||
let len = self.stack.len();
|
let len = self.stack.len();
|
||||||
@@ -203,72 +186,13 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg_attr(debug_assertions, track_caller)]
|
fn drop_n(&mut self, depth: usize) {
|
||||||
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
self.stack.truncate(self.stack.len() - depth);
|
||||||
self.stack
|
|
||||||
.pop()
|
|
||||||
.expect("stack underflow")
|
|
||||||
.restrict()
|
|
||||||
.expect("forced")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Force the top `T::WIDTH` stack slots and return them as `T`.
|
|
||||||
///
|
|
||||||
/// If any slot holds a pending thunk, this method pushes a call frame
|
|
||||||
/// whose resume PC is the **start of the current instruction**
|
|
||||||
/// (`reader.inst_start_pc()`), enters the thunk, and returns
|
|
||||||
/// `Break::Force`. When the thunk eventually returns, the VM will
|
|
||||||
/// **re-execute the entire opcode handler from the beginning**.
|
|
||||||
///
|
|
||||||
/// # Invariants
|
|
||||||
///
|
|
||||||
/// * **Do not call this method more than once in a single handler.**
|
|
||||||
/// If you need to force multiple values, use a tuple type such as
|
|
||||||
/// `(StrictValue, StrictValue)` so they are forced and popped in one
|
|
||||||
/// atomic operation. Calling `force_and_retry` twice (or more)
|
|
||||||
/// means the handler will be re-run from the top after each retry;
|
|
||||||
/// any stack modifications between the two calls would be duplicated
|
|
||||||
/// and corrupt the stack layout.
|
|
||||||
///
|
|
||||||
/// * The caller must ensure that the stack layout at the point of
|
|
||||||
/// invocation is **identical** every time the handler is re-entered.
|
|
||||||
/// In practice this means no pushes, pops, or local mutations may
|
|
||||||
/// happen before the call, and the call must be the first thing
|
|
||||||
/// that consumes the instruction's operand values.
|
|
||||||
///
|
|
||||||
/// * The return value must be propagated with `?` so that
|
|
||||||
/// `Break::Force` correctly unwinds to the dispatch loop.
|
|
||||||
#[inline(always)]
|
|
||||||
fn force_and_retry<T: Forced<'gc>>(
|
|
||||||
&mut self,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
) -> std::ops::ControlFlow<Break, T> {
|
|
||||||
self.force_and_retry_pc(reader, mc, reader.inst_start_pc())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as [`force_and_retry`](Self::force_and_retry) but allows
|
|
||||||
/// specifying a custom resume PC.
|
|
||||||
#[inline(always)]
|
|
||||||
fn force_and_retry_pc<T: Forced<'gc>>(
|
|
||||||
&mut self,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
resume_pc: usize,
|
|
||||||
) -> std::ops::ControlFlow<Break, T> {
|
|
||||||
T::force_and_check(self, reader, mc, 0, resume_pc)?;
|
|
||||||
std::ops::ControlFlow::Continue(T::pop_converted(self))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(unused)]
|
fn stack_len(&self) -> usize {
|
||||||
fn force_slot(
|
self.stack.len()
|
||||||
&mut self,
|
|
||||||
depth: usize,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
) -> Step {
|
|
||||||
self.force_slot_to_pc(depth, reader, mc, reader.inst_start_pc())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -313,54 +237,6 @@ impl<'gc> Vm<'gc> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'gc> Machine<'gc> for Vm<'gc> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn push(&mut self, val: Value<'gc>) {
|
|
||||||
self.push(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn pop(&mut self) -> Value<'gc> {
|
|
||||||
self.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn peek(&self, depth: usize) -> Value<'gc> {
|
|
||||||
Vm::peek(self, depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn peek_forced(&self, depth: usize) -> StrictValue<'gc> {
|
|
||||||
Vm::peek_forced(self, depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
|
||||||
self.pop_forced()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
|
||||||
self.replace(depth, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn stack_len(&self) -> usize {
|
|
||||||
self.stack.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn force_slot_to_pc(
|
|
||||||
&mut self,
|
|
||||||
depth: usize,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
resume_pc: usize,
|
|
||||||
) -> Step {
|
|
||||||
self.force_slot_to_pc(depth, reader, mc, resume_pc)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(
|
fn call(
|
||||||
@@ -370,7 +246,7 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
arg: Value<'gc>,
|
arg: Value<'gc>,
|
||||||
resume_pc: usize,
|
resume_pc: usize,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
self.call(reader, mc, arg, resume_pc)
|
instructions::call(self, reader, mc, arg, resume_pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -410,17 +286,22 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
||||||
self.finish_ok(val)
|
self.result = Some(Ok(val));
|
||||||
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_err(&mut self, err: Box<Error>) -> Step {
|
fn finish_err(&mut self, err: Box<Error>) -> Step {
|
||||||
self.finish_err(err)
|
self.result = Some(Err(err));
|
||||||
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step {
|
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step {
|
||||||
self.finish_type_err(expected, got)
|
self.result = Some(Err(Error::eval_error(format!(
|
||||||
|
"expected {expected}, got {got}"
|
||||||
|
))));
|
||||||
|
Step::Break(Break::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -590,6 +471,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Action {
|
) -> Action {
|
||||||
use fix_bytecode::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
|
use instructions::*;
|
||||||
|
|
||||||
let mut reader = BytecodeReader::new(bytecode, pc);
|
let mut reader = BytecodeReader::new(bytecode, pc);
|
||||||
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
||||||
@@ -603,75 +485,75 @@ impl<'gc> Vm<'gc> {
|
|||||||
let op = reader.read_op();
|
let op = reader.read_op();
|
||||||
|
|
||||||
let result = match op {
|
let result = match op {
|
||||||
PushSmi => self.op_push_smi(&mut reader),
|
PushSmi => op_push_smi(self, &mut reader),
|
||||||
PushBigInt => self.op_push_bigint(&mut reader, mc),
|
PushBigInt => op_push_bigint(self, &mut reader, mc),
|
||||||
PushFloat => self.op_push_float(&mut reader),
|
PushFloat => op_push_float(self, &mut reader),
|
||||||
PushString => self.op_push_string(&mut reader),
|
PushString => op_push_string(self, &mut reader),
|
||||||
PushNull => self.op_push_null(),
|
PushNull => op_push_null(self),
|
||||||
PushTrue => self.op_push_true(),
|
PushTrue => op_push_true(self),
|
||||||
PushFalse => self.op_push_false(),
|
PushFalse => op_push_false(self),
|
||||||
|
|
||||||
LoadLocal => self.op_load_local(&mut reader),
|
LoadLocal => op_load_local(self, &mut reader),
|
||||||
LoadOuter => self.op_load_outer(&mut reader),
|
LoadOuter => op_load_outer(self, &mut reader),
|
||||||
StoreLocal => self.op_store_local(&mut reader, mc),
|
StoreLocal => op_store_local(self, &mut reader, mc),
|
||||||
AllocLocals => self.op_alloc_locals(&mut reader, mc),
|
AllocLocals => op_alloc_locals(self, &mut reader, mc),
|
||||||
|
|
||||||
MakeThunk => self.op_make_thunk(&mut reader, mc),
|
MakeThunk => op_make_thunk(self, &mut reader, mc),
|
||||||
MakeClosure => self.op_make_closure(&mut reader, mc),
|
MakeClosure => op_make_closure(self, &mut reader, mc),
|
||||||
MakePatternClosure => self.op_make_pattern_closure(&mut reader, mc),
|
MakePatternClosure => op_make_pattern_closure(self, &mut reader, mc),
|
||||||
|
|
||||||
Call => self.op_call(ctx, &mut reader, mc),
|
Call => op_call(self, ctx, &mut reader, mc),
|
||||||
DispatchPrimOp => self.op_dispatch_primop(ctx, &mut reader, mc),
|
DispatchPrimOp => op_dispatch_primop(self, ctx, &mut reader, mc),
|
||||||
Return => self.op_return(ctx, &mut reader, mc),
|
Return => op_return(self, ctx, &mut reader, mc),
|
||||||
|
|
||||||
MakeAttrs => self.op_make_attrs(ctx, &mut reader, mc),
|
MakeAttrs => op_make_attrs(self, ctx, &mut reader, mc),
|
||||||
MakeEmptyAttrs => self.op_make_empty_attrs(),
|
MakeEmptyAttrs => op_make_empty_attrs(self),
|
||||||
SelectStatic => self.op_select_static(ctx, &mut reader, mc),
|
SelectStatic => op_select_static(self, ctx, &mut reader, mc),
|
||||||
SelectDynamic => self.op_select_dynamic(ctx, &mut reader, mc),
|
SelectDynamic => op_select_dynamic(self, ctx, &mut reader, mc),
|
||||||
HasAttrPathStatic => self.op_has_attr_path_static(ctx, &mut reader, mc),
|
HasAttrPathStatic => op_has_attr_path_static(self, ctx, &mut reader, mc),
|
||||||
HasAttrPathDynamic => self.op_has_attr_path_dynamic(ctx, &mut reader, mc),
|
HasAttrPathDynamic => op_has_attr_path_dynamic(self, ctx, &mut reader, mc),
|
||||||
HasAttrStatic => self.op_has_attr_static(&mut reader, mc),
|
HasAttrStatic => op_has_attr_static(self, &mut reader, mc),
|
||||||
HasAttrDynamic => self.op_has_attr_dynamic(ctx, &mut reader, mc),
|
HasAttrDynamic => op_has_attr_dynamic(self, ctx, &mut reader, mc),
|
||||||
HasAttrResolve => self.op_has_attr_resolve(),
|
HasAttrResolve => op_has_attr_resolve(self),
|
||||||
JumpIfSelectFailed => self.op_jump_if_select_failed(&mut reader),
|
JumpIfSelectFailed => op_jump_if_select_failed(self, &mut reader),
|
||||||
JumpIfSelectSucceeded => self.op_jump_if_select_succeeded(&mut reader),
|
JumpIfSelectSucceeded => op_jump_if_select_succeeded(self, &mut reader),
|
||||||
|
|
||||||
MakeList => self.op_make_list(ctx, &mut reader, mc),
|
MakeList => op_make_list(self, ctx, &mut reader, mc),
|
||||||
MakeEmptyList => self.op_make_empty_list(),
|
MakeEmptyList => op_make_empty_list(self),
|
||||||
|
|
||||||
OpAdd => self.op_add(ctx, &mut reader, mc),
|
OpAdd => op_add(self, ctx, &mut reader, mc),
|
||||||
OpSub => self.op_sub(&mut reader, mc),
|
OpSub => op_sub(self, &mut reader, mc),
|
||||||
OpMul => self.op_mul(&mut reader, mc),
|
OpMul => op_mul(self, &mut reader, mc),
|
||||||
OpDiv => self.op_div(&mut reader, mc),
|
OpDiv => op_div(self, &mut reader, mc),
|
||||||
OpEq => self.op_eq(ctx, &mut reader, mc),
|
OpEq => op_eq(self, ctx, &mut reader, mc),
|
||||||
OpNeq => self.op_neq(ctx, &mut reader, mc),
|
OpNeq => op_neq(self, ctx, &mut reader, mc),
|
||||||
OpLt => self.op_lt(ctx, &mut reader, mc),
|
OpLt => op_lt(self, ctx, &mut reader, mc),
|
||||||
OpGt => self.op_gt(ctx, &mut reader, mc),
|
OpGt => op_gt(self, ctx, &mut reader, mc),
|
||||||
OpLeq => self.op_leq(ctx, &mut reader, mc),
|
OpLeq => op_leq(self, ctx, &mut reader, mc),
|
||||||
OpGeq => self.op_geq(ctx, &mut reader, mc),
|
OpGeq => op_geq(self, ctx, &mut reader, mc),
|
||||||
OpConcat => self.op_concat(&mut reader, mc),
|
OpConcat => op_concat(self, &mut reader, mc),
|
||||||
OpUpdate => self.op_update(&mut reader, mc),
|
OpUpdate => op_update(self, &mut reader, mc),
|
||||||
|
|
||||||
OpNeg => self.op_neg(&mut reader, mc),
|
OpNeg => op_neg(self, &mut reader, mc),
|
||||||
OpNot => self.op_not(&mut reader, mc),
|
OpNot => op_not(self, &mut reader, mc),
|
||||||
|
|
||||||
JumpIfFalse => self.op_jump_if_false(&mut reader, mc),
|
JumpIfFalse => op_jump_if_false(self, &mut reader, mc),
|
||||||
JumpIfTrue => self.op_jump_if_true(&mut reader, mc),
|
JumpIfTrue => op_jump_if_true(self, &mut reader, mc),
|
||||||
Jump => self.op_jump(&mut reader),
|
Jump => op_jump(self, &mut reader),
|
||||||
|
|
||||||
ConcatStrings => self.op_concat_strings(ctx, &mut reader, mc),
|
ConcatStrings => op_concat_strings(self, ctx, &mut reader, mc),
|
||||||
CoerceToString => self.op_coerce_to_string(&mut reader, mc),
|
CoerceToString => op_coerce_to_string(self, &mut reader, mc),
|
||||||
ResolvePath => self.op_resolve_path(ctx, &mut reader, mc),
|
ResolvePath => op_resolve_path(self, ctx, &mut reader, mc),
|
||||||
|
|
||||||
Assert => self.op_assert(ctx, &mut reader, mc),
|
Assert => op_assert(self, ctx, &mut reader, mc),
|
||||||
|
|
||||||
LookupWith => self.op_lookup_with(ctx, &mut reader, mc),
|
LookupWith => op_lookup_with(self, ctx, &mut reader, mc),
|
||||||
|
|
||||||
LoadBuiltins => self.op_load_builtins(),
|
LoadBuiltins => op_load_builtins(self),
|
||||||
LoadBuiltin => self.op_load_builtin(&mut reader),
|
LoadBuiltin => op_load_builtin(self, &mut reader),
|
||||||
|
|
||||||
LoadReplBinding => self.op_load_repl_binding(&mut reader),
|
LoadReplBinding => op_load_repl_binding(self, &mut reader),
|
||||||
LoadScopedBinding => self.op_load_scoped_binding(ctx, &mut reader, mc),
|
LoadScopedBinding => op_load_scoped_binding(self, ctx, &mut reader, mc),
|
||||||
|
|
||||||
Illegal => unreachable!(),
|
Illegal => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-13
@@ -3,6 +3,18 @@ name = "fix"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
harness = false
|
||||||
|
name = "basic_ops"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
harness = false
|
||||||
|
name = "builtins"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
harness = false
|
||||||
|
name = "thunk_scope"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mimalloc = "0.1"
|
mimalloc = "0.1"
|
||||||
|
|
||||||
@@ -17,9 +29,9 @@ clap = { version = "4", features = ["derive"] }
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
|
miette = { version = "7.4", features = ["fancy"] }
|
||||||
# Error Reporting
|
# Error Reporting
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
miette = { version = "7.4", features = ["fancy"] }
|
|
||||||
|
|
||||||
# Data Structure
|
# Data Structure
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
@@ -38,15 +50,3 @@ fix-vm = { path = "../fix-vm" }
|
|||||||
criterion = { version = "0.8", features = ["html_reports"] }
|
criterion = { version = "0.8", features = ["html_reports"] }
|
||||||
tempfile = "3.24"
|
tempfile = "3.24"
|
||||||
test-log = { version = "0.2", features = ["trace"] }
|
test-log = { version = "0.2", features = ["trace"] }
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "basic_ops"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "builtins"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "thunk_scope"
|
|
||||||
harness = false
|
|
||||||
|
|||||||
Reference in New Issue
Block a user