docs: 补充注释与格式化

This commit is contained in:
2026-08-06 23:10:27 +08:00
parent e36a69976e
commit 31e28a03a6
156 changed files with 2393 additions and 1687 deletions
+21 -21
View File
@@ -11,7 +11,7 @@ use crate::string_forge::PermInterner;
macro_rules! bind_method {
($world:expr, $target:expr, $sf:expr, $sh:expr, $name:literal, $func:expr, $nargs:expr) => {{
let _raw: *const () = $func as *const ();
// SAFETY: $func is a NativeFn fn-item; a fn-item coerced to *const () is always valid.
// SAFETY: $func 是 NativeFn 函数项;函数项强转为 *const () 始终有效。
let _func_ptr = unsafe { oxide_types::object::NativeFnPtr::from_raw(_raw) };
let _ = $world.bind_method($target, $sh, $sf, $name, _func_ptr, $nargs);
}};
@@ -31,7 +31,7 @@ macro_rules! bind_methods_static {
$(($name:literal, $func:expr, $nargs:expr)),* $(,)?) => {
$({
let _raw: *const () = $func as *const ();
// SAFETY: $func is a NativeFn fn-item; valid to coerce and wrap.
// SAFETY: $func 是 NativeFn 函数项;强转并包装合法。
let _func_ptr = unsafe { oxide_types::object::NativeFnPtr::from_raw(_raw) };
let _ = $crate::builtin::BuiltinWorld::bind_method_static(
$target, $sh, $sf, $name, _func_ptr, $nargs, $wrapper_proto,
@@ -245,7 +245,7 @@ fn make_pair(
si_name: u32,
) -> (P<JsObject>, P<JsObject>) {
intern_label(string_forge, name);
let name_si = string_forge.intern(name).0; // intern the constructor's name value too
let name_si = string_forge.intern(name).0; // 同时 intern 构造器名作为属性值
let mut proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
let mut ctor = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
@@ -258,18 +258,18 @@ fn make_pair(
ctor.set_shape_id(ctor_shape2);
ctor.set_function(true);
// Pre-allocate slots: proto[0]="constructor", ctor[0]="prototype", ctor[1]="name"
proto.ensure_hash_props().push(JsValue::undefined()); // placeholder
ctor.ensure_hash_props().push(JsValue::undefined()); // placeholder for "prototype"
// Set the actual name value immediately (ctor vec[1])
// 预分配槽位:proto[0]="constructor"、ctor[0]="prototype"、ctor[1]="name"。
proto.ensure_hash_props().push(JsValue::undefined()); // 占位
ctor.ensure_hash_props().push(JsValue::undefined()); // "prototype" 占位
// 立即写入实际 name 值(ctor vec[1])。
ctor.ensure_hash_props()
.push(JsValue::perm_string(string_forge.string_ptr(name_si)));
// Set attribute metadata: .prototype (ctor[0]) = writable:false, enumerable:false, configurable:false
// 属性元数据:.prototype(ctor[0])= writable:false, enumerable:false, configurable:false。
ctor.set_data_meta(0u32, oxide_types::object::PropAttributes::new(false, false, false));
// .name (ctor[1]) = writable:false, enumerable:false, configurable:true
// .name(ctor[1])= writable:false, enumerable:false, configurable:true。
ctor.set_data_meta(1u32, oxide_types::object::PropAttributes::new(false, false, true));
// .constructor (proto[0]) = writable:true, enumerable:false, configurable:true
// (JS spec: prototype.constructor is non-enumerable so it doesn't leak into for-in)
// .constructor(proto[0])= writable:true, enumerable:false, configurable:true。
// (JS 规范:prototype.constructor 非枚举,避免泄漏进 for-in。)
proto.set_data_meta(0u32, oxide_types::object::PropAttributes::new(true, false, true));
(P::new(proto), P::new(ctor))
@@ -398,9 +398,9 @@ fn make_typed_array_family(
}
}
/// Overwrite placeholder slots (set up by make_pair) with real values.
/// ctor.vec[0] = constructor.prototype -> proto
/// proto.vec[0] = proto.constructor -> ctor
/// 用真实值覆盖(`make_pair` 设置的)占位槽。
/// ctor.vec[0] = constructor.prototype → proto。
/// proto.vec[0] = proto.constructor → ctor。
fn wire_ctor_proto(ctor: &P<JsObject>, proto: &P<JsObject>) {
let ctor_ptr = ctor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
@@ -1009,7 +1009,7 @@ impl BuiltinWorld {
let si = string_forge.intern("@@iterator").0;
let raw = methods.values;
// SAFETY: methods.values is a NativeFn fn-item passed by the VM binding layer.
// SAFETY: methods.values 是 VM 绑定层传入的 NativeFn 函数项。
let func_ptr = unsafe { NativeFnPtr::from_raw(raw) };
let _ =
Self::bind_method_static(proto, shape_forge, string_forge, "@@iterator", func_ptr, 0, self.fn_proto_val());
@@ -1177,8 +1177,8 @@ impl BuiltinWorld {
wrapper.set_proto(wrapper_proto).ok();
}
wrapper.set_function(true);
// The NativeFnPtr invariant is upheld by callers (see bind_method / bind_method_static
// callers, all of which use fn-item expressions).
// NativeFnPtr 不变量由调用方维护(见 bind_method / bind_method_static
// 的调用方,均使用函数项表达式)。
wrapper.set_native_fn(Some(native_fn_ptr));
wrapper.set_native_arg_count(arg_count);
// 设置 .length (ES spec: Function.length = formal parameter count,
@@ -1202,8 +1202,8 @@ impl BuiltinWorld {
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
proto.set_shape_id(new_shape);
proto.ensure_hash_props().push(wrapper_val);
// Built-in prototype methods are non-enumerable per the ES spec; otherwise
// they leak into for-in enumeration (e.g. array push/pop showing up in `for k in []`).
// 内置原型方法按 ES 规范非枚举;否则会泄漏进 for-in 枚举
// (例如 `for k in []` 中会出现 array push/pop)。
let method_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
proto.set_data_meta(method_pos, oxide_types::object::PropAttributes::new(true, false, true));
proto.bump_generation();
@@ -1270,9 +1270,9 @@ mod tests {
#[test]
fn test_protos_have_null_proto() {
let w = make_world();
// Object.prototype is the root - its __proto__ is null.
// Object.prototype 是根——其 __proto__ 为 null。
assert!(w.object_proto.proto().is_null());
// All other constructor prototypes inherit from Object.prototype.
// 其余构造器原型均继承自 Object.prototype。
assert!(w.array_proto.proto().is_object());
assert!(w.function_proto.proto().is_object());
assert!(w.string_proto.proto().is_object());
+14 -14
View File
@@ -113,8 +113,8 @@ impl Default for KernelConfig {
}
}
/// Immutable, permanently shared state across all VM instances.
/// Never rebuilt after construction — forge tables are append-only.
/// 不可变、跨所有 VM 实例永久共享的状态。
/// 构造后从不重建——forge 表为 append-only。
pub struct KernelCore {
pub config: KernelConfig,
pub perm_interner: Arc<PermInterner>,
@@ -199,10 +199,10 @@ impl KernelCore {
/// 字符串 intern 表是 append-only,无需清理;仅当 shape 或 prop 表超过阈值时才执行
/// [`ShapeForge::clear_transient`] 与 [`PropForge::clear`]。
pub fn sweep_runner_forges(&self) {
// The key interner is append-only (no per-run sweep); only the transient
// shape/prop tables need bounding at the test262 per-test boundary.
// test262 creates a fresh VM/session per test. At this boundary no JS object
// from prior tests may retain transient shapes/templates.
// 键 interner 是 append-only(无逐次清理);只有瞬时 shape/prop 表
// 需要在 test262 每测试边界做上限约束。
// test262 每测试新建 VM/session。在该边界,此前测试产生的 JS 对象
// 不应保留任何瞬时 shape/模板。
if self.shape_forge.len() > 50_000 {
self.shape_forge.clear_transient();
self.prop_forge.clear();
@@ -212,19 +212,19 @@ impl KernelCore {
}
}
/// Per-session mutable state: builtin prototype objects and the global object.
/// Rebuilt on full_reset() to achieve complete isolation between JS executions.
/// 每会话可变状态:内置原型对象与全局对象。
/// 在 full_reset() 时重建,实现 JS 执行之间的完全隔离。
pub struct KernelSession {
pub builtin_world: Arc<BuiltinWorld>,
pub global_object: P<JsObject>,
pub builtin_snapshot: BuiltinSnapshot,
}
/// Generation snapshot for reset dirty checks.
/// 供 reset 脏检查用的世代快照。
///
/// Maintenance: every new `BuiltinWorld` object field must be added here and to
/// `KernelSession::dirty_since_snapshot()` so selective reset can rebuild the
/// correct builtin family.
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
/// builtin 家族。
pub const NUM_BUILTINS: usize = 66;
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
@@ -484,8 +484,8 @@ impl KernelSession {
P::new(global_obj)
}
/// Build a fresh session from a KernelCore. All string/shape intern calls hit
/// cache on second and subsequent calls — net cost is < 0.5 ms.
/// 从 KernelCore 构建一个全新 session。所有 string/shape intern 调用在
/// 第二次及后续调用时命中缓存——净开销 < 0.5 ms。
pub fn new(core: &KernelCore) -> Self {
let builtin_world = Arc::new(BuiltinWorld::new(&core.perm_interner, &core.shape_forge));
let global_object = Self::new_global_object(core);
+11 -12
View File
@@ -24,18 +24,17 @@ pub struct Shape {
pub id: ShapeId,
pub property_name: StringIndex,
pub parent: Option<ShapeId>,
/// Number of non-sentinel properties from the root to this shape.
/// 从根到本 shape 的非哨兵属性数。
pub depth: u32,
}
/// Shared hidden-class store.
/// 共享隐藏类存储。
///
/// Concurrency model:
/// - `shapes` is append-mostly behind an `RwLock`.
/// - `transitions` is a sharded `DashMap` keyed by `(parent_shape, property_name)`.
/// - `positions` caches `(shape_id, prop_name) → slot` for O(1) repeat lookups.
/// - Shard count is fixed for reproducible contention behavior across machines instead
/// of relying on DashMap's CPU-dependent default.
/// 并发模型:
/// - `shapes` 为 append-mostly,受 `RwLock` 保护。
/// - `transitions` 是以 `(parent_shape, property_name)` 为键的分片 `DashMap`。
/// - `positions` 缓存 `(shape_id, prop_name) → slot`,供 O(1) 重复查询。
/// - 分片数固定,保证跨机器的争用行为可复现,不依赖 DashMap 的 CPU 相关默认值。
pub struct ShapeForge {
shapes: RwLock<Vec<Option<Arc<Shape>>>>,
transitions: DashMap<u64, ShapeId>,
@@ -109,7 +108,7 @@ impl ShapeForge {
return new_id;
}
// Compute depth from parent (O(1) — just read the cached value)
// 从父节点计算深度(O(1)——只读缓存值)。
let parent_depth = {
let shapes = self.shapes.read().unwrap();
shapes
@@ -307,9 +306,9 @@ mod tests {
let s2 = forge.make_shape(s1, 1_000_020);
let s3 = forge.make_shape(s2, 1_000_030);
// First call populates cache
// 首次调用填充缓存。
assert_eq!(forge.lookup_position(s3, 1_000_020), Some(1));
// Second call hits cache (verify no regression)
// 二次调用命中缓存(验证无回归)。
assert_eq!(forge.lookup_position(s3, 1_000_020), Some(1));
assert_eq!(forge.lookup_position(s3, 1_000_030), Some(2));
assert_eq!(forge.lookup_position(s3, 1_000_030), Some(2));
@@ -321,7 +320,7 @@ mod tests {
let s1 = forge.make_shape(EMPTY_SHAPE_ID, 1_000_010);
assert_eq!(forge.lookup_position(s1, 1_000_010), Some(0));
forge.clear_transient();
// After clear, EMPTY_SHAPE remains but others are gone
// 清理后只剩 EMPTY_SHAPE,其余全部移除。
assert_eq!(forge.shapes.read().unwrap().len(), 1);
}
+28 -29
View File
@@ -7,41 +7,40 @@ use rustc_hash::FxHasher;
use crate::{kernel_debug, kernel_trace};
/// Full 64-bit content hash. Replaces the old 16-bit `hash16` so the interner's
/// hash→candidate map has negligible collision risk.
/// 完整 64 位内容哈希。替代旧的 16 位 `hash16`,使 interner 的
/// hash→候选表碰撞风险可忽略。
fn hash64(s: &str) -> u64 {
let mut h = FxHasher::default();
s.hash(&mut h);
h.finish()
}
/// One interned key. `data` is a leaked `&'static str` — permanent keys are never
/// freed (append-only by design), so the leak is the storage model, not a bug.
/// 一条 intern 过的键。`data` 是泄漏的 `&'static str`——永久键从不释放
/// (按设计 append-only),所以泄漏即存储模型,而非 bug。
#[derive(Clone, Copy)]
struct PermEntry {
data: &'static str,
hash: u64,
}
/// Append-only, never-move, lock-free-read key interner shared by all VMs.
/// 所有 VM 共享的 append-only、永不移动、读无锁的键 interner。
///
/// Replaces the old ref-counted `StringForge` (and its buggy `maybe_sweep`
/// renumber path). Keys (property names, method names) are interned once and
/// addressed by a stable `u32` id that the shape/IC system keys on. Runtime
/// string *values* are no longer interned here — they are heap `JsString`
/// pointers (see `oxide_vm::Vm::new_string`).
/// 取代旧的引用计数 `StringForge`(及其有缺陷的 `maybe_sweep` 重编号路径)。
/// 键(属性名、方法名)只 intern 一次,以 shape/IC 系统所依赖的稳定 `u32` id
/// 寻址。运行时字符串*值*不再在此 intern——它们是堆上的 `JsString`
/// 指针(见 `oxide_vm::Vm::new_string`)。
///
/// Concurrency:
/// - `hash_map` (`DashMap`) gives sharded, lock-free reads of the hash→candidates
/// mapping on the hot intern path.
/// - `entries` sits behind a short `RwLock`; reads copy out a `&'static str`
/// (Copy) so the borrow outlives the guard.
/// 并发性:
/// - `hash_map`(`DashMap`)在热 intern 路径上提供分片、无锁的
/// hash→候选映射读。
/// - `entries` 位于短 `RwLock` 之后;读时拷贝出 `&'static str`(Copy),
/// 使借用存活时间超过锁守卫。
pub struct PermInterner {
hash_map: DashMap<u64, Vec<u32>>,
entries: RwLock<Vec<PermEntry>>,
/// Lazily materialized permanent `JsString` values, indexed by entry id.
/// Used when a permanent key must also exist as a JS string *value*
/// (e.g. method names exposed as property values at builtin init).
/// 惰性物化的永久 `JsString` 值,按下标索引。
/// 用于永久键还必须以 JS 字符串*值*存在的情形
/// (如 builtin 初始化时作为属性值暴露的方法名)。
permanent_strings: RwLock<Vec<Option<Box<JsString>>>>,
}
@@ -55,12 +54,12 @@ impl PermInterner {
}
}
/// Intern a key. Returns its stable id and full 64-bit hash. Each unique
/// string is stored exactly once (single allocation, no double `to_string`).
/// Intern 一个键。返回其稳定 id 与完整 64 位哈希。每个唯一字符串
/// 恰好存储一次(单次分配,无双重 `to_string`)。
pub fn intern(&self, s: &str) -> (u32, u64) {
let hash = hash64(s);
// Fast path: lock-free candidate read, short entries read-lock.
// 快路径:无锁候选读,短 entries 读锁。
if let Some(candidates) = self.hash_map.get(&hash) {
let entries = self.entries.read().unwrap();
for &id in candidates.iter() {
@@ -71,7 +70,7 @@ impl PermInterner {
}
}
// Slow path: append under write lock, re-checking for a racing insert.
// 慢路径:写锁下追加,重查是否存在并发插入。
let mut entries = self.entries.write().unwrap();
if let Some(candidates) = self.hash_map.get(&hash) {
for &id in candidates.iter() {
@@ -93,20 +92,20 @@ impl PermInterner {
(id, hash)
}
/// Resolve a key id to its text with zero clone. The returned `&'static str`
/// is valid for the program lifetime (keys are never freed).
/// 以零克隆解析键 id 的文本。返回的 `&'static str`
/// 在整个程序生命周期内有效(键从不释放)。
pub fn lookup(&self, id: u32) -> Option<&'static str> {
let entries = self.entries.read().unwrap();
entries.get(id as usize).map(|e| e.data)
}
/// Full 64-bit hash for a key id.
/// 键 id 的完整 64 位哈希。
pub fn get_hash(&self, id: u32) -> Option<u64> {
let entries = self.entries.read().unwrap();
entries.get(id as usize).map(|e| e.hash)
}
/// Total number of unique keys interned.
/// 全部唯一 intern 键的数量。
pub fn entry_count(&self) -> u32 {
self.entries.read().unwrap().len() as u32
}
@@ -116,8 +115,8 @@ impl PermInterner {
self.entry_count() == 0
}
/// Materialize (once) and return a stable pointer to a permanent `JsString`
/// for the given key id. The `JsString` lives for the program lifetime.
/// 物化(仅一次)并返回指定键 id 的永久 `JsString` 稳定指针。
/// 该 `JsString` 在整个程序生命周期内存活。
pub fn string_ptr(&self, id: u32) -> *const JsString {
{
let perm = self.permanent_strings.read().unwrap();
@@ -204,7 +203,7 @@ mod tests {
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
// Second call returns the same stable pointer (materialized once).
// 二次调用返回同一稳定指针(仅物化一次)。
assert_eq!(interner.string_ptr(id), ptr);
}
}