refactor: 重写 abstract_eq 为 ES §7.2.15 11 步算法

This commit is contained in:
爱特每羊队
2026-06-27 13:23:35 +08:00
parent fe2042864c
commit 32000b6661
5 changed files with 136 additions and 56 deletions
+16 -1
View File
@@ -2,9 +2,10 @@ use std::sync::Arc;
use oxide_kernel::builtin::ArrayMethods;
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::JsObject;
use oxide_types::object::{JsObject, PropAttributes};
use crate::bind_constructor;
use crate::bindings::apply_binding_table;
pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let _array_methods = ArrayMethods {
@@ -46,6 +47,20 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
core.shape_forge().as_ref(),
);
let array_proto_ptr = session.builtin_world().array_proto.as_ptr() as *mut JsObject;
let array_proto = unsafe { &mut *array_proto_ptr };
apply_binding_table(
session.builtin_world(),
array_proto,
core,
&[("toString", oxide_builtins::array::array_to_string::<crate::vm::Vm> as *const (), 0)],
);
// Built-in prototype methods are non-enumerable (otherwise they leak into for-in).
let si = core.perm_interner().intern("toString").0;
if let Some(pos) = core.shape_forge().lookup_position(array_proto.shape_id(), si) {
array_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
}
let ctor_ptr = session.builtin_world().array_constructor.as_ptr() as *mut JsObject;
bind_constructor!(core, global, "Array", ctor_ptr, oxide_builtins::array::array_constructor::<crate::vm::Vm>, 1, hash: true);
}
+25 -1
View File
@@ -2,9 +2,10 @@ use std::sync::Arc;
use oxide_kernel::builtin::ObjectMethods;
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::JsObject;
use oxide_types::object::{JsObject, PropAttributes};
use crate::bind_constructor;
use crate::bindings::apply_binding_table;
pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let methods = ObjectMethods {
@@ -36,6 +37,29 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
.builtin_world()
.bind_object_methods(&methods, core.perm_interner().as_ref(), core.shape_forge().as_ref());
let object_proto_ptr = session.builtin_world().object_proto.as_ptr() as *mut JsObject;
let object_proto = unsafe { &mut *object_proto_ptr };
apply_binding_table(
session.builtin_world(),
object_proto,
core,
&[
(
"toString",
oxide_builtins::object::object_proto_to_string::<crate::vm::Vm> as *const (),
0,
),
("valueOf", oxide_builtins::object::object_proto_value_of::<crate::vm::Vm> as *const (), 0),
],
);
// Built-in prototype methods are non-enumerable (otherwise they leak into for-in).
for name in ["toString", "valueOf"] {
let si = core.perm_interner().intern(name).0;
if let Some(pos) = core.shape_forge().lookup_position(object_proto.shape_id(), si) {
object_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
}
}
let ctor_ptr = session.builtin_world().object_constructor.as_ptr() as *mut JsObject;
bind_constructor!(
core,
+30 -16
View File
@@ -5,69 +5,83 @@ use oxide_types::value::JsValue;
impl Vm {
#[inline(always)]
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("EQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let eq = coercion::abstract_eq(self.regs[a], self.regs[b]);
let lhs = self.regs[a];
let rhs = self.regs[b];
let eq = coercion::abstract_eq(lhs, rhs, self)?;
self.regs[rd] = JsValue::bool(eq);
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("NEQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let ne = !coercion::abstract_eq(self.regs[a], self.regs[b]);
let lhs = self.regs[a];
let rhs = self.regs[b];
let ne = !coercion::abstract_eq(lhs, rhs, self)?;
self.regs[rd] = JsValue::bool(ne);
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("LT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
match rel {
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
match coercion::relational_compare(va, vb) {
Some(r) => self.regs[rd] = JsValue::bool(r),
None => {
vm_trace!("LT incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
self.regs[rd] = JsValue::bool(false);
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("GT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
match rel {
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
match coercion::relational_compare(vb, va) {
Some(r) => self.regs[rd] = JsValue::bool(r),
None => {
vm_trace!("GT incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
self.regs[rd] = JsValue::bool(false);
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("LTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
match rel {
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
match coercion::relational_compare(vb, va) {
Some(r) => self.regs[rd] = JsValue::bool(!r),
None => {
vm_trace!("LTE incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
self.regs[rd] = JsValue::bool(false);
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) {
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("GTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
match rel {
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
match coercion::relational_compare(va, vb) {
Some(r) => self.regs[rd] = JsValue::bool(!r),
None => {
vm_trace!("GTE incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
self.regs[rd] = JsValue::bool(false);
}
}
Ok(())
}
#[inline(always)]
+6 -6
View File
@@ -830,27 +830,27 @@ impl Vm {
}
OpCode::EQ => {
self.dispatch_eq(rd, a, b);
self.dispatch_eq(rd, a, b)?;
}
OpCode::NEQ => {
self.dispatch_neq(rd, a, b);
self.dispatch_neq(rd, a, b)?;
}
OpCode::LT => {
self.dispatch_lt(rd, a, b);
self.dispatch_lt(rd, a, b)?;
}
OpCode::GT => {
self.dispatch_gt(rd, a, b);
self.dispatch_gt(rd, a, b)?;
}
OpCode::LTE => {
self.dispatch_lte(rd, a, b);
self.dispatch_lte(rd, a, b)?;
}
OpCode::GTE => {
self.dispatch_gte(rd, a, b);
self.dispatch_gte(rd, a, b)?;
}
OpCode::STRICT_EQ => {