implement Select and HasAttr

This commit is contained in:
2026-04-22 08:32:11 +08:00
parent e469d1b819
commit 21036aba46
9 changed files with 316 additions and 96 deletions
+53 -15
View File
@@ -47,8 +47,13 @@ pub enum Op {
MakeEmptyAttrs,
SelectStatic,
SelectDynamic,
HasAttrPathStatic,
HasAttrPathDynamic,
HasAttrStatic,
HasAttrDynamic,
HasAttrResolve,
JumpIfSelectSucceeded,
HasAttr,
JumpIfSelectFailed,
MakeList,
MakeEmptyList,
@@ -822,6 +827,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
) {
self.emit_expr(expr);
let mut dynamic_patches = Vec::new();
for attr in attrpath.iter() {
match *attr {
Attr::Str(sym, _) => {
@@ -831,6 +837,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_str_id(sym);
}
Attr::Dynamic(key_expr, _) => {
self.emit_op(Op::JumpIfSelectFailed);
dynamic_patches.push(self.emit_i32_placeholder());
self.emit_expr(key_expr);
let span_id = self.ctx.register_span(span);
self.emit_op(Op::SelectDynamic);
@@ -840,35 +848,65 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
if let Some(default) = default {
let before: i32 = self.ctx.get_code().len().try_into().unwrap();
for patch in dynamic_patches {
self.patch_jump_target(patch);
}
self.emit_op(Op::JumpIfSelectSucceeded);
let placeholder = self.emit_i32_placeholder();
let before: i32 = self.ctx.get_code().len().try_into().unwrap();
self.emit_expr(default);
let after: i32 = self.ctx.get_code().len().try_into().unwrap();
self.patch_i32(placeholder, after - before);
// Offset is relative to after the placeholder, so subtract the
// size of JumpIfSelectSucceeded (1) + placeholder (4).
self.patch_i32(placeholder, after - before - 5);
} else {
for patch in dynamic_patches {
self.patch_jump_target(patch);
}
}
}
fn emit_has_attr(&mut self, lhs: RawIrRef<'_>, rhs: &[Attr<RawIrRef<'_>>]) {
self.emit_expr(lhs);
for attr in rhs.iter() {
if let Attr::Dynamic(expr, _) = *attr {
self.emit_expr(expr);
}
}
self.emit_op(Op::HasAttr);
self.emit_u16(rhs.len() as u16);
for attr in rhs.iter() {
let mut dynamic_patches = Vec::new();
let [attrs @ .., last] = rhs else {
panic!("attrpath is empty");
};
for attr in attrs {
match *attr {
Attr::Str(sym, _) => {
self.emit_u8(AttrKeyType::Static as u8);
Attr::Str(sym, span) => {
let span_id = self.ctx.register_span(span);
self.emit_op(Op::HasAttrPathStatic);
self.emit_u32(span_id);
self.emit_str_id(sym);
}
Attr::Dynamic(_, _) => {
self.emit_u8(AttrKeyType::Dynamic as u8);
Attr::Dynamic(key_expr, span) => {
self.emit_op(Op::JumpIfSelectFailed);
dynamic_patches.push(self.emit_i32_placeholder());
self.emit_expr(key_expr);
let span_id = self.ctx.register_span(span);
self.emit_op(Op::HasAttrPathDynamic);
self.emit_u32(span_id);
}
}
}
match *last {
Attr::Str(sym, _) => {
self.emit_op(Op::HasAttrStatic);
self.emit_str_id(sym);
}
Attr::Dynamic(key_expr, _) => {
self.emit_op(Op::JumpIfSelectFailed);
dynamic_patches.push(self.emit_i32_placeholder());
self.emit_expr(key_expr);
self.emit_op(Op::HasAttrDynamic);
}
}
for patch in dynamic_patches {
self.patch_jump_target(patch);
}
self.emit_op(Op::HasAttrResolve);
}
fn emit_with(