mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-26 03:31:12 +08:00
feat: 实现 console 全局对象 (log/warn/error/info/debug/trace)
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
//! Console 内置对象实现。
|
||||||
|
//!
|
||||||
|
//! 单例全局对象,所有方法输出到 stderr(使用 eprintln!)。
|
||||||
|
//! trace 除消息外额外打印调用栈(std::backtrace::Backtrace)。
|
||||||
|
|
||||||
|
use oxide_runtime_api::{NativeResult, VmHost};
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
fn multi_string<H: VmHost>(vm: &mut H, args: &[u8]) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
let mut first = true;
|
||||||
|
for i in 1..args.len() as usize {
|
||||||
|
if !first {
|
||||||
|
output.push(' ');
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
let val = vm.reg(args[i]);
|
||||||
|
let part = if val.is_string() {
|
||||||
|
oxide_runtime_api::to_string(val)
|
||||||
|
} else {
|
||||||
|
let primitive =
|
||||||
|
oxide_runtime_api::to_primitive(val, oxide_runtime_api::ToPrimitiveHint::String, vm)
|
||||||
|
.unwrap_or_else(|_| JsValue::undefined());
|
||||||
|
oxide_runtime_api::to_string(primitive)
|
||||||
|
};
|
||||||
|
output.push_str(&part);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.log(...args)`:输出消息到 stderr。
|
||||||
|
pub fn log<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.warn(...args)`:输出警告到 stderr。
|
||||||
|
pub fn warn<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
eprintln!("warn: {}", msg);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.error(...args)`:输出错误到 stderr。
|
||||||
|
pub fn error<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
eprintln!("error: {}", msg);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.info(...args)`:输出信息到 stderr。
|
||||||
|
pub fn info<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
eprintln!("info: {}", msg);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.debug(...args)`:输出调试消息到 stderr。
|
||||||
|
pub fn debug<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
eprintln!("debug: {}", msg);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `console.trace(...args)`:输出跟踪消息及调用栈到 stderr。
|
||||||
|
pub fn trace<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let msg = multi_string(vm, args);
|
||||||
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
|
eprintln!("trace: {}\n{:?}", msg, backtrace);
|
||||||
|
NativeResult::Ok(JsValue::undefined())
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ pub mod bigint;
|
|||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。
|
/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。
|
||||||
pub mod builtins_log;
|
pub mod builtins_log;
|
||||||
|
/// Console 全局对象实现(log/warn/error/info/debug/trace 方法)。
|
||||||
|
pub mod console;
|
||||||
/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。
|
/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。
|
||||||
pub mod data_view;
|
pub mod data_view;
|
||||||
/// Date 内置对象实现(时间戳存取、get/set 系列与 to*String 系列)。
|
/// Date 内置对象实现(时间戳存取、get/set 系列与 to*String 系列)。
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ pub struct BuiltinWorld {
|
|||||||
/// `AsyncDisposableStack.prototype`:异步资源栈原型(形状与同步栈一致)。
|
/// `AsyncDisposableStack.prototype`:异步资源栈原型(形状与同步栈一致)。
|
||||||
pub async_disposable_stack_proto: P<JsObject>,
|
pub async_disposable_stack_proto: P<JsObject>,
|
||||||
pub stub_objects: Vec<P<JsObject>>,
|
pub stub_objects: Vec<P<JsObject>>,
|
||||||
|
pub console_object: P<JsObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intern_label(string_forge: &PermInterner, label: &str) -> u32 {
|
fn intern_label(string_forge: &PermInterner, label: &str) -> u32 {
|
||||||
@@ -614,6 +615,9 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
set_proto_if_changed(&world.temporal_object, obj_proto_val);
|
set_proto_if_changed(&world.temporal_object, obj_proto_val);
|
||||||
set_proto_if_changed(&world.temporal_now_object, obj_proto_val);
|
set_proto_if_changed(&world.temporal_now_object, obj_proto_val);
|
||||||
|
|
||||||
|
// Console 对象(单例命名空间)继承 Object.prototype。
|
||||||
|
set_proto_if_changed(&world.console_object, obj_proto_val);
|
||||||
|
|
||||||
// 迭代器原型链:%IteratorPrototype% → Object.prototype;各集合迭代器原型
|
// 迭代器原型链:%IteratorPrototype% → Object.prototype;各集合迭代器原型
|
||||||
// → %IteratorPrototype%(next/@@iterator 方法由绑定层安装到对应原型)。
|
// → %IteratorPrototype%(next/@@iterator 方法由绑定层安装到对应原型)。
|
||||||
set_proto_if_changed(&world.iterator_proto, obj_proto_val);
|
set_proto_if_changed(&world.iterator_proto, obj_proto_val);
|
||||||
@@ -768,6 +772,7 @@ impl BuiltinWorld {
|
|||||||
BuiltinId::PlainDateTimeProto => &self.plain_date_time_proto,
|
BuiltinId::PlainDateTimeProto => &self.plain_date_time_proto,
|
||||||
BuiltinId::BigIntConstructor => &self.bigint_constructor,
|
BuiltinId::BigIntConstructor => &self.bigint_constructor,
|
||||||
BuiltinId::BigIntProto => &self.bigint_proto,
|
BuiltinId::BigIntProto => &self.bigint_proto,
|
||||||
|
BuiltinId::Console => &self.console_object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -791,6 +796,8 @@ impl BuiltinWorld {
|
|||||||
|
|
||||||
let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
|
||||||
|
let console_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
|
||||||
let (date_proto, date_constructor) = make_named_pair(string_forge, shape_forge, labels, "Date");
|
let (date_proto, date_constructor) = make_named_pair(string_forge, shape_forge, labels, "Date");
|
||||||
let (set_proto, set_constructor) = make_named_pair(string_forge, shape_forge, labels, "Set");
|
let (set_proto, set_constructor) = make_named_pair(string_forge, shape_forge, labels, "Set");
|
||||||
let (map_proto, map_constructor) = make_named_pair(string_forge, shape_forge, labels, "Map");
|
let (map_proto, map_constructor) = make_named_pair(string_forge, shape_forge, labels, "Map");
|
||||||
@@ -941,6 +948,7 @@ impl BuiltinWorld {
|
|||||||
disposable_stack_proto,
|
disposable_stack_proto,
|
||||||
async_disposable_stack_proto,
|
async_disposable_stack_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
|
console_object,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
kernel_info!("BuiltinWorld initialized");
|
kernel_info!("BuiltinWorld initialized");
|
||||||
@@ -1205,6 +1213,11 @@ impl BuiltinWorld {
|
|||||||
(current.bigint_proto.clone(), current.bigint_constructor.clone())
|
(current.bigint_proto.clone(), current.bigint_constructor.clone())
|
||||||
};
|
};
|
||||||
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
||||||
|
let console_object = if dirty.console {
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
||||||
|
} else {
|
||||||
|
current.console_object.clone()
|
||||||
|
};
|
||||||
|
|
||||||
// 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时
|
// 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时
|
||||||
// 一并重建,否则旧原型链指向已释放的 object_proto。
|
// 一并重建,否则旧原型链指向已释放的 object_proto。
|
||||||
@@ -1345,6 +1358,7 @@ impl BuiltinWorld {
|
|||||||
disposable_stack_proto,
|
disposable_stack_proto,
|
||||||
async_disposable_stack_proto,
|
async_disposable_stack_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
|
console_object,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
world
|
world
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ pub struct KernelSession {
|
|||||||
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
||||||
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
||||||
/// builtin 家族。
|
/// builtin 家族。
|
||||||
pub const NUM_BUILTINS: usize = 89;
|
pub const NUM_BUILTINS: usize = 90;
|
||||||
|
|
||||||
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
||||||
///
|
///
|
||||||
@@ -324,6 +324,7 @@ pub enum BuiltinId {
|
|||||||
SymAsyncDispose = 86,
|
SymAsyncDispose = 86,
|
||||||
SymDispose = 87,
|
SymDispose = 87,
|
||||||
SuppressedErrorProto = 88,
|
SuppressedErrorProto = 88,
|
||||||
|
Console = 89,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinId {
|
impl BuiltinId {
|
||||||
@@ -420,6 +421,7 @@ impl BuiltinId {
|
|||||||
BuiltinId::SymAsyncDispose,
|
BuiltinId::SymAsyncDispose,
|
||||||
BuiltinId::SymDispose,
|
BuiltinId::SymDispose,
|
||||||
BuiltinId::SuppressedErrorProto,
|
BuiltinId::SuppressedErrorProto,
|
||||||
|
BuiltinId::Console,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,6 +482,7 @@ pub struct BuiltinDirtySet {
|
|||||||
pub temporal: bool,
|
pub temporal: bool,
|
||||||
pub stubs: bool,
|
pub stubs: bool,
|
||||||
pub global: bool,
|
pub global: bool,
|
||||||
|
pub console: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinDirtySet {
|
impl BuiltinDirtySet {
|
||||||
@@ -504,6 +507,7 @@ impl BuiltinDirtySet {
|
|||||||
|| self.typed_array_family
|
|| self.typed_array_family
|
||||||
|| self.temporal
|
|| self.temporal
|
||||||
|| self.stubs
|
|| self.stubs
|
||||||
|
|| self.console
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 是否存在任何污染(builtin world 或 global object)。
|
/// 是否存在任何污染(builtin world 或 global object)。
|
||||||
@@ -683,6 +687,7 @@ impl KernelSession {
|
|||||||
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|
||||||
|| gen(BuiltinId::BigIntProto) != snap(BuiltinId::BigIntProto),
|
|| gen(BuiltinId::BigIntProto) != snap(BuiltinId::BigIntProto),
|
||||||
global: BuiltinSnapshot::gen(&self.global_object) != snapshot.global_object_generation,
|
global: BuiltinSnapshot::gen(&self.global_object) != snapshot.global_object_generation,
|
||||||
|
console: gen(BuiltinId::Console) != snap(BuiltinId::Console),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::bindings::bind_global_value;
|
||||||
|
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||||
|
use oxide_types::object::JsObject;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
/// 把 Console 单例对象及其方法(log/warn/error/info/debug/trace)绑定到 global。
|
||||||
|
pub fn bind_console(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||||
|
let console_ptr = session.builtin_world().console_object.as_ptr() as *mut JsObject;
|
||||||
|
let console = unsafe { &mut *console_ptr };
|
||||||
|
|
||||||
|
let bw = session.builtin_world();
|
||||||
|
let sf = core.perm_interner().as_ref();
|
||||||
|
let sh = core.shape_forge().as_ref();
|
||||||
|
|
||||||
|
oxide_kernel::bind_methods!(
|
||||||
|
bw,
|
||||||
|
console,
|
||||||
|
sf,
|
||||||
|
sh,
|
||||||
|
("log", oxide_builtins::console::log::<crate::vm::Vm>, 1),
|
||||||
|
("warn", oxide_builtins::console::warn::<crate::vm::Vm>, 1),
|
||||||
|
("error", oxide_builtins::console::error::<crate::vm::Vm>, 1),
|
||||||
|
("info", oxide_builtins::console::info::<crate::vm::Vm>, 1),
|
||||||
|
("debug", oxide_builtins::console::debug::<crate::vm::Vm>, 1),
|
||||||
|
("trace", oxide_builtins::console::trace::<crate::vm::Vm>, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
bind_global_value(
|
||||||
|
core,
|
||||||
|
global,
|
||||||
|
"console",
|
||||||
|
JsValue::from_js_object(session.builtin_world().console_object.as_ptr() as *mut JsObject),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -52,6 +52,8 @@ pub mod bind_symbol;
|
|||||||
pub mod bind_temporal;
|
pub mod bind_temporal;
|
||||||
/// 各 TypedArray 构造器与共享原型的 native 方法绑定。
|
/// 各 TypedArray 构造器与共享原型的 native 方法绑定。
|
||||||
pub mod bind_typed_array;
|
pub mod bind_typed_array;
|
||||||
|
/// Console 单例对象及其方法绑定。
|
||||||
|
pub mod bind_console;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -1064,6 +1066,9 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
|
|||||||
if dirty.map_or(true, |d| d.stubs) {
|
if dirty.map_or(true, |d| d.stubs) {
|
||||||
bind_bigint::bind_bigint(core, session, global);
|
bind_bigint::bind_bigint(core, session, global);
|
||||||
}
|
}
|
||||||
|
if dirty.map_or(true, |d| d.console) {
|
||||||
|
bind_console::bind_console(core, session, global);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。
|
/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。
|
||||||
@@ -1077,6 +1082,7 @@ pub fn init_kernel_builtins(core: &Arc<KernelCore>, session: &mut KernelSession)
|
|||||||
bind_reflect::bind_reflect(core, session, global);
|
bind_reflect::bind_reflect(core, session, global);
|
||||||
bind_global::bind_global(core, session, global);
|
bind_global::bind_global(core, session, global);
|
||||||
bind_global_value(core, global, "globalThis", JsValue::from_js_object(global_ptr));
|
bind_global_value(core, global, "globalThis", JsValue::from_js_object(global_ptr));
|
||||||
|
bind_console::bind_console(core, session, global);
|
||||||
crate::test262_host::bind_test262_host(core, session, global);
|
crate::test262_host::bind_test262_host(core, session, global);
|
||||||
session.record_snapshot();
|
session.record_snapshot();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user