feat: 支持箭头函数/模板字符串/let/const/delete/instanceof 语法

This commit is contained in:
爱特每羊队
2026-06-08 19:55:05 +08:00
parent a6e448e326
commit 209374fc02
15 changed files with 1379 additions and 91 deletions
+198 -11
View File
@@ -297,12 +297,19 @@ impl Vm {
}
/// Create a function JsObject for a BytecodeFunc constant.
fn create_function_object(&mut self, sub_idx: u32) -> JsValue {
/// When `is_arrow` is true, captures the current `this` (regs[254])
/// for lexical this binding at call time (D-01).
fn create_function_object(&mut self, sub_idx: u32, is_arrow: bool) -> JsValue {
let func_proto_ptr = self.kernel.builtin_world().function_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(func_proto_ptr);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val);
obj.set_function(true);
obj.set_sub_module_index(sub_idx);
if is_arrow {
obj.set_arrow(true);
// Capture lexical `this` from the enclosing scope (regs[254]).
obj.set_captured_this(self.regs[254]);
}
let obj_ptr = self.epoch.alloc(obj);
JsValue::object(obj_ptr as *mut u8)
}
@@ -319,14 +326,24 @@ impl Vm {
Constant::Boolean(b) => JsValue::bool(*b),
Constant::Null => JsValue::null(),
Constant::Undefined => JsValue::undefined(),
Constant::BytecodeFunc(idx) => self.create_function_object(*idx),
Constant::BytecodeFunc(idx) => {
// 1-indexed: idx 1 = sub_modules[0], idx 0 = sentinel
let sub_idx = *idx as usize;
let is_arrow = if sub_idx > 0 && sub_idx <= module.sub_modules.len() {
module.sub_modules[sub_idx - 1].is_arrow
} else {
false
};
self.create_function_object(*idx, is_arrow)
}
Constant::RegExp(pattern, flags) => {
let pat_si = self.kernel.string_forge().intern(pattern).0;
let flags_si = self.kernel.string_forge().intern(flags).0;
let pat_val = JsValue::string(pat_si, 0);
let flags_val = JsValue::string(flags_si, 0);
let native_fn = self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
let native_fn =
self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &*native_fn };
let ctor_fn = ctor.native_fn();
if ctor_fn.is_none() {
@@ -338,7 +355,8 @@ impl Vm {
self.regs[0] = JsValue::undefined();
self.regs[1] = pat_val;
self.regs[2] = flags_val;
let func: crate::native::NativeFn = unsafe { std::mem::transmute(ctor_fn.unwrap()) };
let func: crate::native::NativeFn =
unsafe { std::mem::transmute(ctor_fn.unwrap()) };
let result = func(self, &[0, 1, 2]);
self.regs[0] = saved_0;
self.regs[1] = saved_1;
@@ -347,7 +365,7 @@ impl Vm {
Ok(val) => val,
Err(_) => JsValue::undefined(),
}
},
}
})
.collect()
}
@@ -654,6 +672,16 @@ impl Vm {
}
OpCode::STORE_VAR => {
if b != 0 {
// const guard: check if already initialized
if !self.regs[rd].is_undefined() {
throw_err!(
self,
TypeError,
"Assignment to constant variable"
);
}
}
self.regs[rd] = self.regs[a];
}
@@ -707,6 +735,7 @@ impl Vm {
let sub_constants = self.sub_modules[sub_idx].constants.clone();
let sub_builtin_count =
self.sub_modules[sub_idx].builtin_reg_map.len();
let sub_is_arrow = self.sub_modules[sub_idx].is_arrow;
// Copy args to regs[sub_builtin_count..sub_builtin_count+n_args]
// (builtins occupy regs[0..sub_builtin_count-1])
@@ -714,8 +743,13 @@ impl Vm {
let src_reg = first_arg_reg.wrapping_add(i as u8) as usize;
self.regs[sub_builtin_count + i] = self.regs[src_reg];
}
// Set this (reg 254 convention)
self.regs[254] = callee;
// Set this (reg 254 convention).
// Arrow functions use captured lexical `this` (D-01).
self.regs[254] = if sub_is_arrow {
obj.captured_this()
} else {
callee
};
// Save current bytecode/constants
self.saved_bytecode_stack
@@ -745,7 +779,15 @@ impl Vm {
Constant::Null => JsValue::null(),
Constant::Undefined => JsValue::undefined(),
Constant::BytecodeFunc(idx) => {
self.create_function_object(*idx)
let idx_usize = *idx as usize;
let nested_is_arrow = if idx_usize > 0
&& idx_usize <= self.sub_modules.len()
{
self.sub_modules[idx_usize - 1].is_arrow
} else {
false
};
self.create_function_object(*idx, nested_is_arrow)
}
Constant::RegExp(_, _) => JsValue::undefined(),
})
@@ -829,6 +871,14 @@ impl Vm {
"NEW_EXPRESSION: constructor is not a function"
);
}
// Arrow functions cannot be used as constructors (D-03)
if ctor_obj.is_arrow() {
throw_err!(
self,
TypeError,
"arrow functions cannot be used as constructors"
);
}
// Read extension word for arg_count
let ext = self.bytecode[self.pc];
@@ -1196,6 +1246,143 @@ impl Vm {
self.regs[rd] = JsValue::undefined();
}
OpCode::TEMPLATE_STR => {
// Read header ext word: (segment_count << 16) | (total_len_hint & 0xFFFF)
let header = self.bytecode[self.pc];
self.pc += 1;
let segment_count = (header >> 16) as usize;
let len_hint = (header & 0xFFFF) as usize;
// Build result string
let mut result = String::with_capacity(len_hint.max(16));
for _ in 0..segment_count {
let seg = self.bytecode[self.pc];
self.pc += 1;
if (seg >> 31) == 1 {
// Expression: register value
let reg = (seg & 0x7F) as u8;
let val = self.regs[reg as usize];
let s = if val.is_string() {
self.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} else {
format!("{}", val)
};
result.push_str(&s);
} else {
// Quasi: constant string
let const_idx = (seg & 0x7FFF_FFFF) as usize;
if const_idx < self.constants.len() {
let val = self.constants[const_idx];
if val.is_string() {
let s = self
.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
result.push_str(&s);
}
}
}
}
let si = self.kernel.string_forge().intern(&result).0;
self.regs[rd] = JsValue::string(si, 0);
}
OpCode::DELETE_PROP_STATIC => {
let obj_val = self.regs[a];
if !obj_val.is_object() {
self.regs[rd] = JsValue::bool(true);
} else {
let obj_ptr = obj_val.as_js_object_ptr();
let obj = unsafe { &mut *obj_ptr };
let ext = self.bytecode[self.pc];
self.pc += 1;
let const_idx = ext as usize;
let prop_name_val = self.constants[const_idx];
if !prop_name_val.is_string() {
self.regs[rd] = JsValue::bool(true);
continue;
}
let prop_name_si = prop_name_val.as_string_index();
if let Some(pos) = self.kernel.shape_forge().lookup_position(
obj.shape_id(),
prop_name_si,
) {
obj.set_prop_at(pos, JsValue::undefined());
}
self.regs[rd] = JsValue::bool(true);
}
}
OpCode::DELETE_PROP_DYNAMIC => {
let obj_val = self.regs[a];
if !obj_val.is_object() {
self.regs[rd] = JsValue::bool(true);
} else {
let key_val = self.regs[b];
let prop_name_si = if key_val.is_string() {
key_val.as_string_index()
} else {
let s = crate::coercion::to_string(
self.kernel.string_forge().as_ref(),
key_val,
);
self.kernel.string_forge().intern(&s).0
};
let obj_ptr = obj_val.as_js_object_ptr();
let obj = unsafe { &mut *obj_ptr };
if let Some(pos) = self.kernel.shape_forge().lookup_position(
obj.shape_id(),
prop_name_si,
) {
obj.set_prop_at(pos, JsValue::undefined());
}
self.regs[rd] = JsValue::bool(true);
}
}
OpCode::INSTANCEOF => {
let lhs_val = self.regs[a];
let rhs_val = self.regs[b];
if !rhs_val.is_object() {
throw_err!(self, TypeError, "INSTANCEOF right-hand side is not callable");
}
if !lhs_val.is_object() {
self.regs[rd] = JsValue::bool(false);
continue;
}
let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() };
let proto_si = self.kernel.string_forge().intern("prototype").0;
let ctor_proto = self.resolve_property(rhs_obj, proto_si);
let ctor_proto_ptr = match ctor_proto {
Some(v) if v.is_object() => v.as_js_object_ptr(),
_ => {
self.regs[rd] = JsValue::bool(false);
continue;
}
};
let mut proto = unsafe { &*lhs_val.as_js_object_ptr() }.proto();
loop {
if !proto.is_object() {
self.regs[rd] = JsValue::bool(false);
break;
}
let proto_ptr = proto.as_js_object_ptr();
if proto_ptr == ctor_proto_ptr {
self.regs[rd] = JsValue::bool(true);
break;
}
proto = unsafe { &*proto_ptr }.proto();
}
}
OpCode::IN => {
let key_val = self.regs[a];
let obj_ptr = self.regs[b].as_object_ptr() as *mut JsObject;
@@ -1581,11 +1768,11 @@ impl Vm {
OpCode::FOR_OF_INIT => {}
OpCode::FOR_OF_NEXT => {},
OpCode::FOR_OF_NEXT => {}
OpCode::FOR_OF_DONE => {},
OpCode::FOR_OF_DONE => {}
OpCode::FOR_OF_CLOSE => {},
OpCode::FOR_OF_CLOSE => {}
OpCode::THROW => {
let exc_value = self.regs[rd];