feat: 支持 class 私有字段与品牌检查

This commit is contained in:
2026-06-17 08:56:58 +08:00
parent e71c236dcd
commit a074636619
14 changed files with 481 additions and 37 deletions
+177 -22
View File
@@ -13,6 +13,21 @@ use crate::compiler::{
use crate::module::Constant;
impl Compiler {
fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> {
ctx.private_name_map
.iter()
.find_map(|(n, id)| (n == name).then_some(*id))
.ok_or_else(|| format!("private name #{name} is not defined"))
}
fn emit_private_id_reg(&self, name: &str, ctx: &mut CompileCtx) -> Result<u8, String> {
let id = self.private_name_id(name, ctx)?;
let idx = ctx.add_constant(Constant::Int(id as i32));
let reg = ctx.alloc_reg();
ctx.emit_load_const(reg, idx);
Ok(reg)
}
fn emit_class_key_reg(&self, key: &PropertyKey, computed: bool, ctx: &mut CompileCtx) -> Result<u8, String> {
if !computed {
let name = self.class_property_name(key)?;
@@ -54,6 +69,32 @@ impl Compiler {
Ok(())
}
fn emit_private_field_init(
&self, target_reg: u8, name: &str, value: Option<&Expression>, ctx: &mut CompileCtx,
) -> Result<(), String> {
let key_reg = self.emit_private_id_reg(name, ctx)?;
let value_reg = if let Some(expr) = value {
self.emit_expression(expr, ctx)?
} else {
self.emit_undefined(ctx)
};
ctx.emit(opcode::encode(OpCode::INIT_PRIVATE, target_reg, value_reg, key_reg));
Ok(())
}
fn emit_private_method_init(
&self, target_reg: u8, method: &oxide_parser::MethodDefinition, home_reg: u8, ctx: &mut CompileCtx,
) -> Result<(), String> {
let PropertyKey::PrivateIdentifier(private) = &method.key else {
return Err("expected private method key".into());
};
let name = private.name.as_str();
let key_reg = self.emit_private_id_reg(name, ctx)?;
let method_reg = self.emit_class_method_function(method, name, home_reg, ctx, &[])?;
ctx.emit(opcode::encode(OpCode::INIT_PRIVATE, target_reg, method_reg, key_reg));
Ok(())
}
fn count_public_field_init(
&self, key: &PropertyKey, computed: bool, value: Option<&Expression>, ctx: &mut CompileCtx,
) {
@@ -67,6 +108,18 @@ impl Compiler {
ctx.projected_pc += 1;
}
fn count_private_field_init(&self, value: Option<&Expression>, ctx: &mut CompileCtx) {
ctx.alloc_reg();
ctx.projected_pc += 1;
if let Some(expr) = value {
self.count_expression(expr, ctx);
} else {
ctx.alloc_reg();
ctx.projected_pc += 1;
}
ctx.projected_pc += 1;
}
fn static_property_name(&self, key: &PropertyKey) -> Result<String, String> {
match key {
PropertyKey::StaticIdentifier(ident) => Ok(ident.name.as_str().to_string()),
@@ -368,14 +421,21 @@ impl Compiler {
fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result<u8, String> {
let mut constructor_method = None;
let mut instance_fields = Vec::new();
let mut private_names = Vec::<(String, u32)>::new();
let is_derived = class.super_class.is_some();
for element in &class.body.body {
match element {
ClassElement::MethodDefinition(method) => {
let method = method.as_ref();
if matches!(method.key, PropertyKey::PrivateIdentifier(_)) {
return Err("private class elements not yet supported".into());
if let PropertyKey::PrivateIdentifier(private) = &method.key {
let name = private.name.as_str().to_string();
if private_names.iter().any(|(existing, _)| existing == &name) {
return Err(format!("duplicate private name #{name}"));
}
let id = ctx.next_private_name_id;
ctx.next_private_name_id = ctx.next_private_name_id.saturating_add(1);
private_names.push((name, id));
}
if method.kind == MethodDefinitionKind::Constructor {
if constructor_method.is_some() {
@@ -386,8 +446,14 @@ impl Compiler {
}
ClassElement::PropertyDefinition(prop) => {
let prop = prop.as_ref();
if matches!(prop.key, PropertyKey::PrivateIdentifier(_)) {
return Err("private class elements not yet supported".into());
if let PropertyKey::PrivateIdentifier(private) = &prop.key {
let name = private.name.as_str().to_string();
if private_names.iter().any(|(existing, _)| existing == &name) {
return Err(format!("duplicate private name #{name}"));
}
let id = ctx.next_private_name_id;
ctx.next_private_name_id = ctx.next_private_name_id.saturating_add(1);
private_names.push((name, id));
}
if !prop.r#static {
instance_fields.push(prop);
@@ -409,7 +475,9 @@ impl Compiler {
};
let saved_derived = ctx.in_derived_constructor;
let saved_private_names = ctx.private_name_map.clone();
ctx.in_derived_constructor = is_derived;
ctx.private_name_map = private_names.clone();
let mut ctor_module = if let Some(method) = constructor_method {
let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?;
@@ -422,18 +490,36 @@ impl Compiler {
FunctionBodyContext::ClassElement,
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| {
for field in &instance_fields {
compiler.count_public_field_init(&field.key, field.computed, field.value.as_ref(), field_ctx);
if matches!(field.key, PropertyKey::PrivateIdentifier(_)) {
compiler.count_private_field_init(field.value.as_ref(), field_ctx);
} else {
compiler.count_public_field_init(
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
);
}
}
}),
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
for field in &instance_fields {
compiler.emit_public_field_init(
254,
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
)?;
if let PropertyKey::PrivateIdentifier(private) = &field.key {
compiler.emit_private_field_init(
254,
private.name.as_str(),
field.value.as_ref(),
field_ctx,
)?;
} else {
compiler.emit_public_field_init(
254,
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
)?;
}
}
Ok(())
}),
@@ -449,18 +535,36 @@ impl Compiler {
FunctionBodyContext::ClassElement,
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| {
for field in &instance_fields {
compiler.count_public_field_init(&field.key, field.computed, field.value.as_ref(), field_ctx);
if matches!(field.key, PropertyKey::PrivateIdentifier(_)) {
compiler.count_private_field_init(field.value.as_ref(), field_ctx);
} else {
compiler.count_public_field_init(
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
);
}
}
}),
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
for field in &instance_fields {
compiler.emit_public_field_init(
254,
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
)?;
if let PropertyKey::PrivateIdentifier(private) = &field.key {
compiler.emit_private_field_init(
254,
private.name.as_str(),
field.value.as_ref(),
field_ctx,
)?;
} else {
compiler.emit_public_field_init(
254,
&field.key,
field.computed,
field.value.as_ref(),
field_ctx,
)?;
}
}
Ok(())
}),
@@ -473,8 +577,19 @@ impl Compiler {
module.bytecode.push(opcode::encode(OpCode::SUPER_CALL, 0, 0, 0));
module.bytecode.push(0);
let mut field_ctx = CompileCtx::new();
field_ctx.private_name_map = private_names.clone();
for field in &instance_fields {
self.emit_public_field_init(254, &field.key, field.computed, field.value.as_ref(), &mut field_ctx)?;
if let PropertyKey::PrivateIdentifier(private) = &field.key {
self.emit_private_field_init(254, private.name.as_str(), field.value.as_ref(), &mut field_ctx)?;
} else {
self.emit_public_field_init(
254,
&field.key,
field.computed,
field.value.as_ref(),
&mut field_ctx,
)?;
}
}
module.bytecode.extend(field_ctx.bytecode);
module.constants = field_ctx.constants;
@@ -524,6 +639,11 @@ impl Compiler {
if method.kind == MethodDefinitionKind::Constructor {
continue;
}
if matches!(method.key, PropertyKey::PrivateIdentifier(_)) {
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
self.emit_private_method_init(home_reg, method, home_reg, ctx)?;
continue;
}
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
let key_reg = self.emit_class_key_reg(&method.key, method.computed, ctx)?;
let method_name = if method.computed {
@@ -564,7 +684,11 @@ impl Compiler {
if prop.r#static {
let saved_static_this = ctx.static_block_this_reg;
ctx.static_block_this_reg = Some(ctor_reg);
self.emit_public_field_init(ctor_reg, &prop.key, prop.computed, prop.value.as_ref(), ctx)?;
if let PropertyKey::PrivateIdentifier(private) = &prop.key {
self.emit_private_field_init(ctor_reg, private.name.as_str(), prop.value.as_ref(), ctx)?;
} else {
self.emit_public_field_init(ctor_reg, &prop.key, prop.computed, prop.value.as_ref(), ctx)?;
}
ctx.static_block_this_reg = saved_static_this;
}
}
@@ -582,6 +706,7 @@ impl Compiler {
}
}
ctx.private_name_map = saved_private_names;
Ok(ctor_reg)
}
@@ -1168,6 +1293,13 @@ impl Compiler {
ctx.emit_load_const(r, idx);
Ok(r)
}
Expression::PrivateInExpression(pin) => {
let obj_reg = self.emit_expression(&pin.right, ctx)?;
let key_reg = self.emit_private_id_reg(pin.left.name.as_str(), ctx)?;
let result_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::PRIVATE_BRAND_IN, result_reg, obj_reg, key_reg));
Ok(result_reg)
}
Expression::BinaryExpression(bin) => {
let checkpoint = ctx.reg_checkpoint();
let left = self.emit_expression(&bin.left, ctx)?;
@@ -1362,6 +1494,13 @@ impl Compiler {
ctx.emit(opcode::encode(OpCode::GET_PROP_DYNAMIC, obj_reg, key_reg, r));
Ok(r)
}
Expression::PrivateFieldExpression(member) => {
let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::GET_PRIVATE, r, obj_reg, key_reg));
Ok(r)
}
Expression::ObjectExpression(obj) => {
let obj_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0));
@@ -1464,6 +1603,15 @@ impl Compiler {
let val_reg = self.emit_expression(&assign.right, ctx)?;
ctx.emit(opcode::encode(OpCode::SET_PROP_DYNAMIC, obj_reg, key_reg, val_reg));
Ok(val_reg)
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
if assign.operator != AssignmentOperator::Assign {
return Err("compound assignment to private fields not supported".into());
}
let obj_reg = self.emit_expression(&member.object, ctx)?;
let val_reg = self.emit_expression(&assign.right, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
ctx.emit(opcode::encode(OpCode::SET_PRIVATE, obj_reg, val_reg, key_reg));
Ok(val_reg)
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
if assign.operator != AssignmentOperator::Assign {
if assign.operator == AssignmentOperator::Addition
@@ -1820,6 +1968,13 @@ impl Compiler {
return Ok(result_reg);
}
let (callee_reg, this_reg) = match &call.callee {
Expression::PrivateFieldExpression(member) => {
let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let callee_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::GET_PRIVATE, callee_reg, obj_reg, key_reg));
(callee_reg, obj_reg)
}
Expression::StaticMemberExpression(member) => {
let is_super_member = matches!(&member.object, Expression::Super(_));
let obj_reg = if is_super_member {