implement dynamic key; implement __curPos; other small changes

This commit is contained in:
2026-05-02 21:23:39 +08:00
parent a66748e42d
commit 4f7d94f41b
13 changed files with 169 additions and 141 deletions
+1 -13
View File
@@ -3,7 +3,7 @@ use fix_common::StringId;
use num_enum::TryFromPrimitive;
use string_interner::Symbol as _;
use crate::{AttrKeyData, OperandData, VmRuntimeCtx};
use crate::{OperandData, VmRuntimeCtx};
pub(crate) struct BytecodeReader<'a> {
bytecode: &'a [u8],
@@ -132,18 +132,6 @@ impl<'a> BytecodeReader<'a> {
}
}
#[inline(always)]
pub(crate) fn read_attr_key_data(&mut self) -> crate::AttrKeyData {
use fix_codegen::AttrKeyType;
let tag = self.read_u8();
let ty = AttrKeyType::try_from_primitive(tag)
.unwrap_or_else(|err| panic!("unknown key tag: {:#04x}", err.number));
match ty {
AttrKeyType::Static => AttrKeyData::Static(self.read_string_id()),
AttrKeyType::Dynamic => AttrKeyData::Dynamic,
}
}
pub(crate) fn pc(&self) -> usize {
self.pc
}
-2
View File
@@ -193,7 +193,6 @@ tail_fn!(op_prepare_with, ());
tail_fn!(op_load_builtins, ());
tail_fn!(op_load_builtin, (reader));
tail_fn!(op_mk_pos, (reader));
tail_fn!(op_load_repl_binding, (reader));
tail_fn!(op_load_scoped_binding, (reader));
@@ -286,7 +285,6 @@ table! {
LoadBuiltins => op_load_builtins,
LoadBuiltin => op_load_builtin,
MkPos => op_mk_pos,
LoadReplBinding => op_load_repl_binding,
LoadScopedBinding => op_load_scoped_binding,
+2 -2
View File
@@ -249,8 +249,8 @@ impl<'gc> crate::Vm<'gc> {
return Ok(true);
}
if let (Some(a), Some(b)) = (lhs.as_gc::<crate::AttrSet>(), rhs.as_gc::<crate::AttrSet>()) {
let a = a.entries.borrow();
let b = b.entries.borrow();
let a = &a.entries;
let b = &b.entries;
if a.len() != b.len() {
return Ok(false);
}
+5 -5
View File
@@ -165,7 +165,7 @@ impl<'gc> Vm<'gc> {
let e1 = self.peek_forced(1);
let children: SmallVec<_> = if let Some(attrs) = e1.as_gc::<AttrSet>() {
let attrs = attrs.entries.borrow();
let attrs = &attrs.entries;
if attrs.is_empty() {
SmallVec::new()
} else {
@@ -243,7 +243,7 @@ impl<'gc> Vm<'gc> {
let mut added: usize = 0;
if let Some(attrs) = item.as_gc::<AttrSet>() {
let attrs = attrs.entries.borrow();
let attrs = &attrs.entries;
#[allow(clippy::unwrap_used)]
let seen = self.peek_forced(2).as_gc::<List<'gc>>().unwrap();
if !self.is_value_in_seen(seen, item) {
@@ -291,7 +291,7 @@ impl<'gc> Vm<'gc> {
let val = self.peek_forced(0);
let (count, has_children) = if let Some(attrs) = val.as_gc::<AttrSet>() {
let len = attrs.entries.borrow().len();
let len = attrs.entries.len();
(len, len > 0)
} else if let Some(list) = val.as_gc::<List<'gc>>() {
let len = list.inner.borrow().len();
@@ -331,7 +331,7 @@ impl<'gc> Vm<'gc> {
let val = self.peek_forced(2);
let child = if let Some(attrs) = val.as_gc::<AttrSet>() {
attrs.entries.borrow().get(idx as usize).map(|&(_, v)| v)
attrs.entries.get(idx as usize).map(|&(_, v)| v)
} else if let Some(list) = val.as_gc::<List<'gc>>() {
list.inner.borrow().get(idx as usize).copied()
} else {
@@ -400,7 +400,7 @@ impl<'gc> Vm<'gc> {
)));
}
}
for &(key, _) in attrset.entries.borrow().iter() {
for &(key, _) in attrset.entries.iter() {
let is_expected =
pattern.required.contains(&key) || pattern.optional.contains(&key);
if !is_expected {
+35 -23
View File
@@ -5,8 +5,7 @@ use smallvec::SmallVec;
use crate::value::NixType;
use crate::{
AttrKeyData, AttrSet, BytecodeReader, List, OperandData, Step, StrictValue, Value,
VmRuntimeCtx, VmRuntimeCtxExt,
AttrSet, BytecodeReader, List, Step, StrictValue, Value, VmRuntimeCtx, VmRuntimeCtxExt,
};
impl<'gc> crate::Vm<'gc> {
@@ -17,25 +16,43 @@ impl<'gc> crate::Vm<'gc> {
reader: &mut BytecodeReader<'_>,
mc: &gc_arena::Mutation<'gc>,
) -> Step {
let count = reader.read_u32() as usize;
let mut entries: SmallVec<[AttrEntry; 4]> = SmallVec::with_capacity(count);
for _ in 0..count {
let key = reader.read_attr_key_data();
let val = reader.read_operand_data(ctx);
let _span_id = reader.read_u32();
entries.push(AttrEntry { key, val });
let static_count = reader.read_u32() as usize;
let dynamic_count = reader.read_u32() as usize;
for i in 0..dynamic_count {
let depth = dynamic_count - 1 - i;
self.force_slot_to_pc(depth, reader, mc, reader.inst_start_pc())?;
}
let mut kv: SmallVec<[(crate::StringId, Value); 4]> = SmallVec::with_capacity(count);
for entry in &entries {
let key_sid = match &entry.key {
AttrKeyData::Static(sid) => *sid,
AttrKeyData::Dynamic => {
todo!()
}
let mut dyn_keys: SmallVec<[crate::StringId; 2]> = SmallVec::with_capacity(dynamic_count);
for i in 0..dynamic_count {
let depth = dynamic_count - 1 - i;
let key_val = self.peek_forced(depth);
let key_sid = match ctx.get_string_id(key_val) {
Ok(id) => id,
Err(got) => return self.finish_type_err(NixType::String, got),
};
let val = entry.val.resolve(mc, self);
kv.push((key_sid, val));
dyn_keys.push(key_sid);
}
self.stack.truncate(self.stack.len() - dynamic_count);
let mut kv: SmallVec<[(crate::StringId, Value); 4]> =
SmallVec::with_capacity(static_count + dynamic_count);
for _ in 0..static_count {
let key = reader.read_string_id();
let val = reader.read_operand_data(ctx).resolve(mc, self);
let _span_id = reader.read_u32();
kv.push((key, val));
}
for i in 0..dynamic_count {
let val = reader.read_operand_data(ctx).resolve(mc, self);
let _span_id = reader.read_u32();
kv.push((dyn_keys[i], val));
}
kv.sort_by_key(|(k, _)| *k);
let attrs = Gc::new(mc, AttrSet::from_sorted_unchecked(kv));
self.push(Value::new_gc(attrs));
@@ -312,8 +329,3 @@ impl<'gc> crate::Vm<'gc> {
Step::Continue(())
}
}
pub(crate) struct AttrEntry {
pub(crate) key: AttrKeyData,
pub(crate) val: OperandData,
}
-6
View File
@@ -22,12 +22,6 @@ impl<'gc> crate::Vm<'gc> {
Step::Continue(())
}
#[inline(always)]
pub(crate) fn op_mk_pos(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
let _span_id = reader.read_u32();
todo!("MkPos");
}
#[inline(always)]
pub(crate) fn op_load_repl_binding(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
let _name = reader.read_string_id();
+13 -12
View File
@@ -143,14 +143,14 @@ impl<T: VmRuntimeCtx> ConvertValueWithSeen for T {
Value::String(ns.as_str().to_owned())
} else if let Some(attrs) = val.as_gc::<AttrSet>() {
let bits = val.to_bits();
if attrs.entries.borrow().is_empty() {
if attrs.entries.is_empty() {
return Value::AttrSet(Default::default());
}
if !seen.insert(bits) {
return Value::Repeated;
}
let mut map = std::collections::BTreeMap::new();
for &(key, val) in attrs.entries.borrow().iter() {
for &(key, val) in attrs.entries.iter() {
let key = self.resolve_string(key).to_owned();
let converted = self.convert_value_with_seen(val, seen);
map.insert(fix_common::Symbol::from(key), converted);
@@ -265,11 +265,6 @@ impl OperandData {
}
}
pub(crate) enum AttrKeyData {
Static(StringId),
Dynamic,
}
fn init_builtins<'gc>(mc: &Mutation<'gc>, ctx: &mut impl VmRuntimeCtx) -> Value<'gc> {
let mut entries = SmallVec::with_capacity(BUILTINS.len());
@@ -471,7 +466,6 @@ impl<'gc> Vm<'gc> {
match *state {
ThunkState::Pending { ip, env, with_env } => {
*state = ThunkState::Blackhole;
drop(state);
self.call_stack.push(CallFrame {
thunk: Some(thunk),
stack_depth: depth,
@@ -485,13 +479,21 @@ impl<'gc> Vm<'gc> {
Step::Break(Break::Force)
}
ThunkState::Evaluated(v) => {
drop(state);
self.replace(depth, v.relax());
Step::Continue(())
}
ThunkState::Apply { .. } => todo!("force apply"),
ThunkState::Apply { func, arg } => {
self.call_stack.push(CallFrame {
thunk: Some(thunk),
stack_depth: depth,
pc: resume_pc,
env: self.env,
with_env: self.with_env,
});
self.push(func);
self.call(reader, mc, arg, resume_pc)
},
ThunkState::Blackhole => {
drop(state);
self.finish_err(Error::eval_error("infinite recursion encountered"))
}
}
@@ -676,7 +678,6 @@ impl<'gc> Vm<'gc> {
LoadBuiltins => self.op_load_builtins(),
LoadBuiltin => self.op_load_builtin(&mut reader),
MkPos => self.op_mk_pos(&mut reader),
LoadReplBinding => self.op_load_repl_binding(&mut reader),
LoadScopedBinding => self.op_load_scoped_binding(&mut reader),
+15 -35
View File
@@ -435,78 +435,58 @@ impl fmt::Debug for NixString {
#[derive(Collect, Debug, Default)]
#[collect(no_drop)]
pub(crate) struct AttrSet<'gc> {
pub(crate) entries: RefLock<SmallVec<[(StringId, Value<'gc>); 4]>>,
}
impl<'gc> Unlock for AttrSet<'gc> {
type Unlocked = RefCell<SmallVec<[(StringId, Value<'gc>); 4]>>;
unsafe fn unlock_unchecked(&self) -> &Self::Unlocked {
unsafe { self.entries.unlock_unchecked() }
}
pub(crate) entries: SmallVec<[(StringId, Value<'gc>); 4]>,
}
impl<'gc> AttrSet<'gc> {
pub(crate) fn from_sorted_unchecked(entries: SmallVec<[(StringId, Value<'gc>); 4]>) -> Self {
debug_assert!(entries.is_sorted_by_key(|(key, _)| *key));
Self {
entries: RefLock::new(entries),
}
Self { entries }
}
pub(crate) fn lookup(&self, key: StringId) -> Option<Value<'gc>> {
let entries = self.entries.borrow();
entries
self.entries
.binary_search_by_key(&key, |(k, _)| *k)
.ok()
.map(|i| entries[i].1)
.map(|i| self.entries[i].1)
}
pub(crate) fn has(&self, key: StringId) -> bool {
self.entries
.borrow()
.binary_search_by_key(&key, |(k, _)| *k)
.is_ok()
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::*;
let self_entries = self.entries.borrow();
let other_entries = other.entries.borrow();
debug_assert!(self_entries.is_sorted_by_key(|(key, _)| *key));
debug_assert!(other_entries.is_sorted_by_key(|(key, _)| *key));
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) {
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]);
entries.push(self.entries[i]);
i += 1;
}
Greater => {
entries.push(other_entries[j]);
entries.push(other.entries[j]);
j += 1;
}
Equal => {
entries.push(other_entries[j]);
entries.push(other.entries[j]);
i += 1;
j += 1;
}
}
}
entries.extend(other_entries[j..].iter().cloned());
entries.extend(self_entries[i..].iter().cloned());
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: RefLock::new(entries),
},
)
Gc::new(mc, AttrSet { entries })
}
}