Compare commits
3
Commits
1083b8fdfa
...
198d847151
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
198d847151
|
||
|
|
40d00a6c47
|
||
|
|
0c9a391618
|
No files matched your search
@@ -16,9 +16,6 @@ vim.lsp.config("rust_analyzer", {
|
|||||||
settings = {
|
settings = {
|
||||||
["rust-analyzer"] = {
|
["rust-analyzer"] = {
|
||||||
cargo = {
|
cargo = {
|
||||||
features = {
|
|
||||||
"inspector"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+127
-963
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"nix-js",
|
"fix",
|
||||||
"nix-js-macros"
|
"boxing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.profiling]
|
[profile.profiling]
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "boxing"
|
||||||
|
version = "0.1.3"
|
||||||
|
edition = "2021"
|
||||||
|
description = "NaN-boxing primitives (local fork with bool fix)"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sptr = "0.3"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod nan;
|
||||||
|
mod utils;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod raw;
|
||||||
|
|
||||||
|
pub use raw::RawBox;
|
||||||
|
|
||||||
|
const SIGN_MASK: u64 = 0x7FFF_FFFF_FFFF_FFFF;
|
||||||
|
const QUIET_NAN: u64 = 0x7FF8_0000_0000_0000;
|
||||||
|
const NEG_QUIET_NAN: u64 = 0xFFF8_0000_0000_0000;
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
use super::{NEG_QUIET_NAN, QUIET_NAN, SIGN_MASK};
|
||||||
|
use crate::utils::ArrayExt;
|
||||||
|
use sptr::Strict;
|
||||||
|
use std::fmt;
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
|
pub trait RawStore: Sized {
|
||||||
|
fn to_val(self, value: &mut Value);
|
||||||
|
fn from_val(value: &Value) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawStore for [u8; 6] {
|
||||||
|
#[inline]
|
||||||
|
fn to_val(self, value: &mut Value) {
|
||||||
|
value.set_data(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_val(value: &Value) -> Self {
|
||||||
|
*value.data()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawStore for bool {
|
||||||
|
#[inline]
|
||||||
|
fn to_val(self, value: &mut Value) {
|
||||||
|
value.set_data([u8::from(self)].truncate_to());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_val(value: &Value) -> Self {
|
||||||
|
value.data()[0] == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! int_store {
|
||||||
|
($ty:ty) => {
|
||||||
|
impl RawStore for $ty {
|
||||||
|
#[inline]
|
||||||
|
fn to_val(self, value: &mut Value) {
|
||||||
|
let bytes = self.to_ne_bytes();
|
||||||
|
value.set_data(bytes.truncate_to());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_val(value: &Value) -> Self {
|
||||||
|
<$ty>::from_ne_bytes(value.data().truncate_to())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int_store!(u8);
|
||||||
|
int_store!(u16);
|
||||||
|
int_store!(u32);
|
||||||
|
|
||||||
|
int_store!(i8);
|
||||||
|
int_store!(i16);
|
||||||
|
int_store!(i32);
|
||||||
|
|
||||||
|
fn store_ptr<P: Strict + Copy>(value: &mut Value, ptr: P) {
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
ptr.addr() <= 0x0000_FFFF_FFFF_FFFF,
|
||||||
|
"Pointer too large to store in NaN box"
|
||||||
|
);
|
||||||
|
|
||||||
|
let val = (unsafe { value.whole_mut() } as *mut [u8; 8]).cast::<P>();
|
||||||
|
|
||||||
|
let ptr = Strict::map_addr(ptr, |addr| {
|
||||||
|
addr | (usize::from(value.header().into_raw()) << 48)
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe { val.write(ptr) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
{
|
||||||
|
let _ = (value, ptr);
|
||||||
|
unimplemented!("32-bit pointer storage not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_ptr<P: Strict>(value: &Value) -> P {
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
{
|
||||||
|
let val = (unsafe { value.whole() } as *const [u8; 8]).cast::<P>();
|
||||||
|
let ptr = unsafe { val.read() };
|
||||||
|
Strict::map_addr(ptr, |addr| addr & 0x0000_FFFF_FFFF_FFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
{
|
||||||
|
let _ = value;
|
||||||
|
unimplemented!("32-bit pointer storage not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawStore for *const T {
|
||||||
|
fn to_val(self, value: &mut Value) {
|
||||||
|
store_ptr::<*const T>(value, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_val(value: &Value) -> Self {
|
||||||
|
load_ptr::<*const T>(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RawStore for *mut T {
|
||||||
|
fn to_val(self, value: &mut Value) {
|
||||||
|
store_ptr::<*mut T>(value, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_val(value: &Value) -> Self {
|
||||||
|
load_ptr::<*mut T>(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
enum TagVal {
|
||||||
|
_P1,
|
||||||
|
_P2,
|
||||||
|
_P3,
|
||||||
|
_P4,
|
||||||
|
_P5,
|
||||||
|
_P6,
|
||||||
|
_P7,
|
||||||
|
|
||||||
|
_N1,
|
||||||
|
_N2,
|
||||||
|
_N3,
|
||||||
|
_N4,
|
||||||
|
_N5,
|
||||||
|
_N6,
|
||||||
|
_N7,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct RawTag(TagVal);
|
||||||
|
|
||||||
|
impl RawTag {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(neg: bool, val: NonZeroU8) -> RawTag {
|
||||||
|
unsafe { Self::new_unchecked(neg, val.get() & 0x07) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new_checked(neg: bool, val: u8) -> Option<RawTag> {
|
||||||
|
Some(RawTag(match (neg, val) {
|
||||||
|
(false, 1) => TagVal::_P1,
|
||||||
|
(false, 2) => TagVal::_P2,
|
||||||
|
(false, 3) => TagVal::_P3,
|
||||||
|
(false, 4) => TagVal::_P4,
|
||||||
|
(false, 5) => TagVal::_P5,
|
||||||
|
(false, 6) => TagVal::_P6,
|
||||||
|
(false, 7) => TagVal::_P7,
|
||||||
|
|
||||||
|
(true, 1) => TagVal::_N1,
|
||||||
|
(true, 2) => TagVal::_N2,
|
||||||
|
(true, 3) => TagVal::_N3,
|
||||||
|
(true, 4) => TagVal::_N4,
|
||||||
|
(true, 5) => TagVal::_N5,
|
||||||
|
(true, 6) => TagVal::_N6,
|
||||||
|
(true, 7) => TagVal::_N7,
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `val` must be in the range `1..8`
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag {
|
||||||
|
RawTag(match (neg, val) {
|
||||||
|
(false, 1) => TagVal::_P1,
|
||||||
|
(false, 2) => TagVal::_P2,
|
||||||
|
(false, 3) => TagVal::_P3,
|
||||||
|
(false, 4) => TagVal::_P4,
|
||||||
|
(false, 5) => TagVal::_P5,
|
||||||
|
(false, 6) => TagVal::_P6,
|
||||||
|
(false, 7) => TagVal::_P7,
|
||||||
|
|
||||||
|
(true, 1) => TagVal::_N1,
|
||||||
|
(true, 2) => TagVal::_N2,
|
||||||
|
(true, 3) => TagVal::_N3,
|
||||||
|
(true, 4) => TagVal::_N4,
|
||||||
|
(true, 5) => TagVal::_N5,
|
||||||
|
(true, 6) => TagVal::_N6,
|
||||||
|
(true, 7) => TagVal::_N7,
|
||||||
|
|
||||||
|
_ => unsafe { core::hint::unreachable_unchecked() },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_neg(self) -> bool {
|
||||||
|
matches!(self.0, |TagVal::_N1| TagVal::_N2
|
||||||
|
| TagVal::_N3
|
||||||
|
| TagVal::_N4
|
||||||
|
| TagVal::_N5
|
||||||
|
| TagVal::_N6
|
||||||
|
| TagVal::_N7)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn val(self) -> NonZeroU8 {
|
||||||
|
match self.0 {
|
||||||
|
TagVal::_P1 | TagVal::_N1 => NonZeroU8::MIN,
|
||||||
|
TagVal::_P2 | TagVal::_N2 => NonZeroU8::MIN.saturating_add(1),
|
||||||
|
TagVal::_P3 | TagVal::_N3 => NonZeroU8::MIN.saturating_add(2),
|
||||||
|
TagVal::_P4 | TagVal::_N4 => NonZeroU8::MIN.saturating_add(3),
|
||||||
|
TagVal::_P5 | TagVal::_N5 => NonZeroU8::MIN.saturating_add(4),
|
||||||
|
TagVal::_P6 | TagVal::_N6 => NonZeroU8::MIN.saturating_add(5),
|
||||||
|
TagVal::_P7 | TagVal::_N7 => NonZeroU8::MIN.saturating_add(6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn neg_val(self) -> (bool, u8) {
|
||||||
|
match self.0 {
|
||||||
|
TagVal::_P1 => (false, 1),
|
||||||
|
TagVal::_P2 => (false, 2),
|
||||||
|
TagVal::_P3 => (false, 3),
|
||||||
|
TagVal::_P4 => (false, 4),
|
||||||
|
TagVal::_P5 => (false, 5),
|
||||||
|
TagVal::_P6 => (false, 6),
|
||||||
|
TagVal::_P7 => (false, 7),
|
||||||
|
TagVal::_N1 => (true, 1),
|
||||||
|
TagVal::_N2 => (true, 2),
|
||||||
|
TagVal::_N3 => (true, 3),
|
||||||
|
TagVal::_N4 => (true, 4),
|
||||||
|
TagVal::_N5 => (true, 5),
|
||||||
|
TagVal::_N6 => (true, 6),
|
||||||
|
TagVal::_N7 => (true, 7),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for RawTag {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("RawTag")
|
||||||
|
.field("neg", &self.is_neg())
|
||||||
|
.field("val", &self.val())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Header(u16);
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
#[inline]
|
||||||
|
fn new(tag: RawTag) -> Header {
|
||||||
|
let (neg, val) = tag.neg_val();
|
||||||
|
Header(0x7FF8 | (u16::from(neg) << 15) | u16::from(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn tag(self) -> RawTag {
|
||||||
|
unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_sign(self) -> bool {
|
||||||
|
self.0 & 0x8000 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_tag(self) -> u8 {
|
||||||
|
(self.0 & 0x0007) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_raw(self) -> u16 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
pub struct Value {
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
header: Header,
|
||||||
|
data: [u8; 6],
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
header: Header,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(tag: RawTag, data: [u8; 6]) -> Value {
|
||||||
|
Value {
|
||||||
|
header: Header::new(tag),
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn empty(tag: RawTag) -> Value {
|
||||||
|
Value::new(tag, [0; 6])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store<T: RawStore>(tag: RawTag, val: T) -> Value {
|
||||||
|
let mut v = Value::new(tag, [0; 6]);
|
||||||
|
T::to_val(val, &mut v);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load<T: RawStore>(self) -> T {
|
||||||
|
T::from_val(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn tag(&self) -> RawTag {
|
||||||
|
self.header.tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn header(&self) -> &Header {
|
||||||
|
&self.header
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_data(&mut self, val: [u8; 6]) {
|
||||||
|
self.data = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn data(&self) -> &[u8; 6] {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn data_mut(&mut self) -> &mut [u8; 6] {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn whole(&self) -> &[u8; 8] {
|
||||||
|
let ptr = (self as *const Value).cast::<[u8; 8]>();
|
||||||
|
unsafe { &*ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn whole_mut(&mut self) -> &mut [u8; 8] {
|
||||||
|
let ptr = (self as *mut Value).cast::<[u8; 8]>();
|
||||||
|
unsafe { &mut *ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union RawBox {
|
||||||
|
float: f64,
|
||||||
|
value: ManuallyDrop<Value>,
|
||||||
|
bits: u64,
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
ptr: *const (),
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
ptr: (u32, *const ()),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawBox {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_float(val: f64) -> RawBox {
|
||||||
|
match (val.is_nan(), val.is_sign_positive()) {
|
||||||
|
(true, true) => RawBox {
|
||||||
|
float: f64::from_bits(QUIET_NAN),
|
||||||
|
},
|
||||||
|
(true, false) => RawBox {
|
||||||
|
float: f64::from_bits(NEG_QUIET_NAN),
|
||||||
|
},
|
||||||
|
(false, _) => RawBox { float: val },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_value(value: Value) -> RawBox {
|
||||||
|
RawBox {
|
||||||
|
value: ManuallyDrop::new(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn tag(&self) -> Option<RawTag> {
|
||||||
|
if self.is_value() {
|
||||||
|
Some(unsafe { self.value.tag() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_float(&self) -> bool {
|
||||||
|
(unsafe { !self.float.is_nan() } || unsafe { self.bits & SIGN_MASK == QUIET_NAN })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_value(&self) -> bool {
|
||||||
|
(unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn float(&self) -> Option<&f64> {
|
||||||
|
if self.is_float() {
|
||||||
|
Some(unsafe { &self.float })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn value(&self) -> Option<&Value> {
|
||||||
|
if self.is_value() {
|
||||||
|
Some(unsafe { &self.value })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_float_unchecked(self) -> f64 {
|
||||||
|
unsafe { self.float }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for RawBox {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
RawBox {
|
||||||
|
ptr: unsafe { self.ptr },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for RawBox {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.float() {
|
||||||
|
Some(val) => f.debug_tuple("RawBox::Float").field(val).finish(),
|
||||||
|
None => {
|
||||||
|
let val = self.value().expect("RawBox is neither float nor value");
|
||||||
|
|
||||||
|
f.debug_struct("RawBox::Data")
|
||||||
|
.field("tag", &val.tag())
|
||||||
|
.field("data", val.data())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
pub trait ArrayExt<const LEN: usize> {
|
||||||
|
type Elem;
|
||||||
|
|
||||||
|
fn truncate_to<const M: usize>(self) -> [Self::Elem; M];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Copy, const N: usize> ArrayExt<N> for [T; N] {
|
||||||
|
type Elem = T;
|
||||||
|
|
||||||
|
fn truncate_to<const M: usize>(self) -> [Self::Elem; M] {
|
||||||
|
let copy_len = usize::min(N, M);
|
||||||
|
let mut out = [T::default(); M];
|
||||||
|
out[0..copy_len].copy_from_slice(&self[0..copy_len]);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,21 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nix-js"
|
name = "fix"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mimalloc = "0.1"
|
mimalloc = "0.1"
|
||||||
|
|
||||||
tokio = { version = "1.41", features = ["rt-multi-thread", "sync", "net", "io-util"] }
|
tokio = { version = "1.41", features = [
|
||||||
nix-compat = { git = "https://git.snix.dev/snix/snix.git", version = "0.1.0", features = ["wire", "async"] }
|
"rt-multi-thread",
|
||||||
|
"sync",
|
||||||
|
"net",
|
||||||
|
"io-util",
|
||||||
|
] }
|
||||||
|
nix-compat = { git = "https://git.snix.dev/snix/snix.git", version = "0.1.0", features = [
|
||||||
|
"wire",
|
||||||
|
"async",
|
||||||
|
] }
|
||||||
|
|
||||||
# REPL
|
# REPL
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
@@ -27,7 +34,11 @@ miette = { version = "7.4", features = ["fancy"] }
|
|||||||
|
|
||||||
hashbrown = "0.16"
|
hashbrown = "0.16"
|
||||||
string-interner = "0.19"
|
string-interner = "0.19"
|
||||||
bumpalo = { version = "3.20", features = ["allocator-api2", "boxed", "collections"] }
|
bumpalo = { version = "3.20", features = [
|
||||||
|
"allocator-api2",
|
||||||
|
"boxed",
|
||||||
|
"collections",
|
||||||
|
] }
|
||||||
|
|
||||||
rust-embed = "8.11"
|
rust-embed = "8.11"
|
||||||
|
|
||||||
@@ -35,9 +46,6 @@ itertools = "0.14"
|
|||||||
|
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
|
|
||||||
deno_core = "0.385"
|
|
||||||
deno_error = "0.7"
|
|
||||||
|
|
||||||
nix-nar = "0.3"
|
nix-nar = "0.3"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
@@ -46,7 +54,10 @@ hex = "0.4"
|
|||||||
|
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
|
|
||||||
reqwest = { version = "0.13", features = ["blocking", "rustls"], default-features = false }
|
reqwest = { version = "0.13", features = [
|
||||||
|
"blocking",
|
||||||
|
"rustls",
|
||||||
|
], default-features = false }
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
xz2 = "0.1"
|
xz2 = "0.1"
|
||||||
@@ -54,7 +65,7 @@ bzip2 = "0.6"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
# spec 1.0.0
|
# spec 1.0.0
|
||||||
toml = "0.9.9"
|
toml = "=0.9.9"
|
||||||
dirs = "6.0"
|
dirs = "6.0"
|
||||||
tempfile = "3.24"
|
tempfile = "3.24"
|
||||||
rusqlite = { version = "0.38", features = ["bundled"] }
|
rusqlite = { version = "0.38", features = ["bundled"] }
|
||||||
@@ -62,24 +73,21 @@ rusqlite = { version = "0.38", features = ["bundled"] }
|
|||||||
rnix = "0.14"
|
rnix = "0.14"
|
||||||
rowan = "0.16"
|
rowan = "0.16"
|
||||||
|
|
||||||
nix-js-macros = { path = "../nix-js-macros" }
|
|
||||||
ere = "0.2.4"
|
ere = "0.2.4"
|
||||||
num_enum = "0.7.5"
|
num_enum = "0.7.5"
|
||||||
tap = "1.0.1"
|
tap = "1.0.1"
|
||||||
|
|
||||||
# Inspector (optional)
|
ghost-cell = "0.2"
|
||||||
fastwebsockets = { version = "0.10", features = ["upgrade"], optional = true }
|
colored = "3.1"
|
||||||
hyper = { version = "1", features = ["http1", "server"], optional = true }
|
boxing = { path = "../boxing" }
|
||||||
hyper-util = { version = "0.1", features = ["tokio"], optional = true }
|
sealed = "0.6"
|
||||||
http-body-util = { version = "0.1", optional = true }
|
small-map = "0.1"
|
||||||
http = { version = "1", optional = true }
|
smallvec = "1.15"
|
||||||
uuid = { version = "1", features = ["v4"], optional = true }
|
|
||||||
ghost-cell = "0.2.6"
|
|
||||||
colored = "3.1.1"
|
|
||||||
|
|
||||||
[features]
|
[dependencies.gc-arena]
|
||||||
inspector = ["dep:fastwebsockets", "dep:hyper", "dep:hyper-util", "dep:http-body-util", "dep:http", "dep:uuid"]
|
git = "https://github.com/kyren/gc-arena"
|
||||||
prof = []
|
rev = "75671ae03f53718357b741ed4027560f14e90836"
|
||||||
|
features = ["allocator-api2", "hashbrown", "smallvec"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.8", features = ["html_reports"] }
|
criterion = { version = "0.8", features = ["html_reports"] }
|
||||||
@@ -96,7 +104,3 @@ harness = false
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "thunk_scope"
|
name = "thunk_scope"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "compile_time"
|
|
||||||
harness = false
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,18 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use fix::error::{Result, Source};
|
||||||
|
use fix::runtime::Runtime;
|
||||||
|
use fix::value::Value;
|
||||||
|
|
||||||
|
pub fn eval(expr: &str) -> Value {
|
||||||
|
Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval(Source::new_eval(expr.into()).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_result(expr: &str) -> Result<Value> {
|
||||||
|
Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval(Source::new_eval(expr.into()).unwrap())
|
||||||
|
}
|
||||||
@@ -1,101 +1,101 @@
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
|
use gc_arena::Collect;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
|
use string_interner::Symbol as _;
|
||||||
|
|
||||||
use crate::ir::{ArgId, Attr, BinOpKind, Ir, Param, RawIrRef, SymId, ThunkId, UnOpKind};
|
use crate::ir::{ArgId, Attr, BinOpKind, Ir, Param, RawIrRef, StringId, ThunkId, UnOpKind};
|
||||||
|
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
pub(crate) struct InstructionPtr(pub usize);
|
||||||
pub(crate) enum Constant {
|
|
||||||
Int(i64),
|
|
||||||
Float(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[derive(Collect)]
|
||||||
|
#[collect(require_static)]
|
||||||
pub struct Bytecode {
|
pub struct Bytecode {
|
||||||
pub code: Box<[u8]>,
|
pub code: Box<[u8]>,
|
||||||
pub current_dir: String,
|
pub current_dir: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait BytecodeContext {
|
pub(crate) trait BytecodeContext {
|
||||||
fn intern_string(&mut self, s: &str) -> u32;
|
fn intern_string(&mut self, s: &str) -> StringId;
|
||||||
fn intern_constant(&mut self, c: Constant) -> u32;
|
fn register_span(&mut self, range: TextRange) -> u32;
|
||||||
fn register_span(&self, range: TextRange) -> u32;
|
fn get_code(&self) -> &[u8];
|
||||||
fn get_sym(&self, id: SymId) -> &str;
|
fn get_code_mut(&mut self) -> &mut Vec<u8>;
|
||||||
fn get_current_dir(&self) -> &Path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, TryFromPrimitive)]
|
#[derive(Clone, Copy, TryFromPrimitive)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
PushConst = 0x01,
|
PushSmi,
|
||||||
PushString = 0x02,
|
PushBigInt,
|
||||||
PushNull = 0x03,
|
PushFloat,
|
||||||
PushTrue = 0x04,
|
PushString,
|
||||||
PushFalse = 0x05,
|
PushNull,
|
||||||
|
PushTrue,
|
||||||
|
PushFalse,
|
||||||
|
|
||||||
LoadLocal = 0x06,
|
LoadLocal,
|
||||||
LoadOuter = 0x07,
|
LoadOuter,
|
||||||
StoreLocal = 0x08,
|
StoreLocal,
|
||||||
AllocLocals = 0x09,
|
AllocLocals,
|
||||||
|
|
||||||
MakeThunk = 0x0A,
|
MakeThunk,
|
||||||
MakeClosure = 0x0B,
|
MakeClosure,
|
||||||
MakePatternClosure = 0x0C,
|
MakePatternClosure,
|
||||||
|
|
||||||
Call = 0x0D,
|
Call,
|
||||||
CallNoSpan = 0x0E,
|
CallNoSpan,
|
||||||
|
|
||||||
MakeAttrs = 0x0F,
|
MakeAttrs,
|
||||||
MakeAttrsDyn = 0x10,
|
MakeAttrsDyn,
|
||||||
MakeEmptyAttrs = 0x11,
|
MakeEmptyAttrs,
|
||||||
Select = 0x12,
|
Select,
|
||||||
SelectDefault = 0x13,
|
SelectDefault,
|
||||||
HasAttr = 0x14,
|
HasAttr,
|
||||||
|
|
||||||
MakeList = 0x15,
|
MakeList,
|
||||||
|
|
||||||
OpAdd = 0x16,
|
OpAdd,
|
||||||
OpSub = 0x17,
|
OpSub,
|
||||||
OpMul = 0x18,
|
OpMul,
|
||||||
OpDiv = 0x19,
|
OpDiv,
|
||||||
OpEq = 0x20,
|
OpEq,
|
||||||
OpNeq = 0x21,
|
OpNeq,
|
||||||
OpLt = 0x22,
|
OpLt,
|
||||||
OpGt = 0x23,
|
OpGt,
|
||||||
OpLeq = 0x24,
|
OpLeq,
|
||||||
OpGeq = 0x25,
|
OpGeq,
|
||||||
OpConcat = 0x26,
|
OpConcat,
|
||||||
OpUpdate = 0x27,
|
OpUpdate,
|
||||||
|
|
||||||
OpNeg = 0x28,
|
OpNeg,
|
||||||
OpNot = 0x29,
|
OpNot,
|
||||||
|
|
||||||
ForceBool = 0x30,
|
ForceBool,
|
||||||
JumpIfFalse = 0x31,
|
JumpIfFalse,
|
||||||
JumpIfTrue = 0x32,
|
JumpIfTrue,
|
||||||
Jump = 0x33,
|
Jump,
|
||||||
|
|
||||||
ConcatStrings = 0x34,
|
ConcatStrings,
|
||||||
ResolvePath = 0x35,
|
ResolvePath,
|
||||||
|
|
||||||
Assert = 0x36,
|
Assert,
|
||||||
|
|
||||||
PushWith = 0x37,
|
PushWith,
|
||||||
PopWith = 0x38,
|
PopWith,
|
||||||
WithLookup = 0x39,
|
WithLookup,
|
||||||
|
|
||||||
LoadBuiltins = 0x40,
|
LoadBuiltins,
|
||||||
LoadBuiltin = 0x41,
|
LoadBuiltin,
|
||||||
|
|
||||||
MkPos = 0x43,
|
MkPos,
|
||||||
|
|
||||||
LoadReplBinding = 0x44,
|
LoadReplBinding,
|
||||||
LoadScopedBinding = 0x45,
|
LoadScopedBinding,
|
||||||
|
|
||||||
Return = 0x46,
|
Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScopeInfo {
|
struct ScopeInfo {
|
||||||
@@ -106,71 +106,78 @@ struct ScopeInfo {
|
|||||||
|
|
||||||
struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
|
struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
|
||||||
ctx: &'a mut Ctx,
|
ctx: &'a mut Ctx,
|
||||||
code: Vec<u8>,
|
|
||||||
scope_stack: Vec<ScopeInfo>,
|
scope_stack: Vec<ScopeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> Bytecode {
|
pub(crate) fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
||||||
let current_dir = ctx.get_current_dir().to_string_lossy().to_string();
|
let ip = ctx.get_code().len();
|
||||||
let mut emitter = BytecodeEmitter::new(ctx);
|
let mut emitter = BytecodeEmitter::new(ctx);
|
||||||
emitter.emit_toplevel(ir);
|
emitter.emit_toplevel(ir);
|
||||||
Bytecode {
|
InstructionPtr(ip)
|
||||||
code: emitter.code.into_boxed_slice(),
|
|
||||||
current_dir,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compile_bytecode_scoped(
|
|
||||||
ir: RawIrRef<'_>,
|
|
||||||
ctx: &mut impl BytecodeContext,
|
|
||||||
) -> Bytecode {
|
|
||||||
let current_dir = ctx.get_current_dir().to_string_lossy().to_string();
|
|
||||||
let mut emitter = BytecodeEmitter::new(ctx);
|
|
||||||
emitter.emit_toplevel_scoped(ir);
|
|
||||||
Bytecode {
|
|
||||||
code: emitter.code.into_boxed_slice(),
|
|
||||||
current_dir,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
||||||
fn new(ctx: &'a mut Ctx) -> Self {
|
fn new(ctx: &'a mut Ctx) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
code: Vec::with_capacity(4096),
|
|
||||||
scope_stack: Vec::with_capacity(32),
|
scope_stack: Vec::with_capacity(32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_op(&mut self, op: Op) {
|
fn emit_op(&mut self, op: Op) {
|
||||||
self.code.push(op as u8);
|
self.ctx.get_code_mut().push(op as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_u8(&mut self, val: u8) {
|
fn emit_u8(&mut self, val: u8) {
|
||||||
self.code.push(val);
|
self.ctx.get_code_mut().push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_u16(&mut self, val: u16) {
|
fn emit_u16(&mut self, val: u16) {
|
||||||
self.code.extend_from_slice(&val.to_le_bytes());
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&val.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_u32(&mut self, val: u32) {
|
fn emit_u32(&mut self, val: u32) {
|
||||||
self.code.extend_from_slice(&val.to_le_bytes());
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&val.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn emit_i32(&mut self, val: i32) {
|
||||||
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&val.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn emit_i64(&mut self, val: i64) {
|
||||||
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&val.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn emit_f64(&mut self, val: f64) {
|
||||||
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&val.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_i32_placeholder(&mut self) -> usize {
|
fn emit_i32_placeholder(&mut self) -> usize {
|
||||||
let offset = self.code.len();
|
let offset = self.ctx.get_code_mut().len();
|
||||||
self.code.extend_from_slice(&[0u8; 4]);
|
self.ctx.get_code_mut().extend_from_slice(&[0u8; 4]);
|
||||||
offset
|
offset
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn patch_i32(&mut self, offset: usize, val: i32) {
|
fn patch_i32(&mut self, offset: usize, val: i32) {
|
||||||
self.code[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
|
self.ctx.get_code_mut()[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -181,11 +188,18 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn patch_jump_target(&mut self, placeholder_offset: usize) {
|
fn patch_jump_target(&mut self, placeholder_offset: usize) {
|
||||||
let current_pos = self.code.len();
|
let current_pos = self.ctx.get_code_mut().len();
|
||||||
let relative_offset = (current_pos as i32) - (placeholder_offset as i32) - 4;
|
let relative_offset = (current_pos as i32) - (placeholder_offset as i32) - 4;
|
||||||
self.patch_i32(placeholder_offset, relative_offset);
|
self.patch_i32(placeholder_offset, relative_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn emit_str_id(&mut self, id: StringId) {
|
||||||
|
self.ctx
|
||||||
|
.get_code_mut()
|
||||||
|
.extend_from_slice(&(id.0.to_usize() as u32).to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
fn current_depth(&self) -> u16 {
|
fn current_depth(&self) -> u16 {
|
||||||
self.scope_stack.last().map_or(0, |s| s.depth)
|
self.scope_stack.last().map_or(0, |s| s.depth)
|
||||||
}
|
}
|
||||||
@@ -397,50 +411,19 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_toplevel_scoped(&mut self, ir: RawIrRef<'_>) {
|
|
||||||
match ir.deref() {
|
|
||||||
Ir::TopLevel { body, thunks } => {
|
|
||||||
let with_thunk_count = self.count_with_thunks(*body);
|
|
||||||
let total_slots = thunks.len() + with_thunk_count;
|
|
||||||
|
|
||||||
let all_thunks = self.collect_all_thunks(thunks, *body);
|
|
||||||
let thunk_ids: Vec<ThunkId> = all_thunks.iter().map(|&(id, _)| id).collect();
|
|
||||||
|
|
||||||
self.push_scope(false, None, &thunk_ids);
|
|
||||||
|
|
||||||
if total_slots > 0 {
|
|
||||||
self.emit_op(Op::AllocLocals);
|
|
||||||
self.emit_u32(total_slots as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_scope_thunks(thunks);
|
|
||||||
self.emit_expr(*body);
|
|
||||||
self.emit_op(Op::Return);
|
|
||||||
|
|
||||||
self.pop_scope();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.push_scope(false, None, &[]);
|
|
||||||
self.emit_expr(ir);
|
|
||||||
self.emit_op(Op::Return);
|
|
||||||
self.pop_scope();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_scope_thunks(&mut self, thunks: &[(ThunkId, RawIrRef<'_>)]) {
|
fn emit_scope_thunks(&mut self, thunks: &[(ThunkId, RawIrRef<'_>)]) {
|
||||||
for &(id, inner) in thunks {
|
for &(id, inner) in thunks {
|
||||||
let label = format!("e{}", id.0);
|
let label = format!("e{}", id.0);
|
||||||
let label_idx = self.ctx.intern_string(&label);
|
let label_idx = self.ctx.intern_string(&label);
|
||||||
|
|
||||||
let skip_patch = self.emit_jump_placeholder();
|
let skip_patch = self.emit_jump_placeholder();
|
||||||
let entry_point = self.code.len() as u32;
|
let entry_point = self.ctx.get_code_mut().len() as u32;
|
||||||
self.emit_expr(inner);
|
self.emit_expr(inner);
|
||||||
self.emit_op(Op::Return);
|
self.emit_op(Op::Return);
|
||||||
self.patch_jump_target(skip_patch);
|
self.patch_jump_target(skip_patch);
|
||||||
self.emit_op(Op::MakeThunk);
|
self.emit_op(Op::MakeThunk);
|
||||||
self.emit_u32(entry_point);
|
self.emit_u32(entry_point);
|
||||||
self.emit_u32(label_idx);
|
self.emit_str_id(label_idx);
|
||||||
let (_, local_idx) = self.resolve_thunk(id);
|
let (_, local_idx) = self.resolve_thunk(id);
|
||||||
self.emit_op(Op::StoreLocal);
|
self.emit_op(Op::StoreLocal);
|
||||||
self.emit_u32(local_idx);
|
self.emit_u32(local_idx);
|
||||||
@@ -450,14 +433,17 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
fn emit_expr(&mut self, ir: RawIrRef<'_>) {
|
fn emit_expr(&mut self, ir: RawIrRef<'_>) {
|
||||||
match ir.deref() {
|
match ir.deref() {
|
||||||
&Ir::Int(x) => {
|
&Ir::Int(x) => {
|
||||||
let idx = self.ctx.intern_constant(Constant::Int(x));
|
if x <= i32::MAX as i64 {
|
||||||
self.emit_op(Op::PushConst);
|
self.emit_op(Op::PushSmi);
|
||||||
self.emit_u32(idx);
|
self.emit_i32(x as i32);
|
||||||
|
} else {
|
||||||
|
self.emit_op(Op::PushBigInt);
|
||||||
|
self.emit_i64(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&Ir::Float(x) => {
|
&Ir::Float(x) => {
|
||||||
let idx = self.ctx.intern_constant(Constant::Float(x.to_bits()));
|
self.emit_op(Op::PushFloat);
|
||||||
self.emit_op(Op::PushConst);
|
self.emit_f64(x);
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
&Ir::Bool(true) => self.emit_op(Op::PushTrue),
|
&Ir::Bool(true) => self.emit_op(Op::PushTrue),
|
||||||
&Ir::Bool(false) => self.emit_op(Op::PushFalse),
|
&Ir::Bool(false) => self.emit_op(Op::PushFalse),
|
||||||
@@ -465,7 +451,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
Ir::Str(s) => {
|
Ir::Str(s) => {
|
||||||
let idx = self.ctx.intern_string(s.deref());
|
let idx = self.ctx.intern_string(s.deref());
|
||||||
self.emit_op(Op::PushString);
|
self.emit_op(Op::PushString);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(idx);
|
||||||
}
|
}
|
||||||
&Ir::Path(p) => {
|
&Ir::Path(p) => {
|
||||||
self.emit_expr(p);
|
self.emit_expr(p);
|
||||||
@@ -477,20 +463,20 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
self.emit_op(Op::JumpIfFalse);
|
self.emit_op(Op::JumpIfFalse);
|
||||||
let else_placeholder = self.emit_i32_placeholder();
|
let else_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jif = self.code.len();
|
let after_jif = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
self.emit_expr(consq);
|
self.emit_expr(consq);
|
||||||
|
|
||||||
self.emit_op(Op::Jump);
|
self.emit_op(Op::Jump);
|
||||||
let end_placeholder = self.emit_i32_placeholder();
|
let end_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jump = self.code.len();
|
let after_jump = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
let else_offset = (after_jump as i32) - (after_jif as i32);
|
let else_offset = (after_jump as i32) - (after_jif as i32);
|
||||||
self.patch_i32(else_placeholder, else_offset);
|
self.patch_i32(else_placeholder, else_offset);
|
||||||
|
|
||||||
self.emit_expr(alter);
|
self.emit_expr(alter);
|
||||||
|
|
||||||
let end_offset = (self.code.len() as i32) - (after_jump as i32);
|
let end_offset = (self.ctx.get_code_mut().len() as i32) - (after_jump as i32);
|
||||||
self.patch_i32(end_placeholder, end_offset);
|
self.patch_i32(end_placeholder, end_offset);
|
||||||
}
|
}
|
||||||
&Ir::BinOp { lhs, rhs, kind } => {
|
&Ir::BinOp { lhs, rhs, kind } => {
|
||||||
@@ -554,10 +540,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LoadBuiltins);
|
self.emit_op(Op::LoadBuiltins);
|
||||||
}
|
}
|
||||||
&Ir::Builtin(name) => {
|
&Ir::Builtin(name) => {
|
||||||
let sym = self.ctx.get_sym(name).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&sym);
|
|
||||||
self.emit_op(Op::LoadBuiltin);
|
self.emit_op(Op::LoadBuiltin);
|
||||||
self.emit_u32(idx);
|
self.emit_u32(name.0.to_usize() as u32);
|
||||||
}
|
}
|
||||||
&Ir::ConcatStrings {
|
&Ir::ConcatStrings {
|
||||||
ref parts,
|
ref parts,
|
||||||
@@ -584,7 +568,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_expr(*assertion);
|
self.emit_expr(*assertion);
|
||||||
self.emit_expr(*expr);
|
self.emit_expr(*expr);
|
||||||
self.emit_op(Op::Assert);
|
self.emit_op(Op::Assert);
|
||||||
self.emit_u32(raw_idx);
|
self.emit_str_id(raw_idx);
|
||||||
self.emit_u32(span_id);
|
self.emit_u32(span_id);
|
||||||
}
|
}
|
||||||
&Ir::CurPos(span) => {
|
&Ir::CurPos(span) => {
|
||||||
@@ -593,16 +577,12 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_u32(span_id);
|
self.emit_u32(span_id);
|
||||||
}
|
}
|
||||||
&Ir::ReplBinding(name) => {
|
&Ir::ReplBinding(name) => {
|
||||||
let sym = self.ctx.get_sym(name).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&sym);
|
|
||||||
self.emit_op(Op::LoadReplBinding);
|
self.emit_op(Op::LoadReplBinding);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
&Ir::ScopedImportBinding(name) => {
|
&Ir::ScopedImportBinding(name) => {
|
||||||
let sym = self.ctx.get_sym(name).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&sym);
|
|
||||||
self.emit_op(Op::LoadScopedBinding);
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
&Ir::With {
|
&Ir::With {
|
||||||
namespace,
|
namespace,
|
||||||
@@ -612,10 +592,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_with(namespace, body, thunks);
|
self.emit_with(namespace, body, thunks);
|
||||||
}
|
}
|
||||||
&Ir::WithLookup(name) => {
|
&Ir::WithLookup(name) => {
|
||||||
let sym = self.ctx.get_sym(name).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&sym);
|
|
||||||
self.emit_op(Op::WithLookup);
|
self.emit_op(Op::WithLookup);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -628,20 +606,20 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::JumpIfFalse);
|
self.emit_op(Op::JumpIfFalse);
|
||||||
let skip_placeholder = self.emit_i32_placeholder();
|
let skip_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jif = self.code.len();
|
let after_jif = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
self.emit_expr(rhs);
|
self.emit_expr(rhs);
|
||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::Jump);
|
self.emit_op(Op::Jump);
|
||||||
let end_placeholder = self.emit_i32_placeholder();
|
let end_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jump = self.code.len();
|
let after_jump = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
let false_offset = (after_jump as i32) - (after_jif as i32);
|
let false_offset = (after_jump as i32) - (after_jif as i32);
|
||||||
self.patch_i32(skip_placeholder, false_offset);
|
self.patch_i32(skip_placeholder, false_offset);
|
||||||
|
|
||||||
self.emit_op(Op::PushFalse);
|
self.emit_op(Op::PushFalse);
|
||||||
|
|
||||||
let end_offset = (self.code.len() as i32) - (after_jump as i32);
|
let end_offset = (self.ctx.get_code_mut().len() as i32) - (after_jump as i32);
|
||||||
self.patch_i32(end_placeholder, end_offset);
|
self.patch_i32(end_placeholder, end_offset);
|
||||||
}
|
}
|
||||||
Or => {
|
Or => {
|
||||||
@@ -649,20 +627,20 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::JumpIfTrue);
|
self.emit_op(Op::JumpIfTrue);
|
||||||
let skip_placeholder = self.emit_i32_placeholder();
|
let skip_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jit = self.code.len();
|
let after_jit = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
self.emit_expr(rhs);
|
self.emit_expr(rhs);
|
||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::Jump);
|
self.emit_op(Op::Jump);
|
||||||
let end_placeholder = self.emit_i32_placeholder();
|
let end_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jump = self.code.len();
|
let after_jump = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
let true_offset = (after_jump as i32) - (after_jit as i32);
|
let true_offset = (after_jump as i32) - (after_jit as i32);
|
||||||
self.patch_i32(skip_placeholder, true_offset);
|
self.patch_i32(skip_placeholder, true_offset);
|
||||||
|
|
||||||
self.emit_op(Op::PushTrue);
|
self.emit_op(Op::PushTrue);
|
||||||
|
|
||||||
let end_offset = (self.code.len() as i32) - (after_jump as i32);
|
let end_offset = (self.ctx.get_code_mut().len() as i32) - (after_jump as i32);
|
||||||
self.patch_i32(end_placeholder, end_offset);
|
self.patch_i32(end_placeholder, end_offset);
|
||||||
}
|
}
|
||||||
Impl => {
|
Impl => {
|
||||||
@@ -670,20 +648,20 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::JumpIfFalse);
|
self.emit_op(Op::JumpIfFalse);
|
||||||
let skip_placeholder = self.emit_i32_placeholder();
|
let skip_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jif = self.code.len();
|
let after_jif = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
self.emit_expr(rhs);
|
self.emit_expr(rhs);
|
||||||
self.emit_op(Op::ForceBool);
|
self.emit_op(Op::ForceBool);
|
||||||
self.emit_op(Op::Jump);
|
self.emit_op(Op::Jump);
|
||||||
let end_placeholder = self.emit_i32_placeholder();
|
let end_placeholder = self.emit_i32_placeholder();
|
||||||
let after_jump = self.code.len();
|
let after_jump = self.ctx.get_code_mut().len();
|
||||||
|
|
||||||
let true_offset = (after_jump as i32) - (after_jif as i32);
|
let true_offset = (after_jump as i32) - (after_jif as i32);
|
||||||
self.patch_i32(skip_placeholder, true_offset);
|
self.patch_i32(skip_placeholder, true_offset);
|
||||||
|
|
||||||
self.emit_op(Op::PushTrue);
|
self.emit_op(Op::PushTrue);
|
||||||
|
|
||||||
let end_offset = (self.code.len() as i32) - (after_jump as i32);
|
let end_offset = (self.ctx.get_code_mut().len() as i32) - (after_jump as i32);
|
||||||
self.patch_i32(end_placeholder, end_offset);
|
self.patch_i32(end_placeholder, end_offset);
|
||||||
}
|
}
|
||||||
PipeL => {
|
PipeL => {
|
||||||
@@ -732,7 +710,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
let thunk_ids: Vec<ThunkId> = all_thunks.iter().map(|&(id, _)| id).collect();
|
let thunk_ids: Vec<ThunkId> = all_thunks.iter().map(|&(id, _)| id).collect();
|
||||||
|
|
||||||
let skip_patch = self.emit_jump_placeholder();
|
let skip_patch = self.emit_jump_placeholder();
|
||||||
let entry_point = self.code.len() as u32;
|
let entry_point = self.ctx.get_code().len() as u32;
|
||||||
self.push_scope(true, Some(arg), &thunk_ids);
|
self.push_scope(true, Some(arg), &thunk_ids);
|
||||||
self.emit_scope_thunks(thunks);
|
self.emit_scope_thunks(thunks);
|
||||||
self.emit_expr(body);
|
self.emit_expr(body);
|
||||||
@@ -754,20 +732,14 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_u8(if *ellipsis { 1 } else { 0 });
|
self.emit_u8(if *ellipsis { 1 } else { 0 });
|
||||||
|
|
||||||
for &(sym, _) in required.iter() {
|
for &(sym, _) in required.iter() {
|
||||||
let name = self.ctx.get_sym(sym).to_string();
|
self.emit_str_id(sym);
|
||||||
let idx = self.ctx.intern_string(&name);
|
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
for &(sym, _) in optional.iter() {
|
for &(sym, _) in optional.iter() {
|
||||||
let name = self.ctx.get_sym(sym).to_string();
|
self.emit_str_id(sym);
|
||||||
let idx = self.ctx.intern_string(&name);
|
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
for &(sym, span) in required.iter().chain(optional.iter()) {
|
for &(sym, span) in required.iter().chain(optional.iter()) {
|
||||||
let name = self.ctx.get_sym(sym).to_string();
|
|
||||||
let name_idx = self.ctx.intern_string(&name);
|
|
||||||
let span_id = self.ctx.register_span(span);
|
let span_id = self.ctx.register_span(span);
|
||||||
self.emit_u32(name_idx);
|
self.emit_str_id(sym);
|
||||||
self.emit_u32(span_id);
|
self.emit_u32(span_id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -779,7 +751,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
fn emit_attrset(
|
fn emit_attrset(
|
||||||
&mut self,
|
&mut self,
|
||||||
stcs: &crate::ir::HashMap<'_, SymId, (RawIrRef<'_>, TextRange)>,
|
stcs: &crate::ir::HashMap<'_, StringId, (RawIrRef<'_>, TextRange)>,
|
||||||
dyns: &[(RawIrRef<'_>, RawIrRef<'_>, TextRange)],
|
dyns: &[(RawIrRef<'_>, RawIrRef<'_>, TextRange)],
|
||||||
) {
|
) {
|
||||||
if stcs.is_empty() && dyns.is_empty() {
|
if stcs.is_empty() && dyns.is_empty() {
|
||||||
@@ -789,42 +761,35 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
if !dyns.is_empty() {
|
if !dyns.is_empty() {
|
||||||
for (&sym, &(val, _)) in stcs.iter() {
|
for (&sym, &(val, _)) in stcs.iter() {
|
||||||
let key = self.ctx.get_sym(sym).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&key);
|
|
||||||
self.emit_op(Op::PushString);
|
self.emit_op(Op::PushString);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(sym);
|
||||||
self.emit_expr(val);
|
self.emit_expr(val);
|
||||||
}
|
}
|
||||||
for (_, &(_, span)) in stcs.iter() {
|
for (_, &(_, span)) in stcs.iter() {
|
||||||
let span_id = self.ctx.register_span(span);
|
let span_id = self.ctx.register_span(span);
|
||||||
let idx = self.ctx.intern_constant(Constant::Int(span_id as i64));
|
self.emit_op(Op::PushSmi);
|
||||||
self.emit_op(Op::PushConst);
|
self.emit_u32(span_id);
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
for &(key, val, span) in dyns.iter() {
|
for &(key, val, span) in dyns.iter() {
|
||||||
self.emit_expr(key);
|
self.emit_expr(key);
|
||||||
self.emit_expr(val);
|
self.emit_expr(val);
|
||||||
let span_id = self.ctx.register_span(span);
|
let span_id = self.ctx.register_span(span);
|
||||||
let idx = self.ctx.intern_constant(Constant::Int(span_id as i64));
|
self.emit_op(Op::PushSmi);
|
||||||
self.emit_op(Op::PushConst);
|
self.emit_u32(span_id);
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
self.emit_op(Op::MakeAttrsDyn);
|
self.emit_op(Op::MakeAttrsDyn);
|
||||||
self.emit_u32(stcs.len() as u32);
|
self.emit_u32(stcs.len() as u32);
|
||||||
self.emit_u32(dyns.len() as u32);
|
self.emit_u32(dyns.len() as u32);
|
||||||
} else {
|
} else {
|
||||||
for (&sym, &(val, _)) in stcs.iter() {
|
for (&sym, &(val, _)) in stcs.iter() {
|
||||||
let key = self.ctx.get_sym(sym).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&key);
|
|
||||||
self.emit_op(Op::PushString);
|
self.emit_op(Op::PushString);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(sym);
|
||||||
self.emit_expr(val);
|
self.emit_expr(val);
|
||||||
}
|
}
|
||||||
for (_, &(_, span)) in stcs.iter() {
|
for (_, &(_, span)) in stcs.iter() {
|
||||||
let span_id = self.ctx.register_span(span);
|
let span_id = self.ctx.register_span(span);
|
||||||
let idx = self.ctx.intern_constant(Constant::Int(span_id as i64));
|
self.emit_op(Op::PushSmi);
|
||||||
self.emit_op(Op::PushConst);
|
self.emit_u32(span_id);
|
||||||
self.emit_u32(idx);
|
|
||||||
}
|
}
|
||||||
self.emit_op(Op::MakeAttrs);
|
self.emit_op(Op::MakeAttrs);
|
||||||
self.emit_u32(stcs.len() as u32);
|
self.emit_u32(stcs.len() as u32);
|
||||||
@@ -840,15 +805,13 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
) {
|
) {
|
||||||
self.emit_expr(expr);
|
self.emit_expr(expr);
|
||||||
for attr in attrpath.iter() {
|
for attr in attrpath.iter() {
|
||||||
match attr {
|
match *attr {
|
||||||
Attr::Str(sym, _) => {
|
Attr::Str(sym, _) => {
|
||||||
let key = self.ctx.get_sym(*sym).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&key);
|
|
||||||
self.emit_op(Op::PushString);
|
self.emit_op(Op::PushString);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(sym);
|
||||||
}
|
}
|
||||||
Attr::Dynamic(expr, _) => {
|
Attr::Dynamic(expr, _) => {
|
||||||
self.emit_expr(*expr);
|
self.emit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -870,15 +833,13 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
fn emit_has_attr(&mut self, lhs: RawIrRef<'_>, rhs: &[Attr<RawIrRef<'_>>]) {
|
fn emit_has_attr(&mut self, lhs: RawIrRef<'_>, rhs: &[Attr<RawIrRef<'_>>]) {
|
||||||
self.emit_expr(lhs);
|
self.emit_expr(lhs);
|
||||||
for attr in rhs.iter() {
|
for attr in rhs.iter() {
|
||||||
match attr {
|
match *attr {
|
||||||
Attr::Str(sym, _) => {
|
Attr::Str(sym, _) => {
|
||||||
let key = self.ctx.get_sym(*sym).to_string();
|
|
||||||
let idx = self.ctx.intern_string(&key);
|
|
||||||
self.emit_op(Op::PushString);
|
self.emit_op(Op::PushString);
|
||||||
self.emit_u32(idx);
|
self.emit_str_id(sym);
|
||||||
}
|
}
|
||||||
Attr::Dynamic(expr, _) => {
|
Attr::Dynamic(expr, _) => {
|
||||||
self.emit_expr(*expr);
|
self.emit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
@@ -3,11 +3,10 @@ use std::fmt::Write;
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use crate::bytecode::{Bytecode, Constant, Op};
|
use crate::codegen::{Bytecode, Op};
|
||||||
|
|
||||||
pub(crate) trait DisassemblerContext {
|
pub(crate) trait DisassemblerContext {
|
||||||
fn lookup_string(&self, id: u32) -> &str;
|
fn lookup_string(&self, id: u32) -> &str;
|
||||||
fn lookup_constant(&self, id: u32) -> &Constant;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Disassembler<'a, Ctx> {
|
pub(crate) struct Disassembler<'a, Ctx> {
|
||||||
@@ -55,6 +54,22 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
i32::from_le_bytes(bytes)
|
i32::from_le_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_i64(&mut self) -> i64 {
|
||||||
|
let bytes = self.code[self.pos..self.pos + 8]
|
||||||
|
.try_into()
|
||||||
|
.expect("no enough bytes");
|
||||||
|
self.pos += 8;
|
||||||
|
i64::from_le_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_f64(&mut self) -> f64 {
|
||||||
|
let bytes = self.code[self.pos..self.pos + 8]
|
||||||
|
.try_into()
|
||||||
|
.expect("no enough bytes");
|
||||||
|
self.pos += 8;
|
||||||
|
f64::from_le_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disassemble(&mut self) -> String {
|
pub fn disassemble(&mut self) -> String {
|
||||||
self.disassemble_impl(false)
|
self.disassemble_impl(false)
|
||||||
}
|
}
|
||||||
@@ -144,14 +159,17 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
let op = Op::try_from_primitive(op_byte).expect("invalid op code");
|
let op = Op::try_from_primitive(op_byte).expect("invalid op code");
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Op::PushConst => {
|
Op::PushSmi => {
|
||||||
let idx = self.read_u32();
|
let val = self.read_i32();
|
||||||
let val = self.ctx.lookup_constant(idx);
|
("PushSmi", format!("{}", val))
|
||||||
let val_str = match val {
|
}
|
||||||
Constant::Int(i) => format!("Int({})", i),
|
Op::PushBigInt => {
|
||||||
Constant::Float(f) => format!("Float(bits: {})", f),
|
let val = self.read_i64();
|
||||||
};
|
("PushBigInt", format!("{}", val))
|
||||||
("PushConst", format!("@{} ({})", idx, val_str))
|
}
|
||||||
|
Op::PushFloat => {
|
||||||
|
let val = self.read_f64();
|
||||||
|
("PushFloat", format!("{}", val))
|
||||||
}
|
}
|
||||||
Op::PushString => {
|
Op::PushString => {
|
||||||
let idx = self.read_u32();
|
let idx = self.read_u32();
|
||||||
@@ -50,16 +50,16 @@ pub trait DowngradeContext<'id: 'ir, 'ir> {
|
|||||||
fn new_arg(&mut self) -> ArgId;
|
fn new_arg(&mut self) -> ArgId;
|
||||||
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir>;
|
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir>;
|
||||||
|
|
||||||
fn new_sym(&mut self, sym: String) -> SymId;
|
fn new_sym(&mut self, sym: String) -> StringId;
|
||||||
fn get_sym(&self, id: SymId) -> Symbol<'_>;
|
fn get_sym(&self, id: StringId) -> Symbol<'_>;
|
||||||
fn lookup(&self, sym: SymId, span: TextRange) -> Result<IrRef<'id, 'ir>>;
|
fn lookup(&self, sym: StringId, span: TextRange) -> Result<IrRef<'id, 'ir>>;
|
||||||
|
|
||||||
fn get_current_source(&self) -> Source;
|
fn get_current_source(&self) -> Source;
|
||||||
|
|
||||||
fn with_param_scope<F, R>(&mut self, param: SymId, arg: ArgId, f: F) -> R
|
fn with_param_scope<F, R>(&mut self, param: StringId, arg: ArgId, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> R;
|
F: FnOnce(&mut Self) -> R;
|
||||||
fn with_let_scope<F, R>(&mut self, bindings: &[SymId], f: F) -> Result<R>
|
fn with_let_scope<F, R>(&mut self, bindings: &[StringId], f: F) -> Result<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> Result<(Vec<'ir, IrRef<'id, 'ir>>, R)>;
|
F: FnOnce(&mut Self) -> Result<(Vec<'ir, IrRef<'id, 'ir>>, R)>;
|
||||||
fn with_with_scope<F, R>(&mut self, f: F) -> R
|
fn with_with_scope<F, R>(&mut self, f: F) -> R
|
||||||
@@ -456,8 +456,8 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
|
|||||||
|
|
||||||
enum PendingValue<'ir> {
|
enum PendingValue<'ir> {
|
||||||
Expr(ast::Expr),
|
Expr(ast::Expr),
|
||||||
InheritFrom(ast::Expr, SymId, TextRange),
|
InheritFrom(ast::Expr, StringId, TextRange),
|
||||||
InheritScope(SymId, TextRange),
|
InheritScope(StringId, TextRange),
|
||||||
Set(PendingAttrSet<'ir>),
|
Set(PendingAttrSet<'ir>),
|
||||||
ExtendedRecAttrSet {
|
ExtendedRecAttrSet {
|
||||||
base: ast::AttrSet,
|
base: ast::AttrSet,
|
||||||
@@ -466,7 +466,7 @@ enum PendingValue<'ir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PendingAttrSet<'ir> {
|
struct PendingAttrSet<'ir> {
|
||||||
stcs: HashMap<'ir, SymId, (PendingValue<'ir>, TextRange)>,
|
stcs: HashMap<'ir, StringId, (PendingValue<'ir>, TextRange)>,
|
||||||
dyns: Vec<'ir, (ast::Attr, PendingValue<'ir>, TextRange)>,
|
dyns: Vec<'ir, (ast::Attr, PendingValue<'ir>, TextRange)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
|
|||||||
|
|
||||||
fn insert_static(
|
fn insert_static(
|
||||||
&mut self,
|
&mut self,
|
||||||
sym: SymId,
|
sym: StringId,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
path: &[ast::Attr],
|
path: &[ast::Attr],
|
||||||
value: ast::Expr,
|
value: ast::Expr,
|
||||||
@@ -846,7 +846,7 @@ fn make_attrpath_value_entry<'ir>(path: Vec<'ir, ast::Attr>, value: ast::Expr) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct FinalizedAttrSet<'id, 'ir> {
|
struct FinalizedAttrSet<'id, 'ir> {
|
||||||
stcs: HashMap<'ir, SymId, (IrRef<'id, 'ir>, TextRange)>,
|
stcs: HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>,
|
||||||
dyns: Vec<'ir, (IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)>,
|
dyns: Vec<'ir, (IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,23 +930,23 @@ fn downgrade_attrpath<'id, 'ir>(
|
|||||||
|
|
||||||
struct PatternBindings<'id, 'ir> {
|
struct PatternBindings<'id, 'ir> {
|
||||||
body: IrRef<'id, 'ir>,
|
body: IrRef<'id, 'ir>,
|
||||||
required: Vec<'ir, (SymId, TextRange)>,
|
required: Vec<'ir, (StringId, TextRange)>,
|
||||||
optional: Vec<'ir, (SymId, TextRange)>,
|
optional: Vec<'ir, (StringId, TextRange)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downgrade_pattern_bindings<'id, 'ir, Ctx>(
|
fn downgrade_pattern_bindings<'id, 'ir, Ctx>(
|
||||||
pat_entries: impl Iterator<Item = ast::PatEntry>,
|
pat_entries: impl Iterator<Item = ast::PatEntry>,
|
||||||
alias: Option<SymId>,
|
alias: Option<StringId>,
|
||||||
arg: ArgId,
|
arg: ArgId,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
body_fn: impl FnOnce(&mut Ctx, &[SymId]) -> Result<IrRef<'id, 'ir>>,
|
body_fn: impl FnOnce(&mut Ctx, &[StringId]) -> Result<IrRef<'id, 'ir>>,
|
||||||
) -> Result<PatternBindings<'id, 'ir>>
|
) -> Result<PatternBindings<'id, 'ir>>
|
||||||
where
|
where
|
||||||
Ctx: DowngradeContext<'id, 'ir>,
|
Ctx: DowngradeContext<'id, 'ir>,
|
||||||
{
|
{
|
||||||
let arg = ctx.new_expr(Ir::Arg(arg));
|
let arg = ctx.new_expr(Ir::Arg(arg));
|
||||||
struct Param {
|
struct Param {
|
||||||
sym: SymId,
|
sym: StringId,
|
||||||
sym_span: TextRange,
|
sym_span: TextRange,
|
||||||
default: Option<ast::Expr>,
|
default: Option<ast::Expr>,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
@@ -1045,7 +1045,7 @@ fn downgrade_let_bindings<'id, 'ir, Ctx, F>(
|
|||||||
) -> Result<IrRef<'id, 'ir>>
|
) -> Result<IrRef<'id, 'ir>>
|
||||||
where
|
where
|
||||||
Ctx: DowngradeContext<'id, 'ir>,
|
Ctx: DowngradeContext<'id, 'ir>,
|
||||||
F: FnOnce(&mut Ctx, &[SymId]) -> Result<IrRef<'id, 'ir>>,
|
F: FnOnce(&mut Ctx, &[StringId]) -> Result<IrRef<'id, 'ir>>,
|
||||||
{
|
{
|
||||||
downgrade_rec_attrs_impl::<_, _, false>(entries, ctx, |ctx, binding_keys, _dyns| {
|
downgrade_rec_attrs_impl::<_, _, false>(entries, ctx, |ctx, binding_keys, _dyns| {
|
||||||
body_fn(ctx, binding_keys)
|
body_fn(ctx, binding_keys)
|
||||||
@@ -1081,7 +1081,7 @@ where
|
|||||||
Ctx: DowngradeContext<'id, 'ir>,
|
Ctx: DowngradeContext<'id, 'ir>,
|
||||||
F: FnOnce(
|
F: FnOnce(
|
||||||
&mut Ctx,
|
&mut Ctx,
|
||||||
&[SymId],
|
&[StringId],
|
||||||
&[(IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)],
|
&[(IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)],
|
||||||
) -> Result<IrRef<'id, 'ir>>,
|
) -> Result<IrRef<'id, 'ir>>,
|
||||||
{
|
{
|
||||||
@@ -1106,7 +1106,7 @@ where
|
|||||||
fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
|
fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
|
||||||
entries: &[ast::Entry],
|
entries: &[ast::Entry],
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<HashMap<'ir, SymId, (IrRef<'id, 'ir>, TextRange)>> {
|
) -> Result<HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>> {
|
||||||
let mut inherit_lookups = HashMap::new_in(ctx.bump());
|
let mut inherit_lookups = HashMap::new_in(ctx.bump());
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if let ast::Entry::Inherit(inherit) = entry
|
if let ast::Entry::Inherit(inherit) = entry
|
||||||
@@ -1128,7 +1128,7 @@ fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
|
|||||||
fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
||||||
pending: &PendingAttrSet,
|
pending: &PendingAttrSet,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<HashSet<SymId>> {
|
) -> Result<HashSet<StringId>> {
|
||||||
let mut binding_syms = HashSet::new();
|
let mut binding_syms = HashSet::new();
|
||||||
|
|
||||||
for (sym, (_, span)) in &pending.stcs {
|
for (sym, (_, span)) in &pending.stcs {
|
||||||
@@ -1146,7 +1146,7 @@ fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const AL
|
|||||||
|
|
||||||
fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
||||||
pending: PendingAttrSet,
|
pending: PendingAttrSet,
|
||||||
inherit_lookups: &HashMap<SymId, (IrRef<'id, 'ir>, TextRange)>,
|
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<FinalizedAttrSet<'id, 'ir>> {
|
) -> Result<FinalizedAttrSet<'id, 'ir>> {
|
||||||
let mut stcs = HashMap::new_in(ctx.bump());
|
let mut stcs = HashMap::new_in(ctx.bump());
|
||||||
@@ -1176,7 +1176,7 @@ fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_D
|
|||||||
|
|
||||||
fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
|
||||||
value: PendingValue,
|
value: PendingValue,
|
||||||
inherit_lookups: &HashMap<SymId, (IrRef<'id, 'ir>, TextRange)>,
|
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<IrRef<'id, 'ir>> {
|
) -> Result<IrRef<'id, 'ir>> {
|
||||||
match value {
|
match value {
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use deno_core::error::JsError;
|
|
||||||
use deno_error::JsErrorClass as _;
|
|
||||||
use itertools::Itertools as _;
|
|
||||||
use miette::{Diagnostic, NamedSource, SourceSpan};
|
use miette::{Diagnostic, NamedSource, SourceSpan};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::runtime::RuntimeContext;
|
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Box<Error>>;
|
pub type Result<T> = core::result::Result<T, Box<Error>>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -229,127 +224,3 @@ pub struct StackFrame {
|
|||||||
#[source_code]
|
#[source_code]
|
||||||
pub src: NamedSource<Arc<str>>,
|
pub src: NamedSource<Arc<str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_STACK_FRAMES: usize = 20;
|
|
||||||
const FRAMES_AT_START: usize = 15;
|
|
||||||
const FRAMES_AT_END: usize = 5;
|
|
||||||
|
|
||||||
pub(crate) fn parse_js_error(error: Box<JsError>, ctx: &impl RuntimeContext) -> Error {
|
|
||||||
let (span, src, frames) = if let Some(stack) = &error.stack {
|
|
||||||
let mut frames = parse_frames(stack, ctx);
|
|
||||||
|
|
||||||
if let Some(last_frame) = frames.pop() {
|
|
||||||
(
|
|
||||||
Some(text_range_to_source_span(last_frame.span)),
|
|
||||||
Some(last_frame.src.into()),
|
|
||||||
frames,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, None, frames)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(None, None, Vec::new())
|
|
||||||
};
|
|
||||||
let stack_trace = if std::env::var("NIX_JS_STACK_TRACE").is_ok() {
|
|
||||||
truncate_stack_trace(frames)
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
let message = error.get_message().to_string();
|
|
||||||
let js_backtrace = error.stack.map(|stack| {
|
|
||||||
stack
|
|
||||||
.lines()
|
|
||||||
.filter(|line| !line.starts_with("NIX_STACK_FRAME:"))
|
|
||||||
.join("\n")
|
|
||||||
});
|
|
||||||
|
|
||||||
Error::EvalError {
|
|
||||||
src,
|
|
||||||
span,
|
|
||||||
message,
|
|
||||||
js_backtrace,
|
|
||||||
stack_trace,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NixStackFrame {
|
|
||||||
span: rnix::TextRange,
|
|
||||||
message: String,
|
|
||||||
src: Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NixStackFrame> for StackFrame {
|
|
||||||
fn from(NixStackFrame { span, message, src }: NixStackFrame) -> Self {
|
|
||||||
StackFrame {
|
|
||||||
span: text_range_to_source_span(span),
|
|
||||||
message,
|
|
||||||
src: src.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_frames(stack: &str, ctx: &impl RuntimeContext) -> Vec<NixStackFrame> {
|
|
||||||
let mut frames = Vec::new();
|
|
||||||
|
|
||||||
for line in stack.lines() {
|
|
||||||
// Format: NIX_STACK_FRAME:span_id:message
|
|
||||||
let Some(rest) = line.strip_prefix("NIX_STACK_FRAME:") else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let parts: Vec<&str> = rest.splitn(2, ':').collect();
|
|
||||||
|
|
||||||
if parts.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let span_id: usize = match parts[0].parse() {
|
|
||||||
Ok(id) => id,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
let (source_id, span) = ctx.get_span(span_id);
|
|
||||||
let src = ctx.get_source(source_id);
|
|
||||||
|
|
||||||
let message = if parts.len() == 2 {
|
|
||||||
parts[1].to_string()
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
frames.push(NixStackFrame { span, message, src });
|
|
||||||
}
|
|
||||||
|
|
||||||
frames.dedup_by(|a, b| a.span == b.span && a.message == b.message);
|
|
||||||
|
|
||||||
frames
|
|
||||||
}
|
|
||||||
|
|
||||||
fn truncate_stack_trace(frames: Vec<NixStackFrame>) -> Vec<StackFrame> {
|
|
||||||
let reversed: Vec<_> = frames.into_iter().rev().collect();
|
|
||||||
let total = reversed.len();
|
|
||||||
|
|
||||||
if total <= MAX_STACK_FRAMES {
|
|
||||||
return reversed.into_iter().map(Into::into).collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
let omitted_count = total - FRAMES_AT_START - FRAMES_AT_END;
|
|
||||||
|
|
||||||
reversed
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, frame)| {
|
|
||||||
if i < FRAMES_AT_START {
|
|
||||||
Some(frame.into())
|
|
||||||
} else if i == FRAMES_AT_START {
|
|
||||||
Some(StackFrame {
|
|
||||||
span: text_range_to_source_span(frame.span),
|
|
||||||
message: format!("... ({} more frames omitted)", omitted_count),
|
|
||||||
src: frame.src.into(),
|
|
||||||
})
|
|
||||||
} else if i >= total - FRAMES_AT_END {
|
|
||||||
Some(frame.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,6 @@ use nix_compat::nixhash::HashAlgo;
|
|||||||
use nix_compat::nixhash::NixHash;
|
use nix_compat::nixhash::NixHash;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
use crate::runtime::OpStateExt;
|
|
||||||
use crate::runtime::RuntimeContext;
|
|
||||||
use crate::store::Store as _;
|
use crate::store::Store as _;
|
||||||
|
|
||||||
mod archive;
|
mod archive;
|
||||||
@@ -20,7 +18,6 @@ pub use download::Downloader;
|
|||||||
pub use metadata_cache::MetadataCache;
|
pub use metadata_cache::MetadataCache;
|
||||||
|
|
||||||
use crate::nar;
|
use crate::nar;
|
||||||
use crate::runtime::NixRuntimeError;
|
|
||||||
|
|
||||||
#[derive(ToV8)]
|
#[derive(ToV8)]
|
||||||
pub struct FetchUrlResult {
|
pub struct FetchUrlResult {
|
||||||
@@ -306,11 +303,3 @@ fn normalize_hash(hash: &str) -> String {
|
|||||||
}
|
}
|
||||||
hash.to_string()
|
hash.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_ops<Ctx: RuntimeContext>() -> Vec<deno_core::OpDecl> {
|
|
||||||
vec![
|
|
||||||
op_fetch_url::<Ctx>(),
|
|
||||||
op_fetch_tarball::<Ctx>(),
|
|
||||||
op_fetch_git::<Ctx>(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
File renamed without changes.
@@ -10,7 +10,7 @@ impl FetcherCache {
|
|||||||
pub fn new() -> Result<Self, std::io::Error> {
|
pub fn new() -> Result<Self, std::io::Error> {
|
||||||
let base_dir = dirs::cache_dir()
|
let base_dir = dirs::cache_dir()
|
||||||
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||||
.join("nix-js")
|
.join("fix")
|
||||||
.join("fetchers");
|
.join("fetchers");
|
||||||
|
|
||||||
fs::create_dir_all(&base_dir)?;
|
fs::create_dir_all(&base_dir)?;
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -4,6 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use bumpalo::{Bump, boxed::Box, collections::Vec};
|
use bumpalo::{Bump, boxed::Box, collections::Vec};
|
||||||
|
use gc_arena::Collect;
|
||||||
use ghost_cell::{GhostCell, GhostToken};
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
use rnix::{TextRange, ast};
|
use rnix::{TextRange, ast};
|
||||||
use string_interner::symbol::SymbolU32;
|
use string_interner::symbol::SymbolU32;
|
||||||
@@ -23,6 +24,10 @@ impl<'id, 'ir> IrRef<'id, 'ir> {
|
|||||||
Self(bump.alloc(GhostCell::new(ir)))
|
Self(bump.alloc(GhostCell::new(ir)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow<'a>(&'a self, token: &'a GhostToken<'id>) -> &'a Ir<'ir, Self> {
|
||||||
|
self.0.borrow(token)
|
||||||
|
}
|
||||||
|
|
||||||
/// Freeze a mutable IR reference into a read-only one, consuming the
|
/// Freeze a mutable IR reference into a read-only one, consuming the
|
||||||
/// `GhostToken` to prevent any further mutation.
|
/// `GhostToken` to prevent any further mutation.
|
||||||
///
|
///
|
||||||
@@ -42,13 +47,6 @@ impl<'id, 'ir> IrRef<'id, 'ir> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'id, 'ir> Deref for IrRef<'id, 'ir> {
|
|
||||||
type Target = GhostCell<'id, Ir<'ir, IrRef<'id, 'ir>>>;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct RawIrRef<'ir>(&'ir Ir<'ir, Self>);
|
pub struct RawIrRef<'ir>(&'ir Ir<'ir, Self>);
|
||||||
@@ -68,7 +66,7 @@ pub enum Ir<'ir, Ref> {
|
|||||||
Null,
|
Null,
|
||||||
Str(Box<'ir, String>),
|
Str(Box<'ir, String>),
|
||||||
AttrSet {
|
AttrSet {
|
||||||
stcs: HashMap<'ir, SymId, (Ref, TextRange)>,
|
stcs: HashMap<'ir, StringId, (Ref, TextRange)>,
|
||||||
dyns: Vec<'ir, (Ref, Ref, TextRange)>,
|
dyns: Vec<'ir, (Ref, Ref, TextRange)>,
|
||||||
},
|
},
|
||||||
List {
|
List {
|
||||||
@@ -119,7 +117,7 @@ pub enum Ir<'ir, Ref> {
|
|||||||
body: Ref,
|
body: Ref,
|
||||||
thunks: Vec<'ir, (ThunkId, Ref)>,
|
thunks: Vec<'ir, (ThunkId, Ref)>,
|
||||||
},
|
},
|
||||||
WithLookup(SymId),
|
WithLookup(StringId),
|
||||||
|
|
||||||
// Function related
|
// Function related
|
||||||
Func {
|
Func {
|
||||||
@@ -137,7 +135,7 @@ pub enum Ir<'ir, Ref> {
|
|||||||
|
|
||||||
// Builtins
|
// Builtins
|
||||||
Builtins,
|
Builtins,
|
||||||
Builtin(SymId),
|
Builtin(StringId),
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
TopLevel {
|
TopLevel {
|
||||||
@@ -146,19 +144,26 @@ pub enum Ir<'ir, Ref> {
|
|||||||
},
|
},
|
||||||
Thunk(ThunkId),
|
Thunk(ThunkId),
|
||||||
CurPos(TextRange),
|
CurPos(TextRange),
|
||||||
ReplBinding(SymId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(SymId),
|
ScopedImportBinding(StringId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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);
|
||||||
|
|
||||||
pub type SymId = SymbolU32;
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Collect)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub struct StringId(pub SymbolU32);
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ArgId(pub usize);
|
pub struct ArgId(pub u32);
|
||||||
|
|
||||||
|
#[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)]
|
||||||
@@ -169,7 +174,7 @@ pub enum Attr<Ref> {
|
|||||||
Dynamic(Ref, TextRange),
|
Dynamic(Ref, TextRange),
|
||||||
/// A static attribute key.
|
/// A static attribute key.
|
||||||
/// Example: `attrs.key`
|
/// Example: `attrs.key`
|
||||||
Str(SymId, TextRange),
|
Str(StringId, TextRange),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The kinds of binary operations supported in Nix.
|
/// The kinds of binary operations supported in Nix.
|
||||||
@@ -248,8 +253,8 @@ impl From<ast::UnaryOpKind> for UnOpKind {
|
|||||||
/// Describes the parameters of a function.
|
/// Describes the parameters of a function.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Param<'ir> {
|
pub struct Param<'ir> {
|
||||||
pub required: Vec<'ir, (SymId, TextRange)>,
|
pub required: Vec<'ir, (StringId, TextRange)>,
|
||||||
pub optional: Vec<'ir, (SymId, TextRange)>,
|
pub optional: Vec<'ir, (StringId, TextRange)>,
|
||||||
pub ellipsis: bool,
|
pub ellipsis: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
#![warn(clippy::unwrap_used)]
|
#![warn(clippy::unwrap_used)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
pub mod context;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
pub mod runtime;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
mod bytecode;
|
|
||||||
mod codegen;
|
mod codegen;
|
||||||
mod derivation;
|
mod derivation;
|
||||||
mod disassembler;
|
mod disassembler;
|
||||||
mod downgrade;
|
mod downgrade;
|
||||||
mod fetcher;
|
// mod fetcher;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod nar;
|
mod nar;
|
||||||
mod nix_utils;
|
mod nix_utils;
|
||||||
mod runtime;
|
|
||||||
mod store;
|
mod store;
|
||||||
mod string_context;
|
mod string_context;
|
||||||
|
|
||||||
File renamed without changes.
@@ -3,23 +3,15 @@ use std::process::exit;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use fix::error::Source;
|
||||||
|
use fix::runtime::Runtime;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use nix_js::context::Context;
|
|
||||||
use nix_js::error::Source;
|
|
||||||
use rustyline::DefaultEditor;
|
use rustyline::DefaultEditor;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "nix-js", about = "Nix expression evaluator")]
|
#[command(name = "nix-js", about = "Nix expression evaluator")]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
#[arg(long, value_name = "HOST:PORT", num_args = 0..=1, default_missing_value = "127.0.0.1:9229")]
|
|
||||||
inspect: Option<String>,
|
|
||||||
|
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
#[arg(long, value_name = "HOST:PORT", num_args = 0..=1, default_missing_value = "127.0.0.1:9229")]
|
|
||||||
inspect_brk: Option<String>,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Command,
|
command: Command,
|
||||||
}
|
}
|
||||||
@@ -48,28 +40,7 @@ struct ExprSource {
|
|||||||
file: Option<PathBuf>,
|
file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_context(#[cfg(feature = "inspector")] cli: &Cli) -> Result<Context> {
|
fn run_compile(runtime: &mut Runtime, src: ExprSource, silent: bool) -> Result<()> {
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
{
|
|
||||||
let (addr_str, wait) = if let Some(ref addr) = cli.inspect_brk {
|
|
||||||
(Some(addr.as_str()), true)
|
|
||||||
} else if let Some(ref addr) = cli.inspect {
|
|
||||||
(Some(addr.as_str()), false)
|
|
||||||
} else {
|
|
||||||
(None, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(addr_str) = addr_str {
|
|
||||||
let addr: std::net::SocketAddr = addr_str
|
|
||||||
.parse()
|
|
||||||
.map_err(|e| anyhow::anyhow!("invalid inspector address '{}': {}", addr_str, e))?;
|
|
||||||
return Ok(Context::new_with_inspector(addr, wait)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Context::new()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_compile(context: &mut Context, src: ExprSource, silent: bool) -> Result<()> {
|
|
||||||
let src = if let Some(expr) = src.expr {
|
let src = if let Some(expr) = src.expr {
|
||||||
Source::new_eval(expr)?
|
Source::new_eval(expr)?
|
||||||
} else if let Some(file) = src.file {
|
} else if let Some(file) = src.file {
|
||||||
@@ -77,10 +48,11 @@ fn run_compile(context: &mut Context, src: ExprSource, silent: bool) -> Result<(
|
|||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
match context.compile_bytecode(src) {
|
todo!()
|
||||||
|
/* match runtime.compile_bytecode(src) {
|
||||||
Ok(compiled) => {
|
Ok(compiled) => {
|
||||||
if !silent {
|
if !silent {
|
||||||
println!("{}", context.disassemble_colored(&compiled));
|
println!("{}", runtime.disassemble_colored(&compiled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -88,12 +60,10 @@ fn run_compile(context: &mut Context, src: ExprSource, silent: bool) -> Result<(
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#[cfg(feature = "inspector")]
|
Ok(()) */
|
||||||
context.wait_for_inspector_disconnect();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_eval(context: &mut Context, src: ExprSource) -> Result<()> {
|
fn run_eval(runtime: &mut Runtime, src: ExprSource) -> Result<()> {
|
||||||
let src = if let Some(expr) = src.expr {
|
let src = if let Some(expr) = src.expr {
|
||||||
Source::new_eval(expr)?
|
Source::new_eval(expr)?
|
||||||
} else if let Some(file) = src.file {
|
} else if let Some(file) = src.file {
|
||||||
@@ -101,7 +71,7 @@ fn run_eval(context: &mut Context, src: ExprSource) -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
match context.eval_deep(src) {
|
match runtime.eval_deep(src) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
println!("{}", value.display_compat());
|
println!("{}", value.display_compat());
|
||||||
}
|
}
|
||||||
@@ -110,12 +80,10 @@ fn run_eval(context: &mut Context, src: ExprSource) -> Result<()> {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
context.wait_for_inspector_disconnect();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_repl(context: &mut Context) -> Result<()> {
|
fn run_repl(runtime: &mut Runtime) -> Result<()> {
|
||||||
let mut rl = DefaultEditor::new()?;
|
let mut rl = DefaultEditor::new()?;
|
||||||
let mut scope = HashSet::new();
|
let mut scope = HashSet::new();
|
||||||
const RE: ere::Regex<3> = ere::compile_regex!("^[ \t]*([a-zA-Z_][a-zA-Z0-9_'-]*)[ \t]*(.*)$");
|
const RE: ere::Regex<3> = ere::compile_regex!("^[ \t]*([a-zA-Z_][a-zA-Z0-9_'-]*)[ \t]*(.*)$");
|
||||||
@@ -134,20 +102,20 @@ fn run_repl(context: &mut Context) -> Result<()> {
|
|||||||
eprintln!("Error: missing expression after '='");
|
eprintln!("Error: missing expression after '='");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match context.add_binding(ident, expr, &mut scope) {
|
match runtime.add_binding(ident, expr, &mut scope) {
|
||||||
Ok(value) => println!("{} = {}", ident, value),
|
Ok(value) => println!("{} = {}", ident, value),
|
||||||
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let src = Source::new_repl(line)?;
|
let src = Source::new_repl(line)?;
|
||||||
match context.eval_repl(src, &scope) {
|
match runtime.eval_repl(src, &scope) {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(value) => println!("{value}"),
|
||||||
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let src = Source::new_repl(line)?;
|
let src = Source::new_repl(line)?;
|
||||||
match context.eval_shallow(src) {
|
match runtime.eval_repl(src, &scope) {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(value) => println!("{value}"),
|
||||||
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
Err(err) => eprintln!("{:?}", miette::Report::new(*err)),
|
||||||
}
|
}
|
||||||
@@ -170,18 +138,15 @@ fn run_repl(context: &mut Context) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
nix_js::logging::init_logging();
|
fix::logging::init_logging();
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
let mut context = create_context(
|
let mut runtime = Runtime::new()?;
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
&cli,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Command::Compile { source, silent } => run_compile(&mut context, source, silent),
|
Command::Compile { source, silent } => run_compile(&mut runtime, source, silent),
|
||||||
Command::Eval { source } => run_eval(&mut context, source),
|
Command::Eval { source } => run_eval(&mut runtime, source),
|
||||||
Command::Repl => run_repl(&mut context),
|
Command::Repl => run_repl(&mut runtime),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,531 @@
|
|||||||
|
use std::hash::BuildHasher;
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use gc_arena::{Arena, Rootable, arena::CollectionPhase};
|
||||||
|
use ghost_cell::{GhostCell, GhostToken};
|
||||||
|
use hashbrown::{DefaultHashBuilder, HashMap, HashSet, HashTable};
|
||||||
|
use rnix::TextRange;
|
||||||
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
|
use crate::codegen::{BytecodeContext, InstructionPtr};
|
||||||
|
use crate::downgrade::{Downgrade as _, DowngradeContext};
|
||||||
|
use crate::error::{Error, Result, Source};
|
||||||
|
use crate::ir::{ArgId, Ir, IrKey, IrRef, RawIrRef, StringId, ThunkId, ir_content_eq};
|
||||||
|
use crate::runtime::builtins::new_builtins_env;
|
||||||
|
use crate::store::{DaemonStore, StoreConfig};
|
||||||
|
use crate::value::Symbol;
|
||||||
|
|
||||||
|
mod builtins;
|
||||||
|
mod stack;
|
||||||
|
mod value;
|
||||||
|
mod vm;
|
||||||
|
use vm::{Action, VM};
|
||||||
|
|
||||||
|
pub struct Runtime {
|
||||||
|
bytecode: Vec<u8>,
|
||||||
|
global_env: HashMap<StringId, Ir<'static, RawIrRef<'static>>>,
|
||||||
|
sources: Vec<Source>,
|
||||||
|
store: DaemonStore,
|
||||||
|
spans: Vec<(usize, TextRange)>,
|
||||||
|
thunk_count: usize,
|
||||||
|
strings: DefaultStringInterner,
|
||||||
|
arena: Arena<Rootable![VM<'_>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
const COLLECTOR_GRANULARITY: f64 = 1024.0;
|
||||||
|
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let mut strings = DefaultStringInterner::new();
|
||||||
|
let global_env = new_builtins_env(&mut strings);
|
||||||
|
|
||||||
|
let config = StoreConfig::from_env();
|
||||||
|
let store = DaemonStore::connect(&config.daemon_socket)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
global_env,
|
||||||
|
store,
|
||||||
|
strings,
|
||||||
|
thunk_count: 0,
|
||||||
|
bytecode: Vec::new(),
|
||||||
|
sources: Vec::new(),
|
||||||
|
spans: Vec::new(),
|
||||||
|
arena: Arena::new(|mc| VM::new(mc)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval(&mut self, source: Source) -> Result<crate::value::Value> {
|
||||||
|
let root = self.downgrade(source, None)?;
|
||||||
|
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
||||||
|
self.run(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_shallow(&mut self, _source: Source) -> Result<crate::value::Value> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_deep(&mut self, source: Source) -> Result<crate::value::Value> {
|
||||||
|
// FIXME: deep
|
||||||
|
let root = self.downgrade(source, None)?;
|
||||||
|
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
||||||
|
self.run(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_repl(
|
||||||
|
&mut self,
|
||||||
|
source: Source,
|
||||||
|
scope: &HashSet<StringId>,
|
||||||
|
) -> Result<crate::value::Value> {
|
||||||
|
// FIXME: shallow
|
||||||
|
let root = self.downgrade(source, Some(Scope::Repl(scope)))?;
|
||||||
|
let ip = crate::codegen::compile_bytecode(root.as_ref(), self);
|
||||||
|
self.run(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_binding(
|
||||||
|
&mut self,
|
||||||
|
_ident: &str,
|
||||||
|
_expr: &str,
|
||||||
|
_scope: &mut HashSet<StringId>,
|
||||||
|
) -> Result<crate::value::Value> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_ctx<'a, 'bump, 'id>(
|
||||||
|
&'a mut self,
|
||||||
|
bump: &'bump Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
extra_scope: Option<Scope<'a>>,
|
||||||
|
) -> DowngradeCtx<'a, 'id, 'bump> {
|
||||||
|
let Runtime {
|
||||||
|
global_env,
|
||||||
|
sources,
|
||||||
|
thunk_count,
|
||||||
|
strings,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
DowngradeCtx {
|
||||||
|
bump,
|
||||||
|
token,
|
||||||
|
strings,
|
||||||
|
source: sources.last().unwrap().clone(),
|
||||||
|
scopes: [Scope::Global(global_env)].into_iter().chain(extra_scope.into_iter()).collect(),
|
||||||
|
with_scope_count: 0,
|
||||||
|
arg_count: 0,
|
||||||
|
thunk_count,
|
||||||
|
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade<'a>(&'a mut self, source: Source, extra_scope: Option<Scope<'a>>) -> Result<OwnedIr> {
|
||||||
|
tracing::debug!("Parsing Nix expression");
|
||||||
|
|
||||||
|
self.sources.push(source.clone());
|
||||||
|
|
||||||
|
let root = rnix::Root::parse(&source.src);
|
||||||
|
handle_parse_error(root.errors(), source).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 ir = self
|
||||||
|
.downgrade_ctx(&bump, token, extra_scope)
|
||||||
|
.downgrade_toplevel(expr)?;
|
||||||
|
let ir = unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) };
|
||||||
|
Ok(OwnedIr { _bump: bump, ir })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, ip: InstructionPtr) -> Result<crate::value::Value> {
|
||||||
|
let mut pc = ip.0;
|
||||||
|
loop {
|
||||||
|
let Runtime {
|
||||||
|
bytecode,
|
||||||
|
strings,
|
||||||
|
arena,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let action =
|
||||||
|
arena.mutate_root(|mc, root| root.run_batch(bytecode, &mut pc, mc, strings));
|
||||||
|
match action {
|
||||||
|
Action::NeedGc => {
|
||||||
|
if self.arena.collection_phase() == CollectionPhase::Sweeping {
|
||||||
|
self.arena.collect_debt();
|
||||||
|
} else if let Some(marked) = self.arena.mark_debt() {
|
||||||
|
marked.start_sweeping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::Done(done) => {
|
||||||
|
break done;
|
||||||
|
}
|
||||||
|
Action::Continue => (),
|
||||||
|
Action::IoRequest(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
strings: &'ctx mut DefaultStringInterner,
|
||||||
|
source: Source,
|
||||||
|
scopes: Vec<Scope<'ctx>>,
|
||||||
|
with_scope_count: u32,
|
||||||
|
arg_count: u32,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_thunk<'id>(ir: IrRef<'id, '_>, token: &GhostToken<'id>) -> bool {
|
||||||
|
!matches!(
|
||||||
|
ir.borrow(token),
|
||||||
|
Ir::Builtin(_)
|
||||||
|
| Ir::Builtins
|
||||||
|
| Ir::Int(_)
|
||||||
|
| Ir::Float(_)
|
||||||
|
| Ir::Bool(_)
|
||||||
|
| Ir::Null
|
||||||
|
| Ir::Str(_)
|
||||||
|
| Ir::Thunk(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'id, 'ir> DowngradeCtx<'ctx, 'id, 'ir> {
|
||||||
|
fn new(
|
||||||
|
bump: &'ir Bump,
|
||||||
|
token: GhostToken<'id>,
|
||||||
|
symbols: &'ctx mut DefaultStringInterner,
|
||||||
|
global: &'ctx HashMap<StringId, Ir<'static, RawIrRef<'static>>>,
|
||||||
|
extra_scope: Option<Scope<'ctx>>,
|
||||||
|
thunk_count: &'ctx mut usize,
|
||||||
|
source: Source,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bump,
|
||||||
|
token,
|
||||||
|
strings: symbols,
|
||||||
|
source,
|
||||||
|
scopes: std::iter::once(Scope::Global(global))
|
||||||
|
.chain(extra_scope)
|
||||||
|
.collect(),
|
||||||
|
thunk_count,
|
||||||
|
arg_count: 0,
|
||||||
|
with_scope_count: 0,
|
||||||
|
thunk_scopes: vec![ThunkScope::new_in(bump)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx: 'ir, 'id, 'ir> DowngradeContext<'id, 'ir> for DowngradeCtx<'ctx, 'id, 'ir> {
|
||||||
|
fn new_expr(&self, expr: Ir<'ir, IrRef<'id, 'ir>>) -> IrRef<'id, 'ir> {
|
||||||
|
IrRef::new(self.bump.alloc(GhostCell::new(expr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_arg(&mut self) -> ArgId {
|
||||||
|
self.arg_count += 1;
|
||||||
|
ArgId(self.arg_count - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir> {
|
||||||
|
if !should_thunk(ir, &self.token) {
|
||||||
|
return ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached = self
|
||||||
|
.thunk_scopes
|
||||||
|
.last()
|
||||||
|
.expect("no active cache scope")
|
||||||
|
.lookup_cache(ir, &self.token);
|
||||||
|
|
||||||
|
if let Some(id) = cached {
|
||||||
|
return IrRef::alloc(self.bump, Ir::Thunk(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.token);
|
||||||
|
IrRef::alloc(self.bump, Ir::Thunk(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_sym(&mut self, sym: String) -> StringId {
|
||||||
|
StringId(self.strings.get_or_intern(sym))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sym(&self, id: StringId) -> Symbol<'_> {
|
||||||
|
self.strings.resolve(id.0).expect("no symbol found").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, sym: StringId, span: TextRange) -> Result<IrRef<'id, 'ir>> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
match scope {
|
||||||
|
&Scope::Global(global_scope) => {
|
||||||
|
if let Some(expr) = global_scope.get(&sym) {
|
||||||
|
let ir = match expr {
|
||||||
|
Ir::Builtins => Ir::Builtins,
|
||||||
|
Ir::Builtin(s) => Ir::Builtin(*s),
|
||||||
|
Ir::Bool(b) => Ir::Bool(*b),
|
||||||
|
Ir::Null => Ir::Null,
|
||||||
|
_ => unreachable!("globals should only contain leaf IR nodes"),
|
||||||
|
};
|
||||||
|
return Ok(self.new_expr(ir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Repl(repl_bindings) => {
|
||||||
|
if repl_bindings.contains(&sym) {
|
||||||
|
return Ok(self.new_expr(Ir::ReplBinding(sym)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::ScopedImport(scoped_bindings) => {
|
||||||
|
if scoped_bindings.contains(&sym) {
|
||||||
|
return Ok(self.new_expr(Ir::ScopedImportBinding(sym)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::Let(let_scope) => {
|
||||||
|
if let Some(&expr) = let_scope.get(&sym) {
|
||||||
|
return Ok(self.new_expr(Ir::Thunk(expr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Scope::Param(param_sym, id) => {
|
||||||
|
if param_sym == sym {
|
||||||
|
return Ok(self.new_expr(Ir::Arg(id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.with_scope_count > 0 {
|
||||||
|
Ok(self.new_expr(Ir::WithLookup(sym)))
|
||||||
|
} else {
|
||||||
|
Err(Error::downgrade_error(
|
||||||
|
format!("'{}' not found", self.get_sym(sym)),
|
||||||
|
self.get_current_source(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_source(&self) -> Source {
|
||||||
|
self.source.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_let_scope<F, R>(&mut self, keys: &[StringId], f: F) -> Result<R>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Result<(bumpalo::collections::Vec<'ir, IrRef<'id, 'ir>>, R)>,
|
||||||
|
{
|
||||||
|
let base = *self.thunk_count;
|
||||||
|
*self.thunk_count = self
|
||||||
|
.thunk_count
|
||||||
|
.checked_add(keys.len())
|
||||||
|
.expect("thunk id overflow");
|
||||||
|
let iter = keys.iter().enumerate().map(|(offset, &key)| {
|
||||||
|
(
|
||||||
|
key,
|
||||||
|
ThunkId(unsafe { base.checked_add(offset).unwrap_unchecked() }),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
self.scopes.push(Scope::Let(iter.collect()));
|
||||||
|
let (vals, ret) = {
|
||||||
|
let mut guard = ScopeGuard { ctx: self };
|
||||||
|
f(guard.as_ctx())?
|
||||||
|
};
|
||||||
|
assert_eq!(keys.len(), vals.len());
|
||||||
|
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
|
||||||
|
scope.extend_bindings((base..base + keys.len()).map(ThunkId).zip(vals));
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_param_scope<F, R>(&mut self, param: StringId, arg: ArgId, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R,
|
||||||
|
{
|
||||||
|
self.scopes.push(Scope::Param(param, arg));
|
||||||
|
let mut guard = ScopeGuard { ctx: self };
|
||||||
|
f(guard.as_ctx())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_with_scope<F, R>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R,
|
||||||
|
{
|
||||||
|
self.with_scope_count += 1;
|
||||||
|
let ret = f(self);
|
||||||
|
self.with_scope_count -= 1;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_thunk_scope<F, R>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
) -> (
|
||||||
|
R,
|
||||||
|
bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R,
|
||||||
|
{
|
||||||
|
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> DowngradeCtx<'ctx, 'id, 'ir> {
|
||||||
|
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;
|
||||||
|
let ir = IrRef::alloc(self.bump, Ir::TopLevel { body, thunks });
|
||||||
|
Ok(ir.freeze(self.token))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ThunkScope<'id, 'ir> {
|
||||||
|
bindings: bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>,
|
||||||
|
cache: HashTable<(IrRef<'id, 'ir>, ThunkId)>,
|
||||||
|
hasher: DefaultHashBuilder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
||||||
|
fn new_in(bump: &'ir Bump) -> Self {
|
||||||
|
Self {
|
||||||
|
bindings: bumpalo::collections::Vec::new_in(bump),
|
||||||
|
cache: HashTable::new(),
|
||||||
|
hasher: DefaultHashBuilder::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_cache(&self, key: IrRef<'id, 'ir>, token: &GhostToken<'id>) -> Option<ThunkId> {
|
||||||
|
let hash = self.hasher.hash_one(IrKey(key, token));
|
||||||
|
self.cache
|
||||||
|
.find(hash, |&(ir, _)| ir_content_eq(key, ir, token))
|
||||||
|
.map(|&(_, id)| id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_binding(&mut self, id: ThunkId, ir: IrRef<'id, 'ir>, token: &GhostToken<'id>) {
|
||||||
|
self.bindings.push((id, ir));
|
||||||
|
let hash = self.hasher.hash_one(IrKey(ir, token));
|
||||||
|
self.cache.insert_unique(hash, (ir, id), |&(ir, _)| {
|
||||||
|
self.hasher.hash_one(IrKey(ir, token))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_bindings(&mut self, iter: impl IntoIterator<Item = (ThunkId, IrRef<'id, 'ir>)>) {
|
||||||
|
self.bindings.extend(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Scope<'ctx> {
|
||||||
|
Global(&'ctx HashMap<StringId, Ir<'static, RawIrRef<'static>>>),
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport(HashSet<StringId>),
|
||||||
|
Let(HashMap<StringId, ThunkId>),
|
||||||
|
Param(StringId, ArgId),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScopeGuard<'a, 'ctx, 'id, 'ir> {
|
||||||
|
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ScopeGuard<'_, '_, '_, '_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ctx.scopes.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'id, 'ir, 'ctx> ScopeGuard<'_, 'ctx, 'id, 'ir> {
|
||||||
|
fn as_ctx(&mut self) -> &mut DowngradeCtx<'ctx, 'id, 'ir> {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OwnedIr {
|
||||||
|
_bump: Bump,
|
||||||
|
ir: RawIrRef<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedIr {
|
||||||
|
unsafe fn new(ir: RawIrRef<'_>, bump: Bump) -> Self {
|
||||||
|
Self {
|
||||||
|
_bump: bump,
|
||||||
|
ir: unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_ref(&self) -> RawIrRef<'_> {
|
||||||
|
self.ir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytecodeContext for Runtime {
|
||||||
|
fn intern_string(&mut self, s: &str) -> StringId {
|
||||||
|
StringId(self.strings.get_or_intern(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_span(&mut self, range: TextRange) -> u32 {
|
||||||
|
let id = self.spans.len();
|
||||||
|
let source_id = self
|
||||||
|
.sources
|
||||||
|
.len()
|
||||||
|
.checked_sub(1)
|
||||||
|
.expect("current_source not set");
|
||||||
|
self.spans.push((source_id, range));
|
||||||
|
id as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code(&self) -> &[u8] {
|
||||||
|
&self.bytecode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code_mut(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.bytecode
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
|
use crate::ir::{Ir, RawIrRef, StringId};
|
||||||
|
|
||||||
|
pub(super) fn new_builtins_env(
|
||||||
|
interner: &mut DefaultStringInterner,
|
||||||
|
) -> HashMap<StringId, Ir<'static, RawIrRef<'static>>> {
|
||||||
|
let mut builtins = HashMap::new();
|
||||||
|
let builtins_sym = StringId(interner.get_or_intern("builtins"));
|
||||||
|
builtins.insert(builtins_sym, Ir::Builtins);
|
||||||
|
|
||||||
|
let free_globals = [
|
||||||
|
"abort",
|
||||||
|
"baseNameOf",
|
||||||
|
"break",
|
||||||
|
"dirOf",
|
||||||
|
"derivation",
|
||||||
|
"derivationStrict",
|
||||||
|
"fetchGit",
|
||||||
|
"fetchMercurial",
|
||||||
|
"fetchTarball",
|
||||||
|
"fetchTree",
|
||||||
|
"fromTOML",
|
||||||
|
"import",
|
||||||
|
"isNull",
|
||||||
|
"map",
|
||||||
|
"placeholder",
|
||||||
|
"removeAttrs",
|
||||||
|
"scopedImport",
|
||||||
|
"throw",
|
||||||
|
"toString",
|
||||||
|
];
|
||||||
|
let consts = [
|
||||||
|
("true", Ir::Bool(true)),
|
||||||
|
("false", Ir::Bool(false)),
|
||||||
|
("null", Ir::Null),
|
||||||
|
];
|
||||||
|
|
||||||
|
for name in free_globals {
|
||||||
|
let name = StringId(interner.get_or_intern(name));
|
||||||
|
let value = Ir::Builtin(name);
|
||||||
|
builtins.insert(name, value);
|
||||||
|
}
|
||||||
|
for (name, value) in consts {
|
||||||
|
let name = StringId(interner.get_or_intern(name));
|
||||||
|
builtins.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
builtins
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,89 @@
|
|||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use gc_arena::Collect;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
// FIXME: Drop???
|
||||||
|
pub(super) struct Stack<const N: usize, T> {
|
||||||
|
inner: Box<[MaybeUninit<T>; N]>,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'gc, const N: usize, T: Collect<'gc> + 'gc> Collect<'gc> for Stack<N, T> {
|
||||||
|
const NEEDS_TRACE: bool = true;
|
||||||
|
fn trace<U: gc_arena::collect::Trace<'gc>>(&self, cc: &mut U) {
|
||||||
|
for item in self.inner[..self.len].iter() {
|
||||||
|
unsafe {
|
||||||
|
item.assume_init_ref().trace(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, T> Default for Stack<N, T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, T> Stack<N, T> {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new([const { MaybeUninit::uninit() }; N]),
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn push(&mut self, val: T) -> Result<(), T> {
|
||||||
|
if self.len == N {
|
||||||
|
return Err(val);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
self.inner.get_unchecked_mut(self.len).write(val);
|
||||||
|
}
|
||||||
|
self.len += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn pop(&mut self) -> Option<T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ret = unsafe {
|
||||||
|
self.inner
|
||||||
|
.get_unchecked_mut(self.len - 1)
|
||||||
|
.assume_init_read()
|
||||||
|
};
|
||||||
|
self.len -= 1;
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn tos(&self) -> Option<&T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(unsafe { self.inner.get_unchecked(self.len - 1).assume_init_ref() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn tos_mut(&mut self) -> Option<&mut T> {
|
||||||
|
if self.len == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(unsafe { self.inner.get_unchecked_mut(self.len - 1).assume_init_mut() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn pop_n<const M: usize>(&mut self, n: usize) -> SmallVec<[T; M]> {
|
||||||
|
assert!(n <= self.len, "pop_n: not enough items on stack");
|
||||||
|
let mut result = SmallVec::new();
|
||||||
|
let start = self.len - n;
|
||||||
|
for i in start..self.len {
|
||||||
|
result.push(unsafe { self.inner.get_unchecked(i).assume_init_read() });
|
||||||
|
}
|
||||||
|
self.len = start;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,489 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use boxing::nan::raw::{RawBox, RawStore, RawTag, Value as RawValue};
|
||||||
|
use gc_arena::{Collect, Gc, Mutation, RefLock, collect::Trace};
|
||||||
|
use sealed::sealed;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
use string_interner::{Symbol, symbol::SymbolU32};
|
||||||
|
|
||||||
|
use crate::ir::StringId;
|
||||||
|
|
||||||
|
#[sealed]
|
||||||
|
pub(crate) trait Storable {
|
||||||
|
const TAG: (bool, u8);
|
||||||
|
}
|
||||||
|
pub(crate) trait InlineStorable: Storable + RawStore {}
|
||||||
|
pub(crate) trait GcStorable: Storable {}
|
||||||
|
|
||||||
|
macro_rules! define_value_types {
|
||||||
|
(
|
||||||
|
inline { $($itype:ty => $itag:expr, $iname:literal;)* }
|
||||||
|
gc { $($gtype:ty => $gtag:expr, $gname:literal;)* }
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
#[sealed]
|
||||||
|
impl Storable for $itype {
|
||||||
|
const TAG: (bool, u8) = $itag;
|
||||||
|
}
|
||||||
|
impl InlineStorable for $itype {}
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
#[sealed]
|
||||||
|
impl Storable for $gtype {
|
||||||
|
const TAG: (bool, u8) = $gtag;
|
||||||
|
}
|
||||||
|
impl GcStorable for $gtype {}
|
||||||
|
)*
|
||||||
|
|
||||||
|
const _: () = assert!(size_of::<Value<'static>>() == 8);
|
||||||
|
$(const _: () = assert!(size_of::<$itype>() <= 6);)*
|
||||||
|
$(const _: () = { let (_, val) = $itag; assert!(val >= 1 && val <= 7); };)*
|
||||||
|
$(const _: () = { let (_, val) = $gtag; assert!(val >= 1 && val <= 7); };)*
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
let tags: &[(bool, u8)] = &[$($itag),*, $($gtag),*];
|
||||||
|
let mut mask_false: u8 = 0;
|
||||||
|
let mut mask_true: u8 = 0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < tags.len() {
|
||||||
|
let (neg, val) = tags[i];
|
||||||
|
let bit = 1 << val;
|
||||||
|
if neg {
|
||||||
|
assert!(mask_true & bit == 0, "duplicate true tag id");
|
||||||
|
mask_true |= bit;
|
||||||
|
} else {
|
||||||
|
assert!(mask_false & bit == 0, "duplicate false tag id");
|
||||||
|
mask_false |= bit;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe impl<'gc> Collect<'gc> for Value<'gc> {
|
||||||
|
const NEEDS_TRACE: bool = true;
|
||||||
|
fn trace<T: Trace<'gc>>(&self, cc: &mut T) {
|
||||||
|
let Some(tag) = self.raw.tag() else { return };
|
||||||
|
match tag.neg_val() {
|
||||||
|
$(<$gtype as Storable>::TAG => unsafe {
|
||||||
|
self.load_gc::<$gtype>().trace(cc)
|
||||||
|
},)*
|
||||||
|
$(<$itype as Storable>::TAG => (),)*
|
||||||
|
(neg, val) => unreachable!("invalid tag: neg={neg}, val={val}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Value<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.tag() {
|
||||||
|
None => write!(f, "Float({:?})", unsafe {
|
||||||
|
self.raw.float().unwrap_unchecked()
|
||||||
|
}),
|
||||||
|
$(Some(<$itype as Storable>::TAG) => write!(f, "{}({:?})", $iname, unsafe {
|
||||||
|
self.as_inline::<$itype>().unwrap_unchecked()
|
||||||
|
}),)*
|
||||||
|
$(Some(<$gtype as Storable>::TAG) =>
|
||||||
|
write!(f, "{}({:?})", $gname, unsafe { self.as_gc::<$gtype>().unwrap_unchecked() }),)*
|
||||||
|
Some((neg, val)) => write!(f, "Unknown(neg={neg}, val={val})"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
define_value_types! {
|
||||||
|
inline {
|
||||||
|
i32 => (false, 1), "SmallInt";
|
||||||
|
bool => (false, 2), "Bool";
|
||||||
|
Null => (false, 3), "Null";
|
||||||
|
StringId => (false, 4), "SmallString";
|
||||||
|
PrimOp => (false, 5), "PrimOp";
|
||||||
|
}
|
||||||
|
gc {
|
||||||
|
i64 => (false, 6), "BigInt";
|
||||||
|
NixString => (false, 7), "String";
|
||||||
|
AttrSet<'_> => (true, 1), "AttrSet";
|
||||||
|
List<'_> => (true, 2), "List";
|
||||||
|
Thunk<'_> => (true, 3), "Thunk";
|
||||||
|
Closure<'_> => (true, 4), "Closure";
|
||||||
|
PrimOpApp<'_> => (true, 5), "PrimOpApp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Nix runtime value representation
|
||||||
|
///
|
||||||
|
/// NaN-boxed value fitting in 8 bytes.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct Value<'gc> {
|
||||||
|
raw: RawBox,
|
||||||
|
_marker: PhantomData<Gc<'gc, ()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Value<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: self.raw.clone(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Value<'_> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_inline(Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Value<'gc> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn mk_tag(neg: bool, val: u8) -> RawTag {
|
||||||
|
// Safety: val is asserted to be in 1..=7.
|
||||||
|
unsafe { RawTag::new_unchecked(neg, val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_raw_value(rv: RawValue) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RawBox::from_value(rv),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a GC pointer from a value with a negative tag.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The value must actually store a `Gc<'gc, T>` with the matching type.
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn load_gc<T: GcStorable>(&self) -> Gc<'gc, T> {
|
||||||
|
unsafe {
|
||||||
|
let rv = self.raw.value().unwrap_unchecked();
|
||||||
|
let ptr: *const T = <*const T as RawStore>::from_val(rv);
|
||||||
|
Gc::from_ptr(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `(negative, val)` tag, or `None` for a float.
|
||||||
|
#[inline(always)]
|
||||||
|
fn tag(&self) -> Option<(bool, u8)> {
|
||||||
|
self.raw.tag().map(|t| t.neg_val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Value<'gc> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new_float(val: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RawBox::from_float(val),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new_inline<T: InlineStorable>(val: T) -> Self {
|
||||||
|
Self::from_raw_value(RawValue::store(Self::mk_tag(T::TAG.0, T::TAG.1), val))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new_gc<T: GcStorable>(gc: Gc<'gc, T>) -> Self {
|
||||||
|
let ptr = Gc::as_ptr(gc);
|
||||||
|
Self::from_raw_value(RawValue::store(Self::mk_tag(T::TAG.0, T::TAG.1), ptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Value<'gc> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is_float(&self) -> bool {
|
||||||
|
self.raw.is_float()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is<T: Storable>(&self) -> bool {
|
||||||
|
self.tag() == Some(T::TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Value<'gc> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn as_float(&self) -> Option<f64> {
|
||||||
|
self.raw.float().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn as_inline<T: InlineStorable>(&self) -> Option<T> {
|
||||||
|
if self.is::<T>() {
|
||||||
|
Some(unsafe {
|
||||||
|
let rv = self.raw.value().unwrap_unchecked();
|
||||||
|
T::from_val(rv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn as_gc<T: GcStorable>(&self) -> Option<Gc<'gc, T>> {
|
||||||
|
if self.is::<T>() {
|
||||||
|
Some(unsafe {
|
||||||
|
let rv = self.raw.value().unwrap_unchecked();
|
||||||
|
let ptr: *const T = <*const T as RawStore>::from_val(rv);
|
||||||
|
Gc::from_ptr(ptr)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(crate) struct Null;
|
||||||
|
impl RawStore for Null {
|
||||||
|
fn to_val(self, value: &mut RawValue) {
|
||||||
|
value.set_data([0; 6]);
|
||||||
|
}
|
||||||
|
fn from_val(_: &RawValue) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawStore for StringId {
|
||||||
|
fn to_val(self, value: &mut RawValue) {
|
||||||
|
(self.0.to_usize() as u32).to_val(value);
|
||||||
|
}
|
||||||
|
fn from_val(value: &RawValue) -> Self {
|
||||||
|
Self(
|
||||||
|
SymbolU32::try_from_usize(u32::from_val(value) as usize)
|
||||||
|
.expect("failed to read StringId from Value"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Heap-allocated Nix string.
|
||||||
|
///
|
||||||
|
/// Stored on the GC heap via `Gc<'gc, NixString>`. The string data itself
|
||||||
|
/// lives in a standard `Box<str>` owned by this struct; the GC only manages
|
||||||
|
/// the outer allocation.
|
||||||
|
#[derive(Collect)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub(crate) struct NixString {
|
||||||
|
data: Box<str>,
|
||||||
|
// TODO: string context for derivation dependency tracking
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NixString {
|
||||||
|
pub(crate) fn new(s: impl Into<Box<str>>) -> Self {
|
||||||
|
Self { data: s.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_str(&self) -> &str {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for NixString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.data, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) struct AttrSet<'gc> {
|
||||||
|
pub(crate) entries: SmallVec<[(StringId, Value<'gc>); 4]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> AttrSet<'gc> {
|
||||||
|
pub(crate) fn from_sorted(entries: SmallVec<[(StringId, Value<'gc>); 4]>) -> Self {
|
||||||
|
debug_assert!(entries.is_sorted_by_key(|(key, _)| *key));
|
||||||
|
Self { entries }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup(&self, key: StringId) -> Option<Value<'gc>> {
|
||||||
|
self.entries
|
||||||
|
.binary_search_by_key(&key, |(k, _)| *k)
|
||||||
|
.ok()
|
||||||
|
.map(|i| self.entries[i].1.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has(&self, key: StringId) -> bool {
|
||||||
|
self.entries
|
||||||
|
.binary_search_by_key(&key, |(k, _)| *k)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn merge(&self, other: &Self, mc: &Mutation<'gc>) -> Gc<'gc, Self> {
|
||||||
|
use std::cmp::Ordering::*;
|
||||||
|
|
||||||
|
debug_assert!(self.entries.is_sorted_by_key(|(key, _)| *key));
|
||||||
|
debug_assert!(other.entries.is_sorted_by_key(|(key, _)| *key));
|
||||||
|
|
||||||
|
let mut entries = SmallVec::new();
|
||||||
|
let mut i = 0;
|
||||||
|
let mut j = 0;
|
||||||
|
while i < self.entries.len() && j < other.entries.len() {
|
||||||
|
match self.entries[i].0.cmp(&other.entries[j].0) {
|
||||||
|
Less => {
|
||||||
|
entries.push(self.entries[i].clone());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Greater => {
|
||||||
|
entries.push(other.entries[j].clone());
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
Equal => {
|
||||||
|
entries.push(other.entries[j].clone());
|
||||||
|
i += 1;
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.extend(other.entries[j..].iter().cloned());
|
||||||
|
entries.extend(self.entries[i..].iter().cloned());
|
||||||
|
|
||||||
|
debug_assert!(entries.is_sorted_by_key(|(key, _)| *key));
|
||||||
|
|
||||||
|
Gc::new(mc, AttrSet { entries })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) struct List<'gc> {
|
||||||
|
pub(crate) inner: SmallVec<[Value<'gc>; 4]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type Thunk<'gc> = RefLock<ThunkState<'gc>>;
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) enum ThunkState<'gc> {
|
||||||
|
Pending {
|
||||||
|
ip: u32,
|
||||||
|
env: Gc<'gc, RefLock<Env<'gc>>>,
|
||||||
|
},
|
||||||
|
Blackhole,
|
||||||
|
Evaluated(Value<'gc>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) struct Env<'gc> {
|
||||||
|
pub(crate) locals: SmallVec<[Value<'gc>; 4]>,
|
||||||
|
pub(crate) prev: Option<Gc<'gc, RefLock<Env<'gc>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Env<'gc> {
|
||||||
|
pub(crate) fn empty() -> Self {
|
||||||
|
Env {
|
||||||
|
locals: SmallVec::new(),
|
||||||
|
prev: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_arg(
|
||||||
|
arg: Value<'gc>,
|
||||||
|
n_locals: u32,
|
||||||
|
prev: Gc<'gc, RefLock<Env<'gc>>>,
|
||||||
|
) -> Self {
|
||||||
|
let mut locals = smallvec::smallvec![Value::default(); 1 + n_locals as usize];
|
||||||
|
locals[0] = arg;
|
||||||
|
Env {
|
||||||
|
locals,
|
||||||
|
prev: Some(prev),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) struct Closure<'gc> {
|
||||||
|
pub(crate) ip: u32,
|
||||||
|
pub(crate) n_locals: u32,
|
||||||
|
pub(crate) env: Gc<'gc, RefLock<Env<'gc>>>,
|
||||||
|
pub(crate) pattern: Option<Gc<'gc, PatternInfo>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub(crate) struct PatternInfo {
|
||||||
|
pub(crate) required: SmallVec<[StringId; 4]>,
|
||||||
|
pub(crate) optional: SmallVec<[StringId; 4]>,
|
||||||
|
pub(crate) ellipsis: bool,
|
||||||
|
pub(crate) param_spans: Box<[(StringId, u32)]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Collect)]
|
||||||
|
#[collect(require_static)]
|
||||||
|
pub(crate) struct PrimOp {
|
||||||
|
pub(crate) id: u8,
|
||||||
|
pub(crate) arity: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawStore for PrimOp {
|
||||||
|
fn to_val(self, value: &mut RawValue) {
|
||||||
|
value.set_data([0, 0, 0, 0, self.id, self.arity]);
|
||||||
|
}
|
||||||
|
fn from_val(value: &RawValue) -> Self {
|
||||||
|
let [.., id, arity] = *value.data();
|
||||||
|
Self { id, arity }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub(crate) struct PrimOpApp<'gc> {
|
||||||
|
pub(crate) primop: PrimOp,
|
||||||
|
pub(crate) args: SmallVec<[Value<'gc>; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct StrictValue<'gc>(Value<'gc>);
|
||||||
|
|
||||||
|
impl<'gc> StrictValue<'gc> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn try_from_forced(val: Value<'gc>) -> Option<Self> {
|
||||||
|
if !val.is::<Thunk<'gc>>() {
|
||||||
|
Some(Self(val))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn into_relaxed(self) -> Value<'gc> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Deref for StrictValue<'gc> {
|
||||||
|
type Target = Value<'gc>;
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Value<'gc> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for StrictValue<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for StrictValue<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'gc> Collect<'gc> for StrictValue<'gc> {
|
||||||
|
const NEEDS_TRACE: bool = true;
|
||||||
|
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, cc: &mut T) {
|
||||||
|
self.0.trace(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
|||||||
use nix_js::value::Value;
|
use fix::value::Value;
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use nix_js::value::{AttrSet, List, Value};
|
use fix::value::{AttrSet, List, Value};
|
||||||
|
|
||||||
use crate::utils::eval;
|
use crate::utils::eval;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use nix_js::value::Value;
|
use fix::value::Value;
|
||||||
|
|
||||||
use crate::utils::eval_result;
|
use crate::utils::eval_result;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use nix_js::value::Value;
|
use fix::value::Value;
|
||||||
|
|
||||||
use crate::utils::{eval_deep, eval_deep_result};
|
use crate::utils::{eval_deep, eval_deep_result};
|
||||||
|
|
||||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
|||||||
use nix_js::value::{List, Value};
|
use fix::value::{List, Value};
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use nix_js::value::Value;
|
use fix::value::Value;
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use nix_js::context::Context;
|
use fix::error::Source;
|
||||||
use nix_js::error::Source;
|
use fix::runtime::Runtime;
|
||||||
use nix_js::value::Value;
|
use fix::value::Value;
|
||||||
|
|
||||||
use crate::utils::{eval, eval_result};
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ fn import_with_complex_dependency_graph() {
|
|||||||
|
|
||||||
#[test_log::test]
|
#[test_log::test]
|
||||||
fn path_with_file() {
|
fn path_with_file() {
|
||||||
let mut ctx = Context::new().unwrap();
|
let mut ctx = Runtime::new().unwrap();
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_file = temp_dir.path().join("test.txt");
|
let test_file = temp_dir.path().join("test.txt");
|
||||||
std::fs::write(&test_file, "Hello, World!").unwrap();
|
std::fs::write(&test_file, "Hello, World!").unwrap();
|
||||||
@@ -107,7 +107,7 @@ fn path_with_file() {
|
|||||||
|
|
||||||
// Should return a store path string
|
// Should return a store path string
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.starts_with(ctx.get_store_dir()));
|
assert!(store_path.starts_with("/nix/store"));
|
||||||
assert!(store_path.contains("test.txt"));
|
assert!(store_path.contains("test.txt"));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected string, got {:?}", result);
|
panic!("Expected string, got {:?}", result);
|
||||||
@@ -136,7 +136,7 @@ fn path_with_custom_name() {
|
|||||||
|
|
||||||
#[test_log::test]
|
#[test_log::test]
|
||||||
fn path_with_directory_recursive() {
|
fn path_with_directory_recursive() {
|
||||||
let mut ctx = Context::new().unwrap();
|
let mut ctx = Runtime::new().unwrap();
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_dir = temp_dir.path().join("mydir");
|
let test_dir = temp_dir.path().join("mydir");
|
||||||
std::fs::create_dir_all(&test_dir).unwrap();
|
std::fs::create_dir_all(&test_dir).unwrap();
|
||||||
@@ -150,7 +150,7 @@ fn path_with_directory_recursive() {
|
|||||||
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
|
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
|
||||||
|
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.starts_with(ctx.get_store_dir()));
|
assert!(store_path.starts_with("/nix/store"));
|
||||||
assert!(store_path.contains("mydir"));
|
assert!(store_path.contains("mydir"));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected string, got {:?}", result);
|
panic!("Expected string, got {:?}", result);
|
||||||
@@ -159,7 +159,7 @@ fn path_with_directory_recursive() {
|
|||||||
|
|
||||||
#[test_log::test]
|
#[test_log::test]
|
||||||
fn path_flat_with_file() {
|
fn path_flat_with_file() {
|
||||||
let mut ctx = Context::new().unwrap();
|
let mut ctx = Runtime::new().unwrap();
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_file = temp_dir.path().join("flat.txt");
|
let test_file = temp_dir.path().join("flat.txt");
|
||||||
std::fs::write(&test_file, "Flat content").unwrap();
|
std::fs::write(&test_file, "Flat content").unwrap();
|
||||||
@@ -171,7 +171,7 @@ fn path_flat_with_file() {
|
|||||||
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
|
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
|
||||||
|
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.starts_with(ctx.get_store_dir()));
|
assert!(store_path.starts_with("/nix/store"));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected string, got {:?}", result);
|
panic!("Expected string, got {:?}", result);
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nix_js::context::Context;
|
use fix::error::Source;
|
||||||
use nix_js::error::Source;
|
use fix::runtime::Runtime;
|
||||||
use nix_js::value::Value;
|
use fix::value::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")
|
||||||
@@ -16,9 +16,9 @@ fn eval_file(name: &str) -> Result<(Value, Source), String> {
|
|||||||
|
|
||||||
let expr = format!(r#"import "{}""#, nix_path.display());
|
let expr = format!(r#"import "{}""#, nix_path.display());
|
||||||
|
|
||||||
let mut ctx = Context::new().map_err(|e| e.to_string())?;
|
let mut ctx = Runtime::new().map_err(|e| e.to_string())?;
|
||||||
let source = Source {
|
let source = Source {
|
||||||
ty: nix_js::error::SourceType::File(nix_path.into()),
|
ty: fix::error::SourceType::File(nix_path.into()),
|
||||||
src: expr.into(),
|
src: expr.into(),
|
||||||
};
|
};
|
||||||
ctx.eval_deep(source.clone())
|
ctx.eval_deep(source.clone())
|
||||||
@@ -247,7 +247,7 @@ eval_fail_test!(fail_abort);
|
|||||||
eval_fail_test!(fail_addDrvOutputDependencies_empty_context);
|
eval_fail_test!(fail_addDrvOutputDependencies_empty_context);
|
||||||
eval_fail_test!(fail_addDrvOutputDependencies_multi_elem_context);
|
eval_fail_test!(fail_addDrvOutputDependencies_multi_elem_context);
|
||||||
eval_fail_test!(fail_addDrvOutputDependencies_wrong_element_kind);
|
eval_fail_test!(fail_addDrvOutputDependencies_wrong_element_kind);
|
||||||
eval_fail_test!(fail_addErrorContext_example);
|
eval_fail_test!(fail_addErrorRuntime_example);
|
||||||
eval_fail_test!(fail_assert);
|
eval_fail_test!(fail_assert);
|
||||||
eval_fail_test!(fail_assert_equal_attrs_names);
|
eval_fail_test!(fail_assert_equal_attrs_names);
|
||||||
eval_fail_test!(fail_assert_equal_attrs_names_2);
|
eval_fail_test!(fail_assert_equal_attrs_names_2);
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 522 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user