feat(iter): 迭代协议支持对象字面量 computed key 与 upvalue 捕获

This commit is contained in:
爱特每羊队
2026-08-07 10:49:24 +08:00
parent d1675c217d
commit dd291250e2
4 changed files with 118 additions and 17 deletions
+24
View File
@@ -123,6 +123,30 @@ fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValu
if value.is_object() {
let obj = unsafe { &*value.as_js_object_ptr() };
// 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。
let sym_iter_si = vm.kernel_core().perm_interner().intern("@@iterator").0;
let method = vm.ordinary_get(obj, sym_iter_si, value);
if let Ok(method) = method {
if is_callable(method) {
let iterator = match vm.call_function_sync(method, value, &[]) {
Ok(it) => it,
Err(err) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &err));
return Err(exc);
}
};
if !iterator.is_object() {
return Err(crate::error::create_type_error(
vm,
"Result of the Symbol.iterator method is not an object",
));
}
return Ok(iterator);
}
}
// 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。
let next_si = vm.kernel_core().perm_interner().intern("next").0;
if let Ok(next) = vm.ordinary_get(obj, next_si, value) {
if is_callable(next) {
+60
View File
@@ -491,6 +491,66 @@ impl Emitter {
}
}
}
Expression::ObjectExpression(o) => {
for prop in &o.properties {
if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
self.collect_captured_expr(&p.value, own, out);
}
}
}
Expression::NewExpression(ne) => {
self.collect_captured_expr(&ne.callee, own, out);
for a in &ne.arguments {
if let Some(e) = a.as_expression() {
self.collect_captured_expr(e, own, out);
}
}
}
Expression::ComputedMemberExpression(m) => {
self.collect_captured_expr(&m.object, own, out);
self.collect_captured_expr(&m.expression, own, out);
}
Expression::StaticMemberExpression(m) => self.collect_captured_expr(&m.object, own, out),
Expression::PrivateFieldExpression(m) => self.collect_captured_expr(&m.object, own, out),
Expression::TemplateLiteral(tl) => {
for e in &tl.expressions {
self.collect_captured_expr(e, own, out);
}
}
Expression::TaggedTemplateExpression(tt) => {
self.collect_captured_expr(&tt.tag, own, out);
for e in &tt.quasi.expressions {
self.collect_captured_expr(e, own, out);
}
}
Expression::ParenthesizedExpression(p) => self.collect_captured_expr(&p.expression, own, out),
Expression::ChainExpression(c) => self.collect_captured_chain(&c.expression, own, out),
_ => {}
}
}
fn collect_captured_chain(
&self, element: &oxide_parser::ChainElement, own: &HashSet<String>, out: &mut HashSet<String>,
) {
match element {
oxide_parser::ChainElement::StaticMemberExpression(m) => {
self.collect_captured_expr(&m.object, own, out);
}
oxide_parser::ChainElement::ComputedMemberExpression(m) => {
self.collect_captured_expr(&m.object, own, out);
self.collect_captured_expr(&m.expression, own, out);
}
oxide_parser::ChainElement::PrivateFieldExpression(m) => {
self.collect_captured_expr(&m.object, own, out);
}
oxide_parser::ChainElement::CallExpression(c) => {
self.collect_captured_expr(&c.callee, own, out);
for a in &c.arguments {
if let Some(e) = a.as_expression() {
self.collect_captured_expr(e, own, out);
}
}
}
_ => {}
}
}
@@ -5,7 +5,7 @@ use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind};
use oxide_parser::{Expression, ObjectPropertyKind, PropertyKind};
impl Emitter {
pub(crate) fn emit_object_expression(
@@ -17,13 +17,17 @@ impl Emitter {
let ObjectPropertyKind::ObjectProperty(p) = prop else {
return Err("spread properties not yet supported".into());
};
let prop_name = match &p.key {
PropertyKey::StaticIdentifier(ident) => ident.name.as_str().to_string(),
PropertyKey::StringLiteral(s) => s.value.to_string(),
_ => return Err("unsupported object property key type".into()),
let computed = p.computed;
let prop_name = if computed {
"<computed>".to_string()
} else {
self.class_property_name(&p.key)?
};
match p.kind {
PropertyKind::Get | PropertyKind::Set => {
if computed {
return Err("computed object accessors not yet supported".into());
}
let accessor_reg = self.emit_expression(&p.value, ctx)?;
if let Some(sub_mod) = ctx.nested.last_mut() {
sub_mod.function_name = Some(prop_name.to_string());
@@ -43,20 +47,31 @@ impl Emitter {
));
}
_ => {
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
let key_reg = if computed {
self.emit_expression(p.key.to_expression(), ctx)?
} else {
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
reg
};
let val_reg = self.emit_expression(&p.value, ctx)?;
if matches!(&p.value, Expression::ArrowFunctionExpression(_)) {
if let Some(sub_mod) = ctx.nested.last_mut() {
sub_mod.function_name = Some(prop_name.to_string());
}
}
let op = if computed { OpCode::SET_PROP_DYNAMIC } else { OpCode::SET_PROP };
let operands = if computed {
(Operand::Reg(key_reg), Operand::Reg(val_reg))
} else {
(Operand::Reg(val_reg), Operand::Reg(key_reg))
};
ctx.inst(Inst::new(
OpCode::SET_PROP,
op,
Operand::Reg(obj_reg),
Operand::Reg(val_reg),
Operand::Reg(key_reg),
operands.0,
operands.1,
));
}
}
+8 -6
View File
@@ -261,11 +261,12 @@ impl Emitter {
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied();
if let Some(uv) = uv_idx {
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE
let val_reg = ctx.alloc_reg();
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
// 后缀形式返回旧值(前缀返回新值)。
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(val_reg),
Operand::Reg(old_reg),
Operand::Imm(uv as u16),
Operand::None,
));
@@ -277,14 +278,15 @@ impl Emitter {
} else {
OpCode::SUB
};
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
let new_reg = ctx.alloc_reg();
ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg)));
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(val_reg),
Operand::Reg(new_reg),
Operand::Imm(uv as u16),
));
Ok(val_reg)
Ok(if update.prefix { new_reg } else { old_reg })
} else if let Some(cell_idx) = captured_cell {
// 被捕获 cell:CELL_GET + 常量 1 + ADD/SUB + CELL_SET
let val_reg = ctx.alloc_reg();