Compare commits
7 Commits
4aa694aa3a
..
new
| Author | SHA1 | Date | |
|---|---|---|---|
|
dde3052e2d
|
|||
|
f0e3f1eeca
|
|||
|
afbc471e40
|
|||
|
c1b4ac4d8f
|
|||
|
81ac08fb5a
|
|||
|
9412c319f9
|
|||
|
b420a950a3
|
Generated
+28
-84
@@ -445,22 +445,18 @@ name = "fix"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bumpalo",
|
|
||||||
"clap",
|
"clap",
|
||||||
"criterion",
|
"criterion",
|
||||||
"ere",
|
"ere",
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
"fix-compiler",
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-ir",
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
"fix-vm",
|
"fix-vm",
|
||||||
"ghost-cell",
|
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"miette",
|
"miette",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"rnix",
|
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -471,51 +467,31 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-abstract-vm"
|
name = "fix-bytecode"
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
|
||||||
"gc-arena",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"likely_stable",
|
|
||||||
"num_enum",
|
|
||||||
"smallvec",
|
|
||||||
"sptr",
|
|
||||||
"string-interner",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fix-builtins"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"gc-arena",
|
|
||||||
"num_enum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fix-codegen"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"fix-builtins",
|
"fix-lang",
|
||||||
"fix-common",
|
|
||||||
"fix-ir",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"rnix",
|
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-common"
|
name = "fix-compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ere",
|
"bumpalo",
|
||||||
"gc-arena",
|
"colored",
|
||||||
|
"fix-bytecode",
|
||||||
|
"fix-error",
|
||||||
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
|
"ghost-cell",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"rnix",
|
||||||
|
"rowan",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -528,34 +504,26 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-ir"
|
name = "fix-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"ere",
|
||||||
"fix-builtins",
|
"gc-arena",
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
|
||||||
"ghost-cell",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"rnix",
|
|
||||||
"rowan",
|
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fix-primops"
|
name = "fix-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
|
"fix-lang",
|
||||||
"gc-arena",
|
"gc-arena",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"num_enum",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"sptr",
|
||||||
"string-interner",
|
"string-interner",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -563,19 +531,13 @@ dependencies = [
|
|||||||
name = "fix-vm"
|
name = "fix-vm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-primops",
|
"fix-lang",
|
||||||
|
"fix-runtime",
|
||||||
"gc-arena",
|
"gc-arena",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"likely_stable",
|
|
||||||
"num_enum",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sptr",
|
|
||||||
"string-interner",
|
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -773,15 +735,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "likely_stable"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d61f7017d8abea1fc23ff7f01a8147b2656dea3aeb24d519aab6e2177eaf671c"
|
|
||||||
dependencies = [
|
|
||||||
"rustc_version",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@@ -1158,15 +1111,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
|
||||||
dependencies = [
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
|||||||
+20
-20
@@ -2,41 +2,41 @@
|
|||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"fix",
|
"fix",
|
||||||
"fix-abstract-vm",
|
"fix-bytecode",
|
||||||
"fix-builtins",
|
"fix-compiler",
|
||||||
"fix-codegen",
|
|
||||||
"fix-common",
|
|
||||||
"fix-error",
|
"fix-error",
|
||||||
"fix-ir",
|
"fix-lang",
|
||||||
"fix-primops",
|
"fix-runtime",
|
||||||
"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"
|
||||||
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
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use fix_codegen::OperandType;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use string_interner::Symbol as _;
|
|
||||||
|
|
||||||
use crate::{OperandData, VmRuntimeCtx};
|
|
||||||
|
|
||||||
pub struct BytecodeReader<'a> {
|
|
||||||
bytecode: &'a [u8],
|
|
||||||
pc: usize,
|
|
||||||
inst_start_pc: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BytecodeReader<'a> {
|
|
||||||
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
bytecode,
|
|
||||||
pc,
|
|
||||||
inst_start_pc: pc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
bytecode,
|
|
||||||
pc: inst_start_pc + 1,
|
|
||||||
inst_start_pc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg_attr(debug_assertions, track_caller)]
|
|
||||||
fn read_array<const N: usize>(&mut self) -> [u8; N] {
|
|
||||||
let ret = self.bytecode[self.pc..self.pc + N]
|
|
||||||
.try_into()
|
|
||||||
.expect("read_array failed");
|
|
||||||
self.pc += N;
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_op(&mut self) -> fix_codegen::Op {
|
|
||||||
use fix_codegen::Op;
|
|
||||||
self.inst_start_pc = self.pc;
|
|
||||||
let byte = self.bytecode[self.pc];
|
|
||||||
if !likely_stable::likely((0..Op::Illegal as u8).contains(&byte)) {
|
|
||||||
panic!("unknown opcode: {byte:#04x}")
|
|
||||||
}
|
|
||||||
self.pc += 1;
|
|
||||||
unsafe { std::mem::transmute::<u8, Op>(byte) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u8(&mut self) -> u8 {
|
|
||||||
let val = self.bytecode[self.pc];
|
|
||||||
self.pc += 1;
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u16(&mut self) -> u16 {
|
|
||||||
u16::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_u32(&mut self) -> u32 {
|
|
||||||
u32::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_i32(&mut self) -> i32 {
|
|
||||||
i32::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_i64(&mut self) -> i64 {
|
|
||||||
i64::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_f64(&mut self) -> f64 {
|
|
||||||
f64::from_le_bytes(self.read_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_string_id(&mut self) -> StringId {
|
|
||||||
let raw = self.read_u32();
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read_operand_data<C: VmRuntimeCtx>(&mut self, ctx: &C) -> OperandData {
|
|
||||||
let tag = self.read_u8();
|
|
||||||
let Ok(ty) = OperandType::try_from_primitive(tag)
|
|
||||||
.map_err(|err| panic!("unknown operand tag: {:#04x}", err.number));
|
|
||||||
match ty {
|
|
||||||
OperandType::Const => {
|
|
||||||
let id = self.read_u32();
|
|
||||||
OperandData::Const(ctx.get_const(id))
|
|
||||||
}
|
|
||||||
OperandType::BigInt => {
|
|
||||||
let val = self.read_i64();
|
|
||||||
OperandData::BigInt(val)
|
|
||||||
}
|
|
||||||
OperandType::Local => {
|
|
||||||
let layer = self.read_u8();
|
|
||||||
let idx = self.read_u32();
|
|
||||||
OperandData::Local { layer, idx }
|
|
||||||
}
|
|
||||||
OperandType::BuiltinConst => {
|
|
||||||
let id = self.read_string_id();
|
|
||||||
OperandData::BuiltinConst(id)
|
|
||||||
}
|
|
||||||
OperandType::Builtins => OperandData::Builtins,
|
|
||||||
OperandType::ReplBinding => {
|
|
||||||
let id = self.read_string_id();
|
|
||||||
OperandData::ReplBinding(id)
|
|
||||||
}
|
|
||||||
OperandType::ScopedImportBinding => {
|
|
||||||
let slot_id = self.read_u32();
|
|
||||||
let name = self.read_string_id();
|
|
||||||
OperandData::ScopedImportBinding { slot_id, name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pc(&self) -> usize {
|
|
||||||
self.pc
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pc(&mut self, pc: usize) {
|
|
||||||
self.pc = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_start_pc(&self) -> usize {
|
|
||||||
self.inst_start_pc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-builtins"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
gc-arena = { workspace = true }
|
|
||||||
@@ -1,399 +0,0 @@
|
|||||||
use gc_arena::Collect;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
|
|
||||||
macro_rules! define_builtins {
|
|
||||||
($(($name:literal, $variant:ident, $arity:expr)),* $(,)?) => {
|
|
||||||
/// Builtin function registry.
|
|
||||||
/// Array index IS the PrimOp id. (name, arity) pairs.
|
|
||||||
pub const BUILTINS: &[(&str, u8)] = &[
|
|
||||||
$(($name, $arity),)*
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Collect)]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[collect(require_static)]
|
|
||||||
pub enum BuiltinId {
|
|
||||||
$($variant,)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_builtins! {
|
|
||||||
("abort", Abort, 1),
|
|
||||||
("__add", Add, 2),
|
|
||||||
("__addErrorContext", AddErrorContext, 2),
|
|
||||||
("__all", All, 2),
|
|
||||||
("__any", Any, 2),
|
|
||||||
("__appendContext", AppendContext, 2),
|
|
||||||
("__attrNames", AttrNames, 1),
|
|
||||||
("__attrValues", AttrValues, 1),
|
|
||||||
("baseNameOf", BaseNameOf, 1),
|
|
||||||
("__bitAnd", BitAnd, 2),
|
|
||||||
("__bitOr", BitOr, 2),
|
|
||||||
("__bitXor", BitXor, 2),
|
|
||||||
("break", Break, 1),
|
|
||||||
("__catAttrs", CatAttrs, 2),
|
|
||||||
("__ceil", Ceil, 1),
|
|
||||||
("__compareVersions", CompareVersions, 2),
|
|
||||||
("__concatLists", ConcatLists, 1),
|
|
||||||
("__concatMap", ConcatMap, 2),
|
|
||||||
("__concatStringsSep", ConcatStringsSep, 2),
|
|
||||||
("__convertHash", ConvertHash, 1),
|
|
||||||
("__deepSeq", DeepSeq, 2),
|
|
||||||
("derivation", Derivation, 1),
|
|
||||||
("derivationStrict", DerivationStrict, 1),
|
|
||||||
("dirOf", DirOf, 1),
|
|
||||||
("__div", Div, 2),
|
|
||||||
("__elem", Elem, 2),
|
|
||||||
("__elemAt", ElemAt, 2),
|
|
||||||
("fetchGit", FetchGit, 1),
|
|
||||||
("fetchMercurial", FetchMercurial, 1),
|
|
||||||
("fetchTarball", FetchTarball, 1),
|
|
||||||
("fetchTree", FetchTree, 1),
|
|
||||||
("__fetchurl", FetchUrl, 1),
|
|
||||||
("__filter", Filter, 2),
|
|
||||||
("__filterSource", FilterSource, 2),
|
|
||||||
("__findFile", FindFile, 2),
|
|
||||||
("__floor", Floor, 1),
|
|
||||||
("__foldl'", FoldlStrict, 3),
|
|
||||||
("__fromJSON", FromJSON, 1),
|
|
||||||
("fromTOML", FromTOML, 1),
|
|
||||||
("__functionArgs", FunctionArgs, 1),
|
|
||||||
("__genList", GenList, 2),
|
|
||||||
("__genericClosure", GenericClosure, 1),
|
|
||||||
("__getAttr", GetAttr, 2),
|
|
||||||
("__getContext", GetContext, 1),
|
|
||||||
("__getEnv", GetEnv, 1),
|
|
||||||
("__groupBy", GroupBy, 2),
|
|
||||||
("__hasAttr", HasAttr, 2),
|
|
||||||
("__hasContext", HasContext, 1),
|
|
||||||
("__hashFile", HashFile, 2),
|
|
||||||
("__hashString", HashString, 2),
|
|
||||||
("__head", Head, 1),
|
|
||||||
("import", Import, 1),
|
|
||||||
("__intersectAttrs", IntersectAttrs, 2),
|
|
||||||
("__isAttrs", IsAttrs, 1),
|
|
||||||
("__isBool", IsBool, 1),
|
|
||||||
("__isFloat", IsFloat, 1),
|
|
||||||
("__isFunction", IsFunction, 1),
|
|
||||||
("__isInt", IsInt, 1),
|
|
||||||
("__isList", IsList, 1),
|
|
||||||
("isNull", IsNull, 1),
|
|
||||||
("__isPath", IsPath, 1),
|
|
||||||
("__isString", IsString, 1),
|
|
||||||
("__length", Length, 1),
|
|
||||||
("__lessThan", LessThan, 2),
|
|
||||||
("__listToAttrs", ListToAttrs, 1),
|
|
||||||
("map", Map, 2),
|
|
||||||
("__mapAttrs", MapAttrs, 2),
|
|
||||||
("__match", Match, 2),
|
|
||||||
("__mul", Mul, 2),
|
|
||||||
("__parseDrvName", ParseDrvName, 1),
|
|
||||||
("__partition", Partition, 2),
|
|
||||||
("__path", Path, 1),
|
|
||||||
("__pathExists", PathExists, 1),
|
|
||||||
("placeholder", Placeholder, 1),
|
|
||||||
("__readDir", ReadDir, 1),
|
|
||||||
("__readFile", ReadFile, 1),
|
|
||||||
("__readFileType", ReadFileType, 1),
|
|
||||||
("removeAttrs", RemoveAttrs, 2),
|
|
||||||
("__replaceStrings", ReplaceStrings, 3),
|
|
||||||
("scopedImport", ScopedImport, 2),
|
|
||||||
("__seq", Seq, 2),
|
|
||||||
("__sort", Sort, 2),
|
|
||||||
("__split", Split, 2),
|
|
||||||
("__splitVersion", SplitVersion, 1),
|
|
||||||
("__storePath", StorePath, 1),
|
|
||||||
("__stringLength", StringLength, 1),
|
|
||||||
("__sub", Sub, 2),
|
|
||||||
("__substring", Substring, 3),
|
|
||||||
("__tail", Tail, 1),
|
|
||||||
("throw", Throw, 1),
|
|
||||||
("__toFile", ToFile, 2),
|
|
||||||
("__toJSON", ToJSON, 1),
|
|
||||||
("__toPath", ToPath, 1),
|
|
||||||
("toString", ToString, 1),
|
|
||||||
("__toXML", ToXML, 1),
|
|
||||||
("__trace", Trace, 2),
|
|
||||||
("__tryEval", TryEval, 1),
|
|
||||||
("__typeOf", TypeOf, 1),
|
|
||||||
("__unsafeDiscardStringContext", UnsafeDiscardStringContext, 1),
|
|
||||||
("__unsafeDiscardOutputDependency", UnsafeDiscardOutputDependency, 1),
|
|
||||||
("__unsafeGetAttrPos", UnsafeGetAttrPos, 2),
|
|
||||||
("__warn", Warn, 2),
|
|
||||||
("__zipAttrsWith", ZipAttrsWith, 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
|
||||||
pub enum PrimOpPhase {
|
|
||||||
Abort,
|
|
||||||
Add,
|
|
||||||
AddErrorContext,
|
|
||||||
All,
|
|
||||||
Any,
|
|
||||||
AppendContext,
|
|
||||||
AttrNames,
|
|
||||||
AttrValues,
|
|
||||||
BaseNameOf,
|
|
||||||
BitAnd,
|
|
||||||
BitOr,
|
|
||||||
BitXor,
|
|
||||||
Break,
|
|
||||||
CatAttrs,
|
|
||||||
Ceil,
|
|
||||||
CompareVersions,
|
|
||||||
ConcatLists,
|
|
||||||
ConcatMap,
|
|
||||||
ConcatStringsSep,
|
|
||||||
ConvertHash,
|
|
||||||
|
|
||||||
DeepSeq,
|
|
||||||
DeepSeqPush,
|
|
||||||
DeepSeqLoop,
|
|
||||||
|
|
||||||
Derivation,
|
|
||||||
DerivationStrict,
|
|
||||||
DirOf,
|
|
||||||
Div,
|
|
||||||
Elem,
|
|
||||||
ElemAt,
|
|
||||||
FetchGit,
|
|
||||||
FetchMercurial,
|
|
||||||
FetchTarball,
|
|
||||||
FetchTree,
|
|
||||||
FetchUrl,
|
|
||||||
|
|
||||||
FilterForceList,
|
|
||||||
FilterCallPred,
|
|
||||||
FilterCheck,
|
|
||||||
|
|
||||||
FilterSource,
|
|
||||||
FindFile,
|
|
||||||
Floor,
|
|
||||||
FoldlStrict,
|
|
||||||
FoldlStrictEmpty,
|
|
||||||
FoldlStrictCall1,
|
|
||||||
FoldlStrictCall2,
|
|
||||||
FoldlStrictUpdate,
|
|
||||||
FromJSON,
|
|
||||||
FromTOML,
|
|
||||||
FunctionArgs,
|
|
||||||
GenList,
|
|
||||||
GenericClosure,
|
|
||||||
GetAttr,
|
|
||||||
GetContext,
|
|
||||||
GetEnv,
|
|
||||||
GroupBy,
|
|
||||||
HasAttr,
|
|
||||||
HasContext,
|
|
||||||
HashFile,
|
|
||||||
HashString,
|
|
||||||
Head,
|
|
||||||
Import,
|
|
||||||
IntersectAttrs,
|
|
||||||
IsAttrs,
|
|
||||||
IsBool,
|
|
||||||
IsFloat,
|
|
||||||
IsFunction,
|
|
||||||
IsInt,
|
|
||||||
IsList,
|
|
||||||
IsNull,
|
|
||||||
IsPath,
|
|
||||||
IsString,
|
|
||||||
Length,
|
|
||||||
LessThan,
|
|
||||||
ListToAttrs,
|
|
||||||
Map,
|
|
||||||
MapAttrs,
|
|
||||||
Match,
|
|
||||||
Mul,
|
|
||||||
ParseDrvName,
|
|
||||||
Partition,
|
|
||||||
Path,
|
|
||||||
PathExists,
|
|
||||||
Placeholder,
|
|
||||||
ReadDir,
|
|
||||||
ReadFile,
|
|
||||||
ReadFileType,
|
|
||||||
RemoveAttrs,
|
|
||||||
ReplaceStrings,
|
|
||||||
ScopedImport,
|
|
||||||
Seq,
|
|
||||||
Sort,
|
|
||||||
Split,
|
|
||||||
SplitVersion,
|
|
||||||
StorePath,
|
|
||||||
StringLength,
|
|
||||||
Sub,
|
|
||||||
Substring,
|
|
||||||
Tail,
|
|
||||||
Throw,
|
|
||||||
ToFile,
|
|
||||||
ToJSON,
|
|
||||||
ToPath,
|
|
||||||
ToString,
|
|
||||||
ToXML,
|
|
||||||
Trace,
|
|
||||||
TryEval,
|
|
||||||
TypeOf,
|
|
||||||
UnsafeDiscardStringContext,
|
|
||||||
UnsafeGetAttrPos,
|
|
||||||
Warn,
|
|
||||||
ZipAttrsWith,
|
|
||||||
|
|
||||||
ForceResultShallow,
|
|
||||||
ForceResultShallowPush,
|
|
||||||
ForceResultShallowLoop,
|
|
||||||
ForceResultDeepFinish,
|
|
||||||
|
|
||||||
EqStep,
|
|
||||||
EqForce,
|
|
||||||
|
|
||||||
// TODO: split into separate enums
|
|
||||||
CallPattern,
|
|
||||||
CallFunctor1,
|
|
||||||
CallFunctor2,
|
|
||||||
|
|
||||||
ImportFinalize,
|
|
||||||
ScopedImportFinalize,
|
|
||||||
|
|
||||||
AppendContextLoop,
|
|
||||||
AppendContextEntryForced,
|
|
||||||
AppendContextOutputsForced,
|
|
||||||
AppendContextOutputElementLoop,
|
|
||||||
AppendContextOutputElementForced,
|
|
||||||
|
|
||||||
UnsafeDiscardOutputDependency,
|
|
||||||
|
|
||||||
Illegal,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u8> for PrimOpPhase {
|
|
||||||
type Error = u8;
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
if (0..Self::Illegal as u8).contains(&value) {
|
|
||||||
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
|
|
||||||
} else {
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltinId {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn entry_phase(self) -> PrimOpPhase {
|
|
||||||
use BuiltinId::*;
|
|
||||||
match self {
|
|
||||||
Abort => PrimOpPhase::Abort,
|
|
||||||
Add => PrimOpPhase::Add,
|
|
||||||
AddErrorContext => PrimOpPhase::AddErrorContext,
|
|
||||||
All => PrimOpPhase::All,
|
|
||||||
Any => PrimOpPhase::Any,
|
|
||||||
AppendContext => PrimOpPhase::AppendContext,
|
|
||||||
AttrNames => PrimOpPhase::AttrNames,
|
|
||||||
AttrValues => PrimOpPhase::AttrValues,
|
|
||||||
BaseNameOf => PrimOpPhase::BaseNameOf,
|
|
||||||
BitAnd => PrimOpPhase::BitAnd,
|
|
||||||
BitOr => PrimOpPhase::BitOr,
|
|
||||||
BitXor => PrimOpPhase::BitXor,
|
|
||||||
Break => PrimOpPhase::Break,
|
|
||||||
CatAttrs => PrimOpPhase::CatAttrs,
|
|
||||||
Ceil => PrimOpPhase::Ceil,
|
|
||||||
CompareVersions => PrimOpPhase::CompareVersions,
|
|
||||||
ConcatLists => PrimOpPhase::ConcatLists,
|
|
||||||
ConcatMap => PrimOpPhase::ConcatMap,
|
|
||||||
ConcatStringsSep => PrimOpPhase::ConcatStringsSep,
|
|
||||||
ConvertHash => PrimOpPhase::ConvertHash,
|
|
||||||
DeepSeq => PrimOpPhase::DeepSeq,
|
|
||||||
Derivation => PrimOpPhase::Derivation,
|
|
||||||
DerivationStrict => PrimOpPhase::DerivationStrict,
|
|
||||||
DirOf => PrimOpPhase::DirOf,
|
|
||||||
Div => PrimOpPhase::Div,
|
|
||||||
Elem => PrimOpPhase::Elem,
|
|
||||||
ElemAt => PrimOpPhase::ElemAt,
|
|
||||||
FetchGit => PrimOpPhase::FetchGit,
|
|
||||||
FetchMercurial => PrimOpPhase::FetchMercurial,
|
|
||||||
FetchTarball => PrimOpPhase::FetchTarball,
|
|
||||||
FetchTree => PrimOpPhase::FetchTree,
|
|
||||||
FetchUrl => PrimOpPhase::FetchUrl,
|
|
||||||
Filter => PrimOpPhase::FilterForceList,
|
|
||||||
FilterSource => PrimOpPhase::FilterSource,
|
|
||||||
FindFile => PrimOpPhase::FindFile,
|
|
||||||
Floor => PrimOpPhase::Floor,
|
|
||||||
FoldlStrict => PrimOpPhase::FoldlStrict,
|
|
||||||
FromJSON => PrimOpPhase::FromJSON,
|
|
||||||
FromTOML => PrimOpPhase::FromTOML,
|
|
||||||
FunctionArgs => PrimOpPhase::FunctionArgs,
|
|
||||||
GenList => PrimOpPhase::GenList,
|
|
||||||
GenericClosure => PrimOpPhase::GenericClosure,
|
|
||||||
GetAttr => PrimOpPhase::GetAttr,
|
|
||||||
GetContext => PrimOpPhase::GetContext,
|
|
||||||
GetEnv => PrimOpPhase::GetEnv,
|
|
||||||
GroupBy => PrimOpPhase::GroupBy,
|
|
||||||
HasAttr => PrimOpPhase::HasAttr,
|
|
||||||
HasContext => PrimOpPhase::HasContext,
|
|
||||||
HashFile => PrimOpPhase::HashFile,
|
|
||||||
HashString => PrimOpPhase::HashString,
|
|
||||||
Head => PrimOpPhase::Head,
|
|
||||||
Import => PrimOpPhase::Import,
|
|
||||||
IntersectAttrs => PrimOpPhase::IntersectAttrs,
|
|
||||||
IsAttrs => PrimOpPhase::IsAttrs,
|
|
||||||
IsBool => PrimOpPhase::IsBool,
|
|
||||||
IsFloat => PrimOpPhase::IsFloat,
|
|
||||||
IsFunction => PrimOpPhase::IsFunction,
|
|
||||||
IsInt => PrimOpPhase::IsInt,
|
|
||||||
IsList => PrimOpPhase::IsList,
|
|
||||||
IsNull => PrimOpPhase::IsNull,
|
|
||||||
IsPath => PrimOpPhase::IsPath,
|
|
||||||
IsString => PrimOpPhase::IsString,
|
|
||||||
Length => PrimOpPhase::Length,
|
|
||||||
LessThan => PrimOpPhase::LessThan,
|
|
||||||
ListToAttrs => PrimOpPhase::ListToAttrs,
|
|
||||||
Map => PrimOpPhase::Map,
|
|
||||||
MapAttrs => PrimOpPhase::MapAttrs,
|
|
||||||
Match => PrimOpPhase::Match,
|
|
||||||
Mul => PrimOpPhase::Mul,
|
|
||||||
ParseDrvName => PrimOpPhase::ParseDrvName,
|
|
||||||
Partition => PrimOpPhase::Partition,
|
|
||||||
Path => PrimOpPhase::Path,
|
|
||||||
PathExists => PrimOpPhase::PathExists,
|
|
||||||
Placeholder => PrimOpPhase::Placeholder,
|
|
||||||
ReadDir => PrimOpPhase::ReadDir,
|
|
||||||
ReadFile => PrimOpPhase::ReadFile,
|
|
||||||
ReadFileType => PrimOpPhase::ReadFileType,
|
|
||||||
RemoveAttrs => PrimOpPhase::RemoveAttrs,
|
|
||||||
ReplaceStrings => PrimOpPhase::ReplaceStrings,
|
|
||||||
ScopedImport => PrimOpPhase::ScopedImport,
|
|
||||||
Seq => PrimOpPhase::Seq,
|
|
||||||
Sort => PrimOpPhase::Sort,
|
|
||||||
Split => PrimOpPhase::Split,
|
|
||||||
SplitVersion => PrimOpPhase::SplitVersion,
|
|
||||||
StorePath => PrimOpPhase::StorePath,
|
|
||||||
StringLength => PrimOpPhase::StringLength,
|
|
||||||
Sub => PrimOpPhase::Sub,
|
|
||||||
Substring => PrimOpPhase::Substring,
|
|
||||||
Tail => PrimOpPhase::Tail,
|
|
||||||
Throw => PrimOpPhase::Throw,
|
|
||||||
ToFile => PrimOpPhase::ToFile,
|
|
||||||
ToJSON => PrimOpPhase::ToJSON,
|
|
||||||
ToPath => PrimOpPhase::ToPath,
|
|
||||||
ToString => PrimOpPhase::ToString,
|
|
||||||
ToXML => PrimOpPhase::ToXML,
|
|
||||||
Trace => PrimOpPhase::Trace,
|
|
||||||
TryEval => PrimOpPhase::TryEval,
|
|
||||||
TypeOf => PrimOpPhase::TypeOf,
|
|
||||||
UnsafeDiscardStringContext => PrimOpPhase::UnsafeDiscardStringContext,
|
|
||||||
UnsafeDiscardOutputDependency => PrimOpPhase::UnsafeDiscardOutputDependency,
|
|
||||||
UnsafeGetAttrPos => PrimOpPhase::UnsafeGetAttrPos,
|
|
||||||
Warn => PrimOpPhase::Warn,
|
|
||||||
ZipAttrsWith => PrimOpPhase::ZipAttrsWith,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrimOpPhase {
|
|
||||||
pub fn ip(self) -> u32 {
|
|
||||||
self as u32 * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "fix-bytecode"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
colored = "3.1.1"
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
string-interner = { workspace = true }
|
||||||
|
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize as _;
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
|
|
||||||
use crate::{InstructionPtr, Op, OperandType};
|
use crate::{InstructionPtr, Op, OperandType, PrimOpPhase};
|
||||||
|
|
||||||
pub trait DisassemblerContext {
|
pub trait DisassemblerContext {
|
||||||
fn resolve_string(&self, id: u32) -> &str;
|
fn resolve_string(&self, id: u32) -> &str;
|
||||||
@@ -82,7 +80,7 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
fn read_operand_data(&mut self) {
|
fn read_operand_data(&mut self) {
|
||||||
use OperandType::*;
|
use OperandType::*;
|
||||||
let tag = self.read_u8();
|
let tag = self.read_u8();
|
||||||
let ty = OperandType::try_from_primitive(tag).expect("invalid operand type");
|
let ty = OperandType::try_from(tag).expect("invalid operand type");
|
||||||
match ty {
|
match ty {
|
||||||
Const => {
|
Const => {
|
||||||
self.read_u32();
|
self.read_u32();
|
||||||
@@ -202,7 +200,7 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decode_instruction(&mut self, op_byte: u8, current_pc: usize) -> (&'static str, String) {
|
fn decode_instruction(&mut self, op_byte: u8, current_pc: usize) -> (&'static str, String) {
|
||||||
let op = Op::try_from_primitive(op_byte).expect("invalid op code");
|
let op = Op::try_from(op_byte).expect("invalid op code");
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Op::PushSmi => {
|
Op::PushSmi => {
|
||||||
@@ -297,8 +295,8 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
("Call", "arg=?".into())
|
("Call", "arg=?".into())
|
||||||
}
|
}
|
||||||
Op::DispatchPrimOp => {
|
Op::DispatchPrimOp => {
|
||||||
let id = BuiltinId::try_from_primitive(self.read_u8()).expect("invalid builtin id");
|
let phase = PrimOpPhase::try_from(self.read_u8()).expect("invalid primop phase");
|
||||||
("DispatchPrimOp", format!("id={id:?}"))
|
("DispatchPrimOp", format!("phase={phase:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::MakeAttrs => {
|
Op::MakeAttrs => {
|
||||||
@@ -0,0 +1,532 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use fix_lang::{BuiltinId, StringId};
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use string_interner::Symbol as _;
|
||||||
|
|
||||||
|
pub mod disassembler;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct InstructionPtr(pub usize);
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum Op {
|
||||||
|
PushSmi,
|
||||||
|
PushBigInt,
|
||||||
|
PushFloat,
|
||||||
|
PushString,
|
||||||
|
PushNull,
|
||||||
|
PushTrue,
|
||||||
|
PushFalse,
|
||||||
|
|
||||||
|
LoadLocal,
|
||||||
|
LoadOuter,
|
||||||
|
StoreLocal,
|
||||||
|
AllocLocals,
|
||||||
|
|
||||||
|
MakeThunk,
|
||||||
|
MakeClosure,
|
||||||
|
MakePatternClosure,
|
||||||
|
|
||||||
|
Call,
|
||||||
|
DispatchPrimOp,
|
||||||
|
|
||||||
|
MakeAttrs,
|
||||||
|
MakeEmptyAttrs,
|
||||||
|
SelectStatic,
|
||||||
|
SelectDynamic,
|
||||||
|
HasAttrPathStatic,
|
||||||
|
HasAttrPathDynamic,
|
||||||
|
HasAttrStatic,
|
||||||
|
HasAttrDynamic,
|
||||||
|
HasAttrResolve,
|
||||||
|
JumpIfSelectSucceeded,
|
||||||
|
JumpIfSelectFailed,
|
||||||
|
|
||||||
|
MakeList,
|
||||||
|
MakeEmptyList,
|
||||||
|
|
||||||
|
OpAdd,
|
||||||
|
OpSub,
|
||||||
|
OpMul,
|
||||||
|
OpDiv,
|
||||||
|
OpEq,
|
||||||
|
OpNeq,
|
||||||
|
OpLt,
|
||||||
|
OpGt,
|
||||||
|
OpLeq,
|
||||||
|
OpGeq,
|
||||||
|
OpConcat,
|
||||||
|
OpUpdate,
|
||||||
|
|
||||||
|
OpNeg,
|
||||||
|
OpNot,
|
||||||
|
|
||||||
|
JumpIfFalse,
|
||||||
|
JumpIfTrue,
|
||||||
|
Jump,
|
||||||
|
|
||||||
|
CoerceToString,
|
||||||
|
|
||||||
|
ConcatStrings,
|
||||||
|
ResolvePath,
|
||||||
|
|
||||||
|
Assert,
|
||||||
|
|
||||||
|
LookupWith,
|
||||||
|
|
||||||
|
LoadBuiltins,
|
||||||
|
LoadBuiltin,
|
||||||
|
|
||||||
|
LoadReplBinding,
|
||||||
|
LoadScopedBinding,
|
||||||
|
|
||||||
|
Return,
|
||||||
|
|
||||||
|
Illegal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
pub enum OperandType {
|
||||||
|
Const,
|
||||||
|
BigInt,
|
||||||
|
Local,
|
||||||
|
BuiltinConst,
|
||||||
|
Builtins,
|
||||||
|
ReplBinding,
|
||||||
|
ScopedImportBinding,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Const {
|
||||||
|
Smi(i32),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(StringId),
|
||||||
|
Path(StringId),
|
||||||
|
PrimOp {
|
||||||
|
id: BuiltinId,
|
||||||
|
arity: u8,
|
||||||
|
dispatch_ip: u32,
|
||||||
|
},
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
|
pub enum AttrKeyType {
|
||||||
|
Static,
|
||||||
|
Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum OperandData {
|
||||||
|
Const(u32),
|
||||||
|
BigInt(i64),
|
||||||
|
Local { layer: u8, idx: u32 },
|
||||||
|
BuiltinConst(StringId),
|
||||||
|
Builtins,
|
||||||
|
ReplBinding(StringId),
|
||||||
|
ScopedImportBinding { slot_id: u32, name: StringId },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub enum PrimOpPhase {
|
||||||
|
Abort,
|
||||||
|
Add,
|
||||||
|
AddErrorContext,
|
||||||
|
|
||||||
|
All,
|
||||||
|
AllCallPred,
|
||||||
|
AllCheck,
|
||||||
|
|
||||||
|
Any,
|
||||||
|
AnyCallPred,
|
||||||
|
AnyCheck,
|
||||||
|
|
||||||
|
AppendContext,
|
||||||
|
AttrNames,
|
||||||
|
AttrValues,
|
||||||
|
BaseNameOf,
|
||||||
|
BitAnd,
|
||||||
|
BitOr,
|
||||||
|
BitXor,
|
||||||
|
Break,
|
||||||
|
CatAttrs,
|
||||||
|
Ceil,
|
||||||
|
CompareVersions,
|
||||||
|
ConcatLists,
|
||||||
|
ConcatMap,
|
||||||
|
ConcatStringsSep,
|
||||||
|
ConvertHash,
|
||||||
|
|
||||||
|
DeepSeq,
|
||||||
|
DeepSeqPush,
|
||||||
|
DeepSeqLoop,
|
||||||
|
|
||||||
|
Derivation,
|
||||||
|
DerivationStrict,
|
||||||
|
DirOf,
|
||||||
|
Div,
|
||||||
|
Elem,
|
||||||
|
ElemAt,
|
||||||
|
FetchGit,
|
||||||
|
FetchMercurial,
|
||||||
|
FetchTarball,
|
||||||
|
FetchTree,
|
||||||
|
FetchUrl,
|
||||||
|
|
||||||
|
FilterForceList,
|
||||||
|
FilterCallPred,
|
||||||
|
FilterCheck,
|
||||||
|
|
||||||
|
FilterSource,
|
||||||
|
FindFile,
|
||||||
|
Floor,
|
||||||
|
FoldlStrict,
|
||||||
|
FoldlStrictEmpty,
|
||||||
|
FoldlStrictCall1,
|
||||||
|
FoldlStrictCall2,
|
||||||
|
FoldlStrictUpdate,
|
||||||
|
FromJSON,
|
||||||
|
FromTOML,
|
||||||
|
FunctionArgs,
|
||||||
|
GenList,
|
||||||
|
GenericClosure,
|
||||||
|
GetAttr,
|
||||||
|
GetContext,
|
||||||
|
GetEnv,
|
||||||
|
GroupBy,
|
||||||
|
HasAttr,
|
||||||
|
HasContext,
|
||||||
|
HashFile,
|
||||||
|
HashString,
|
||||||
|
Head,
|
||||||
|
Import,
|
||||||
|
IntersectAttrs,
|
||||||
|
IsAttrs,
|
||||||
|
IsBool,
|
||||||
|
IsFloat,
|
||||||
|
IsFunction,
|
||||||
|
IsInt,
|
||||||
|
IsList,
|
||||||
|
IsNull,
|
||||||
|
IsPath,
|
||||||
|
IsString,
|
||||||
|
Length,
|
||||||
|
LessThan,
|
||||||
|
ListToAttrs,
|
||||||
|
Map,
|
||||||
|
MapAttrs,
|
||||||
|
Match,
|
||||||
|
Mul,
|
||||||
|
ParseDrvName,
|
||||||
|
Partition,
|
||||||
|
Path,
|
||||||
|
PathExists,
|
||||||
|
Placeholder,
|
||||||
|
ReadDir,
|
||||||
|
ReadFile,
|
||||||
|
ReadFileType,
|
||||||
|
RemoveAttrs,
|
||||||
|
ReplaceStrings,
|
||||||
|
ScopedImport,
|
||||||
|
Seq,
|
||||||
|
Sort,
|
||||||
|
Split,
|
||||||
|
SplitVersion,
|
||||||
|
StorePath,
|
||||||
|
StringLength,
|
||||||
|
Sub,
|
||||||
|
Substring,
|
||||||
|
Tail,
|
||||||
|
Throw,
|
||||||
|
ToFile,
|
||||||
|
ToJSON,
|
||||||
|
ToPath,
|
||||||
|
ToString,
|
||||||
|
ToXML,
|
||||||
|
Trace,
|
||||||
|
TryEval,
|
||||||
|
TypeOf,
|
||||||
|
UnsafeDiscardStringContext,
|
||||||
|
UnsafeGetAttrPos,
|
||||||
|
Warn,
|
||||||
|
ZipAttrsWith,
|
||||||
|
|
||||||
|
ForceResultShallow,
|
||||||
|
ForceResultShallowPush,
|
||||||
|
ForceResultShallowLoop,
|
||||||
|
ForceResultDeepFinish,
|
||||||
|
|
||||||
|
EqStep,
|
||||||
|
EqForce,
|
||||||
|
|
||||||
|
CallPattern,
|
||||||
|
CallFunctor1,
|
||||||
|
CallFunctor2,
|
||||||
|
|
||||||
|
ImportFinalize,
|
||||||
|
ScopedImportFinalize,
|
||||||
|
|
||||||
|
AppendContextLoop,
|
||||||
|
AppendContextEntryForced,
|
||||||
|
AppendContextOutputsForced,
|
||||||
|
AppendContextOutputElementLoop,
|
||||||
|
AppendContextOutputElementForced,
|
||||||
|
|
||||||
|
UnsafeDiscardOutputDependency,
|
||||||
|
|
||||||
|
Illegal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for PrimOpPhase {
|
||||||
|
type Error = u8;
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
if (0..Self::Illegal as u8).contains(&value) {
|
||||||
|
Ok(unsafe { std::mem::transmute::<u8, Self>(value) })
|
||||||
|
} else {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimOpPhase {
|
||||||
|
pub fn entry_for_builtin(id: BuiltinId) -> Self {
|
||||||
|
use BuiltinId::*;
|
||||||
|
match id {
|
||||||
|
Abort => Self::Abort,
|
||||||
|
Add => Self::Add,
|
||||||
|
AddErrorContext => Self::AddErrorContext,
|
||||||
|
All => Self::All,
|
||||||
|
Any => Self::Any,
|
||||||
|
AppendContext => Self::AppendContext,
|
||||||
|
AttrNames => Self::AttrNames,
|
||||||
|
AttrValues => Self::AttrValues,
|
||||||
|
BaseNameOf => Self::BaseNameOf,
|
||||||
|
BitAnd => Self::BitAnd,
|
||||||
|
BitOr => Self::BitOr,
|
||||||
|
BitXor => Self::BitXor,
|
||||||
|
Break => Self::Break,
|
||||||
|
CatAttrs => Self::CatAttrs,
|
||||||
|
Ceil => Self::Ceil,
|
||||||
|
CompareVersions => Self::CompareVersions,
|
||||||
|
ConcatLists => Self::ConcatLists,
|
||||||
|
ConcatMap => Self::ConcatMap,
|
||||||
|
ConcatStringsSep => Self::ConcatStringsSep,
|
||||||
|
ConvertHash => Self::ConvertHash,
|
||||||
|
DeepSeq => Self::DeepSeq,
|
||||||
|
Derivation => Self::Derivation,
|
||||||
|
DerivationStrict => Self::DerivationStrict,
|
||||||
|
DirOf => Self::DirOf,
|
||||||
|
Div => Self::Div,
|
||||||
|
Elem => Self::Elem,
|
||||||
|
ElemAt => Self::ElemAt,
|
||||||
|
FetchGit => Self::FetchGit,
|
||||||
|
FetchMercurial => Self::FetchMercurial,
|
||||||
|
FetchTarball => Self::FetchTarball,
|
||||||
|
FetchTree => Self::FetchTree,
|
||||||
|
FetchUrl => Self::FetchUrl,
|
||||||
|
Filter => Self::FilterForceList,
|
||||||
|
FilterSource => Self::FilterSource,
|
||||||
|
FindFile => Self::FindFile,
|
||||||
|
Floor => Self::Floor,
|
||||||
|
FoldlStrict => Self::FoldlStrict,
|
||||||
|
FromJSON => Self::FromJSON,
|
||||||
|
FromTOML => Self::FromTOML,
|
||||||
|
FunctionArgs => Self::FunctionArgs,
|
||||||
|
GenList => Self::GenList,
|
||||||
|
GenericClosure => Self::GenericClosure,
|
||||||
|
GetAttr => Self::GetAttr,
|
||||||
|
GetContext => Self::GetContext,
|
||||||
|
GetEnv => Self::GetEnv,
|
||||||
|
GroupBy => Self::GroupBy,
|
||||||
|
HasAttr => Self::HasAttr,
|
||||||
|
HasContext => Self::HasContext,
|
||||||
|
HashFile => Self::HashFile,
|
||||||
|
HashString => Self::HashString,
|
||||||
|
Head => Self::Head,
|
||||||
|
Import => Self::Import,
|
||||||
|
IntersectAttrs => Self::IntersectAttrs,
|
||||||
|
IsAttrs => Self::IsAttrs,
|
||||||
|
IsBool => Self::IsBool,
|
||||||
|
IsFloat => Self::IsFloat,
|
||||||
|
IsFunction => Self::IsFunction,
|
||||||
|
IsInt => Self::IsInt,
|
||||||
|
IsList => Self::IsList,
|
||||||
|
IsNull => Self::IsNull,
|
||||||
|
IsPath => Self::IsPath,
|
||||||
|
IsString => Self::IsString,
|
||||||
|
Length => Self::Length,
|
||||||
|
LessThan => Self::LessThan,
|
||||||
|
ListToAttrs => Self::ListToAttrs,
|
||||||
|
Map => Self::Map,
|
||||||
|
MapAttrs => Self::MapAttrs,
|
||||||
|
Match => Self::Match,
|
||||||
|
Mul => Self::Mul,
|
||||||
|
ParseDrvName => Self::ParseDrvName,
|
||||||
|
Partition => Self::Partition,
|
||||||
|
Path => Self::Path,
|
||||||
|
PathExists => Self::PathExists,
|
||||||
|
Placeholder => Self::Placeholder,
|
||||||
|
ReadDir => Self::ReadDir,
|
||||||
|
ReadFile => Self::ReadFile,
|
||||||
|
ReadFileType => Self::ReadFileType,
|
||||||
|
RemoveAttrs => Self::RemoveAttrs,
|
||||||
|
ReplaceStrings => Self::ReplaceStrings,
|
||||||
|
ScopedImport => Self::ScopedImport,
|
||||||
|
Seq => Self::Seq,
|
||||||
|
Sort => Self::Sort,
|
||||||
|
Split => Self::Split,
|
||||||
|
SplitVersion => Self::SplitVersion,
|
||||||
|
StorePath => Self::StorePath,
|
||||||
|
StringLength => Self::StringLength,
|
||||||
|
Sub => Self::Sub,
|
||||||
|
Substring => Self::Substring,
|
||||||
|
Tail => Self::Tail,
|
||||||
|
Throw => Self::Throw,
|
||||||
|
ToFile => Self::ToFile,
|
||||||
|
ToJSON => Self::ToJSON,
|
||||||
|
ToPath => Self::ToPath,
|
||||||
|
ToString => Self::ToString,
|
||||||
|
ToXML => Self::ToXML,
|
||||||
|
Trace => Self::Trace,
|
||||||
|
TryEval => Self::TryEval,
|
||||||
|
TypeOf => Self::TypeOf,
|
||||||
|
UnsafeDiscardStringContext => Self::UnsafeDiscardStringContext,
|
||||||
|
UnsafeDiscardOutputDependency => Self::UnsafeDiscardOutputDependency,
|
||||||
|
UnsafeGetAttrPos => Self::UnsafeGetAttrPos,
|
||||||
|
Warn => Self::Warn,
|
||||||
|
ZipAttrsWith => Self::ZipAttrsWith,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ip(self) -> u32 {
|
||||||
|
self as u32 * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BytecodeReader<'a> {
|
||||||
|
bytecode: &'a [u8],
|
||||||
|
pc: usize,
|
||||||
|
inst_start_pc: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BytecodeReader<'a> {
|
||||||
|
pub fn new(bytecode: &'a [u8], pc: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
bytecode,
|
||||||
|
pc,
|
||||||
|
inst_start_pc: pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_after_op(bytecode: &'a [u8], inst_start_pc: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
bytecode,
|
||||||
|
pc: inst_start_pc + 1,
|
||||||
|
inst_start_pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg_attr(debug_assertions, track_caller)]
|
||||||
|
fn read_array<const N: usize>(&mut self) -> [u8; N] {
|
||||||
|
let ret = self.bytecode[self.pc..self.pc + N]
|
||||||
|
.try_into()
|
||||||
|
.expect("read_array failed");
|
||||||
|
self.pc += N;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_op(&mut self) -> Op {
|
||||||
|
self.inst_start_pc = self.pc;
|
||||||
|
let byte = self.bytecode[self.pc];
|
||||||
|
if !(0..Op::Illegal as u8).contains(&byte) {
|
||||||
|
std::hint::cold_path();
|
||||||
|
panic!("unknown opcode: {byte:#04x}")
|
||||||
|
}
|
||||||
|
self.pc += 1;
|
||||||
|
unsafe { std::mem::transmute::<u8, Op>(byte) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u8(&mut self) -> u8 {
|
||||||
|
let val = self.bytecode[self.pc];
|
||||||
|
self.pc += 1;
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u16(&mut self) -> u16 {
|
||||||
|
u16::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_u32(&mut self) -> u32 {
|
||||||
|
u32::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_i32(&mut self) -> i32 {
|
||||||
|
i32::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_i64(&mut self) -> i64 {
|
||||||
|
i64::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_f64(&mut self) -> f64 {
|
||||||
|
f64::from_le_bytes(self.read_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_string_id(&mut self) -> StringId {
|
||||||
|
let raw = self.read_u32();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
StringId(string_interner::symbol::SymbolU32::try_from_usize(raw as usize).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_operand_data(&mut self) -> OperandData {
|
||||||
|
let tag = self.read_u8();
|
||||||
|
let Ok(ty) = OperandType::try_from_primitive(tag)
|
||||||
|
.map_err(|err| panic!("unknown operand tag: {:#04x}", err.number));
|
||||||
|
match ty {
|
||||||
|
OperandType::Const => OperandData::Const(self.read_u32()),
|
||||||
|
OperandType::BigInt => OperandData::BigInt(self.read_i64()),
|
||||||
|
OperandType::Local => {
|
||||||
|
let layer = self.read_u8();
|
||||||
|
let idx = self.read_u32();
|
||||||
|
OperandData::Local { layer, idx }
|
||||||
|
}
|
||||||
|
OperandType::BuiltinConst => OperandData::BuiltinConst(self.read_string_id()),
|
||||||
|
OperandType::Builtins => OperandData::Builtins,
|
||||||
|
OperandType::ReplBinding => OperandData::ReplBinding(self.read_string_id()),
|
||||||
|
OperandType::ScopedImportBinding => {
|
||||||
|
let slot_id = self.read_u32();
|
||||||
|
let name = self.read_string_id();
|
||||||
|
OperandData::ScopedImportBinding { slot_id, name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pc(&self) -> usize {
|
||||||
|
self.pc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pc(&mut self, pc: usize) {
|
||||||
|
self.pc = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_start_pc(&self) -> usize {
|
||||||
|
self.inst_start_pc
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-codegen"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hashbrown = { workspace = true }
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
rnix = { workspace = true }
|
|
||||||
string-interner = { workspace = true }
|
|
||||||
colored = "3.1.1"
|
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-ir = { path = "../fix-ir" }
|
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-ir"
|
name = "fix-compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
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 }
|
||||||
rnix = { workspace = true }
|
rnix = { workspace = true }
|
||||||
rowan = { workspace = true }
|
rowan = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
|
tracing = "0.1"
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
use bumpalo::Bump;
|
||||||
|
use fix_bytecode::{Const, InstructionPtr, Op, PrimOpPhase};
|
||||||
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{StringId, Symbol};
|
||||||
|
use fix_runtime::{StaticValue, VmCode, VmRuntimeCtx};
|
||||||
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
|
use crate::BytecodeContext;
|
||||||
|
use crate::ir::downgrade::{Downgrade as _, DowngradeContext};
|
||||||
|
use crate::ir::{
|
||||||
|
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
|
||||||
|
ThunkId,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CodeState {
|
||||||
|
pub bytecode: Vec<u8>,
|
||||||
|
pub sources: Vec<Source>,
|
||||||
|
pub spans: Vec<(usize, rnix::TextRange)>,
|
||||||
|
pub thunk_count: usize,
|
||||||
|
pub global_env: HashMap<StringId, MaybeThunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeState {
|
||||||
|
pub fn new(strings: &mut DefaultStringInterner) -> Self {
|
||||||
|
let global_env = crate::ir::new_global_env(strings);
|
||||||
|
let mut bytecode = Vec::with_capacity(PrimOpPhase::Illegal as usize * 2);
|
||||||
|
for phase in 0..=PrimOpPhase::Illegal as u8 {
|
||||||
|
bytecode.push(Op::DispatchPrimOp as u8);
|
||||||
|
bytecode.push(phase);
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
sources: Vec::new(),
|
||||||
|
spans: Vec::new(),
|
||||||
|
thunk_count: 0,
|
||||||
|
bytecode,
|
||||||
|
global_env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_bytecode<'ctx>(
|
||||||
|
&'ctx mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<ExtraScope<'ctx>>,
|
||||||
|
runtime: &'ctx mut impl VmRuntimeCtx,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let mut compiler = CompilerCtx {
|
||||||
|
code: self,
|
||||||
|
runtime,
|
||||||
|
};
|
||||||
|
compiler.compile_bytecode(source, extra_scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmCode for CodeState {
|
||||||
|
fn bytecode(&self) -> &[u8] {
|
||||||
|
&self.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_with_scope(
|
||||||
|
&mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<fix_runtime::ExtraScope>,
|
||||||
|
runtime: &mut impl VmRuntimeCtx,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let extra = extra_scope.map(|s| match s {
|
||||||
|
fix_runtime::ExtraScope::ScopedImport { keys, slot_id } => {
|
||||||
|
ExtraScope::ScopedImport { keys, slot_id }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CodeState::compile_bytecode(self, source, extra, runtime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CompilerCtx<'a, R: VmRuntimeCtx> {
|
||||||
|
code: &'a mut CodeState,
|
||||||
|
runtime: &'a mut R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
||||||
|
fn compile_bytecode(
|
||||||
|
&mut self,
|
||||||
|
source: Source,
|
||||||
|
extra_scope: Option<ExtraScope>,
|
||||||
|
) -> Result<InstructionPtr> {
|
||||||
|
let root = self.downgrade(source, extra_scope)?;
|
||||||
|
let ip = crate::compile_bytecode(root.as_ref(), self);
|
||||||
|
Ok(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade(&mut self, source: Source, extra_scope: Option<ExtraScope>) -> Result<OwnedIr> {
|
||||||
|
tracing::debug!("Parsing Nix expression");
|
||||||
|
|
||||||
|
self.code.sources.push(source.clone());
|
||||||
|
|
||||||
|
let root = rnix::Root::parse(&source.src);
|
||||||
|
handle_parse_error(root.errors(), source.clone()).map_or(Ok(()), Err)?;
|
||||||
|
|
||||||
|
tracing::debug!("Downgrading Nix expression");
|
||||||
|
let expr = root
|
||||||
|
.tree()
|
||||||
|
.expr()
|
||||||
|
.ok_or_else(|| Error::parse_error("unexpected EOF".into()))?;
|
||||||
|
let bump = Bump::new();
|
||||||
|
GhostToken::new(|token| {
|
||||||
|
let downgrade_ctx = DowngradeCtx::new(
|
||||||
|
&bump,
|
||||||
|
token,
|
||||||
|
self.runtime,
|
||||||
|
&self.code.global_env,
|
||||||
|
extra_scope.map(Into::into),
|
||||||
|
&mut self.code.thunk_count,
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
let ir = downgrade_ctx.downgrade_toplevel(expr)?;
|
||||||
|
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
||||||
|
Ok(OwnedIr { _bump: bump, ir })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
||||||
|
fn intern_string(&mut self, s: &str) -> StringId {
|
||||||
|
self.runtime.intern_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_span(&mut self, range: rnix::TextRange) -> u32 {
|
||||||
|
let id = self.code.spans.len();
|
||||||
|
let source_id = self
|
||||||
|
.code
|
||||||
|
.sources
|
||||||
|
.len()
|
||||||
|
.checked_sub(1)
|
||||||
|
.expect("current_source not set");
|
||||||
|
self.code.spans.push((source_id, range));
|
||||||
|
id as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code(&self) -> &[u8] {
|
||||||
|
&self.code.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.code.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_constant(&mut self, val: Const) -> u32 {
|
||||||
|
use Const::*;
|
||||||
|
let val = match val {
|
||||||
|
Smi(x) => StaticValue::new_inline(x),
|
||||||
|
Float(x) => StaticValue::new_float(x),
|
||||||
|
Bool(x) => StaticValue::new_inline(x),
|
||||||
|
String(x) => StaticValue::new_inline(x),
|
||||||
|
Path(x) => StaticValue::new_inline(fix_runtime::Path(x)),
|
||||||
|
PrimOp {
|
||||||
|
id,
|
||||||
|
arity,
|
||||||
|
dispatch_ip,
|
||||||
|
} => StaticValue::new_primop(id, arity, dispatch_ip),
|
||||||
|
Null => StaticValue::default(),
|
||||||
|
};
|
||||||
|
self.runtime.add_const(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_source_dir(&mut self) -> StringId {
|
||||||
|
let dir = self
|
||||||
|
.code
|
||||||
|
.sources
|
||||||
|
.last()
|
||||||
|
.expect("current_source not set")
|
||||||
|
.get_dir()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
self.runtime.intern_string(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_error_span(error: &rnix::ParseError) -> Option<rnix::TextRange> {
|
||||||
|
use rnix::ParseError::*;
|
||||||
|
match error {
|
||||||
|
Unexpected(range)
|
||||||
|
| UnexpectedExtra(range)
|
||||||
|
| UnexpectedWanted(_, range, _)
|
||||||
|
| UnexpectedDoubleBind(range)
|
||||||
|
| DuplicatedArgs(range, _) => Some(*range),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_parse_error<'a>(
|
||||||
|
errors: impl IntoIterator<Item = &'a rnix::ParseError>,
|
||||||
|
source: Source,
|
||||||
|
) -> Option<Box<Error>> {
|
||||||
|
for err in errors {
|
||||||
|
if let Some(span) = parse_error_span(err) {
|
||||||
|
return Some(
|
||||||
|
Error::parse_error(err.to_string())
|
||||||
|
.with_source(source)
|
||||||
|
.with_span(span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DowngradeCtx<'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
runtime: &'ctx mut R,
|
||||||
|
source: Source,
|
||||||
|
scopes: Vec<Scope<'ctx, 'id, 'ir>>,
|
||||||
|
with_stack: Vec<GhostRoMaybeThunkRef<'id, 'ir>>,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
fn new(
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
runtime: &'ctx mut R,
|
||||||
|
global: &'ctx HashMap<StringId, MaybeThunk>,
|
||||||
|
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
source: Source,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bump,
|
||||||
|
token,
|
||||||
|
runtime,
|
||||||
|
source,
|
||||||
|
scopes: std::iter::once(Scope::Global(global))
|
||||||
|
.chain(extra_scope)
|
||||||
|
.collect(),
|
||||||
|
thunk_count,
|
||||||
|
with_stack: Vec::new(),
|
||||||
|
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
||||||
|
for DowngradeCtx<'ctx, 'id, 'ir, R>
|
||||||
|
{
|
||||||
|
fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
|
||||||
|
self.bump.alloc(GhostCell::new(expr).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
|
||||||
|
use MaybeThunk::*;
|
||||||
|
let expr = (|| {
|
||||||
|
let expr = match *ir.borrow(&self.token) {
|
||||||
|
Ir::Builtin(x) => Builtin(x),
|
||||||
|
Ir::Int(x) => Int(x),
|
||||||
|
Ir::Float(x) => Float(x),
|
||||||
|
Ir::Bool(x) => Bool(x),
|
||||||
|
Ir::Str(x) => Str(x),
|
||||||
|
Ir::Arg { layer } => Arg { layer },
|
||||||
|
Ir::Builtins => Builtins,
|
||||||
|
Ir::Null => Null,
|
||||||
|
Ir::MaybeThunk(thunk) => return Some(thunk),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(self.bump.alloc(GhostCell::new(expr).into()))
|
||||||
|
})();
|
||||||
|
if let Some(thunk) = expr {
|
||||||
|
return thunk;
|
||||||
|
}
|
||||||
|
let id = ThunkId(*self.thunk_count);
|
||||||
|
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
||||||
|
self.thunk_scopes
|
||||||
|
.last_mut()
|
||||||
|
.expect("no active cache scope")
|
||||||
|
.add_binding(id, ir);
|
||||||
|
self.bump.alloc(GhostCell::new(Thunk(id)).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
||||||
|
self.runtime.intern_string(sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_sym(&self, id: StringId) -> Symbol<'_> {
|
||||||
|
self.runtime.resolve_string(id).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(
|
||||||
|
&mut self,
|
||||||
|
sym: StringId,
|
||||||
|
span: rnix::TextRange,
|
||||||
|
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
match scope {
|
||||||
|
&Scope::Global(global_scope) => {
|
||||||
|
if let Some(expr) = global_scope.get(&sym) {
|
||||||
|
return Ok(expr.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Repl(repl_bindings) => {
|
||||||
|
if repl_bindings.contains(&sym) {
|
||||||
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::ScopedImport { ref keys, slot_id } => {
|
||||||
|
if keys.contains(&sym) {
|
||||||
|
return Ok(self.bump.alloc(
|
||||||
|
GhostCell::new(MaybeThunk::ScopedImportBinding { sym, slot_id }).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::Let(let_scope) => {
|
||||||
|
if let Some(&expr) = let_scope.get(&sym) {
|
||||||
|
return Ok(expr.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Param {
|
||||||
|
sym: param_sym,
|
||||||
|
abs_layer,
|
||||||
|
} => {
|
||||||
|
if param_sym == sym {
|
||||||
|
let layers: u8 =
|
||||||
|
self.thunk_scopes.len().try_into().expect("scope too deep!");
|
||||||
|
let layer = layers - abs_layer;
|
||||||
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.with_stack.is_empty() {
|
||||||
|
let id = ThunkId(*self.thunk_count);
|
||||||
|
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
||||||
|
let mut namespaces =
|
||||||
|
bumpalo::collections::Vec::with_capacity_in(self.with_stack.len(), self.bump);
|
||||||
|
namespaces.extend(self.with_stack.iter().rev().copied());
|
||||||
|
let body = self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(Ir::WithLookup { sym, namespaces }).into());
|
||||||
|
self.thunk_scopes
|
||||||
|
.last_mut()
|
||||||
|
.expect("no active thunk scope")
|
||||||
|
.add_binding(id, body);
|
||||||
|
Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Thunk(id)).into()))
|
||||||
|
} else {
|
||||||
|
Err(Error::downgrade_error(
|
||||||
|
format!("'{}' not found", self.resolve_sym(sym)),
|
||||||
|
self.get_current_source(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_source(&self) -> Source {
|
||||||
|
self.source.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
&mut Self,
|
||||||
|
) -> Result<(
|
||||||
|
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
|
||||||
|
Ret,
|
||||||
|
)>,
|
||||||
|
{
|
||||||
|
let base = *self.thunk_count;
|
||||||
|
*self.thunk_count = self
|
||||||
|
.thunk_count
|
||||||
|
.checked_add(keys.len())
|
||||||
|
.expect("thunk id overflow");
|
||||||
|
let handles = (base..base + keys.len())
|
||||||
|
.map(|id| {
|
||||||
|
&*self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
|
||||||
|
self.scopes.push(Scope::Let(scope));
|
||||||
|
let (vals, ret) = { f(self)? };
|
||||||
|
self.scopes.pop();
|
||||||
|
assert_eq!(keys.len(), vals.len());
|
||||||
|
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
|
||||||
|
for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
|
||||||
|
let thunk = *val.borrow(&self.token);
|
||||||
|
*handle.borrow_mut(&mut self.token) = thunk;
|
||||||
|
let id = ThunkId(base + i);
|
||||||
|
let ir_ref = self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
|
||||||
|
scope.add_binding(id, ir_ref);
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_param_scope<F, Ret>(&mut self, sym: StringId, f: F) -> Ret
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
self.scopes.push(Scope::Param {
|
||||||
|
sym,
|
||||||
|
abs_layer: self.thunk_scopes.len().try_into().expect("scope too deep!"),
|
||||||
|
});
|
||||||
|
let mut guard = ScopeGuard { ctx: self };
|
||||||
|
f(guard.as_ctx())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_with_scope<F, Ret>(&mut self, namespace: GhostRoMaybeThunkRef<'id, 'ir>, f: F) -> Ret
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
self.with_stack.push(namespace);
|
||||||
|
let ret = f(self);
|
||||||
|
self.with_stack.pop();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_thunk_scope<F, Ret>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
) -> (
|
||||||
|
Ret,
|
||||||
|
bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Ret,
|
||||||
|
{
|
||||||
|
if self.thunk_scopes.len() == u8::MAX as usize {
|
||||||
|
panic!("scope too deep!");
|
||||||
|
}
|
||||||
|
self.thunk_scopes.push(ThunkScope::new_in(self.bump));
|
||||||
|
let ret = f(self);
|
||||||
|
(
|
||||||
|
ret,
|
||||||
|
self.thunk_scopes
|
||||||
|
.pop()
|
||||||
|
.expect("no thunk scope left???")
|
||||||
|
.bindings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bump(&self) -> &'ir bumpalo::Bump {
|
||||||
|
self.bump
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
fn downgrade_toplevel(mut self, root: rnix::ast::Expr) -> Result<RawIrRef<'ir>> {
|
||||||
|
let body = root.downgrade(&mut self)?;
|
||||||
|
let thunks = self
|
||||||
|
.thunk_scopes
|
||||||
|
.pop()
|
||||||
|
.expect("no thunk scope left???")
|
||||||
|
.bindings;
|
||||||
|
Ok(Ir::freeze(
|
||||||
|
self.new_expr(Ir::TopLevel { body, thunks }),
|
||||||
|
self.token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThunkScope<'id, 'ir> {
|
||||||
|
bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
||||||
|
fn new_in(bump: &'ir Bump) -> Self {
|
||||||
|
Self {
|
||||||
|
bindings: bumpalo::collections::Vec::new_in(bump),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
|
||||||
|
self.bindings.push((id, ir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Scope<'ctx, 'id, 'ir> {
|
||||||
|
Global(&'ctx HashMap<StringId, MaybeThunk>),
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport {
|
||||||
|
keys: HashSet<StringId>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
|
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
||||||
|
Param {
|
||||||
|
sym: StringId,
|
||||||
|
abs_layer: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ExtraScope<'ctx> {
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport {
|
||||||
|
keys: HashSet<StringId>,
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> From<ExtraScope<'ctx>> for Scope<'ctx, '_, '_> {
|
||||||
|
fn from(value: ExtraScope<'ctx>) -> Self {
|
||||||
|
use ExtraScope::*;
|
||||||
|
match value {
|
||||||
|
ScopedImport { keys, slot_id } => Scope::ScopedImport { keys, slot_id },
|
||||||
|
Repl(scope) => Scope::Repl(scope),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
||||||
|
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> Drop for ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ctx.scopes.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
||||||
|
fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OwnedIr {
|
||||||
|
_bump: Bump,
|
||||||
|
ir: RawIrRef<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedIr {
|
||||||
|
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
||||||
|
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,8 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
use fix_common::StringId;
|
|
||||||
use ghost_cell::{GhostCell, GhostToken};
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
use num_enum::TryFromPrimitive as _;
|
|
||||||
use rnix::{TextRange, ast};
|
use rnix::{TextRange, ast};
|
||||||
use string_interner::DefaultStringInterner;
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
@@ -92,7 +90,7 @@ pub enum MaybeThunk {
|
|||||||
BuiltinConst(StringId),
|
BuiltinConst(StringId),
|
||||||
Builtins,
|
Builtins,
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding { slot_id: u32, sym: StringId },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Ref<'ir> {
|
pub trait Ref<'ir> {
|
||||||
@@ -213,17 +211,16 @@ pub enum Ir<'ir, R: RefExt<'ir> + ?Sized + 'ir> {
|
|||||||
},
|
},
|
||||||
MaybeThunk(R::MaybeThunkRef),
|
MaybeThunk(R::MaybeThunkRef),
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding {
|
||||||
|
sym: StringId,
|
||||||
|
slot_id: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ThunkId(pub usize);
|
pub struct ThunkId(pub usize);
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct SpanId(pub u32);
|
|
||||||
|
|
||||||
/// Represents a key in an attribute path.
|
/// Represents a key in an attribute path.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -295,7 +292,7 @@ pub fn new_global_env(
|
|||||||
global_env.insert(builtins_sym, MaybeThunk::Builtins);
|
global_env.insert(builtins_sym, MaybeThunk::Builtins);
|
||||||
|
|
||||||
for (idx, &(name, _)) in BUILTINS.iter().enumerate() {
|
for (idx, &(name, _)) in BUILTINS.iter().enumerate() {
|
||||||
let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible");
|
let id = BuiltinId::try_from(idx as u8).expect("infallible");
|
||||||
let name = StringId(strings.get_or_intern(name));
|
let name = StringId(strings.get_or_intern(name));
|
||||||
global_env.insert(name, MaybeThunk::Builtin(id));
|
global_env.insert(name, MaybeThunk::Builtin(id));
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use fix_common::Symbol;
|
|
||||||
use fix_error::{Error, Result, Source};
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{BuiltinId, Symbol};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_bytecode::{Const, InstructionPtr, Op, OperandType, PrimOpPhase};
|
||||||
use fix_common::StringId;
|
use fix_lang::{BUILTINS, StringId};
|
||||||
use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
use string_interner::Symbol as _;
|
use string_interner::Symbol as _;
|
||||||
|
|
||||||
pub mod disassembler;
|
mod context;
|
||||||
|
pub mod ir;
|
||||||
pub struct InstructionPtr(pub usize);
|
pub use context::{CodeState, ExtraScope};
|
||||||
|
pub use fix_bytecode::disassembler;
|
||||||
|
pub use ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
||||||
|
|
||||||
pub trait BytecodeContext {
|
pub trait BytecodeContext {
|
||||||
fn intern_string(&mut self, s: &str) -> StringId;
|
fn intern_string(&mut self, s: &str) -> StringId;
|
||||||
@@ -17,86 +17,6 @@ pub trait BytecodeContext {
|
|||||||
fn get_code_mut(&mut self) -> &mut Vec<u8>;
|
fn get_code_mut(&mut self) -> &mut Vec<u8>;
|
||||||
fn add_constant(&mut self, val: Const) -> u32;
|
fn add_constant(&mut self, val: Const) -> u32;
|
||||||
fn current_source_dir(&mut self) -> StringId;
|
fn current_source_dir(&mut self) -> StringId;
|
||||||
fn current_scope_slot(&self) -> Option<u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
pub enum Op {
|
|
||||||
PushSmi,
|
|
||||||
PushBigInt,
|
|
||||||
PushFloat,
|
|
||||||
PushString,
|
|
||||||
PushNull,
|
|
||||||
PushTrue,
|
|
||||||
PushFalse,
|
|
||||||
|
|
||||||
LoadLocal,
|
|
||||||
LoadOuter,
|
|
||||||
StoreLocal,
|
|
||||||
AllocLocals,
|
|
||||||
|
|
||||||
MakeThunk,
|
|
||||||
MakeClosure,
|
|
||||||
MakePatternClosure,
|
|
||||||
|
|
||||||
Call,
|
|
||||||
DispatchPrimOp,
|
|
||||||
|
|
||||||
MakeAttrs,
|
|
||||||
MakeEmptyAttrs,
|
|
||||||
SelectStatic,
|
|
||||||
SelectDynamic,
|
|
||||||
HasAttrPathStatic,
|
|
||||||
HasAttrPathDynamic,
|
|
||||||
HasAttrStatic,
|
|
||||||
HasAttrDynamic,
|
|
||||||
HasAttrResolve,
|
|
||||||
JumpIfSelectSucceeded,
|
|
||||||
JumpIfSelectFailed,
|
|
||||||
|
|
||||||
MakeList,
|
|
||||||
MakeEmptyList,
|
|
||||||
|
|
||||||
OpAdd,
|
|
||||||
OpSub,
|
|
||||||
OpMul,
|
|
||||||
OpDiv,
|
|
||||||
OpEq,
|
|
||||||
OpNeq,
|
|
||||||
OpLt,
|
|
||||||
OpGt,
|
|
||||||
OpLeq,
|
|
||||||
OpGeq,
|
|
||||||
OpConcat,
|
|
||||||
OpUpdate,
|
|
||||||
|
|
||||||
OpNeg,
|
|
||||||
OpNot,
|
|
||||||
|
|
||||||
JumpIfFalse,
|
|
||||||
JumpIfTrue,
|
|
||||||
Jump,
|
|
||||||
|
|
||||||
CoerceToString,
|
|
||||||
|
|
||||||
ConcatStrings,
|
|
||||||
ResolvePath,
|
|
||||||
|
|
||||||
Assert,
|
|
||||||
|
|
||||||
LookupWith,
|
|
||||||
|
|
||||||
LoadBuiltins,
|
|
||||||
LoadBuiltin,
|
|
||||||
|
|
||||||
LoadReplBinding,
|
|
||||||
LoadScopedBinding,
|
|
||||||
|
|
||||||
Return,
|
|
||||||
|
|
||||||
Illegal,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScopeInfo {
|
struct ScopeInfo {
|
||||||
@@ -109,39 +29,6 @@ struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
|
|||||||
scope_stack: Vec<ScopeInfo>,
|
scope_stack: Vec<ScopeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
pub enum OperandType {
|
|
||||||
Const,
|
|
||||||
BigInt,
|
|
||||||
Local,
|
|
||||||
BuiltinConst,
|
|
||||||
Builtins,
|
|
||||||
ReplBinding,
|
|
||||||
ScopedImportBinding,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Const {
|
|
||||||
Smi(i32),
|
|
||||||
Float(f64),
|
|
||||||
Bool(bool),
|
|
||||||
String(StringId),
|
|
||||||
Path(StringId),
|
|
||||||
PrimOp {
|
|
||||||
id: BuiltinId,
|
|
||||||
arity: u8,
|
|
||||||
dispatch_ip: u32,
|
|
||||||
},
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
|
||||||
pub enum AttrKeyType {
|
|
||||||
Static,
|
|
||||||
Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum InlineOperand {
|
pub enum InlineOperand {
|
||||||
Const(Const),
|
Const(Const),
|
||||||
BigInt(i64),
|
BigInt(i64),
|
||||||
@@ -149,7 +36,7 @@ pub enum InlineOperand {
|
|||||||
BuiltinConst(StringId),
|
BuiltinConst(StringId),
|
||||||
Builtins,
|
Builtins,
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding { id: StringId, slot_id: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
||||||
@@ -193,13 +80,15 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
InlineOperand::Const(Const::PrimOp {
|
InlineOperand::Const(Const::PrimOp {
|
||||||
id,
|
id,
|
||||||
arity,
|
arity,
|
||||||
dispatch_ip: id.entry_phase().ip(),
|
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
|
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
|
||||||
Builtins => InlineOperand::Builtins,
|
Builtins => InlineOperand::Builtins,
|
||||||
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
||||||
ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id),
|
ScopedImportBinding { slot_id, sym: id } => {
|
||||||
|
InlineOperand::ScopedImportBinding { slot_id, id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,13 +121,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_u8(OperandType::ReplBinding as u8);
|
self.emit_u8(OperandType::ReplBinding as u8);
|
||||||
self.emit_str_id(id);
|
self.emit_str_id(id);
|
||||||
}
|
}
|
||||||
ScopedImportBinding(id) => {
|
ScopedImportBinding { id, slot_id } => {
|
||||||
self.emit_u8(OperandType::ScopedImportBinding as u8);
|
self.emit_u8(OperandType::ScopedImportBinding as u8);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(id);
|
self.emit_str_id(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,14 +440,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LoadReplBinding);
|
self.emit_op(Op::LoadReplBinding);
|
||||||
self.emit_str_id(name);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
&Ir::ScopedImportBinding(name) => {
|
&Ir::ScopedImportBinding { sym, slot_id } => {
|
||||||
self.emit_op(Op::LoadScopedBinding);
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
self.emit_str_id(sym);
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(name);
|
|
||||||
}
|
}
|
||||||
Ir::WithLookup { sym, namespaces } => {
|
Ir::WithLookup { sym, namespaces } => {
|
||||||
// counter
|
// counter
|
||||||
@@ -629,14 +510,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LoadReplBinding);
|
self.emit_op(Op::LoadReplBinding);
|
||||||
self.emit_str_id(name);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
ScopedImportBinding(name) => {
|
ScopedImportBinding { slot_id, sym } => {
|
||||||
self.emit_op(Op::LoadScopedBinding);
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
let slot = self
|
self.emit_u32(slot_id);
|
||||||
.ctx
|
self.emit_str_id(sym);
|
||||||
.current_scope_slot()
|
|
||||||
.expect("ScopedImportBinding outside scoped compilation");
|
|
||||||
self.emit_u32(slot);
|
|
||||||
self.emit_str_id(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,7 +652,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
fn emit_attrset(
|
fn emit_attrset(
|
||||||
&mut self,
|
&mut self,
|
||||||
stcs: &fix_ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
|
stcs: &ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
|
||||||
dyns: &[(RawIrRef<'_>, &MaybeThunk, TextRange)],
|
dyns: &[(RawIrRef<'_>, &MaybeThunk, TextRange)],
|
||||||
) {
|
) {
|
||||||
if stcs.is_empty() && dyns.is_empty() {
|
if stcs.is_empty() && dyns.is_empty() {
|
||||||
@@ -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,9 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-common"
|
name = "fix-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gc-arena = { workspace = true }
|
|
||||||
string-interner = { workspace = true }
|
|
||||||
ere = { workspace = true }
|
ere = { workspace = true }
|
||||||
|
gc-arena = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
string-interner = { workspace = true }
|
||||||
@@ -4,6 +4,128 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use gc_arena::Collect;
|
use gc_arena::Collect;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
|
macro_rules! define_builtins {
|
||||||
|
($(($name:literal, $variant:ident, $arity:expr)),* $(,)?) => {
|
||||||
|
pub const BUILTINS: &[(&str, u8)] = &[
|
||||||
|
$(($name, $arity),)*
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Collect)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub enum BuiltinId {
|
||||||
|
$($variant,)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
define_builtins! {
|
||||||
|
("abort", Abort, 1),
|
||||||
|
("__add", Add, 2),
|
||||||
|
("__addErrorContext", AddErrorContext, 2),
|
||||||
|
("__all", All, 2),
|
||||||
|
("__any", Any, 2),
|
||||||
|
("__appendContext", AppendContext, 2),
|
||||||
|
("__attrNames", AttrNames, 1),
|
||||||
|
("__attrValues", AttrValues, 1),
|
||||||
|
("baseNameOf", BaseNameOf, 1),
|
||||||
|
("__bitAnd", BitAnd, 2),
|
||||||
|
("__bitOr", BitOr, 2),
|
||||||
|
("__bitXor", BitXor, 2),
|
||||||
|
("break", Break, 1),
|
||||||
|
("__catAttrs", CatAttrs, 2),
|
||||||
|
("__ceil", Ceil, 1),
|
||||||
|
("__compareVersions", CompareVersions, 2),
|
||||||
|
("__concatLists", ConcatLists, 1),
|
||||||
|
("__concatMap", ConcatMap, 2),
|
||||||
|
("__concatStringsSep", ConcatStringsSep, 2),
|
||||||
|
("__convertHash", ConvertHash, 1),
|
||||||
|
("__deepSeq", DeepSeq, 2),
|
||||||
|
("derivation", Derivation, 1),
|
||||||
|
("derivationStrict", DerivationStrict, 1),
|
||||||
|
("dirOf", DirOf, 1),
|
||||||
|
("__div", Div, 2),
|
||||||
|
("__elem", Elem, 2),
|
||||||
|
("__elemAt", ElemAt, 2),
|
||||||
|
("fetchGit", FetchGit, 1),
|
||||||
|
("fetchMercurial", FetchMercurial, 1),
|
||||||
|
("fetchTarball", FetchTarball, 1),
|
||||||
|
("fetchTree", FetchTree, 1),
|
||||||
|
("__fetchurl", FetchUrl, 1),
|
||||||
|
("__filter", Filter, 2),
|
||||||
|
("__filterSource", FilterSource, 2),
|
||||||
|
("__findFile", FindFile, 2),
|
||||||
|
("__floor", Floor, 1),
|
||||||
|
("__foldl'", FoldlStrict, 3),
|
||||||
|
("__fromJSON", FromJSON, 1),
|
||||||
|
("fromTOML", FromTOML, 1),
|
||||||
|
("__functionArgs", FunctionArgs, 1),
|
||||||
|
("__genList", GenList, 2),
|
||||||
|
("__genericClosure", GenericClosure, 1),
|
||||||
|
("__getAttr", GetAttr, 2),
|
||||||
|
("__getContext", GetContext, 1),
|
||||||
|
("__getEnv", GetEnv, 1),
|
||||||
|
("__groupBy", GroupBy, 2),
|
||||||
|
("__hasAttr", HasAttr, 2),
|
||||||
|
("__hasContext", HasContext, 1),
|
||||||
|
("__hashFile", HashFile, 2),
|
||||||
|
("__hashString", HashString, 2),
|
||||||
|
("__head", Head, 1),
|
||||||
|
("import", Import, 1),
|
||||||
|
("__intersectAttrs", IntersectAttrs, 2),
|
||||||
|
("__isAttrs", IsAttrs, 1),
|
||||||
|
("__isBool", IsBool, 1),
|
||||||
|
("__isFloat", IsFloat, 1),
|
||||||
|
("__isFunction", IsFunction, 1),
|
||||||
|
("__isInt", IsInt, 1),
|
||||||
|
("__isList", IsList, 1),
|
||||||
|
("isNull", IsNull, 1),
|
||||||
|
("__isPath", IsPath, 1),
|
||||||
|
("__isString", IsString, 1),
|
||||||
|
("__length", Length, 1),
|
||||||
|
("__lessThan", LessThan, 2),
|
||||||
|
("__listToAttrs", ListToAttrs, 1),
|
||||||
|
("map", Map, 2),
|
||||||
|
("__mapAttrs", MapAttrs, 2),
|
||||||
|
("__match", Match, 2),
|
||||||
|
("__mul", Mul, 2),
|
||||||
|
("__parseDrvName", ParseDrvName, 1),
|
||||||
|
("__partition", Partition, 2),
|
||||||
|
("__path", Path, 1),
|
||||||
|
("__pathExists", PathExists, 1),
|
||||||
|
("placeholder", Placeholder, 1),
|
||||||
|
("__readDir", ReadDir, 1),
|
||||||
|
("__readFile", ReadFile, 1),
|
||||||
|
("__readFileType", ReadFileType, 1),
|
||||||
|
("removeAttrs", RemoveAttrs, 2),
|
||||||
|
("__replaceStrings", ReplaceStrings, 3),
|
||||||
|
("scopedImport", ScopedImport, 2),
|
||||||
|
("__seq", Seq, 2),
|
||||||
|
("__sort", Sort, 2),
|
||||||
|
("__split", Split, 2),
|
||||||
|
("__splitVersion", SplitVersion, 1),
|
||||||
|
("__storePath", StorePath, 1),
|
||||||
|
("__stringLength", StringLength, 1),
|
||||||
|
("__sub", Sub, 2),
|
||||||
|
("__substring", Substring, 3),
|
||||||
|
("__tail", Tail, 1),
|
||||||
|
("throw", Throw, 1),
|
||||||
|
("__toFile", ToFile, 2),
|
||||||
|
("__toJSON", ToJSON, 1),
|
||||||
|
("__toPath", ToPath, 1),
|
||||||
|
("toString", ToString, 1),
|
||||||
|
("__toXML", ToXML, 1),
|
||||||
|
("__trace", Trace, 2),
|
||||||
|
("__tryEval", TryEval, 1),
|
||||||
|
("__typeOf", TypeOf, 1),
|
||||||
|
("__unsafeDiscardStringContext", UnsafeDiscardStringContext, 1),
|
||||||
|
("__unsafeDiscardOutputDependency", UnsafeDiscardOutputDependency, 1),
|
||||||
|
("__unsafeGetAttrPos", UnsafeGetAttrPos, 2),
|
||||||
|
("__warn", Warn, 2),
|
||||||
|
("__zipAttrsWith", ZipAttrsWith, 2),
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Collect)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Collect)]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fix-primops"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gc-arena = { workspace = true }
|
|
||||||
hashbrown = { workspace = true }
|
|
||||||
num_enum = { workspace = true }
|
|
||||||
smallvec = { workspace = true }
|
|
||||||
string-interner = { workspace = true }
|
|
||||||
|
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fix-abstract-vm"
|
name = "fix-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gc-arena = { workspace = true }
|
gc-arena = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { 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-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
|
fix-lang = { path = "../fix-lang" }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use fix_common::StringId;
|
use fix_lang::StringId;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
use fix_codegen::InstructionPtr;
|
use fix_bytecode::InstructionPtr;
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Source;
|
use fix_error::Source;
|
||||||
|
use fix_lang::{self, BUILTINS, StringId};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AttrSet, Closure, ExtraScope, List, NixString, NixType, Null, Path, PrimOp, PrimOpApp, StaticValue, StrictValue, StringContext, Thunk, ThunkState, Value
|
AttrSet, Closure, ExtraScope, List, NixString, NixType, Null, Path, PrimOp, PrimOpApp,
|
||||||
|
StaticValue, StrictValue, StringContext, Thunk, ThunkState, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait VmContext {
|
pub trait VmContext {
|
||||||
@@ -38,7 +39,7 @@ pub trait VmRuntimeCtxExt: VmRuntimeCtx {
|
|||||||
/// Returns the string context attached to `val`, or `&[]` if `val` is
|
/// Returns the string context attached to `val`, or `&[]` if `val` is
|
||||||
/// either a non-string or a string without context.
|
/// either a non-string or a string without context.
|
||||||
fn get_string_context<'gc>(&self, val: StrictValue<'gc>) -> &'gc StringContext;
|
fn get_string_context<'gc>(&self, val: StrictValue<'gc>) -> &'gc StringContext;
|
||||||
fn convert_value(&self, val: Value) -> fix_common::Value;
|
fn convert_value(&self, val: Value) -> fix_lang::Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
||||||
@@ -83,18 +84,18 @@ impl<T: VmRuntimeCtx> VmRuntimeCtxExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(&self, val: Value) -> fix_common::Value {
|
fn convert_value(&self, val: Value) -> fix_lang::Value {
|
||||||
self.convert_value_with_seen(val, &mut HashSet::new())
|
self.convert_value_with_seen(val, &mut HashSet::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ConvertValueWithSeen: VmRuntimeCtx {
|
pub(crate) trait ConvertValueWithSeen: VmRuntimeCtx {
|
||||||
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value;
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_lang::Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
||||||
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_common::Value {
|
fn convert_value_with_seen(&self, val: Value, seen: &mut HashSet<u64>) -> fix_lang::Value {
|
||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
if let Some(i) = val.as_inline::<i32>() {
|
if let Some(i) = val.as_inline::<i32>() {
|
||||||
Value::Int(i as i64)
|
Value::Int(i as i64)
|
||||||
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
||||||
@@ -124,9 +125,9 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
for &(key, val) in attrs.entries.iter() {
|
for &(key, val) in attrs.entries.iter() {
|
||||||
let key = self.resolve_string(key).to_owned();
|
let key = self.resolve_string(key).to_owned();
|
||||||
let converted = self.convert_value_with_seen(val, seen);
|
let converted = self.convert_value_with_seen(val, seen);
|
||||||
map.insert(fix_common::Symbol::from(key), converted);
|
map.insert(fix_lang::Symbol::from(key), converted);
|
||||||
}
|
}
|
||||||
Value::AttrSet(fix_common::AttrSet::new(map))
|
Value::AttrSet(fix_lang::AttrSet::new(map))
|
||||||
} else if let Some(list) = val.as_gc::<List>() {
|
} else if let Some(list) = val.as_gc::<List>() {
|
||||||
let bits = val.to_bits();
|
let bits = val.to_bits();
|
||||||
if list.inner.borrow().is_empty() {
|
if list.inner.borrow().is_empty() {
|
||||||
@@ -142,7 +143,7 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
.copied()
|
.copied()
|
||||||
.map(|v| self.convert_value_with_seen(v, seen))
|
.map(|v| self.convert_value_with_seen(v, seen))
|
||||||
.collect();
|
.collect();
|
||||||
Value::List(fix_common::List::new(items))
|
Value::List(fix_lang::List::new(items))
|
||||||
} else if val.is::<Closure>() {
|
} else if val.is::<Closure>() {
|
||||||
Value::Func
|
Value::Func
|
||||||
} else if let Some(thunk) = val.as_gc::<Thunk>() {
|
} else if let Some(thunk) = val.as_gc::<Thunk>() {
|
||||||
@@ -152,10 +153,10 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
|
|||||||
Value::Thunk
|
Value::Thunk
|
||||||
}
|
}
|
||||||
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
} else if let Some(primop) = val.as_inline::<PrimOp>() {
|
||||||
let name = fix_builtins::BUILTINS[primop.id as usize].0;
|
let name = BUILTINS[primop.id as usize].0;
|
||||||
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
Value::PrimOp(name.strip_prefix("__").unwrap_or(name))
|
||||||
} else if let Some(app) = val.as_gc::<PrimOpApp>() {
|
} else if let Some(app) = val.as_gc::<PrimOpApp>() {
|
||||||
let name = fix_builtins::BUILTINS[app.primop.id as usize].0;
|
let name = BUILTINS[app.primop.id as usize].0;
|
||||||
Value::PrimOpApp(name.strip_prefix("__").unwrap_or(name))
|
Value::PrimOpApp(name.strip_prefix("__").unwrap_or(name))
|
||||||
} else {
|
} else {
|
||||||
Value::Null
|
Value::Null
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
mod boxing;
|
mod boxing;
|
||||||
mod bytecode_reader;
|
|
||||||
mod forced;
|
mod forced;
|
||||||
mod host;
|
mod host;
|
||||||
mod machine;
|
mod machine;
|
||||||
@@ -9,7 +8,7 @@ mod state;
|
|||||||
mod string_context;
|
mod string_context;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use bytecode_reader::*;
|
pub use fix_bytecode::{BytecodeReader, OperandData};
|
||||||
pub use forced::*;
|
pub use forced::*;
|
||||||
pub use host::*;
|
pub use host::*;
|
||||||
pub use machine::*;
|
pub use machine::*;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::{self, StringId};
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -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(
|
||||||
@@ -96,7 +100,7 @@ pub trait Machine<'gc> {
|
|||||||
cur.borrow().locals[idx as usize]
|
cur.borrow().locals[idx as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_ok(&mut self, val: fix_common::Value) -> Step;
|
fn finish_ok(&mut self, val: fix_lang::Value) -> Step;
|
||||||
fn finish_err(&mut self, err: Box<Error>) -> Step;
|
fn finish_err(&mut self, err: Box<Error>) -> Step;
|
||||||
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step;
|
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step;
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use fix_bytecode::OperandData;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
|
|
||||||
use crate::{AttrSet, Machine, OperandData, Value};
|
use crate::{AttrSet, Machine, Value, VmRuntimeCtx};
|
||||||
|
|
||||||
/// Resolve a decoded operand into a runtime [`Value`].
|
/// Resolve a decoded operand into a runtime [`Value`].
|
||||||
///
|
///
|
||||||
@@ -11,11 +12,12 @@ use crate::{AttrSet, Machine, OperandData, Value};
|
|||||||
pub fn resolve_operand<'gc, M: Machine<'gc>>(
|
pub fn resolve_operand<'gc, M: Machine<'gc>>(
|
||||||
op: &OperandData,
|
op: &OperandData,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
|
ctx: &impl VmRuntimeCtx,
|
||||||
m: &M,
|
m: &M,
|
||||||
) -> Value<'gc> {
|
) -> Value<'gc> {
|
||||||
use OperandData::*;
|
use OperandData::*;
|
||||||
match *op {
|
match *op {
|
||||||
Const(sv) => sv.into(),
|
Const(id) => ctx.get_const(id).into(),
|
||||||
BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
||||||
Local { layer, idx } => m.local(layer, idx),
|
Local { layer, idx } => m.local(layer, idx),
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
use gc_arena::{Collect, Gc};
|
use gc_arena::{Collect, Gc};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use crate::{GcEnv, StaticValue, Thunk};
|
use crate::{GcEnv, Thunk};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
@@ -87,13 +87,3 @@ pub enum ExtraScope {
|
|||||||
slot_id: u32,
|
slot_id: u32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OperandData {
|
|
||||||
Const(StaticValue),
|
|
||||||
BigInt(i64),
|
|
||||||
Local { layer: u8, idx: u32 },
|
|
||||||
BuiltinConst(StringId),
|
|
||||||
Builtins,
|
|
||||||
ReplBinding(StringId),
|
|
||||||
ScopedImportBinding { slot_id: u32, name: StringId },
|
|
||||||
}
|
|
||||||
@@ -89,7 +89,7 @@ impl<'a> IntoIterator for &'a mut StringContext {
|
|||||||
impl FromIterator<StringContextElem> for StringContext {
|
impl FromIterator<StringContextElem> for StringContext {
|
||||||
fn from_iter<T: IntoIterator<Item = StringContextElem>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = StringContextElem>>(iter: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: iter.into_iter().collect()
|
data: iter.into_iter().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,13 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fix_builtins::BuiltinId;
|
use fix_lang::*;
|
||||||
use fix_common::*;
|
|
||||||
use gc_arena::barrier::Unlock;
|
use gc_arena::barrier::Unlock;
|
||||||
use gc_arena::collect::Trace;
|
use gc_arena::collect::Trace;
|
||||||
use gc_arena::{Collect, Gc, GcRefLock, Mutation, RefLock};
|
use gc_arena::{Collect, Gc, GcRefLock, Mutation, RefLock};
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use string_interner::Symbol;
|
use string_interner::Symbol;
|
||||||
use string_interner::symbol::SymbolU32;
|
use string_interner::symbol::SymbolU32;
|
||||||
@@ -25,14 +21,12 @@ mod private {
|
|||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// TAG must be unique among all implementors.
|
/// [`Self::TAG`] must be unique among all implementors.
|
||||||
#[allow(private_interfaces)]
|
unsafe trait Storable: private::Cealed {
|
||||||
pub unsafe trait Storable: private::Cealed {
|
|
||||||
const TAG: RawTag;
|
const TAG: RawTag;
|
||||||
}
|
}
|
||||||
#[allow(private_bounds)]
|
trait InlineStorable: Storable + RawStore {}
|
||||||
pub trait InlineStorable: Storable + RawStore {}
|
trait GcStorable: Storable {}
|
||||||
pub trait GcStorable: Storable {}
|
|
||||||
|
|
||||||
macro_rules! define_value_types {
|
macro_rules! define_value_types {
|
||||||
(
|
(
|
||||||
@@ -40,7 +34,6 @@ macro_rules! define_value_types {
|
|||||||
gc { $($gtype:ty => $gtag:expr, $gname:literal;)* }
|
gc { $($gtype:ty => $gtag:expr, $gname:literal;)* }
|
||||||
) => {
|
) => {
|
||||||
$(
|
$(
|
||||||
#[allow(private_interfaces)]
|
|
||||||
unsafe impl Storable for $itype {
|
unsafe impl Storable for $itype {
|
||||||
const TAG: RawTag = $itag;
|
const TAG: RawTag = $itag;
|
||||||
}
|
}
|
||||||
@@ -48,7 +41,6 @@ macro_rules! define_value_types {
|
|||||||
impl private::Cealed for $itype {}
|
impl private::Cealed for $itype {}
|
||||||
)*
|
)*
|
||||||
$(
|
$(
|
||||||
#[allow(private_interfaces)]
|
|
||||||
unsafe impl Storable for $gtype {
|
unsafe impl Storable for $gtype {
|
||||||
const TAG: RawTag = $gtag;
|
const TAG: RawTag = $gtag;
|
||||||
}
|
}
|
||||||
@@ -117,16 +109,16 @@ define_value_types! {
|
|||||||
Null => RawTag::P3, "Null";
|
Null => RawTag::P3, "Null";
|
||||||
StringId => RawTag::P4, "SmallString";
|
StringId => RawTag::P4, "SmallString";
|
||||||
PrimOp => RawTag::P5, "PrimOp";
|
PrimOp => RawTag::P5, "PrimOp";
|
||||||
Path => RawTag::N6, "Path";
|
Path => RawTag::P6, "Path";
|
||||||
}
|
}
|
||||||
gc {
|
gc {
|
||||||
i64 => RawTag::P6, "BigInt";
|
i64 => RawTag::P7, "BigInt";
|
||||||
NixString => RawTag::P7, "String";
|
NixString => RawTag::N1, "String";
|
||||||
AttrSet<'_> => RawTag::N1, "AttrSet";
|
AttrSet<'_> => RawTag::N2, "AttrSet";
|
||||||
List<'_> => RawTag::N2, "List";
|
List<'_> => RawTag::N3, "List";
|
||||||
Thunk<'_> => RawTag::N3, "Thunk";
|
Thunk<'_> => RawTag::N4, "Thunk";
|
||||||
Closure<'_> => RawTag::N4, "Closure";
|
Closure<'_> => RawTag::N5, "Closure";
|
||||||
PrimOpApp<'_> => RawTag::N5, "PrimOpApp";
|
PrimOpApp<'_> => RawTag::N6, "PrimOpApp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,11 +178,13 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn new_inline<T: InlineStorable>(val: T) -> Self {
|
pub fn new_inline<T: InlineStorable>(val: T) -> Self {
|
||||||
Self::from_raw_value(RawValue::store(T::TAG, val))
|
Self::from_raw_value(RawValue::store(T::TAG, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn new_gc<T: GcStorable>(gc: Gc<'gc, T>) -> Self {
|
pub fn new_gc<T: GcStorable>(gc: Gc<'gc, T>) -> Self {
|
||||||
let ptr = Gc::as_ptr(gc);
|
let ptr = Gc::as_ptr(gc);
|
||||||
Self::from_raw_value(RawValue::store(T::TAG, ptr))
|
Self::from_raw_value(RawValue::store(T::TAG, ptr))
|
||||||
@@ -213,6 +207,7 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn is<T: Storable>(self) -> bool {
|
pub fn is<T: Storable>(self) -> bool {
|
||||||
self.tag() == Some(T::TAG)
|
self.tag() == Some(T::TAG)
|
||||||
}
|
}
|
||||||
@@ -225,6 +220,7 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
pub fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
Some(unsafe {
|
Some(unsafe {
|
||||||
@@ -237,6 +233,7 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn as_gc<T: GcStorable>(self) -> Option<Gc<'gc, T>> {
|
pub fn as_gc<T: GcStorable>(self) -> Option<Gc<'gc, T>> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
Some(unsafe {
|
Some(unsafe {
|
||||||
@@ -308,11 +305,13 @@ impl<'gc> Value<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn expect_inline<T: InlineStorable>(self) -> Result<T, NixType> {
|
pub fn expect_inline<T: InlineStorable>(self) -> Result<T, NixType> {
|
||||||
self.as_inline::<T>().ok_or_else(|| self.ty())
|
self.as_inline::<T>().ok_or_else(|| self.ty())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn expect_gc<T: GcStorable>(self) -> Result<Gc<'gc, T>, NixType> {
|
pub fn expect_gc<T: GcStorable>(self) -> Result<Gc<'gc, T>, NixType> {
|
||||||
self.as_gc::<T>().ok_or_else(|| self.ty())
|
self.as_gc::<T>().ok_or_else(|| self.ty())
|
||||||
}
|
}
|
||||||
@@ -351,6 +350,7 @@ impl StaticValue {
|
|||||||
Self(Value::new_float(val))
|
Self(Value::new_float(val))
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn new_inline<T: InlineStorable>(val: T) -> Self {
|
pub fn new_inline<T: InlineStorable>(val: T) -> Self {
|
||||||
Self(Value::new_inline(val))
|
Self(Value::new_inline(val))
|
||||||
}
|
}
|
||||||
@@ -367,6 +367,7 @@ impl StaticValue {
|
|||||||
self.0.is_float()
|
self.0.is_float()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn is<T: InlineStorable>(self) -> bool {
|
pub fn is<T: InlineStorable>(self) -> bool {
|
||||||
self.0.is::<T>()
|
self.0.is::<T>()
|
||||||
}
|
}
|
||||||
@@ -375,6 +376,7 @@ impl StaticValue {
|
|||||||
self.0.as_float()
|
self.0.as_float()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(private_bounds)]
|
||||||
pub fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
pub fn as_inline<T: InlineStorable>(self) -> Option<T> {
|
||||||
self.0.as_inline::<T>()
|
self.0.as_inline::<T>()
|
||||||
}
|
}
|
||||||
@@ -638,7 +640,7 @@ impl RawStore for PrimOp {
|
|||||||
fn from_val(value: &RawValue) -> Self {
|
fn from_val(value: &RawValue) -> Self {
|
||||||
let [id, arity, bytes @ ..] = *value.data();
|
let [id, arity, bytes @ ..] = *value.data();
|
||||||
Self {
|
Self {
|
||||||
id: BuiltinId::try_from_primitive(id).expect("invalid BuiltinId"),
|
id: BuiltinId::try_from(id).expect("invalid BuiltinId"),
|
||||||
arity,
|
arity,
|
||||||
dispatch_ip: u32::from_le_bytes(bytes),
|
dispatch_ip: u32::from_le_bytes(bytes),
|
||||||
}
|
}
|
||||||
+6
-12
@@ -3,22 +3,16 @@ 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 }
|
||||||
num_enum = { workspace = true }
|
|
||||||
smallvec = { workspace = true }
|
smallvec = { workspace = true }
|
||||||
string-interner = { workspace = true }
|
|
||||||
likely_stable = { workspace = true }
|
|
||||||
sptr = "0.3"
|
|
||||||
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
sysinfo = { version = "0.38", default-features = false, features = ["system"] }
|
||||||
|
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
fix-primops = { path = "../fix-primops" }
|
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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -202,16 +202,16 @@ macro_rules! table {
|
|||||||
impl<'gc, C: VmRuntimeCtx> DispatchTable<'gc, C> {
|
impl<'gc, C: VmRuntimeCtx> DispatchTable<'gc, C> {
|
||||||
pub(crate) const NEW: Self = {
|
pub(crate) const NEW: Self = {
|
||||||
let mut arr: [OpFn<'gc, C>; 256] = [op_illegal; 256];
|
let mut arr: [OpFn<'gc, C>; 256] = [op_illegal; 256];
|
||||||
$( arr[fix_codegen::Op::$variant as usize] = $fn; )*
|
$( arr[fix_bytecode::Op::$variant as usize] = $fn; )*
|
||||||
DispatchTable(arr)
|
DispatchTable(arr)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exhaustiveness check: fails to compile if `fix_codegen::Op` gains,
|
// Exhaustiveness check: fails to compile if `fix_bytecode::Op` gains,
|
||||||
// loses, or renames a variant that isn't wired up above.
|
// loses, or renames a variant that isn't wired up above.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const _: fn(fix_codegen::Op) = |op| match op {
|
const _: fn(fix_bytecode::Op) = |op| match op {
|
||||||
$( fix_codegen::Op::$variant => (), )*
|
$( fix_bytecode::Op::$variant => (), )*
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use fix_abstract_vm::*;
|
use fix_runtime::*;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_add(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
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_abstract_vm::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,143 +107,151 @@ 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)?;
|
||||||
fix_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)?;
|
||||||
fix_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),
|
||||||
},
|
},
|
||||||
)));
|
)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
rhs: StrictValue<'gc>,
|
rhs: StrictValue<'gc>,
|
||||||
) -> crate::VmResult<()> {
|
) -> crate::VmResult<()> {
|
||||||
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) {
|
||||||
let ord = match (a, b) {
|
let ord = match (a, b) {
|
||||||
(NixNum::Int(a), NixNum::Int(b)) => a.cmp(&b),
|
(NixNum::Int(a), NixNum::Int(b)) => a.cmp(&b),
|
||||||
@@ -244,27 +263,30 @@ 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
|
||||||
Err(crate::vm_err("cannot compare these types"))
|
Err(crate::vm_err(format!(
|
||||||
}
|
"cannot compare {} with {}",
|
||||||
|
lhs.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>() {
|
||||||
Some(NixNum::Int(i as i64))
|
Some(NixNum::Int(i64::from(i)))
|
||||||
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
} else if let Some(gc_i) = val.as_gc::<i64>() {
|
||||||
Some(NixNum::Int(*gc_i))
|
Some(NixNum::Int(*gc_i))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_abstract_vm::{resolve_operand, *};
|
use fix_bytecode::PrimOpPhase;
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{resolve_operand, *};
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -8,29 +8,28 @@ use crate::{
|
|||||||
VmRuntimeCtxExt,
|
VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'gc> crate::Vm<'gc> {
|
#[inline(always)]
|
||||||
#[inline(always)]
|
pub(crate) fn call<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn call(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
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,78 +82,78 @@ 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()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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(ctx), mc, 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 {
|
||||||
fix_primops::dispatch_primop(self, ctx, reader, mc)
|
crate::primops::dispatch_primop(m, ctx, reader, mc)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
|
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_make_thunk(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let thunk = Gc::new(
|
let thunk = Gc::new(
|
||||||
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 {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let n_locals = reader.read_u32();
|
let n_locals = reader.read_u32();
|
||||||
let closure = Gc::new(
|
let closure = Gc::new(
|
||||||
@@ -34,20 +34,20 @@ 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 {
|
||||||
let entry_point = reader.read_u32();
|
let entry_point = reader.read_u32();
|
||||||
let n_locals = reader.read_u32();
|
let n_locals = reader.read_u32();
|
||||||
let req_count = reader.read_u16() as usize;
|
let req_count = reader.read_u16() as usize;
|
||||||
@@ -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_abstract_vm::{NixType, resolve_operand};
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
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,48 +8,47 @@ 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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_make_attrs(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
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 static_count = reader.read_u32() as usize;
|
let static_count = reader.read_u32() as usize;
|
||||||
let dynamic_count = reader.read_u32() as usize;
|
let dynamic_count = reader.read_u32() as usize;
|
||||||
|
|
||||||
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(ctx), mc, 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(ctx), mc, 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,73 +57,73 @@ 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>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skip the rest of a **Select** attrpath after a missing attribute.
|
/// Skip the rest of a **Select** attrpath after a missing attribute.
|
||||||
/// 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<'_>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
use fix_codegen::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
loop {
|
loop {
|
||||||
match reader.read_op() {
|
match reader.read_op() {
|
||||||
SelectStatic => {
|
SelectStatic => {
|
||||||
@@ -143,17 +142,16 @@ 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")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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_codegen::Op::*;
|
use fix_bytecode::Op::*;
|
||||||
loop {
|
loop {
|
||||||
match reader.read_op() {
|
match reader.read_op() {
|
||||||
HasAttrPathStatic => {
|
HasAttrPathStatic => {
|
||||||
@@ -182,46 +180,46 @@ 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>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
let key = reader.read_string_id();
|
||||||
|
|
||||||
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))
|
||||||
@@ -269,23 +273,23 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader.set_pc(reader.pc() + 1);
|
reader.set_pc(reader.pc() + 1);
|
||||||
|
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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))
|
||||||
@@ -295,26 +299,26 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader.set_pc(reader.pc() + 1);
|
reader.set_pc(reader.pc() + 1);
|
||||||
|
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let count = reader.read_u32() as usize;
|
let count = reader.read_u32() as usize;
|
||||||
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
items.push(resolve_operand(&reader.read_operand_data(ctx), mc, 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_make_empty_list<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
|
m.push(m.empty_list());
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_make_empty_list(&mut self) -> Step {
|
|
||||||
self.push(self.empty_list);
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,58 @@
|
|||||||
use fix_abstract_vm::*;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::*;
|
||||||
use gc_arena::Mutation;
|
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_jump_if_false(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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);
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_push_float<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
|
let val = reader.read_f64();
|
||||||
|
m.push(Value::new_float(val));
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_push_string<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
|
let sid = reader.read_string_id();
|
||||||
|
m.push(Value::new_inline(sid));
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_push_null<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
|
m.push(Value::new_inline(crate::Null));
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_push_true<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
|
m.push(Value::new_inline(true));
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn op_push_false<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
|
m.push(Value::new_inline(false));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
|
||||||
let val = reader.read_f64();
|
|
||||||
self.push(Value::new_float(val));
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
|
||||||
let sid = reader.read_string_id();
|
|
||||||
self.push(Value::new_inline(sid));
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_push_null(&mut self) -> Step {
|
|
||||||
self.push(Value::new_inline(crate::Null));
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_push_true(&mut self) -> Step {
|
|
||||||
self.push(Value::new_inline(true));
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn op_push_false(&mut self) -> Step {
|
|
||||||
self.push(Value::new_inline(false));
|
|
||||||
Step::Continue(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +1,102 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
AttrSet, NixString, Path, StrictValue, StringContext, canon_path_str
|
|
||||||
};
|
|
||||||
use fix_builtins::BuiltinId;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use num_enum::TryFromPrimitive;
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
|
use fix_runtime::{
|
||||||
|
AttrSet, Machine, MachineExt, NixString, Path, StrictValue, StringContext, canon_path_str,
|
||||||
|
};
|
||||||
|
|
||||||
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<'gc, M: Machine<'gc>>(m: &mut M) -> Step {
|
||||||
pub(crate) fn op_load_builtins(&mut self) -> Step {
|
m.push(m.builtins());
|
||||||
self.push(self.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>>(
|
||||||
let Ok(id) = BuiltinId::try_from_primitive(reader.read_u8())
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
) -> Step {
|
||||||
|
let Ok(id) = BuiltinId::try_from(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: fix_builtins::BUILTINS[id as usize].1,
|
arity: BUILTINS[id as usize].1,
|
||||||
dispatch_ip: id.entry_phase().ip(),
|
dispatch_ip: PrimOpPhase::entry_for_builtin(id).ip(),
|
||||||
}));
|
}));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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)
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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());
|
||||||
}
|
}
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let count = reader.read_u16() as usize;
|
let count = reader.read_u16() as usize;
|
||||||
let _force_string = reader.read_u8() != 0;
|
let _force_string = reader.read_u8() != 0;
|
||||||
|
|
||||||
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() {
|
||||||
@@ -110,7 +107,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 {
|
||||||
@@ -121,36 +118,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()
|
||||||
)));
|
)));
|
||||||
@@ -158,12 +155,11 @@ 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>> {
|
||||||
|
|||||||
@@ -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,56 @@
|
|||||||
|
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
|
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>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
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>>(
|
||||||
&mut self,
|
m: &mut M,
|
||||||
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(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
use fix_abstract_vm::{resolve_operand, *};
|
|
||||||
use fix_common::Symbol;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_lang::Symbol;
|
||||||
|
use fix_runtime::{resolve_operand, *};
|
||||||
use smallvec::SmallVec;
|
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<'gc, M: Machine<'gc>>(
|
||||||
pub(crate) fn op_lookup_with(
|
m: &mut M,
|
||||||
&mut self,
|
|
||||||
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(ctx), mc, 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(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-211
@@ -7,20 +7,19 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_builtins::{BUILTINS, BuiltinId};
|
use fix_bytecode::{InstructionPtr, PrimOpPhase};
|
||||||
use fix_codegen::InstructionPtr;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::{Error, Result, Source};
|
use fix_error::{Error, Result, Source};
|
||||||
|
use fix_lang::{BUILTINS, BuiltinId, StringId};
|
||||||
use gc_arena::metrics::Pacing;
|
use gc_arena::metrics::Pacing;
|
||||||
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
#[cfg(feature = "tailcall")]
|
#[cfg(feature = "tailcall")]
|
||||||
mod dispatch_tailcall;
|
mod dispatch_tailcall;
|
||||||
pub use fix_abstract_vm::*;
|
pub use fix_runtime::*;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
|
mod primops;
|
||||||
|
|
||||||
type VmResult<T> = std::result::Result<T, VmError>;
|
type VmResult<T> = std::result::Result<T, VmError>;
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ pub struct Vm<'gc> {
|
|||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
|
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
result: Option<Result<fix_common::Value>>,
|
result: Option<Result<fix_lang::Value>>,
|
||||||
|
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
pending_load: Option<PendingLoad>,
|
pending_load: Option<PendingLoad>,
|
||||||
@@ -58,10 +57,10 @@ fn init_builtins<'gc>(mc: &Mutation<'gc>, ctx: &mut impl VmRuntimeCtx) -> Value<
|
|||||||
let mut entries = SmallVec::with_capacity(BUILTINS.len());
|
let mut entries = SmallVec::with_capacity(BUILTINS.len());
|
||||||
|
|
||||||
for (idx, &(name, arity)) in BUILTINS.iter().enumerate() {
|
for (idx, &(name, arity)) in BUILTINS.iter().enumerate() {
|
||||||
let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible");
|
let id = BuiltinId::try_from(idx as u8).expect("infallible");
|
||||||
let name = name.strip_prefix("__").unwrap_or(name);
|
let name = name.strip_prefix("__").unwrap_or(name);
|
||||||
let name = ctx.intern_string(name);
|
let name = ctx.intern_string(name);
|
||||||
let dispatch_ip = id.entry_phase().ip();
|
let dispatch_ip = PrimOpPhase::entry_for_builtin(id).ip();
|
||||||
entries.push((
|
entries.push((
|
||||||
name,
|
name,
|
||||||
Value::new_inline(PrimOp {
|
Value::new_inline(PrimOp {
|
||||||
@@ -137,45 +136,20 @@ impl<'gc> Vm<'gc> {
|
|||||||
functor_sym: ctx.intern_string("__functor"),
|
functor_sym: ctx.intern_string("__functor"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||||
fn finish_ok(&mut self, val: fix_common::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())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 +158,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 +166,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 +185,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 +236,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 +245,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)]
|
||||||
@@ -409,18 +284,23 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish_ok(&mut self, val: fix_common::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)]
|
||||||
@@ -481,7 +361,7 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
|||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
Continue { pc: usize },
|
Continue { pc: usize },
|
||||||
Done(Result<fix_common::Value>),
|
Done(Result<fix_lang::Value>),
|
||||||
LoadFile(PendingLoad),
|
LoadFile(PendingLoad),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +384,7 @@ impl Vm<'_> {
|
|||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
ip: InstructionPtr,
|
ip: InstructionPtr,
|
||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
let (code, runtime) = ctx.split();
|
let (code, runtime) = ctx.split();
|
||||||
let mut arena: Arena<Rootable![Vm<'_>]> = Arena::new(|mc| Vm::new(force_mode, mc, runtime));
|
let mut arena: Arena<Rootable![Vm<'_>]> = Arena::new(|mc| Vm::new(force_mode, mc, runtime));
|
||||||
arena.metrics().set_pacing(Pacing {
|
arena.metrics().set_pacing(Pacing {
|
||||||
@@ -589,7 +469,8 @@ impl<'gc> Vm<'gc> {
|
|||||||
pc: usize,
|
pc: usize,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Action {
|
) -> Action {
|
||||||
use fix_codegen::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 +484,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!(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
//! `builtins.unsafeDiscardStringContext`,
|
//! `builtins.unsafeDiscardStringContext`,
|
||||||
//! `builtins.unsafeDiscardOutputDependency`.
|
//! `builtins.unsafeDiscardOutputDependency`.
|
||||||
//!
|
//!
|
||||||
//! See `fix-abstract-vm/src/string_context.rs` for the
|
//! See `fix-runtime/src/string_context.rs` for the
|
||||||
//! `StringContextElem` type.
|
//! `StringContextElem` type.
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, List as VmList, Machine, MachineExt, NixString, NixType, Step,
|
AttrSet, BytecodeReader, List as VmList, Machine, MachineExt, NixString, NixType, Step,
|
||||||
StrictValue, StringContext, StringContextElem, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
StrictValue, StringContext, StringContextElem, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, Closure, Env, List, Machine, MachineExt, Step, StrictValue, Value,
|
AttrSet, BytecodeReader, Closure, Env, List, Machine, MachineExt, Step, StrictValue, Value,
|
||||||
VmRuntimeCtx, VmRuntimeCtxExt,
|
VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
BytecodeReader, Machine, MachineExt, NixString, NixType, Path, Step, StrictValue, Value,
|
BytecodeReader, Machine, MachineExt, NixString, NixType, Path, Step, StrictValue, Value,
|
||||||
VmRuntimeCtx,
|
VmRuntimeCtx,
|
||||||
};
|
};
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn to_string<'gc, M: Machine<'gc>>(
|
pub fn to_string<'gc, M: Machine<'gc>>(
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, BytecodeReader, CallFrame, List, Machine, MachineExt, NixNum, Null, Path, Step,
|
AttrSet, BytecodeReader, CallFrame, List, Machine, MachineExt, NixNum, Null, Path, Step,
|
||||||
StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -30,10 +30,8 @@ pub fn start_eq<'gc, M: Machine<'gc>>(
|
|||||||
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseAttrs(a, b) => {
|
ShallowEq::RecurseAttrs(a, b) => {
|
||||||
let lhs_init: SmallVec<[Value<'gc>; 4]> =
|
let lhs_init: SmallVec<[Value<'gc>; 4]> = a.entries.iter().map(|&(_, v)| v).collect();
|
||||||
a.entries.iter().map(|&(_, v)| v).collect();
|
let rhs_init: SmallVec<[Value<'gc>; 4]> = b.entries.iter().map(|&(_, v)| v).collect();
|
||||||
let rhs_init: SmallVec<[Value<'gc>; 4]> =
|
|
||||||
b.entries.iter().map(|&(_, v)| v).collect();
|
|
||||||
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
enter_eq_machine(m, reader, mc, negate, lhs_init, rhs_init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +114,12 @@ fn apply_pair<'gc, M: Machine<'gc>>(
|
|||||||
m.replace(2, Value::new_inline(false));
|
m.replace(2, Value::new_inline(false));
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseList(la, lb) => {
|
ShallowEq::RecurseList(la, lb) => {
|
||||||
extend_queues(m, mc, la.inner.borrow().iter().copied(), lb.inner.borrow().iter().copied());
|
extend_queues(
|
||||||
|
m,
|
||||||
|
mc,
|
||||||
|
la.inner.borrow().iter().copied(),
|
||||||
|
lb.inner.borrow().iter().copied(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ShallowEq::RecurseAttrs(a, b) => {
|
ShallowEq::RecurseAttrs(a, b) => {
|
||||||
extend_queues(
|
extend_queues(
|
||||||
@@ -230,9 +233,5 @@ fn shallow_eq<'gc>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bool_outcome<'gc>(b: bool) -> ShallowEq<'gc> {
|
fn bool_outcome<'gc>(b: bool) -> ShallowEq<'gc> {
|
||||||
if b {
|
if b { ShallowEq::True } else { ShallowEq::False }
|
||||||
ShallowEq::True
|
|
||||||
} else {
|
|
||||||
ShallowEq::False
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
|
use fix_error::Error;
|
||||||
|
use fix_lang::StringId;
|
||||||
|
use fix_runtime::{
|
||||||
AttrSet, Break, BytecodeReader, CallFrame, Machine, MachineExt, Path, PendingLoad,
|
AttrSet, Break, BytecodeReader, CallFrame, Machine, MachineExt, Path, PendingLoad,
|
||||||
PendingScope, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt, canon_path_str,
|
PendingScope, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt, canon_path_str,
|
||||||
};
|
};
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_common::StringId;
|
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_bytecode::PrimOpPhase;
|
||||||
BytecodeReader, List, Machine, MachineExt, NixType, Step, StrictValue, Value,
|
use fix_runtime::{BytecodeReader, List, Machine, MachineExt, NixType, Step, StrictValue, Value};
|
||||||
};
|
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn filter_force_list<'gc, M: Machine<'gc>>(
|
pub fn filter_force_list<'gc, M: Machine<'gc>>(
|
||||||
@@ -16,6 +14,7 @@ pub fn filter_force_list<'gc, M: Machine<'gc>>(
|
|||||||
};
|
};
|
||||||
if list.inner.borrow().is_empty() {
|
if list.inner.borrow().is_empty() {
|
||||||
let val = m.pop();
|
let val = m.pop();
|
||||||
|
let _pred = m.pop();
|
||||||
return m.return_from_primop(val, reader);
|
return m.return_from_primop(val, reader);
|
||||||
}
|
}
|
||||||
// prepare stack layout: [ pred list idx acc ]
|
// prepare stack layout: [ pred list idx acc ]
|
||||||
@@ -164,3 +163,121 @@ pub fn foldl_strict_update<'gc, M: Machine<'gc>>(
|
|||||||
reader.set_pc(PrimOpPhase::FoldlStrictCall1.ip() as usize);
|
reader.set_pc(PrimOpPhase::FoldlStrictCall1.ip() as usize);
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_entry<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
m.force_slot(0, reader, mc)?;
|
||||||
|
let list = match m.peek_forced(0).expect_gc::<List>() {
|
||||||
|
Ok(list) => list,
|
||||||
|
Err(got) => return m.finish_type_err(NixType::List, got),
|
||||||
|
};
|
||||||
|
// FIXME: force callable
|
||||||
|
m.force_slot(1, reader, mc)?;
|
||||||
|
if list.inner.borrow().is_empty() {
|
||||||
|
let _list = m.pop();
|
||||||
|
let _pred = m.pop();
|
||||||
|
return m.return_from_primop(Value::new_inline(true), reader);
|
||||||
|
}
|
||||||
|
// prepare stack layout: [ pred list idx ]
|
||||||
|
m.push(Value::new_inline(0));
|
||||||
|
reader.set_pc(PrimOpPhase::AllCallPred.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_call_pred<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let pred = m.peek_forced(2);
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let idx = m.peek(0).as_inline::<i32>().unwrap();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let elem = m.peek_forced(1).as_gc::<List>().unwrap().inner.borrow()[idx as usize];
|
||||||
|
m.push(pred.relax());
|
||||||
|
m.call(reader, mc, elem, PrimOpPhase::AllCheck.ip() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_check<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let ret = m.force_and_retry::<bool>(reader, mc)?;
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let idx = m.peek(0).as_inline::<i32>().unwrap();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let list = m.peek_forced(1).as_gc::<List>().unwrap();
|
||||||
|
let list = list.inner.borrow();
|
||||||
|
if idx as usize == list.len() - 1 || !ret {
|
||||||
|
let _ = m.pop(); // idx
|
||||||
|
let _ = m.pop(); // list
|
||||||
|
let _ = m.pop(); // pred
|
||||||
|
return m.return_from_primop(Value::new_inline(ret), reader);
|
||||||
|
}
|
||||||
|
m.replace(0, Value::new_inline(idx + 1));
|
||||||
|
reader.set_pc(PrimOpPhase::AllCallPred.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_entry<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
m.force_slot(0, reader, mc)?;
|
||||||
|
let list = match m.peek_forced(0).expect_gc::<List>() {
|
||||||
|
Ok(list) => list,
|
||||||
|
Err(got) => return m.finish_type_err(NixType::List, got),
|
||||||
|
};
|
||||||
|
// FIXME: force callable
|
||||||
|
m.force_slot(1, reader, mc)?;
|
||||||
|
if list.inner.borrow().is_empty() {
|
||||||
|
let _list = m.pop();
|
||||||
|
let _pred = m.pop();
|
||||||
|
return m.return_from_primop(Value::new_inline(false), reader);
|
||||||
|
}
|
||||||
|
// prepare stack layout: [ pred list idx ]
|
||||||
|
m.push(Value::new_inline(0));
|
||||||
|
reader.set_pc(PrimOpPhase::AnyCallPred.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_call_pred<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let pred = m.peek_forced(2);
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let idx = m.peek(0).as_inline::<i32>().unwrap();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let elem = m.peek_forced(1).as_gc::<List>().unwrap().inner.borrow()[idx as usize];
|
||||||
|
m.push(pred.relax());
|
||||||
|
m.call(reader, mc, elem, PrimOpPhase::AnyCheck.ip() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_check<'gc, M: Machine<'gc>>(
|
||||||
|
m: &mut M,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let ret = m.force_and_retry::<bool>(reader, mc)?;
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let idx = m.peek(0).as_inline::<i32>().unwrap();
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let list = m.peek_forced(1).as_gc::<List>().unwrap();
|
||||||
|
let list = list.inner.borrow();
|
||||||
|
if idx as usize == list.len() - 1 || ret {
|
||||||
|
let _ = m.pop(); // idx
|
||||||
|
let _ = m.pop(); // list
|
||||||
|
let _ = m.pop(); // pred
|
||||||
|
return m.return_from_primop(Value::new_inline(ret), reader);
|
||||||
|
}
|
||||||
|
m.replace(0, Value::new_inline(idx + 1));
|
||||||
|
reader.set_pc(PrimOpPhase::AnyCallPred.ip() as usize);
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
@@ -10,9 +10,9 @@ pub use context::*;
|
|||||||
pub use control::*;
|
pub use control::*;
|
||||||
pub use conv::*;
|
pub use conv::*;
|
||||||
pub use eq::*;
|
pub use eq::*;
|
||||||
use fix_abstract_vm::{BytecodeReader, Machine, Step, VmRuntimeCtx};
|
use fix_bytecode::PrimOpPhase;
|
||||||
use fix_builtins::PrimOpPhase;
|
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{BytecodeReader, Machine, Step, VmRuntimeCtx};
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
pub use io::*;
|
pub use io::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
@@ -33,6 +33,14 @@ pub fn dispatch_primop<'gc, M: Machine<'gc>>(
|
|||||||
match phase {
|
match phase {
|
||||||
Abort => abort(m, ctx, reader, mc),
|
Abort => abort(m, ctx, reader, mc),
|
||||||
|
|
||||||
|
All => all_entry(m, reader, mc),
|
||||||
|
AllCallPred => all_call_pred(m, reader, mc),
|
||||||
|
AllCheck => all_check(m, reader, mc),
|
||||||
|
|
||||||
|
Any => any_entry(m, reader, mc),
|
||||||
|
AnyCallPred => any_call_pred(m, reader, mc),
|
||||||
|
AnyCheck => any_check(m, reader, mc),
|
||||||
|
|
||||||
DeepSeq => deep_seq_force_top(m, reader, mc),
|
DeepSeq => deep_seq_force_top(m, reader, mc),
|
||||||
DeepSeqPush => deep_seq_push(m, reader, mc),
|
DeepSeqPush => deep_seq_push(m, reader, mc),
|
||||||
DeepSeqLoop => deep_seq_loop(m, reader, mc),
|
DeepSeqLoop => deep_seq_loop(m, reader, mc),
|
||||||
@@ -78,7 +86,9 @@ pub fn dispatch_primop<'gc, M: Machine<'gc>>(
|
|||||||
AppendContextEntryForced => append_context_entry_forced(m, ctx, reader, mc),
|
AppendContextEntryForced => append_context_entry_forced(m, ctx, reader, mc),
|
||||||
AppendContextOutputsForced => append_context_outputs_forced(m, ctx, reader, mc),
|
AppendContextOutputsForced => append_context_outputs_forced(m, ctx, reader, mc),
|
||||||
AppendContextOutputElementLoop => append_context_output_element_loop(m, ctx, reader, mc),
|
AppendContextOutputElementLoop => append_context_output_element_loop(m, ctx, reader, mc),
|
||||||
AppendContextOutputElementForced => append_context_output_element_forced(m, ctx, reader, mc),
|
AppendContextOutputElementForced => {
|
||||||
|
append_context_output_element_forced(m, ctx, reader, mc)
|
||||||
|
}
|
||||||
UnsafeDiscardStringContext => unsafe_discard_string_context(m, ctx, reader, mc),
|
UnsafeDiscardStringContext => unsafe_discard_string_context(m, ctx, reader, mc),
|
||||||
UnsafeDiscardOutputDependency => unsafe_discard_output_dependency(m, ctx, reader, mc),
|
UnsafeDiscardOutputDependency => unsafe_discard_output_dependency(m, ctx, reader, mc),
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use fix_abstract_vm::{
|
use fix_error::Error;
|
||||||
|
use fix_runtime::{
|
||||||
BytecodeReader, Machine, MachineExt, Path, Step, StrictValue, Value, VmRuntimeCtx,
|
BytecodeReader, Machine, MachineExt, Path, Step, StrictValue, Value, VmRuntimeCtx,
|
||||||
VmRuntimeCtxExt, canon_path_str,
|
VmRuntimeCtxExt, canon_path_str,
|
||||||
};
|
};
|
||||||
use fix_error::Error;
|
|
||||||
use gc_arena::Mutation;
|
use gc_arena::Mutation;
|
||||||
|
|
||||||
pub fn to_path<'gc, M: Machine<'gc>>(
|
pub fn to_path<'gc, M: Machine<'gc>>(
|
||||||
+17
-24
@@ -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,43 +29,24 @@ 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 }
|
||||||
string-interner = { workspace = true }
|
string-interner = { workspace = true }
|
||||||
|
|
||||||
# Memory Management
|
|
||||||
bumpalo = { workspace = true }
|
|
||||||
|
|
||||||
rnix = { workspace = true }
|
|
||||||
|
|
||||||
ere = { workspace = true }
|
ere = { workspace = true }
|
||||||
ghost-cell = { workspace = true }
|
|
||||||
|
|
||||||
fix-abstract-vm = { path = "../fix-abstract-vm" }
|
fix-bytecode = { path = "../fix-bytecode" }
|
||||||
fix-builtins = { path = "../fix-builtins" }
|
fix-compiler = { path = "../fix-compiler" }
|
||||||
fix-common = { path = "../fix-common" }
|
|
||||||
fix-codegen = { path = "../fix-codegen" }
|
|
||||||
fix-error = { path = "../fix-error" }
|
fix-error = { path = "../fix-error" }
|
||||||
fix-ir = { path = "../fix-ir" }
|
fix-lang = { path = "../fix-lang" }
|
||||||
|
fix-runtime = { path = "../fix-runtime" }
|
||||||
fix-vm = { path = "../fix-vm" }
|
fix-vm = { path = "../fix-vm" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Result, Source};
|
use fix_error::{Result, Source};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Evaluator::new()
|
Evaluator::new()
|
||||||
|
|||||||
+18
-589
@@ -1,20 +1,13 @@
|
|||||||
#![warn(clippy::unwrap_used)]
|
#![warn(clippy::unwrap_used)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use fix_bytecode::InstructionPtr;
|
||||||
use fix_abstract_vm::{ForceMode, StaticValue, VmCode, VmContext, VmRuntimeCtx};
|
use fix_bytecode::disassembler::{Disassembler, DisassemblerContext};
|
||||||
use fix_builtins::PrimOpPhase;
|
use fix_compiler::{CodeState, ExtraScope};
|
||||||
use fix_codegen::disassembler::{Disassembler, DisassemblerContext};
|
use fix_error::{Result, Source};
|
||||||
use fix_codegen::{BytecodeContext, InstructionPtr, Op};
|
use fix_lang::StringId;
|
||||||
use fix_common::{StringId, Symbol};
|
use fix_runtime::{ForceMode, StaticValue, VmCode, VmContext, VmRuntimeCtx};
|
||||||
use fix_error::{Error, Result, Source};
|
|
||||||
use fix_ir::downgrade::{Downgrade as _, DowngradeContext};
|
|
||||||
use fix_ir::{
|
|
||||||
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
|
|
||||||
ThunkId,
|
|
||||||
};
|
|
||||||
use fix_vm::Vm;
|
use fix_vm::Vm;
|
||||||
use ghost_cell::{GhostCell, GhostToken};
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use string_interner::{DefaultStringInterner, Symbol as _};
|
use string_interner::{DefaultStringInterner, Symbol as _};
|
||||||
|
|
||||||
@@ -29,15 +22,6 @@ pub struct RuntimeState {
|
|||||||
pub constants: Constants,
|
pub constants: Constants,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CodeState {
|
|
||||||
pub bytecode: Vec<u8>,
|
|
||||||
pub sources: Vec<Source>,
|
|
||||||
pub spans: Vec<(usize, rnix::TextRange)>,
|
|
||||||
pub thunk_count: usize,
|
|
||||||
pub global_env: HashMap<StringId, MaybeThunk>,
|
|
||||||
pub current_scope_slot: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Evaluator {
|
pub struct Evaluator {
|
||||||
pub runtime: RuntimeState,
|
pub runtime: RuntimeState,
|
||||||
pub code: CodeState,
|
pub code: CodeState,
|
||||||
@@ -52,37 +36,25 @@ impl Default for Evaluator {
|
|||||||
impl Evaluator {
|
impl Evaluator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut strings = DefaultStringInterner::new();
|
let mut strings = DefaultStringInterner::new();
|
||||||
let global_env = fix_ir::new_global_env(&mut strings);
|
let code = CodeState::new(&mut strings);
|
||||||
let mut bytecode = Vec::with_capacity(PrimOpPhase::Illegal as usize * 2);
|
|
||||||
for phase in 0..=PrimOpPhase::Illegal as u8 {
|
|
||||||
bytecode.push(Op::DispatchPrimOp as u8);
|
|
||||||
bytecode.push(phase);
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
runtime: RuntimeState {
|
runtime: RuntimeState {
|
||||||
strings,
|
strings,
|
||||||
constants: Constants::default(),
|
constants: Constants::default(),
|
||||||
},
|
},
|
||||||
code: CodeState {
|
code,
|
||||||
sources: Vec::new(),
|
|
||||||
spans: Vec::new(),
|
|
||||||
thunk_count: 0,
|
|
||||||
bytecode,
|
|
||||||
global_env,
|
|
||||||
current_scope_slot: None,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::AsIs)
|
self.do_eval(source, None, ForceMode::AsIs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_shallow(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval_shallow(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::Shallow)
|
self.do_eval(source, None, ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_deep(&mut self, source: Source) -> Result<fix_common::Value> {
|
pub fn eval_deep(&mut self, source: Source) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, None, ForceMode::Deep)
|
self.do_eval(source, None, ForceMode::Deep)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +62,7 @@ impl Evaluator {
|
|||||||
&mut self,
|
&mut self,
|
||||||
source: Source,
|
source: Source,
|
||||||
scope: &HashSet<StringId>,
|
scope: &HashSet<StringId>,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
self.do_eval(source, Some(ExtraScope::Repl(scope)), ForceMode::Shallow)
|
self.do_eval(source, Some(ExtraScope::Repl(scope)), ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +71,10 @@ impl Evaluator {
|
|||||||
source: Source,
|
source: Source,
|
||||||
extra_scope: Option<ExtraScope<'ctx>>,
|
extra_scope: Option<ExtraScope<'ctx>>,
|
||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
let ip = {
|
let ip = self
|
||||||
let mut compiler = CompilerCtx {
|
.code
|
||||||
code: &mut self.code,
|
.compile_bytecode(source, extra_scope, &mut self.runtime)?;
|
||||||
runtime: &mut self.runtime,
|
|
||||||
};
|
|
||||||
compiler.compile_bytecode(source, extra_scope)?
|
|
||||||
};
|
|
||||||
Vm::run(self, ip, force_mode)
|
Vm::run(self, ip, force_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,16 +83,12 @@ impl Evaluator {
|
|||||||
_ident: &str,
|
_ident: &str,
|
||||||
_expr: &str,
|
_expr: &str,
|
||||||
_scope: &mut HashSet<StringId>,
|
_scope: &mut HashSet<StringId>,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_lang::Value> {
|
||||||
todo!("add_binding")
|
todo!("add_binding")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_bytecode(&mut self, source: Source) -> Result<InstructionPtr> {
|
pub fn compile_bytecode(&mut self, source: Source) -> Result<InstructionPtr> {
|
||||||
let mut compiler = CompilerCtx {
|
self.code.compile_bytecode(source, None, &mut self.runtime)
|
||||||
code: &mut self.code,
|
|
||||||
runtime: &mut self.runtime,
|
|
||||||
};
|
|
||||||
compiler.compile_bytecode(source, None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble_colored(&self, ip: InstructionPtr) -> String {
|
pub fn disassemble_colored(&self, ip: InstructionPtr) -> String {
|
||||||
@@ -149,151 +113,12 @@ impl VmRuntimeCtx for RuntimeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmCode for CodeState {
|
|
||||||
fn bytecode(&self) -> &[u8] {
|
|
||||||
&self.bytecode
|
|
||||||
}
|
|
||||||
fn compile_with_scope(
|
|
||||||
&mut self,
|
|
||||||
source: Source,
|
|
||||||
extra_scope: Option<fix_abstract_vm::ExtraScope>,
|
|
||||||
runtime: &mut impl VmRuntimeCtx,
|
|
||||||
) -> Result<InstructionPtr> {
|
|
||||||
let mut compiler = CompilerCtx {
|
|
||||||
code: self,
|
|
||||||
runtime,
|
|
||||||
};
|
|
||||||
let extra = extra_scope.map(|s| match s {
|
|
||||||
fix_abstract_vm::ExtraScope::ScopedImport { keys, slot_id } => {
|
|
||||||
ExtraScope::ScopedImport { keys, slot_id }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
compiler.compile_bytecode(source, extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VmContext for Evaluator {
|
impl VmContext for Evaluator {
|
||||||
fn split(&mut self) -> (&mut impl VmCode, &mut impl VmRuntimeCtx) {
|
fn split(&mut self) -> (&mut impl VmCode, &mut impl VmRuntimeCtx) {
|
||||||
(&mut self.code, &mut self.runtime)
|
(&mut self.code, &mut self.runtime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CompilerCtx<'a, R: VmRuntimeCtx> {
|
|
||||||
code: &'a mut CodeState,
|
|
||||||
runtime: &'a mut R,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
|
||||||
fn compile_bytecode(
|
|
||||||
&mut self,
|
|
||||||
source: Source,
|
|
||||||
extra_scope: Option<ExtraScope>,
|
|
||||||
) -> Result<InstructionPtr> {
|
|
||||||
let prev_scope_slot = self.code.current_scope_slot;
|
|
||||||
self.code.current_scope_slot = match &extra_scope {
|
|
||||||
Some(ExtraScope::ScopedImport { slot_id, .. }) => Some(*slot_id),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let result = (|| -> Result<InstructionPtr> {
|
|
||||||
let root = self.downgrade(source, extra_scope)?;
|
|
||||||
let ip = fix_codegen::compile_bytecode(root.as_ref(), self);
|
|
||||||
Ok(ip)
|
|
||||||
})();
|
|
||||||
self.code.current_scope_slot = prev_scope_slot;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downgrade(&mut self, source: Source, extra_scope: Option<ExtraScope>) -> Result<OwnedIr> {
|
|
||||||
tracing::debug!("Parsing Nix expression");
|
|
||||||
|
|
||||||
self.code.sources.push(source.clone());
|
|
||||||
|
|
||||||
let root = rnix::Root::parse(&source.src);
|
|
||||||
handle_parse_error(root.errors(), source.clone()).map_or(Ok(()), Err)?;
|
|
||||||
|
|
||||||
tracing::debug!("Downgrading Nix expression");
|
|
||||||
let expr = root
|
|
||||||
.tree()
|
|
||||||
.expr()
|
|
||||||
.ok_or_else(|| Error::parse_error("unexpected EOF".into()))?;
|
|
||||||
let bump = Bump::new();
|
|
||||||
GhostToken::new(|token| {
|
|
||||||
let downgrade_ctx = DowngradeCtx::new(
|
|
||||||
&bump,
|
|
||||||
token,
|
|
||||||
self.runtime,
|
|
||||||
&self.code.global_env,
|
|
||||||
extra_scope.map(Into::into),
|
|
||||||
&mut self.code.thunk_count,
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
let ir = downgrade_ctx.downgrade_toplevel(expr)?;
|
|
||||||
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
|
||||||
Ok(OwnedIr { _bump: bump, ir })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
|
||||||
fn intern_string(&mut self, s: &str) -> StringId {
|
|
||||||
self.runtime.intern_string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_span(&mut self, range: rnix::TextRange) -> u32 {
|
|
||||||
let id = self.code.spans.len();
|
|
||||||
let source_id = self
|
|
||||||
.code
|
|
||||||
.sources
|
|
||||||
.len()
|
|
||||||
.checked_sub(1)
|
|
||||||
.expect("current_source not set");
|
|
||||||
self.code.spans.push((source_id, range));
|
|
||||||
id as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_code(&self) -> &[u8] {
|
|
||||||
&self.code.bytecode
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_code_mut(&mut self) -> &mut Vec<u8> {
|
|
||||||
&mut self.code.bytecode
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_constant(&mut self, val: fix_codegen::Const) -> u32 {
|
|
||||||
use fix_codegen::Const::*;
|
|
||||||
let val = match val {
|
|
||||||
Smi(x) => StaticValue::new_inline(x),
|
|
||||||
Float(x) => StaticValue::new_float(x),
|
|
||||||
Bool(x) => StaticValue::new_inline(x),
|
|
||||||
String(x) => StaticValue::new_inline(x),
|
|
||||||
Path(_) => todo!("path value type"),
|
|
||||||
PrimOp {
|
|
||||||
id,
|
|
||||||
arity,
|
|
||||||
dispatch_ip,
|
|
||||||
} => StaticValue::new_primop(id, arity, dispatch_ip),
|
|
||||||
Null => StaticValue::default(),
|
|
||||||
};
|
|
||||||
self.runtime.add_const(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_source_dir(&mut self) -> StringId {
|
|
||||||
let dir = self
|
|
||||||
.code
|
|
||||||
.sources
|
|
||||||
.last()
|
|
||||||
.expect("current_source not set")
|
|
||||||
.get_dir()
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
self.runtime.intern_string(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_scope_slot(&self) -> Option<u32> {
|
|
||||||
self.code.current_scope_slot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Constants {
|
pub struct Constants {
|
||||||
data: Vec<StaticValue>,
|
data: Vec<StaticValue>,
|
||||||
@@ -315,402 +140,6 @@ impl Constants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_error_span(error: &rnix::ParseError) -> Option<rnix::TextRange> {
|
|
||||||
use rnix::ParseError::*;
|
|
||||||
match error {
|
|
||||||
Unexpected(range)
|
|
||||||
| UnexpectedExtra(range)
|
|
||||||
| UnexpectedWanted(_, range, _)
|
|
||||||
| UnexpectedDoubleBind(range)
|
|
||||||
| DuplicatedArgs(range, _) => Some(*range),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_parse_error<'a>(
|
|
||||||
errors: impl IntoIterator<Item = &'a rnix::ParseError>,
|
|
||||||
source: Source,
|
|
||||||
) -> Option<Box<Error>> {
|
|
||||||
for err in errors {
|
|
||||||
if let Some(span) = parse_error_span(err) {
|
|
||||||
return Some(
|
|
||||||
Error::parse_error(err.to_string())
|
|
||||||
.with_source(source)
|
|
||||||
.with_span(span),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DowngradeCtx<'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
|
||||||
bump: &'ir Bump,
|
|
||||||
token: GhostToken<'id>,
|
|
||||||
runtime: &'ctx mut R,
|
|
||||||
source: Source,
|
|
||||||
scopes: Vec<Scope<'ctx, 'id, 'ir>>,
|
|
||||||
with_stack: Vec<GhostRoMaybeThunkRef<'id, 'ir>>,
|
|
||||||
arg_count: u32,
|
|
||||||
thunk_count: &'ctx mut usize,
|
|
||||||
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
fn new(
|
|
||||||
bump: &'ir Bump,
|
|
||||||
token: GhostToken<'id>,
|
|
||||||
runtime: &'ctx mut R,
|
|
||||||
global: &'ctx HashMap<StringId, MaybeThunk>,
|
|
||||||
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
|
|
||||||
thunk_count: &'ctx mut usize,
|
|
||||||
source: Source,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
bump,
|
|
||||||
token,
|
|
||||||
runtime,
|
|
||||||
source,
|
|
||||||
scopes: std::iter::once(Scope::Global(global))
|
|
||||||
.chain(extra_scope)
|
|
||||||
.collect(),
|
|
||||||
thunk_count,
|
|
||||||
arg_count: 0,
|
|
||||||
with_stack: Vec::new(),
|
|
||||||
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
|
||||||
for DowngradeCtx<'ctx, 'id, 'ir, R>
|
|
||||||
{
|
|
||||||
fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
|
|
||||||
self.bump.alloc(GhostCell::new(expr).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
|
|
||||||
use MaybeThunk::*;
|
|
||||||
let expr = (|| {
|
|
||||||
let expr = match *ir.borrow(&self.token) {
|
|
||||||
Ir::Builtin(x) => Builtin(x),
|
|
||||||
Ir::Int(x) => Int(x),
|
|
||||||
Ir::Float(x) => Float(x),
|
|
||||||
Ir::Bool(x) => Bool(x),
|
|
||||||
Ir::Str(x) => Str(x),
|
|
||||||
Ir::Arg { layer } => Arg { layer },
|
|
||||||
Ir::Builtins => Builtins,
|
|
||||||
Ir::Null => Null,
|
|
||||||
Ir::MaybeThunk(thunk) => return Some(thunk),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
Some(self.bump.alloc(GhostCell::new(expr).into()))
|
|
||||||
})();
|
|
||||||
if let Some(thunk) = expr {
|
|
||||||
return thunk;
|
|
||||||
}
|
|
||||||
let id = ThunkId(*self.thunk_count);
|
|
||||||
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
|
||||||
self.thunk_scopes
|
|
||||||
.last_mut()
|
|
||||||
.expect("no active cache scope")
|
|
||||||
.add_binding(id, ir);
|
|
||||||
self.bump.alloc(GhostCell::new(Thunk(id)).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
|
||||||
self.runtime.intern_string(sym)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_sym(&self, id: StringId) -> Symbol<'_> {
|
|
||||||
self.runtime.resolve_string(id).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(
|
|
||||||
&mut self,
|
|
||||||
sym: StringId,
|
|
||||||
span: rnix::TextRange,
|
|
||||||
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
|
|
||||||
for scope in self.scopes.iter().rev() {
|
|
||||||
match scope {
|
|
||||||
&Scope::Global(global_scope) => {
|
|
||||||
if let Some(expr) = global_scope.get(&sym) {
|
|
||||||
return Ok(expr.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&Scope::Repl(repl_bindings) => {
|
|
||||||
if repl_bindings.contains(&sym) {
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Scope::ScopedImport { keys, .. } => {
|
|
||||||
if keys.contains(&sym) {
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym)).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Scope::Let(let_scope) => {
|
|
||||||
if let Some(&expr) = let_scope.get(&sym) {
|
|
||||||
return Ok(expr.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&Scope::Param {
|
|
||||||
sym: param_sym,
|
|
||||||
abs_layer,
|
|
||||||
} => {
|
|
||||||
if param_sym == sym {
|
|
||||||
let layers: u8 =
|
|
||||||
self.thunk_scopes.len().try_into().expect("scope too deep!");
|
|
||||||
let layer = layers - abs_layer;
|
|
||||||
return Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.with_stack.is_empty() {
|
|
||||||
let id = ThunkId(*self.thunk_count);
|
|
||||||
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
|
|
||||||
let mut namespaces =
|
|
||||||
bumpalo::collections::Vec::with_capacity_in(self.with_stack.len(), self.bump);
|
|
||||||
namespaces.extend(self.with_stack.iter().rev().copied());
|
|
||||||
let body = self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(Ir::WithLookup { sym, namespaces }).into());
|
|
||||||
self.thunk_scopes
|
|
||||||
.last_mut()
|
|
||||||
.expect("no active thunk scope")
|
|
||||||
.add_binding(id, body);
|
|
||||||
Ok(self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Thunk(id)).into()))
|
|
||||||
} else {
|
|
||||||
Err(Error::downgrade_error(
|
|
||||||
format!("'{}' not found", self.resolve_sym(sym)),
|
|
||||||
self.get_current_source(),
|
|
||||||
span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_source(&self) -> Source {
|
|
||||||
self.source.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
&mut Self,
|
|
||||||
) -> Result<(
|
|
||||||
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
|
|
||||||
Ret,
|
|
||||||
)>,
|
|
||||||
{
|
|
||||||
let base = *self.thunk_count;
|
|
||||||
*self.thunk_count = self
|
|
||||||
.thunk_count
|
|
||||||
.checked_add(keys.len())
|
|
||||||
.expect("thunk id overflow");
|
|
||||||
let handles = (base..base + keys.len())
|
|
||||||
.map(|id| {
|
|
||||||
&*self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
|
|
||||||
self.scopes.push(Scope::Let(scope));
|
|
||||||
let (vals, ret) = { f(self)? };
|
|
||||||
self.scopes.pop();
|
|
||||||
assert_eq!(keys.len(), vals.len());
|
|
||||||
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
|
|
||||||
for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
|
|
||||||
let thunk = *val.borrow(&self.token);
|
|
||||||
*handle.borrow_mut(&mut self.token) = thunk;
|
|
||||||
let id = ThunkId(base + i);
|
|
||||||
let ir_ref = self
|
|
||||||
.bump
|
|
||||||
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
|
|
||||||
scope.add_binding(id, ir_ref);
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_param_scope<F, Ret>(&mut self, sym: StringId, f: F) -> Ret
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
self.scopes.push(Scope::Param {
|
|
||||||
sym,
|
|
||||||
abs_layer: self.thunk_scopes.len().try_into().expect("scope too deep!"),
|
|
||||||
});
|
|
||||||
let mut guard = ScopeGuard { ctx: self };
|
|
||||||
f(guard.as_ctx())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_with_scope<F, Ret>(&mut self, namespace: GhostRoMaybeThunkRef<'id, 'ir>, f: F) -> Ret
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
self.with_stack.push(namespace);
|
|
||||||
let ret = f(self);
|
|
||||||
self.with_stack.pop();
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_thunk_scope<F, Ret>(
|
|
||||||
&mut self,
|
|
||||||
f: F,
|
|
||||||
) -> (
|
|
||||||
Ret,
|
|
||||||
bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Ret,
|
|
||||||
{
|
|
||||||
if self.thunk_scopes.len() == u8::MAX as usize {
|
|
||||||
panic!("scope too deep!");
|
|
||||||
}
|
|
||||||
self.thunk_scopes.push(ThunkScope::new_in(self.bump));
|
|
||||||
let ret = f(self);
|
|
||||||
(
|
|
||||||
ret,
|
|
||||||
self.thunk_scopes
|
|
||||||
.pop()
|
|
||||||
.expect("no thunk scope left???")
|
|
||||||
.bindings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bump(&self) -> &'ir bumpalo::Bump {
|
|
||||||
self.bump
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
fn downgrade_toplevel(mut self, root: rnix::ast::Expr) -> Result<RawIrRef<'ir>> {
|
|
||||||
let body = root.downgrade(&mut self)?;
|
|
||||||
let thunks = self
|
|
||||||
.thunk_scopes
|
|
||||||
.pop()
|
|
||||||
.expect("no thunk scope left???")
|
|
||||||
.bindings;
|
|
||||||
Ok(Ir::freeze(
|
|
||||||
self.new_expr(Ir::TopLevel { body, thunks }),
|
|
||||||
self.token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ThunkScope<'id, 'ir> {
|
|
||||||
bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
|
||||||
fn new_in(bump: &'ir Bump) -> Self {
|
|
||||||
Self {
|
|
||||||
bindings: bumpalo::collections::Vec::new_in(bump),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
|
|
||||||
self.bindings.push((id, ir));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend_bindings(
|
|
||||||
&mut self,
|
|
||||||
iter: impl IntoIterator<Item = (ThunkId, GhostRoIrRef<'id, 'ir>)>,
|
|
||||||
) {
|
|
||||||
self.bindings.extend(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Scope<'ctx, 'id, 'ir> {
|
|
||||||
Global(&'ctx HashMap<StringId, MaybeThunk>),
|
|
||||||
Repl(&'ctx HashSet<StringId>),
|
|
||||||
ScopedImport {
|
|
||||||
keys: HashSet<StringId>,
|
|
||||||
slot_id: u32,
|
|
||||||
},
|
|
||||||
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
|
||||||
Param {
|
|
||||||
sym: StringId,
|
|
||||||
abs_layer: u8,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ExtraScope<'ctx> {
|
|
||||||
Repl(&'ctx HashSet<StringId>),
|
|
||||||
ScopedImport {
|
|
||||||
keys: HashSet<StringId>,
|
|
||||||
slot_id: u32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<ExtraScope<'ctx>> for Scope<'ctx, '_, '_> {
|
|
||||||
fn from(value: ExtraScope<'ctx>) -> Self {
|
|
||||||
use ExtraScope::*;
|
|
||||||
match value {
|
|
||||||
ScopedImport { keys, slot_id } => Scope::ScopedImport { keys, slot_id },
|
|
||||||
Repl(scope) => Scope::Repl(scope),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
|
||||||
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> Drop for ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.ctx.scopes.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> ScopeGuard<'a, 'ctx, 'id, 'ir, R> {
|
|
||||||
fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|
||||||
self.ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OwnedIr {
|
|
||||||
_bump: Bump,
|
|
||||||
ir: RawIrRef<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnedIr {
|
|
||||||
/// # Safety
|
|
||||||
/// `ir` must be an allocation backed by `bump`. The reference's
|
|
||||||
/// lifetime is extended to `'static` as a placeholder; the stored IR
|
|
||||||
/// must only be re-borrowed via [`OwnedIr::as_ref`], which narrows
|
|
||||||
/// the lifetime back to that of the `&self` borrow. Moving `bump`
|
|
||||||
/// into the struct keeps the underlying allocation live for the
|
|
||||||
/// lifetime of the `OwnedIr`.
|
|
||||||
unsafe fn new(ir: RawIrRef<'_>, bump: Bump) -> Self {
|
|
||||||
Self {
|
|
||||||
_bump: bump,
|
|
||||||
// SAFETY: see function docs - caller guarantees `ir` is in `bump`,
|
|
||||||
// and the `'static` lifetime is a placeholder narrowed by `as_ref`.
|
|
||||||
ir: unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
|
||||||
// SAFETY: narrows the placeholder `'static` lifetime stored in
|
|
||||||
// `self.ir` down to `'ir = &'ir self`. Lifetime shortening is
|
|
||||||
// logically sound for covariant positions; the transmute is only
|
|
||||||
// needed because `RawRef<'ir>` carries `'ir` through a GAT
|
|
||||||
// (`Ref::Ref<T>`), which prevents the compiler from inferring
|
|
||||||
// covariance automatically. The bump arena that backs the IR is
|
|
||||||
// owned by `self._bump`, so the data is live for at least `'ir`.
|
|
||||||
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisassemblerContext for Evaluator {
|
impl DisassemblerContext for Evaluator {
|
||||||
fn get_code(&self) -> &[u8] {
|
fn get_code(&self) -> &[u8] {
|
||||||
&self.code.bytecode
|
&self.code.bytecode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::{eval_deep, eval_deep_result};
|
use crate::utils::{eval_deep, eval_deep_result};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::Source;
|
use fix_error::Source;
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Source, SourceType};
|
use fix_error::{Source, SourceType};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
fn get_lang_dir() -> PathBuf {
|
fn get_lang_dir() -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tests/lang")
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tests/lang")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
use fix_lang::Value;
|
||||||
|
|
||||||
use crate::utils::eval_result;
|
use crate::utils::eval_result;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use fix::Evaluator;
|
use fix::Evaluator;
|
||||||
use fix_common::Value;
|
|
||||||
use fix_error::{Result, Source};
|
use fix_error::{Result, Source};
|
||||||
|
use fix_lang::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Evaluator::new()
|
Evaluator::new()
|
||||||
|
|||||||
Generated
+17
-18
@@ -45,16 +45,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778445566,
|
"lastModified": 1778446047,
|
||||||
"narHash": "sha256-oQvcadh2BCkrog+SGrG6YffKJrveYpjj3TdQJWaKhaM=",
|
"narHash": "sha256-oQvcadh2BCkrog+SGrG6YffKJrveYpjj3TdQJWaKhaM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"rev": "2499dedd70744dba1815875b854818a3019e9e4c",
|
"rev": "f2bc12af1a6369648aac41041ceeaa0b866599c6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"ref": "staging-2.1.0",
|
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -67,11 +66,11 @@
|
|||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778919578,
|
"lastModified": 1781343250,
|
||||||
"narHash": "sha256-+z+jgTly48gsAiX8rOe/vs8C/2G4vdCpcEtqMJUpFqw=",
|
"narHash": "sha256-KBJktAwDG9+10j2wMfvOVkBEhZr3yS769xoqqdFI62s=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "ecd6d4ff22cfdb1339b2915455a2ff4dc85bf52e",
|
"rev": "aad7d8bb6936d473c2b9d1a5846a1fe1bc92767a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -127,11 +126,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778929997,
|
"lastModified": 1781330261,
|
||||||
"narHash": "sha256-iAfbBUHBbR0N4DFqFWr4Jtmpc1YcOK7kpVM4f0MK1V8=",
|
"narHash": "sha256-2fFAGel2VVXr5mwrTXldqXva2ng3T3HHxyuBKRIxauI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "llm-agents.nix",
|
"repo": "llm-agents.nix",
|
||||||
"rev": "0da8f0313c9f68c155e0932f880fc1913e7be846",
|
"rev": "24ec6b7b1ddf8896ac8df3b65dc564575e0a1928",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -142,11 +141,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778443072,
|
"lastModified": 1781074563,
|
||||||
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
|
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
|
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -167,11 +166,11 @@
|
|||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778854817,
|
"lastModified": 1781294997,
|
||||||
"narHash": "sha256-iG+VuMy8W585geVVCUd7pR025WsY3ZkgSv5Yt5bxDmQ=",
|
"narHash": "sha256-XjCyIvJw4JtcwItTKRdQz5h1pLF9hr8ZSYeMP+/1d3A=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "1a68212c5683555ad80f0eab71db9715c6d52145",
|
"rev": "3f92cd1612268995d5667bd04fa03ba2916413d9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -204,11 +203,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775636079,
|
"lastModified": 1780220602,
|
||||||
"narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=",
|
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba",
|
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
Reference in New Issue
Block a user