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

This commit is contained in:
爱特每羊队
2026-06-17 08:56:58 +08:00
parent 4fc73a4ed4
commit 1b77028340
14 changed files with 481 additions and 37 deletions
+6
View File
@@ -144,6 +144,8 @@ pub(crate) struct CompileCtx {
pub(crate) in_instance_method: bool,
pub(crate) in_static_method: bool,
pub(crate) static_block_this_reg: Option<u8>,
pub(crate) private_name_map: Vec<(String, u32)>,
pub(crate) next_private_name_id: u32,
pub(crate) after_super_insert: Option<Vec<opcode::Instr>>,
pub(crate) after_super_inserted: bool,
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
@@ -208,6 +210,8 @@ impl CompileCtx {
in_instance_method: false,
in_static_method: false,
static_block_this_reg: None,
private_name_map: Vec::new(),
next_private_name_id: 1,
after_super_insert: None,
after_super_inserted: false,
reg_overflow: false,
@@ -497,6 +501,8 @@ impl Compiler {
// Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.)
// resolve to the correct pre-allocated registers in the sub-module's register file.
ctx.builtin_reg_map = parent_ctx.builtin_reg_map.clone();
ctx.private_name_map = parent_ctx.private_name_map.clone();
ctx.next_private_name_id = parent_ctx.next_private_name_id;
// Propagate enclosing_this_reg so nested arrow functions capture the correct `this`.
ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg;
+31
View File
@@ -132,6 +132,14 @@ impl Compiler {
if matches!(method.kind, MethodDefinitionKind::Constructor) {
continue;
}
if matches!(method.key, oxide_parser::PropertyKey::PrivateIdentifier(_)) {
if method.r#static {
ctx.alloc_reg();
ctx.alloc_reg();
ctx.projected_pc += 3;
}
continue;
}
ctx.alloc_reg(); // key_reg
ctx.projected_pc += 1;
ctx.alloc_reg(); // method_reg
@@ -479,6 +487,12 @@ impl Compiler {
ctx.restore_reg_checkpoint(checkpoint.saturating_add(1));
}
}
Expression::PrivateInExpression(pin) => {
self.count_expression(&pin.right, ctx);
ctx.alloc_reg();
ctx.alloc_reg();
ctx.projected_pc += 2;
}
Expression::UnaryExpression(un) => {
match un.operator {
UnaryOperator::Delete => {
@@ -516,6 +530,12 @@ impl Compiler {
return;
}
match &call.callee {
Expression::PrivateFieldExpression(member) => {
self.count_expression(&member.object, ctx);
ctx.alloc_reg();
ctx.alloc_reg();
ctx.projected_pc += 2;
}
Expression::StaticMemberExpression(member) => {
if matches!(&member.object, Expression::Super(_)) {
ctx.alloc_reg(); // this register
@@ -566,6 +586,11 @@ impl Compiler {
self.count_expression(&assign.right, ctx);
ctx.alloc_reg();
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
self.count_expression(&member.object, ctx);
self.count_expression(&assign.right, ctx);
ctx.alloc_reg();
ctx.projected_pc += 2; // LOAD_CONST private id + SET_PRIVATE
} else if let oxide_parser::AssignmentTarget::ArrayAssignmentTarget(ap) = &assign.left {
self.count_expression(&assign.right, ctx);
self.count_array_assignment(ap, ctx);
@@ -773,6 +798,12 @@ impl Compiler {
ctx.alloc_reg();
ctx.projected_pc += 1; // GET_PROP_DYNAMIC
}
Expression::PrivateFieldExpression(member) => {
self.count_expression(&member.object, ctx);
ctx.alloc_reg();
ctx.alloc_reg();
ctx.projected_pc += 2;
}
Expression::ParenthesizedExpression(p) => {
self.count_expression(&p.expression, ctx);
}
+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 {
+15
View File
@@ -250,6 +250,10 @@ fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher) {
hash_expression(&cond.consequent, h);
hash_expression(&cond.alternate, h);
}
Expression::PrivateInExpression(pin) => {
pin.left.name.as_str().hash(h);
hash_expression(&pin.right, h);
}
Expression::Identifier(_) => {}
Expression::NumericLiteral(num) => {
num.value.to_bits().hash(h);
@@ -262,6 +266,9 @@ fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher) {
}
Expression::AssignmentExpression(assign) => {
std::mem::discriminant(&assign.operator).hash(h);
if let Some(target) = assign.left.as_simple_assignment_target() {
hash_simple_assignment_target(target, h);
}
hash_expression(&assign.right, h);
}
Expression::UpdateExpression(update) => {
@@ -323,6 +330,10 @@ fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher) {
}
}
}
Expression::PrivateFieldExpression(member) => {
hash_expression(&member.object, h);
member.field.name.as_str().hash(h);
}
Expression::Super(_) => {}
Expression::RegExpLiteral(lit) => {
if let Some(raw) = &lit.raw {
@@ -356,6 +367,10 @@ fn hash_simple_assignment_target(target: &SimpleAssignmentTarget, h: &mut rustc_
hash_expression(&member.object, h);
hash_expression(&member.expression, h);
}
SimpleAssignmentTarget::PrivateFieldExpression(member) => {
hash_expression(&member.object, h);
member.field.name.as_str().hash(h);
}
_ => {}
});
}
+12
View File
@@ -123,6 +123,10 @@ pub enum OpCode {
// ── Parallel — placeholders (0x70-0x7F) ──
FORK = 0x70,
JOIN = 0x71,
GET_PRIVATE = 0x72,
SET_PRIVATE = 0x73,
INIT_PRIVATE = 0x74,
PRIVATE_BRAND_IN = 0x75,
// -- Bitwise (0x80-0x8F) --
BIT_AND = 0x80,
@@ -269,6 +273,10 @@ impl TryFrom<u8> for OpCode {
0x66 => Ok(OpCode::PROFILE_CALL),
0x70 => Ok(OpCode::FORK),
0x71 => Ok(OpCode::JOIN),
0x72 => Ok(OpCode::GET_PRIVATE),
0x73 => Ok(OpCode::SET_PRIVATE),
0x74 => Ok(OpCode::INIT_PRIVATE),
0x75 => Ok(OpCode::PRIVATE_BRAND_IN),
0x80 => Ok(OpCode::BIT_AND),
0x81 => Ok(OpCode::BIT_OR),
0x82 => Ok(OpCode::BIT_XOR),
@@ -382,6 +390,10 @@ impl fmt::Display for OpCode {
OpCode::PROFILE_CALL => "PROFILE_CALL",
OpCode::FORK => "FORK",
OpCode::JOIN => "JOIN",
OpCode::GET_PRIVATE => "GET_PRIVATE",
OpCode::SET_PRIVATE => "SET_PRIVATE",
OpCode::INIT_PRIVATE => "INIT_PRIVATE",
OpCode::PRIVATE_BRAND_IN => "PRIVATE_BRAND_IN",
OpCode::BIT_AND => "BIT_AND",
OpCode::BIT_OR => "BIT_OR",
OpCode::BIT_XOR => "BIT_XOR",