mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
style: 修 rustfmt 格式化差异
This commit is contained in:
@@ -322,7 +322,12 @@ fn main() -> ExitCode {
|
|||||||
/// - watchdog spawn 脱离的 server 子进程,崩溃后经同一入口重启。
|
/// - watchdog spawn 脱离的 server 子进程,崩溃后经同一入口重启。
|
||||||
fn server_command(command: ServerCommands) -> ExitCode {
|
fn server_command(command: ServerCommands) -> ExitCode {
|
||||||
match command {
|
match command {
|
||||||
ServerCommands::Start { rm, idle_timeout, workers, foreground } => {
|
ServerCommands::Start {
|
||||||
|
rm,
|
||||||
|
idle_timeout,
|
||||||
|
workers,
|
||||||
|
foreground,
|
||||||
|
} => {
|
||||||
if rm {
|
if rm {
|
||||||
let mut config = RmServerConfig {
|
let mut config = RmServerConfig {
|
||||||
idle_timeout: Duration::from_secs(idle_timeout),
|
idle_timeout: Duration::from_secs(idle_timeout),
|
||||||
@@ -427,11 +432,7 @@ fn server_command(command: ServerCommands) -> ExitCode {
|
|||||||
/// 渲染 `ForgeStatus` 为多行文本:首行条目数,容量 / gc / 清缓存 / lookup
|
/// 渲染 `ForgeStatus` 为多行文本:首行条目数,容量 / gc / 清缓存 / lookup
|
||||||
/// 行按旗标结果附加。
|
/// 行按旗标结果附加。
|
||||||
fn render_forge_status(
|
fn render_forge_status(
|
||||||
target: ForgeTarget,
|
target: ForgeTarget, entries: usize, capacity: usize, gc_collected: Option<usize>, cache_cleared: bool,
|
||||||
entries: usize,
|
|
||||||
capacity: usize,
|
|
||||||
gc_collected: Option<usize>,
|
|
||||||
cache_cleared: bool,
|
|
||||||
lookup: Option<LookupResult>,
|
lookup: Option<LookupResult>,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut lines = vec![format!("{} forge: {entries} entries", target.as_str())];
|
let mut lines = vec![format!("{} forge: {entries} entries", target.as_str())];
|
||||||
@@ -573,10 +574,7 @@ fn restart_command() -> ExitCode {
|
|||||||
fn log_command(follow: bool, level: Option<Level>, lines: Option<usize>) -> ExitCode {
|
fn log_command(follow: bool, level: Option<Level>, lines: Option<usize>) -> ExitCode {
|
||||||
let path = sidecar::well_known_log_path();
|
let path = sidecar::well_known_log_path();
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
eprintln!(
|
eprintln!("{}", Red.paint(format!("日志文件不存在:{}(server 可能未启动过)", path.display())));
|
||||||
"{}",
|
|
||||||
Red.paint(format!("日志文件不存在:{}(server 可能未启动过)", path.display()))
|
|
||||||
);
|
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,7 @@ use super::protocol::{ForgeTarget, LookupResult, ServerResponse};
|
|||||||
/// - --gc 重置各空闲 VM 的 session_bytes_allocated 为清扫后存活字节。
|
/// - --gc 重置各空闲 VM 的 session_bytes_allocated 为清扫后存活字节。
|
||||||
/// - --clear-cache 清空字节码缓存(后续 eval 重编译)。
|
/// - --clear-cache 清空字节码缓存(后续 eval 重编译)。
|
||||||
pub fn handle_forge_query(
|
pub fn handle_forge_query(
|
||||||
target: ForgeTarget,
|
target: ForgeTarget, gc: bool, clear_cache: bool, lookup: Option<String>, kernel: &Arc<KernelCore>,
|
||||||
gc: bool,
|
|
||||||
clear_cache: bool,
|
|
||||||
lookup: Option<String>,
|
|
||||||
kernel: &Arc<KernelCore>,
|
|
||||||
pool: &Arc<VmPool>,
|
pool: &Arc<VmPool>,
|
||||||
) -> ServerResponse {
|
) -> ServerResponse {
|
||||||
// 读目标 forge 条目数与容量。
|
// 读目标 forge 条目数与容量。
|
||||||
@@ -140,7 +136,8 @@ mod tests {
|
|||||||
other => panic!("应得 ForgeStatus 帧,实得 {other:?}"),
|
other => panic!("应得 ForgeStatus 帧,实得 {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = handle_forge_query(ForgeTarget::String, false, false, Some("not-interned".into()), &kernel, &pool);
|
let response =
|
||||||
|
handle_forge_query(ForgeTarget::String, false, false, Some("not-interned".into()), &kernel, &pool);
|
||||||
match response {
|
match response {
|
||||||
ServerResponse::ForgeStatus { lookup, .. } => {
|
ServerResponse::ForgeStatus { lookup, .. } => {
|
||||||
assert_eq!(lookup, Some(LookupResult::Absent), "未 intern 键应得 Absent");
|
assert_eq!(lookup, Some(LookupResult::Absent), "未 intern 键应得 Absent");
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ pub fn liveness_scan_with_waits(
|
|||||||
/// - 可能向 sidecar 记录的进程发 SIGTERM;可能删除 socket 与 sidecar 文件。
|
/// - 可能向 sidecar 记录的进程发 SIGTERM;可能删除 socket 与 sidecar 文件。
|
||||||
/// - 杀进程决策与失败记入跨重启保留的日志文件。
|
/// - 杀进程决策与失败记入跨重启保留的日志文件。
|
||||||
fn kill_zombie(
|
fn kill_zombie(
|
||||||
id: &sidecar::ServerIdentity, sidecar_path: &Path, socket_path: &Path, zombie_exit_window: Duration, kill_wait: Duration,
|
id: &sidecar::ServerIdentity, sidecar_path: &Path, socket_path: &Path, zombie_exit_window: Duration,
|
||||||
|
kill_wait: Duration,
|
||||||
) -> LivenessOutcome {
|
) -> LivenessOutcome {
|
||||||
// 有界等待 sidecar 消失:覆盖旧 server 退出序列窗口(排水 5 秒,sidecar
|
// 有界等待 sidecar 消失:覆盖旧 server 退出序列窗口(排水 5 秒,sidecar
|
||||||
// 最后删除)。消失即无操作,`claim_sidecar` 的 O_EXCL 创建成功。
|
// 最后删除)。消失即无操作,`claim_sidecar` 的 O_EXCL 创建成功。
|
||||||
@@ -313,10 +314,7 @@ mod tests {
|
|||||||
let sidecar = dir.path("sidecar.json");
|
let sidecar = dir.path("sidecar.json");
|
||||||
let socket = dir.path("server.sock");
|
let socket = dir.path("server.sock");
|
||||||
let listener = UnixListener::bind(&socket).expect("绑定监听者应成功");
|
let listener = UnixListener::bind(&socket).expect("绑定监听者应成功");
|
||||||
sidecar::write_exclusive(
|
sidecar::write_exclusive(&sidecar::ServerIdentity::new(socket.to_str().unwrap(), "0.0.0"), &sidecar)
|
||||||
&sidecar::ServerIdentity::new(socket.to_str().unwrap(), "0.0.0"),
|
|
||||||
&sidecar,
|
|
||||||
)
|
|
||||||
.expect("写 sidecar 应成功");
|
.expect("写 sidecar 应成功");
|
||||||
let outcome = liveness_scan(&sidecar, &socket);
|
let outcome = liveness_scan(&sidecar, &socket);
|
||||||
assert_eq!(outcome, LivenessOutcome::Noop, "存活 socket 应判无操作");
|
assert_eq!(outcome, LivenessOutcome::Noop, "存活 socket 应判无操作");
|
||||||
@@ -395,7 +393,8 @@ mod tests {
|
|||||||
sidecar::write_exclusive(&id, &sidecar).expect("写 sidecar 应成功");
|
sidecar::write_exclusive(&id, &sidecar).expect("写 sidecar 应成功");
|
||||||
// 注入短僵尸退出窗口(500 毫秒)加短杀进程等待(300 毫秒):子进程忽略
|
// 注入短僵尸退出窗口(500 毫秒)加短杀进程等待(300 毫秒):子进程忽略
|
||||||
// SIGTERM,扫描判杀进程失败,文件原样。
|
// SIGTERM,扫描判杀进程失败,文件原样。
|
||||||
let outcome = liveness_scan_with_waits(&sidecar, &socket, Duration::from_millis(500), Duration::from_millis(300));
|
let outcome =
|
||||||
|
liveness_scan_with_waits(&sidecar, &socket, Duration::from_millis(500), Duration::from_millis(300));
|
||||||
assert_eq!(outcome, LivenessOutcome::KillFailed, "忽略 SIGTERM 应判杀进程失败");
|
assert_eq!(outcome, LivenessOutcome::KillFailed, "忽略 SIGTERM 应判杀进程失败");
|
||||||
assert!(sidecar.exists(), "sidecar 应原样");
|
assert!(sidecar.exists(), "sidecar 应原样");
|
||||||
assert!(!socket.exists(), "socket 文件应保持不存在");
|
assert!(!socket.exists(), "socket 文件应保持不存在");
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ pub fn line_level(line: &str) -> Option<Level> {
|
|||||||
/// `ERROR`/`WARN`/`INFO` 行。
|
/// `ERROR`/`WARN`/`INFO` 行。
|
||||||
pub fn filter_tail(lines: &[String], min_level: Option<Level>, last_n: Option<usize>) -> Vec<String> {
|
pub fn filter_tail(lines: &[String], min_level: Option<Level>, last_n: Option<usize>) -> Vec<String> {
|
||||||
let mut filtered: Vec<String> = match min_level {
|
let mut filtered: Vec<String> = match min_level {
|
||||||
Some(min) => lines.iter().filter(|line| line_level(line).is_some_and(|level| level <= min)).cloned().collect(),
|
Some(min) => lines
|
||||||
|
.iter()
|
||||||
|
.filter(|line| line_level(line).is_some_and(|level| level <= min))
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
None => lines.to_vec(),
|
None => lines.to_vec(),
|
||||||
};
|
};
|
||||||
match last_n {
|
match last_n {
|
||||||
@@ -85,7 +89,10 @@ pub fn follow(path: &Path, min_level: Option<Level>) {
|
|||||||
/// # 副作用
|
/// # 副作用
|
||||||
/// - 向 writer 写行。
|
/// - 向 writer 写行。
|
||||||
fn follow_to<W: Write>(path: &Path, min_level: Option<Level>, writer: &mut W) {
|
fn follow_to<W: Write>(path: &Path, min_level: Option<Level>, writer: &mut W) {
|
||||||
let mut file = OpenOptions::new().read(true).open(path).expect("日志文件应存在(调用方已检查)");
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(path)
|
||||||
|
.expect("日志文件应存在(调用方已检查)");
|
||||||
let mut offset = file.seek(SeekFrom::End(0)).expect("定位日志文件末尾应成功");
|
let mut offset = file.seek(SeekFrom::End(0)).expect("定位日志文件末尾应成功");
|
||||||
let mut buf = [0u8; 8192];
|
let mut buf = [0u8; 8192];
|
||||||
let mut pending = String::new();
|
let mut pending = String::new();
|
||||||
@@ -176,11 +183,7 @@ mod tests {
|
|||||||
// info 留 ERROR/WARN/INFO 行,丢 DEBUG 行。
|
// info 留 ERROR/WARN/INFO 行,丢 DEBUG 行。
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
filter_tail(&lines, Some(Level::Info), None),
|
filter_tail(&lines, Some(Level::Info), None),
|
||||||
vec![
|
vec!["1700000000.001 INFO a: one", "1700000000.002 ERROR a: two", "1700000000.004 WARN a: four",]
|
||||||
"1700000000.001 INFO a: one",
|
|
||||||
"1700000000.002 ERROR a: two",
|
|
||||||
"1700000000.004 WARN a: four",
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
// debug 留 DEBUG 与更严重行(全留);trace 全留。
|
// debug 留 DEBUG 与更严重行(全留);trace 全留。
|
||||||
assert_eq!(filter_tail(&lines, Some(Level::Debug), None), lines);
|
assert_eq!(filter_tail(&lines, Some(Level::Debug), None), lines);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub mod server;
|
|||||||
pub mod sidecar;
|
pub mod sidecar;
|
||||||
/// spawn 与就绪探测:start、restart、watchdog 三条路径共用的唯一共享入口。
|
/// spawn 与就绪探测:start、restart、watchdog 三条路径共用的唯一共享入口。
|
||||||
pub mod spawn;
|
pub mod spawn;
|
||||||
/// worker 池:固定 N 常驻 worker、各持自有 VM 池、mpsc 轮询路由。
|
|
||||||
pub mod workers;
|
|
||||||
/// watchdog:前台监控进程,三态裁决加崩溃预算,崩溃后自动重启。
|
/// watchdog:前台监控进程,三态裁决加崩溃预算,崩溃后自动重启。
|
||||||
pub mod watchdog;
|
pub mod watchdog;
|
||||||
|
/// worker 池:固定 N 常驻 worker、各持自有 VM 池、mpsc 轮询路由。
|
||||||
|
pub mod workers;
|
||||||
|
|||||||
@@ -201,12 +201,20 @@ impl ServerResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 请求帧可取的 `type` 值(须与 `ServerRequest` 变体保持同步)。
|
/// 请求帧可取的 `type` 值(须与 `ServerRequest` 变体保持同步)。
|
||||||
const REQUEST_TAGS: &[&str] =
|
const REQUEST_TAGS: &[&str] = &["eval", "forge_query", "version", "status", "health", "info", "shutdown", "yield"];
|
||||||
&["eval", "forge_query", "version", "status", "health", "info", "shutdown", "yield"];
|
|
||||||
|
|
||||||
/// 响应帧可取的 `type` 值(须与 `ServerResponse` 变体保持同步)。
|
/// 响应帧可取的 `type` 值(须与 `ServerResponse` 变体保持同步)。
|
||||||
const RESPONSE_TAGS: &[&str] =
|
const RESPONSE_TAGS: &[&str] = &[
|
||||||
&["eval_result", "forge_status", "version", "status", "health", "info", "shutdown", "yield", "error"];
|
"eval_result",
|
||||||
|
"forge_status",
|
||||||
|
"version",
|
||||||
|
"status",
|
||||||
|
"health",
|
||||||
|
"info",
|
||||||
|
"shutdown",
|
||||||
|
"yield",
|
||||||
|
"error",
|
||||||
|
];
|
||||||
|
|
||||||
/// 序列化请求为帧字符串(含结尾换行)。
|
/// 序列化请求为帧字符串(含结尾换行)。
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -208,9 +208,7 @@ pub fn run_server(config: &ServerConfig) -> Result<(), ServerError> {
|
|||||||
// 命令。
|
// 命令。
|
||||||
match liveness::liveness_scan(&config.sidecar_path, &config.socket_path) {
|
match liveness::liveness_scan(&config.sidecar_path, &config.socket_path) {
|
||||||
liveness::LivenessOutcome::Noop | liveness::LivenessOutcome::Cleaned => {}
|
liveness::LivenessOutcome::Noop | liveness::LivenessOutcome::Cleaned => {}
|
||||||
liveness::LivenessOutcome::KillFailed => {
|
liveness::LivenessOutcome::KillFailed => return Err(ServerError::ClaimRefused(ClaimResult::RefusedAmbiguous)),
|
||||||
return Err(ServerError::ClaimRefused(ClaimResult::RefusedAmbiguous))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 认领 sidecar:同版本拒绝启动,异版本走交接路径,存活证据不足即返回错误。
|
// 认领 sidecar:同版本拒绝启动,异版本走交接路径,存活证据不足即返回错误。
|
||||||
@@ -1208,11 +1206,17 @@ mod tests {
|
|||||||
fn yield_takeover_end_to_end() {
|
fn yield_takeover_end_to_end() {
|
||||||
let config = unique_paths("yield_e2e");
|
let config = unique_paths("yield_e2e");
|
||||||
// A 用版本 0.0.1。
|
// A 用版本 0.0.1。
|
||||||
let config_a = ServerConfig { version: "0.0.1".to_string(), ..config.clone() };
|
let config_a = ServerConfig {
|
||||||
|
version: "0.0.1".to_string(),
|
||||||
|
..config.clone()
|
||||||
|
};
|
||||||
let handle_a = start_server(config_a);
|
let handle_a = start_server(config_a);
|
||||||
|
|
||||||
// B 用版本 0.0.2:认领得 VersionMismatch 转交接路径。
|
// B 用版本 0.0.2:认领得 VersionMismatch 转交接路径。
|
||||||
let config_b = ServerConfig { version: "0.0.2".to_string(), ..config.clone() };
|
let config_b = ServerConfig {
|
||||||
|
version: "0.0.2".to_string(),
|
||||||
|
..config.clone()
|
||||||
|
};
|
||||||
let handle_b = std::thread::spawn(move || run_server(&config_b));
|
let handle_b = std::thread::spawn(move || run_server(&config_b));
|
||||||
|
|
||||||
// A 应在 30 秒内有界退出(排水加退出序列)。
|
// A 应在 30 秒内有界退出(排水加退出序列)。
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ pub fn spawn_detached_server(workers: Option<u32>) -> std::io::Result<std::proce
|
|||||||
if let Some(n) = workers {
|
if let Some(n) = workers {
|
||||||
cmd.args(["--workers", &n.to_string()]);
|
cmd.args(["--workers", &n.to_string()]);
|
||||||
}
|
}
|
||||||
cmd.stdin(Stdio::null())
|
cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()).spawn()
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.spawn()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 就绪探测:轮询健康请求直至得 healthy。
|
/// 就绪探测:轮询健康请求直至得 healthy。
|
||||||
|
|||||||
@@ -99,10 +99,7 @@ impl WorkerRouter {
|
|||||||
/// # 注意事项
|
/// # 注意事项
|
||||||
/// - 返回的句柄必须在关闭序列中逐一 join(drop 发送端 → join → drop 内核)。
|
/// - 返回的句柄必须在关闭序列中逐一 join(drop 发送端 → join → drop 内核)。
|
||||||
pub fn spawn_workers(
|
pub fn spawn_workers(
|
||||||
kernel: Arc<KernelCore>,
|
kernel: Arc<KernelCore>, counters: Arc<PoolCounters>, min_pool_size: usize, max_pool_size: Option<usize>,
|
||||||
counters: Arc<PoolCounters>,
|
|
||||||
min_pool_size: usize,
|
|
||||||
max_pool_size: Option<usize>,
|
|
||||||
worker_count: usize,
|
worker_count: usize,
|
||||||
) -> (WorkerRouter, Vec<JoinHandle<()>>) {
|
) -> (WorkerRouter, Vec<JoinHandle<()>>) {
|
||||||
let n = worker_count.max(1);
|
let n = worker_count.max(1);
|
||||||
@@ -126,11 +123,7 @@ pub fn spawn_workers(
|
|||||||
fn worker_loop(receiver: mpsc::Receiver<WorkerTask>, kernel: Arc<KernelCore>, pool: Arc<VmPool>) {
|
fn worker_loop(receiver: mpsc::Receiver<WorkerTask>, kernel: Arc<KernelCore>, pool: Arc<VmPool>) {
|
||||||
for task in receiver {
|
for task in receiver {
|
||||||
match task {
|
match task {
|
||||||
WorkerTask::Eval {
|
WorkerTask::Eval { code, max_steps, reply } => {
|
||||||
code,
|
|
||||||
max_steps,
|
|
||||||
reply,
|
|
||||||
} => {
|
|
||||||
let response = eval::handle_eval(&code, max_steps, &kernel, &pool);
|
let response = eval::handle_eval(&code, max_steps, &kernel, &pool);
|
||||||
let _ = reply.send(response);
|
let _ = reply.send(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ fn server_help_lists_subcommands() {
|
|||||||
assert!(output.status.success(), "server --help 应退 0");
|
assert!(output.status.success(), "server --help 应退 0");
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
for name in [
|
for name in [
|
||||||
"start", "stop", "status", "health", "info", "version", "cleanup", "restart", "log", "forge",
|
"start", "stop", "status", "health", "info", "version", "cleanup", "restart", "log", "forge", "watchdog",
|
||||||
"watchdog",
|
|
||||||
] {
|
] {
|
||||||
assert!(stdout.contains(name), "help 应列出 {name}:{stdout}");
|
assert!(stdout.contains(name), "help 应列出 {name}:{stdout}");
|
||||||
}
|
}
|
||||||
@@ -124,10 +123,7 @@ fn server_control_arms_no_server_exit_1() {
|
|||||||
let output = oxide(&["server", sub]);
|
let output = oxide(&["server", sub]);
|
||||||
assert_eq!(output.status.code(), Some(1), "{sub} 应退 1");
|
assert_eq!(output.status.code(), Some(1), "{sub} 应退 1");
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
assert!(
|
assert!(stderr.contains("无已注册的 server"), "{sub} 应打印无 server 提示:{stderr}");
|
||||||
stderr.contains("无已注册的 server"),
|
|
||||||
"{sub} 应打印无 server 提示:{stderr}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,10 +141,7 @@ fn server_forge_no_server_exit_1() {
|
|||||||
let output = oxide(&["server", "forge", "code"]);
|
let output = oxide(&["server", "forge", "code"]);
|
||||||
assert_eq!(output.status.code(), Some(1), "forge 应退 1");
|
assert_eq!(output.status.code(), Some(1), "forge 应退 1");
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
assert!(
|
assert!(stderr.contains("无已注册的 server"), "forge 应打印无 server 提示:{stderr}");
|
||||||
stderr.contains("无已注册的 server"),
|
|
||||||
"forge 应打印无 server 提示:{stderr}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// cleanup 退 0(幂等:有残留则清理、无残留则报无残留),事后全局路径无文件。
|
/// cleanup 退 0(幂等:有残留则清理、无残留则报无残留),事后全局路径无文件。
|
||||||
|
|||||||
@@ -53,8 +53,7 @@ fn control_client_end_to_end() {
|
|||||||
// 状态请求:worker 池异步预热,轮询至总数不小于 1 后核对字段。
|
// 状态请求:worker 池异步预热,轮询至总数不小于 1 后核对字段。
|
||||||
let deadline = Instant::now() + Duration::from_secs(5);
|
let deadline = Instant::now() + Duration::from_secs(5);
|
||||||
let response = loop {
|
let response = loop {
|
||||||
let response =
|
let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Status).expect("状态请求应成功");
|
||||||
send_control_request_from(&config.sidecar_path, &ServerRequest::Status).expect("状态请求应成功");
|
|
||||||
if let ServerResponse::Status { pool_total, .. } = &response {
|
if let ServerResponse::Status { pool_total, .. } = &response {
|
||||||
if *pool_total >= 1 {
|
if *pool_total >= 1 {
|
||||||
break response;
|
break response;
|
||||||
@@ -89,12 +88,8 @@ fn control_client_end_to_end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关闭请求:得确认帧,server 线程正常退出,socket 与 sidecar 文件均被删。
|
// 关闭请求:得确认帧,server 线程正常退出,socket 与 sidecar 文件均被删。
|
||||||
let response =
|
let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Shutdown).expect("关闭请求应成功");
|
||||||
send_control_request_from(&config.sidecar_path, &ServerRequest::Shutdown).expect("关闭请求应成功");
|
assert!(matches!(response, ServerResponse::Shutdown), "应得关闭确认帧:{response:?}");
|
||||||
assert!(
|
|
||||||
matches!(response, ServerResponse::Shutdown),
|
|
||||||
"应得关闭确认帧:{response:?}"
|
|
||||||
);
|
|
||||||
let result = handle.join().expect("server 线程应正常退出");
|
let result = handle.join().expect("server 线程应正常退出");
|
||||||
assert!(result.is_ok(), "server 应正常退出:{result:?}");
|
assert!(result.is_ok(), "server 应正常退出:{result:?}");
|
||||||
assert!(!config.socket_path.exists(), "socket 文件应被删除");
|
assert!(!config.socket_path.exists(), "socket 文件应被删除");
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ use oxide_cli::server::server::{run_server, ServerConfig, ServerError};
|
|||||||
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
||||||
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
||||||
fn unique_paths(test_name: &str) -> ServerConfig {
|
fn unique_paths(test_name: &str) -> ServerConfig {
|
||||||
let dir = std::env::temp_dir().join(format!(
|
let dir = std::env::temp_dir().join(format!("oxide_server_signal_int_{}_{}", std::process::id(), test_name));
|
||||||
"oxide_server_signal_int_{}_{}",
|
|
||||||
std::process::id(),
|
|
||||||
test_name
|
|
||||||
));
|
|
||||||
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
||||||
ServerConfig {
|
ServerConfig {
|
||||||
socket_path: dir.join("server.sock"),
|
socket_path: dir.join("server.sock"),
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ use oxide_cli::server::server::{run_server, ServerConfig, ServerError};
|
|||||||
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
||||||
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
||||||
fn unique_paths(test_name: &str) -> ServerConfig {
|
fn unique_paths(test_name: &str) -> ServerConfig {
|
||||||
let dir = std::env::temp_dir().join(format!(
|
let dir = std::env::temp_dir().join(format!("oxide_server_signal_term_{}_{}", std::process::id(), test_name));
|
||||||
"oxide_server_signal_term_{}_{}",
|
|
||||||
std::process::id(),
|
|
||||||
test_name
|
|
||||||
));
|
|
||||||
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
||||||
ServerConfig {
|
ServerConfig {
|
||||||
socket_path: dir.join("server.sock"),
|
socket_path: dir.join("server.sock"),
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ fn symbol_key_realm_dimensions_distinct() {
|
|||||||
assert_ne!(encode_symbol_key(0, 15), encode_symbol_key(1, 15));
|
assert_ne!(encode_symbol_key(0, 15), encode_symbol_key(1, 15));
|
||||||
assert_ne!(encode_symbol_key(1, 15), encode_symbol_key(1, 16));
|
assert_ne!(encode_symbol_key(1, 15), encode_symbol_key(1, 16));
|
||||||
// 跨 realm 同名 well-known 符号也产生不同键。
|
// 跨 realm 同名 well-known 符号也产生不同键。
|
||||||
assert_ne!(encode_symbol_key(0, WELL_KNOWN_SYMBOL_ITERATOR), encode_symbol_key(2, WELL_KNOWN_SYMBOL_ITERATOR));
|
assert_ne!(
|
||||||
|
encode_symbol_key(0, WELL_KNOWN_SYMBOL_ITERATOR),
|
||||||
|
encode_symbol_key(2, WELL_KNOWN_SYMBOL_ITERATOR)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ impl Vm {
|
|||||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], promise: JsValue, resolve: JsValue,
|
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], promise: JsValue, resolve: JsValue,
|
||||||
reject: JsValue,
|
reject: JsValue,
|
||||||
) -> JsValue {
|
) -> JsValue {
|
||||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let proto_val =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
obj.type_tag = JsObject::OBJ_TYPE_ASYNC;
|
obj.type_tag = JsObject::OBJ_TYPE_ASYNC;
|
||||||
@@ -607,13 +608,20 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
|||||||
let af_ctor_fn_ptr =
|
let af_ctor_fn_ptr =
|
||||||
unsafe { NativeFnPtr::from_raw(oxide_builtins::function::async_function_constructor::<Vm> as *const ()) };
|
unsafe { NativeFnPtr::from_raw(oxide_builtins::function::async_function_constructor::<Vm> as *const ()) };
|
||||||
let (af_ctor_ptr, af_ctor_is_new) =
|
let (af_ctor_ptr, af_ctor_is_new) =
|
||||||
match vm.realm.session.borrow().builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) {
|
match vm
|
||||||
|
.realm
|
||||||
|
.session
|
||||||
|
.borrow()
|
||||||
|
.builtin_world()
|
||||||
|
.find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1)
|
||||||
|
{
|
||||||
Some(ptr) => (ptr, false),
|
Some(ptr) => (ptr, false),
|
||||||
None => {
|
None => {
|
||||||
// [[Prototype]] = Function 构造器本体(与 Function 构造器同链,
|
// [[Prototype]] = Function 构造器本体(与 Function 构造器同链,
|
||||||
// `Object.getPrototypeOf(AsyncFunction) === Function` 语义)。
|
// `Object.getPrototypeOf(AsyncFunction) === Function` 语义)。
|
||||||
let fn_ctor_val =
|
let fn_ctor_val = JsValue::from_js_object(
|
||||||
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().function_constructor.as_ptr() as *mut JsObject);
|
vm.realm.session.borrow().builtin_world().function_constructor.as_ptr() as *mut JsObject,
|
||||||
|
);
|
||||||
let mut af_ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_ctor_val));
|
let mut af_ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_ctor_val));
|
||||||
af_ctor.set_function(true);
|
af_ctor.set_function(true);
|
||||||
af_ctor.set_native_arg_count(1);
|
af_ctor.set_native_arg_count(1);
|
||||||
@@ -638,7 +646,11 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
|||||||
af_ctor.set_shape_id(tag_shape);
|
af_ctor.set_shape_id(tag_shape);
|
||||||
let af_ctor_ptr = Box::into_raw(af_ctor);
|
let af_ctor_ptr = Box::into_raw(af_ctor);
|
||||||
// 登记进 world 释放表(带复用键):session 收尾统一释放构造器本体与属性区。
|
// 登记进 world 释放表(带复用键):session 收尾统一释放构造器本体与属性区。
|
||||||
vm.realm.session.borrow().builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key);
|
vm.realm
|
||||||
|
.session
|
||||||
|
.borrow()
|
||||||
|
.builtin_world()
|
||||||
|
.track_fn_wrapper(af_ctor_ptr, af_reuse_key);
|
||||||
(af_ctor_ptr, true)
|
(af_ctor_ptr, true)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ impl Vm {
|
|||||||
pub(crate) fn create_async_generator_object(
|
pub(crate) fn create_async_generator_object(
|
||||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue],
|
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue],
|
||||||
) -> Result<JsValue, String> {
|
) -> Result<JsValue, String> {
|
||||||
let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject);
|
let gen_proto_val =
|
||||||
|
JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject);
|
||||||
let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val));
|
let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val));
|
||||||
let obj_ref = unsafe { &mut *obj };
|
let obj_ref = unsafe { &mut *obj };
|
||||||
obj_ref.type_tag = JsObject::OBJ_TYPE_ASYNC_GENERATOR;
|
obj_ref.type_tag = JsObject::OBJ_TYPE_ASYNC_GENERATOR;
|
||||||
@@ -1604,7 +1605,8 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
|||||||
let proto2_si = sf.intern("prototype").0;
|
let proto2_si = sf.intern("prototype").0;
|
||||||
let proto2_shape = sh.make_shape(agf_proto.shape_id(), proto2_si);
|
let proto2_shape = sh.make_shape(agf_proto.shape_id(), proto2_si);
|
||||||
agf_proto.set_shape_id(proto2_shape);
|
agf_proto.set_shape_id(proto2_shape);
|
||||||
let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject));
|
let ppos2 =
|
||||||
|
agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject));
|
||||||
agf_proto.set_data_meta(ppos2, PropAttributes::new(false, false, true));
|
agf_proto.set_data_meta(ppos2, PropAttributes::new(false, false, true));
|
||||||
// agf_proto[Symbol.toStringTag] = "AsyncGeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
// agf_proto[Symbol.toStringTag] = "AsyncGeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
||||||
let tag2_key = oxide_types::private_key::encode_symbol_key(
|
let tag2_key = oxide_types::private_key::encode_symbol_key(
|
||||||
@@ -1624,7 +1626,8 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
|||||||
// SAFETY: agf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。
|
// SAFETY: agf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctor_mut = &mut *agf_ctor_ptr;
|
let ctor_mut = &mut *agf_ctor_ptr;
|
||||||
let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject);
|
let proto_val =
|
||||||
|
JsValue::from_js_object(vm.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject);
|
||||||
let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncGeneratorFunction").0));
|
let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncGeneratorFunction").0));
|
||||||
if agf_ctor_is_new {
|
if agf_ctor_is_new {
|
||||||
let lpos = ctor_mut.push_prop(JsValue::int(1));
|
let lpos = ctor_mut.push_prop(JsValue::int(1));
|
||||||
|
|||||||
@@ -256,7 +256,10 @@ pub fn bind_typed_array(core: &Arc<KernelCore>, session: &KernelSession, global:
|
|||||||
core,
|
core,
|
||||||
session,
|
session,
|
||||||
shared_proto,
|
shared_proto,
|
||||||
oxide_types::private_key::encode_symbol_key(realm_id, oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG),
|
oxide_types::private_key::encode_symbol_key(
|
||||||
|
realm_id,
|
||||||
|
oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG,
|
||||||
|
),
|
||||||
"get [Symbol.toStringTag]",
|
"get [Symbol.toStringTag]",
|
||||||
oxide_builtins::typed_array::typed_array_to_string_tag_getter::<crate::vm::Vm> as *const (),
|
oxide_builtins::typed_array::typed_array_to_string_tag_getter::<crate::vm::Vm> as *const (),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -89,8 +89,10 @@ pub fn bind_weak_map(core: &Arc<KernelCore>, session: &mut KernelSession, global
|
|||||||
proto.ensure_hash_props().push(JsValue::from_js_object(ctor_ptr));
|
proto.ensure_hash_props().push(JsValue::from_js_object(ctor_ptr));
|
||||||
let ctor_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
let ctor_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||||
proto.set_data_meta(ctor_pos, PropAttributes::new(true, false, true));
|
proto.set_data_meta(ctor_pos, PropAttributes::new(true, false, true));
|
||||||
let tag_key =
|
let tag_key = oxide_types::private_key::encode_symbol_key(
|
||||||
oxide_types::private_key::encode_symbol_key(realm_id, oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG);
|
realm_id,
|
||||||
|
oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG,
|
||||||
|
);
|
||||||
let tag_shape = core.shape_forge().make_shape(proto.shape_id(), tag_key);
|
let tag_shape = core.shape_forge().make_shape(proto.shape_id(), tag_key);
|
||||||
proto.set_shape_id(tag_shape);
|
proto.set_shape_id(tag_shape);
|
||||||
proto.ensure_hash_props().push(JsValue::perm_string(
|
proto.ensure_hash_props().push(JsValue::perm_string(
|
||||||
|
|||||||
@@ -510,8 +510,10 @@ fn bind_iterator_protos(core: &Arc<KernelCore>, session: &KernelSession, realm_i
|
|||||||
oxide_builtins::iterator::iterator_constructor_setter::<crate::vm::Vm> as *const (),
|
oxide_builtins::iterator::iterator_constructor_setter::<crate::vm::Vm> as *const (),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let sym_to_string_tag =
|
let sym_to_string_tag = oxide_types::private_key::encode_symbol_key(
|
||||||
oxide_types::private_key::encode_symbol_key(realm_id, oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG);
|
realm_id,
|
||||||
|
oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG,
|
||||||
|
);
|
||||||
if core
|
if core
|
||||||
.shape_forge()
|
.shape_forge()
|
||||||
.lookup_position(iter_proto.shape_id(), sym_to_string_tag)
|
.lookup_position(iter_proto.shape_id(), sym_to_string_tag)
|
||||||
@@ -603,8 +605,10 @@ fn bind_iterator_protos(core: &Arc<KernelCore>, session: &KernelSession, realm_i
|
|||||||
realm_id,
|
realm_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let sym_adispose =
|
let sym_adispose = oxide_types::private_key::encode_symbol_key(
|
||||||
oxide_types::private_key::encode_symbol_key(realm_id, oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_DISPOSE);
|
realm_id,
|
||||||
|
oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_DISPOSE,
|
||||||
|
);
|
||||||
if core
|
if core
|
||||||
.shape_forge()
|
.shape_forge()
|
||||||
.lookup_position(aiter_proto.shape_id(), sym_adispose)
|
.lookup_position(aiter_proto.shape_id(), sym_adispose)
|
||||||
|
|||||||
@@ -892,7 +892,8 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
|||||||
let proto2_si = sf.intern("prototype").0;
|
let proto2_si = sf.intern("prototype").0;
|
||||||
let proto2_shape = sh.make_shape(gf_proto.shape_id(), proto2_si);
|
let proto2_shape = sh.make_shape(gf_proto.shape_id(), proto2_si);
|
||||||
gf_proto.set_shape_id(proto2_shape);
|
gf_proto.set_shape_id(proto2_shape);
|
||||||
let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.borrow().as_ptr() as *mut JsObject));
|
let ppos2 =
|
||||||
|
gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.borrow().as_ptr() as *mut JsObject));
|
||||||
gf_proto.set_data_meta(ppos2, oxide_types::object::PropAttributes::new(false, false, true));
|
gf_proto.set_data_meta(ppos2, oxide_types::object::PropAttributes::new(false, false, true));
|
||||||
// gf_proto[Symbol.toStringTag] = "GeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
// gf_proto[Symbol.toStringTag] = "GeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
||||||
let tag2_key = oxide_types::private_key::encode_symbol_key(
|
let tag2_key = oxide_types::private_key::encode_symbol_key(
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ impl Vm {
|
|||||||
/// 创建聚合静态方法(all/race/allSettled/any)的共享记录对象:剩余计数
|
/// 创建聚合静态方法(all/race/allSettled/any)的共享记录对象:剩余计数
|
||||||
/// 初始 1,结果数组(race 无)与能力 resolve/reject 闭包全部存为自身属性。
|
/// 初始 1,结果数组(race 无)与能力 resolve/reject 闭包全部存为自身属性。
|
||||||
fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
||||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let proto_val =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
@@ -43,7 +44,8 @@ impl Vm {
|
|||||||
/// 创建 keyed 聚合静态方法的共享记录对象:剩余计数(初始 1)、结果平行数组、
|
/// 创建 keyed 聚合静态方法的共享记录对象:剩余计数(初始 1)、结果平行数组、
|
||||||
/// 键平行数组与能力 resolve/reject 闭包存为自身属性。
|
/// 键平行数组与能力 resolve/reject 闭包存为自身属性。
|
||||||
fn make_agg_record_keyed(&mut self, values: JsValue, keys: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
fn make_agg_record_keyed(&mut self, values: JsValue, keys: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
||||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let proto_val =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
@@ -96,7 +98,8 @@ impl Vm {
|
|||||||
|
|
||||||
/// 构造 allSettled 的结算记录 `{status, <value_field>: value}` 普通对象。
|
/// 构造 allSettled 的结算记录 `{status, <value_field>: value}` 普通对象。
|
||||||
fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue {
|
fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue {
|
||||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let proto_val =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 status/value 属性(含 new_string 分配)期间不搬移,借出期间无别名。
|
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 status/value 属性(含 new_string 分配)期间不搬移,借出期间无别名。
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
@@ -132,7 +135,8 @@ impl Vm {
|
|||||||
|
|
||||||
/// 把 errors 可迭代值收集为新数组(IterableToList)。不可迭代抛 TypeError。
|
/// 把 errors 可迭代值收集为新数组(IterableToList)。不可迭代抛 TypeError。
|
||||||
fn aggregate_errors_to_list(&mut self, errors: JsValue) -> Result<JsValue, JsValue> {
|
fn aggregate_errors_to_list(&mut self, errors: JsValue) -> Result<JsValue, JsValue> {
|
||||||
let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
let array_proto =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||||
let list = JsValue::from_js_object(ptr);
|
let list = JsValue::from_js_object(ptr);
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
@@ -148,7 +152,8 @@ impl Vm {
|
|||||||
|
|
||||||
/// 建普通数组对象并依次写入元素(Promise.try 转发实参的承载)。
|
/// 建普通数组对象并依次写入元素(Promise.try 转发实参的承载)。
|
||||||
fn make_plain_array(&mut self, elements: Vec<JsValue>) -> JsValue {
|
fn make_plain_array(&mut self, elements: Vec<JsValue>) -> JsValue {
|
||||||
let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
let array_proto =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;元素写入不搬移对象。
|
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;元素写入不搬移对象。
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
@@ -210,8 +215,9 @@ impl Vm {
|
|||||||
proto.set_data_meta(msg_pos, PropAttributes::new(true, false, true));
|
proto.set_data_meta(msg_pos, PropAttributes::new(true, false, true));
|
||||||
|
|
||||||
// %AggregateError% 构造器:proto = %Error%(NativeError 构造器继承 Error 构造器),length 2。
|
// %AggregateError% 构造器:proto = %Error%(NativeError 构造器继承 Error 构造器),length 2。
|
||||||
let error_ctor_val =
|
let error_ctor_val = JsValue::from_js_object(
|
||||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
self.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject,
|
||||||
|
);
|
||||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_ctor_val));
|
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_ctor_val));
|
||||||
ctor.set_function(true);
|
ctor.set_function(true);
|
||||||
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||||
@@ -239,18 +245,24 @@ impl Vm {
|
|||||||
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor.borrow_mut(), *ctor);
|
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor.borrow_mut(), *ctor);
|
||||||
// SAFETY: aggregate_error_proto 为堆址固定的 P<JsObject>(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。
|
// SAFETY: aggregate_error_proto 为堆址固定的 P<JsObject>(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。
|
||||||
let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.borrow().as_mut_ptr() };
|
let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.borrow().as_mut_ptr() };
|
||||||
proto_mut
|
proto_mut.set_prop_at(
|
||||||
.set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject));
|
0u32,
|
||||||
|
JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject),
|
||||||
|
);
|
||||||
// SAFETY: aggregate_error_constructor 同为堆址固定的 P<JsObject>(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
// SAFETY: aggregate_error_constructor 同为堆址固定的 P<JsObject>(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
||||||
let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.borrow().as_mut_ptr() };
|
let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.borrow().as_mut_ptr() };
|
||||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject));
|
ctor_mut.set_prop_at(
|
||||||
|
2u32,
|
||||||
|
JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject),
|
||||||
|
);
|
||||||
|
|
||||||
// 绑定 global(槽已存在则更新)。
|
// 绑定 global(槽已存在则更新)。
|
||||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||||
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 shape/属性区,期间无 reset 或对象搬移。
|
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 shape/属性区,期间无 reset 或对象搬移。
|
||||||
let global = unsafe { &mut *global_ptr };
|
let global = unsafe { &mut *global_ptr };
|
||||||
let si = self.kernel_core.perm_interner().intern("AggregateError").0;
|
let si = self.kernel_core.perm_interner().intern("AggregateError").0;
|
||||||
let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject);
|
let ctor_val =
|
||||||
|
JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject);
|
||||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
||||||
global.set_prop_at(pos, ctor_val);
|
global.set_prop_at(pos, ctor_val);
|
||||||
} else {
|
} else {
|
||||||
@@ -353,7 +365,8 @@ pub(super) fn promise_static_with_resolvers(vm: &mut Vm, args: &[u8]) -> NativeR
|
|||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(err) => return NativeResult::Err(err),
|
Err(err) => return NativeResult::Err(err),
|
||||||
};
|
};
|
||||||
let object_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let object_proto =
|
||||||
|
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto));
|
let ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto));
|
||||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 promise/resolve/reject 属性期间不搬移,借出期间无别名。
|
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 promise/resolve/reject 属性期间不搬移,借出期间无别名。
|
||||||
let obj = unsafe { &mut *ptr };
|
let obj = unsafe { &mut *ptr };
|
||||||
@@ -478,7 +491,8 @@ fn perform_promise_combine(
|
|||||||
let values = if kind == AggregateKind::Race {
|
let values = if kind == AggregateKind::Race {
|
||||||
JsValue::undefined()
|
JsValue::undefined()
|
||||||
} else {
|
} else {
|
||||||
let array_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
let array_proto =
|
||||||
|
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||||
let ptr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
let ptr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||||
JsValue::from_js_object(ptr)
|
JsValue::from_js_object(ptr)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -117,8 +117,9 @@ impl Vm {
|
|||||||
self.realm.session.borrow().builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
|
self.realm.session.borrow().builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
|
||||||
);
|
);
|
||||||
let this_val = if is_data_view {
|
let this_val = if is_data_view {
|
||||||
let data_view_proto_val =
|
let data_view_proto_val = JsValue::from_js_object(
|
||||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject);
|
self.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject,
|
||||||
|
);
|
||||||
JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val)))
|
JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val)))
|
||||||
} else {
|
} else {
|
||||||
let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
|
let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
|
||||||
@@ -156,7 +157,8 @@ impl Vm {
|
|||||||
&mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue,
|
&mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue,
|
||||||
) -> Result<*mut JsObject, JsValue> {
|
) -> Result<*mut JsObject, JsValue> {
|
||||||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||||||
let object_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let object_proto =
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR;
|
let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||||
let proto_val = if is_native_ctor {
|
let proto_val = if is_native_ctor {
|
||||||
match self.resolve_property(nt_obj, proto_si) {
|
match self.resolve_property(nt_obj, proto_si) {
|
||||||
@@ -337,12 +339,16 @@ impl Vm {
|
|||||||
// SAFETY: `promise_proto` 为 `P<JsObject>`(Arc 透明包装),堆址固定且本行前刚经
|
// SAFETY: `promise_proto` 为 `P<JsObject>`(Arc 透明包装),堆址固定且本行前刚经
|
||||||
// swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。
|
// swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。
|
||||||
let proto_mut = unsafe { &mut *self.realm.promise_proto.borrow().as_mut_ptr() };
|
let proto_mut = unsafe { &mut *self.realm.promise_proto.borrow().as_mut_ptr() };
|
||||||
proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject));
|
proto_mut.set_prop_at(
|
||||||
|
0u32,
|
||||||
|
JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject),
|
||||||
|
);
|
||||||
// SAFETY: `promise_constructor` 同为堆址固定的存活 `P<JsObject>`(Arc 透明包装);
|
// SAFETY: `promise_constructor` 同为堆址固定的存活 `P<JsObject>`(Arc 透明包装);
|
||||||
// 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
// 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
||||||
let ctor_mut = unsafe { &mut *self.realm.promise_constructor.borrow().as_mut_ptr() };
|
let ctor_mut = unsafe { &mut *self.realm.promise_constructor.borrow().as_mut_ptr() };
|
||||||
// prototype 槽位在 length/name 之后(下标 2)。
|
// prototype 槽位在 length/name 之后(下标 2)。
|
||||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject));
|
ctor_mut
|
||||||
|
.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject));
|
||||||
|
|
||||||
// Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链
|
// Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链
|
||||||
// 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。
|
// 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。
|
||||||
|
|||||||
@@ -774,7 +774,14 @@ impl SessionGc {
|
|||||||
/// forwarding 表与根指针重写。在字符串清扫之后运行。
|
/// forwarding 表与根指针重写。在字符串清扫之后运行。
|
||||||
/// 返回释放的字节数。
|
/// 返回释放的字节数。
|
||||||
pub(crate) fn sweep_session_bigints(&mut self, vm: &mut Vm) -> u64 {
|
pub(crate) fn sweep_session_bigints(&mut self, vm: &mut Vm) -> u64 {
|
||||||
let old = vm.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
let old = vm
|
||||||
|
.realm
|
||||||
|
.gc
|
||||||
|
.borrow_mut()
|
||||||
|
.session_bigint_ptrs
|
||||||
|
.borrow_mut()
|
||||||
|
.drain(..)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let mut freed = 0u64;
|
let mut freed = 0u64;
|
||||||
let mut live = Vec::with_capacity(old.len());
|
let mut live = Vec::with_capacity(old.len());
|
||||||
for ptr in old {
|
for ptr in old {
|
||||||
@@ -809,7 +816,14 @@ impl SessionGc {
|
|||||||
/// 统计、不重算存活账目。
|
/// 统计、不重算存活账目。
|
||||||
/// 返回释放的字节数。
|
/// 返回释放的字节数。
|
||||||
pub(crate) fn sweep_session_cells(&mut self, vm: &mut Vm) -> u64 {
|
pub(crate) fn sweep_session_cells(&mut self, vm: &mut Vm) -> u64 {
|
||||||
let old = vm.realm.gc.borrow_mut().session_cell_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
let old = vm
|
||||||
|
.realm
|
||||||
|
.gc
|
||||||
|
.borrow_mut()
|
||||||
|
.session_cell_ptrs
|
||||||
|
.borrow_mut()
|
||||||
|
.drain(..)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let mut freed = 0u64;
|
let mut freed = 0u64;
|
||||||
let mut live = Vec::with_capacity(old.len());
|
let mut live = Vec::with_capacity(old.len());
|
||||||
for ptr in old {
|
for ptr in old {
|
||||||
|
|||||||
@@ -563,7 +563,8 @@ fn weak_map_dead_key_freed_by_in_run_sweep() {
|
|||||||
// 键无强根:仅表内弱边引用。
|
// 键无强根:仅表内弱边引用。
|
||||||
let key = plain_object(&mut vm);
|
let key = plain_object(&mut vm);
|
||||||
// P 键(builtin 原型,不可死):对照条目须保留。
|
// P 键(builtin 原型,不可死):对照条目须保留。
|
||||||
let p_key = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
let p_key =
|
||||||
|
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||||
unsafe {
|
unsafe {
|
||||||
oxide_builtins::weak_map::weak_map_insert(
|
oxide_builtins::weak_map::weak_map_insert(
|
||||||
&mut *wm,
|
&mut *wm,
|
||||||
@@ -1196,7 +1197,10 @@ fn live_closure_cell_survives_full_collect() {
|
|||||||
collect(&mut vm);
|
collect(&mut vm);
|
||||||
|
|
||||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活");
|
assert!(
|
||||||
|
vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr),
|
||||||
|
"活 cell 应跨完整收集存活"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 活跃帧 cell_stack 根:内层函数 upvalue cell 在活跃帧 cell_stack 上(低阈值使
|
/// 活跃帧 cell_stack 根:内层函数 upvalue cell 在活跃帧 cell_stack 上(低阈值使
|
||||||
@@ -1256,7 +1260,10 @@ fn suspended_state_box_cell_root_survives_collect() {
|
|||||||
collect(&mut vm);
|
collect(&mut vm);
|
||||||
|
|
||||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活");
|
assert!(
|
||||||
|
vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr),
|
||||||
|
"挂起帧 cell 应跨完整收集存活"
|
||||||
|
);
|
||||||
|
|
||||||
// 恢复后读值:cell 未被误释放。
|
// 恢复后读值:cell 未被误释放。
|
||||||
let read = vm.run(&Arc::new(compile("globalThis.g.next().value"))).expect("read");
|
let read = vm.run(&Arc::new(compile("globalThis.g.next().value"))).expect("read");
|
||||||
@@ -1394,7 +1401,12 @@ fn strings_only_collection_preserves_all_root_kinds() {
|
|||||||
|
|
||||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(®_str.as_string_ptr_mut()));
|
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(®_str.as_string_ptr_mut()));
|
||||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&prop_str.as_string_ptr_mut()));
|
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&prop_str.as_string_ptr_mut()));
|
||||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&epoch_str.as_string_ptr_mut()));
|
assert!(vm
|
||||||
|
.realm
|
||||||
|
.gc
|
||||||
|
.borrow()
|
||||||
|
.session_string_ptrs
|
||||||
|
.contains(&epoch_str.as_string_ptr_mut()));
|
||||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr));
|
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr));
|
||||||
// 存活串内容可读,地址稳定。
|
// 存活串内容可读,地址稳定。
|
||||||
assert_eq!(unsafe { (*reg_str.as_string_ptr_mut()).as_str() }, "reg-root".repeat(16));
|
assert_eq!(unsafe { (*reg_str.as_string_ptr_mut()).as_str() }, "reg-root".repeat(16));
|
||||||
@@ -1713,7 +1725,10 @@ fn active_and_suspended_for_in_do_not_block_in_run_collection() {
|
|||||||
|
|
||||||
vm.maybe_collect_in_run();
|
vm.maybe_collect_in_run();
|
||||||
assert_eq!(vm.session_gc_stats().total_collections, 1, "无门控:活跃 for-in 存在时收集照常执行");
|
assert_eq!(vm.session_gc_stats().total_collections, 1, "无门控:活跃 for-in 存在时收集照常执行");
|
||||||
assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表");
|
assert!(
|
||||||
|
!vm.realm.gc.borrow().session_object_ptrs.contains(&dead),
|
||||||
|
"死 session 对象应被原地释放并出表"
|
||||||
|
);
|
||||||
|
|
||||||
// 挂起形:生成器在 for-in 内 yield 后 run 结束,迭代器经状态盒持有
|
// 挂起形:生成器在 for-in 内 yield 后 run 结束,迭代器经状态盒持有
|
||||||
// (vm.iters 已空)——键经状态盒边入根收集标活,收集照常执行。
|
// (vm.iters 已空)——键经状态盒边入根收集标活,收集照常执行。
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ impl Vm {
|
|||||||
f(RootGroup::ExceptionValue, self.exception_value.unwrap_or(JsValue::undefined()));
|
f(RootGroup::ExceptionValue, self.exception_value.unwrap_or(JsValue::undefined()));
|
||||||
f(RootGroup::PendingException, self.pending_exception.unwrap_or(JsValue::undefined()));
|
f(RootGroup::PendingException, self.pending_exception.unwrap_or(JsValue::undefined()));
|
||||||
f(RootGroup::LastUncaught, self.last_uncaught_value.unwrap_or(JsValue::undefined()));
|
f(RootGroup::LastUncaught, self.last_uncaught_value.unwrap_or(JsValue::undefined()));
|
||||||
f(RootGroup::PendingLengthException, self.pending_length_exception.unwrap_or(JsValue::undefined()));
|
f(
|
||||||
|
RootGroup::PendingLengthException,
|
||||||
|
self.pending_length_exception.unwrap_or(JsValue::undefined()),
|
||||||
|
);
|
||||||
// 悬挂的 return 完成持有返回值,是 GC 根。
|
// 悬挂的 return 完成持有返回值,是 GC 根。
|
||||||
if let Some(Completion::Return { value, .. }) = self.pending_completion {
|
if let Some(Completion::Return { value, .. }) = self.pending_completion {
|
||||||
f(RootGroup::PendingCompletion, value);
|
f(RootGroup::PendingCompletion, value);
|
||||||
@@ -150,7 +153,10 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f(RootGroup::Global, JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject));
|
f(
|
||||||
|
RootGroup::Global,
|
||||||
|
JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GC 根统一枚举入口:遍历的字段清单与 `for_each_value` 相同。
|
/// GC 根统一枚举入口:遍历的字段清单与 `for_each_value` 相同。
|
||||||
@@ -266,7 +272,8 @@ impl Vm {
|
|||||||
// SAFETY: ptr 在字符串表登记,收尾前有效。
|
// SAFETY: ptr 在字符串表登记,收尾前有效。
|
||||||
bytes += (std::mem::size_of::<JsString>() + unsafe { (*ptr).payload_bytes() }) as u64;
|
bytes += (std::mem::size_of::<JsString>() + unsafe { (*ptr).payload_bytes() }) as u64;
|
||||||
}
|
}
|
||||||
bytes += (self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()) as u64;
|
bytes += (self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>())
|
||||||
|
as u64;
|
||||||
bytes += (self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()) as u64;
|
bytes += (self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()) as u64;
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,10 +114,7 @@ impl VmPool {
|
|||||||
/// # 副作用
|
/// # 副作用
|
||||||
/// - 同步创建 `min_size` 个 Vm。
|
/// - 同步创建 `min_size` 个 Vm。
|
||||||
pub fn with_counters(
|
pub fn with_counters(
|
||||||
kernel_core: Arc<KernelCore>,
|
kernel_core: Arc<KernelCore>, min_size: usize, max_size: Option<usize>, counters: Arc<PoolCounters>,
|
||||||
min_size: usize,
|
|
||||||
max_size: Option<usize>,
|
|
||||||
counters: Arc<PoolCounters>,
|
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let warm = min_size.min(max_size.unwrap_or(min_size));
|
let warm = min_size.min(max_size.unwrap_or(min_size));
|
||||||
let pool = Arc::new(Self {
|
let pool = Arc::new(Self {
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ impl Vm {
|
|||||||
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(
|
||||||
|
EMPTY_SHAPE_ID,
|
||||||
|
JsValue::null(),
|
||||||
|
))),
|
||||||
gc: RefCell::new(GcState {
|
gc: RefCell::new(GcState {
|
||||||
session_gc: crate::session_gc::SessionGc::new(),
|
session_gc: crate::session_gc::SessionGc::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
@@ -230,7 +233,10 @@ impl Vm {
|
|||||||
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||||
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(
|
||||||
|
EMPTY_SHAPE_ID,
|
||||||
|
JsValue::null(),
|
||||||
|
))),
|
||||||
gc: RefCell::new(GcState {
|
gc: RefCell::new(GcState {
|
||||||
session_gc: crate::session_gc::SessionGc::new(),
|
session_gc: crate::session_gc::SessionGc::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
@@ -400,9 +406,7 @@ impl Vm {
|
|||||||
self.realm_id(),
|
self.realm_id(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*self.realm
|
*self.realm.object_prototype.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
||||||
.object_prototype
|
|
||||||
.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
|
||||||
self.init_generator_intrinsics();
|
self.init_generator_intrinsics();
|
||||||
self.init_promise_intrinsics();
|
self.init_promise_intrinsics();
|
||||||
self.init_async_intrinsics();
|
self.init_async_intrinsics();
|
||||||
@@ -418,9 +422,7 @@ impl Vm {
|
|||||||
pub fn full_reset_legacy_for_bench(&mut self) {
|
pub fn full_reset_legacy_for_bench(&mut self) {
|
||||||
*self.realm.session.borrow_mut() = KernelSession::new(&self.kernel_core);
|
*self.realm.session.borrow_mut() = KernelSession::new(&self.kernel_core);
|
||||||
bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut(), self.realm_id());
|
bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut(), self.realm_id());
|
||||||
*self.realm
|
*self.realm.object_prototype.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
||||||
.object_prototype
|
|
||||||
.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
|
||||||
self.init_generator_intrinsics();
|
self.init_generator_intrinsics();
|
||||||
self.init_promise_intrinsics();
|
self.init_promise_intrinsics();
|
||||||
self.init_async_intrinsics();
|
self.init_async_intrinsics();
|
||||||
@@ -762,7 +764,9 @@ impl Vm {
|
|||||||
} else if is_generator {
|
} else if is_generator {
|
||||||
JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject)
|
JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject)
|
||||||
} else {
|
} else {
|
||||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject)
|
JsValue::from_js_object(
|
||||||
|
self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject
|
||||||
|
)
|
||||||
};
|
};
|
||||||
// prototype 子对象与函数本体同走 session 分配:`f.prototype ===
|
// prototype 子对象与函数本体同走 session 分配:`f.prototype ===
|
||||||
// globalThis.f.prototype` 要求两侧同一对象,逃逸写不会克隆出第二份。
|
// globalThis.f.prototype` 要求两侧同一对象,逃逸写不会克隆出第二份。
|
||||||
|
|||||||
@@ -288,7 +288,10 @@ fn full_reset_clean_keeps_session_objects() {
|
|||||||
|
|
||||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)));
|
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)));
|
||||||
assert!(std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr()));
|
assert!(std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr()));
|
||||||
assert!(std::ptr::eq(object_proto_ptr, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
assert!(std::ptr::eq(
|
||||||
|
object_proto_ptr,
|
||||||
|
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||||
|
));
|
||||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,8 +329,14 @@ fn full_reset_dirty_builtin_rebinds_global_slot() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
assert!(std::ptr::eq(
|
||||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
old_object_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||||
|
));
|
||||||
|
assert!(!std::ptr::eq(
|
||||||
|
old_array_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().array_proto.as_ptr()
|
||||||
|
));
|
||||||
assert!(std::ptr::eq(
|
assert!(std::ptr::eq(
|
||||||
global_prop(&vm, "Array").as_js_object_ptr(),
|
global_prop(&vm, "Array").as_js_object_ptr(),
|
||||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||||
@@ -599,7 +608,10 @@ fn full_reset_refreshes_object_prototype_after_object_dirty() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
assert!(!std::ptr::eq(
|
||||||
|
old_object_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||||
|
));
|
||||||
assert!(std::ptr::eq(
|
assert!(std::ptr::eq(
|
||||||
vm.realm.object_prototype.borrow().as_ptr(),
|
vm.realm.object_prototype.borrow().as_ptr(),
|
||||||
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||||
@@ -618,8 +630,14 @@ fn full_reset_object_dirty_rebinds_iterator_family() {
|
|||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
// object 家族与迭代器原型全部重建(新原型链到新 Object.prototype)。
|
// object 家族与迭代器原型全部重建(新原型链到新 Object.prototype)。
|
||||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
assert!(!std::ptr::eq(
|
||||||
assert!(!std::ptr::eq(old_iterator_proto, vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr()));
|
old_object_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||||
|
));
|
||||||
|
assert!(!std::ptr::eq(
|
||||||
|
old_iterator_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr()
|
||||||
|
));
|
||||||
// global 保留(dirty.global=false):其 Iterator 函数对象的 prototype
|
// global 保留(dirty.global=false):其 Iterator 函数对象的 prototype
|
||||||
// 属性须对齐到重建后的 %IteratorPrototype%。
|
// 属性须对齐到重建后的 %IteratorPrototype%。
|
||||||
let iter_val = global_prop(&vm, "Iterator");
|
let iter_val = global_prop(&vm, "Iterator");
|
||||||
@@ -872,7 +890,10 @@ fn full_reset_value_overwrite_on_builtin_proto_rebuilds_family() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
assert!(!std::ptr::eq(
|
||||||
|
old_array_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().array_proto.as_ptr()
|
||||||
|
));
|
||||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||||
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
||||||
@@ -892,7 +913,10 @@ fn full_reset_value_overwrite_with_object_value_drops_stale_pointer() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
assert!(!std::ptr::eq(
|
||||||
|
old_array_proto,
|
||||||
|
vm.realm.session.borrow().builtin_world().array_proto.as_ptr()
|
||||||
|
));
|
||||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||||
@@ -1028,7 +1052,10 @@ fn full_reset_global_function_wrapper_property_does_not_survive() {
|
|||||||
let _ = run_source(&mut vm, "0");
|
let _ = run_source(&mut vm, "0");
|
||||||
|
|
||||||
let _ = run_source(&mut vm, "parseInt.custom = 1; 0");
|
let _ = run_source(&mut vm, "parseInt.custom = 1; 0");
|
||||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获");
|
assert!(
|
||||||
|
vm.realm.session.borrow().is_dirty_since_snapshot(),
|
||||||
|
"global 函数 wrapper 写应被脏检测捕获"
|
||||||
|
);
|
||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
@@ -1080,7 +1107,10 @@ fn benign_user_object_writes_do_not_dirty_leaked_objects() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "无污染不应重建 world");
|
assert!(
|
||||||
|
std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)),
|
||||||
|
"无污染不应重建 world"
|
||||||
|
);
|
||||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1101,7 +1131,10 @@ fn benign_run_does_not_falsely_dirty_builtins() {
|
|||||||
|
|
||||||
vm.full_reset();
|
vm.full_reset();
|
||||||
|
|
||||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "builtin world 不应重建");
|
assert!(
|
||||||
|
std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)),
|
||||||
|
"builtin world 不应重建"
|
||||||
|
);
|
||||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,17 +41,11 @@ fn gc_pressure_mode_env_toggle() {
|
|||||||
let result = eval_on(&mut vm, SCRIPT).expect("脚本不应运行失败");
|
let result = eval_on(&mut vm, SCRIPT).expect("脚本不应运行失败");
|
||||||
assert!(result.as_bool(), "存活钉读回应完整");
|
assert!(result.as_bool(), "存活钉读回应完整");
|
||||||
let total = vm.session_gc_stats().total_collections;
|
let total = vm.session_gc_stats().total_collections;
|
||||||
assert!(
|
assert!(total > 100, "压力模式应每个顶层边界做一次完整收集,实际 {total}");
|
||||||
total > 100,
|
|
||||||
"压力模式应每个顶层边界做一次完整收集,实际 {total}"
|
|
||||||
);
|
|
||||||
// 关臂:移除环境变量后同一脚本走退化路径。
|
// 关臂:移除环境变量后同一脚本走退化路径。
|
||||||
std::env::remove_var("OXIDE_GC_PRESSURE");
|
std::env::remove_var("OXIDE_GC_PRESSURE");
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let result = eval_on(&mut vm, SCRIPT).expect("脚本不应运行失败");
|
let result = eval_on(&mut vm, SCRIPT).expect("脚本不应运行失败");
|
||||||
assert!(result.as_bool(), "存活钉读回应完整");
|
assert!(result.as_bool(), "存活钉读回应完整");
|
||||||
assert_eq!(
|
assert_eq!(vm.session_gc_stats().total_collections, 0, "关臂小脚本分配不超水位,收集应为零");
|
||||||
vm.session_gc_stats().total_collections, 0,
|
|
||||||
"关臂小脚本分配不超水位,收集应为零"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user