mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 九原型新增 toSource 遗留扩展方法
This commit is contained in:
@@ -75,6 +75,8 @@ pub mod symbol;
|
||||
pub mod ta_codec;
|
||||
/// Temporal 内置对象实现(目录模块树:Now / Instant / PlainDate / PlainTime / PlainDateTime / PlainMonthDay / PlainYearMonth / ZonedDateTime / Duration)。
|
||||
pub mod temporal;
|
||||
/// toSource 遗留扩展(九原型 own 方法与 null/undefined this 门禁)。
|
||||
pub mod to_source;
|
||||
/// TypedArray 内置对象实现(元素访问、fill/slice/subarray/set 等)。
|
||||
pub mod typed_array;
|
||||
/// WeakMap 条目表实现(弱键 → 强值盒与 GC 六站点接线函数族)。
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
//! toSource(SpiderMonkey 遗留扩展):Object / Function / Array / String / Boolean /
|
||||
//! Number / Date / RegExp / Error 九原型各一个 own 方法。
|
||||
//!
|
||||
//! 关键约定:
|
||||
//! - 门禁统一走 CheckObjectCoercible 口径——this 为 null / undefined 抛 TypeError,
|
||||
//! 其余原始值与原对象放行;
|
||||
//! - 返回值取最小合理源码串(不追求 SpiderMonkey 完整序列化):Function 空串、
|
||||
//! Object / Array 恒形、String 带引号、Boolean / Number 字面量、Date / Error 构造形、
|
||||
//! RegExp 斜杠形;
|
||||
//! - 本模块不提供 test262 度量面,验收靠单测钉 own 存在性与门禁抛错。
|
||||
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 九原型 toSource 共享门禁:this 为 null / undefined 抛 TypeError(CheckObjectCoercible
|
||||
/// 口径),其余原样返回 this 值供各臂消费。
|
||||
fn to_source_check_this<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<JsValue, JsValue> {
|
||||
let this_val = vm.reg(args[0]);
|
||||
if this_val.is_null() || this_val.is_undefined() {
|
||||
return Err(crate::error::create_type_error(vm, "toSource called on null or undefined"));
|
||||
}
|
||||
Ok(this_val)
|
||||
}
|
||||
|
||||
/// `Object.prototype.toSource`:门禁后恒返 `"{}"`。
|
||||
pub fn to_source_object<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if let Err(err) = to_source_check_this(vm, args) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
NativeResult::Ok(vm.new_string_owned("{}".to_string()))
|
||||
}
|
||||
|
||||
/// `Function.prototype.toSource`:引擎无函数源文本,门禁后恒返空串(不抛错)。
|
||||
pub fn to_source_function<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if let Err(err) = to_source_check_this(vm, args) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
NativeResult::Ok(vm.new_string_owned(String::new()))
|
||||
}
|
||||
|
||||
/// `Array.prototype.toSource`:门禁后恒返 `"[]"`。
|
||||
pub fn to_source_array<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if let Err(err) = to_source_check_this(vm, args) {
|
||||
return NativeResult::Err(err);
|
||||
}
|
||||
NativeResult::Ok(vm.new_string_owned("[]".to_string()))
|
||||
}
|
||||
|
||||
/// `String.prototype.toSource`:门禁后读 this 串值,双引号包裹返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - this 为字符串时直读单元文本;非字符串走完整 ToString(对象经 ToPrimitive,
|
||||
/// 用户转换异常原样传播)。
|
||||
pub fn to_source_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let text = if this_val.is_string() {
|
||||
vm.lookup_str(this_val).unwrap_or_default()
|
||||
} else {
|
||||
match oxide_runtime_api::to_string_full(this_val, vm) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
|
||||
}
|
||||
};
|
||||
NativeResult::Ok(vm.new_string_owned(format!("\"{}\"", text)))
|
||||
}
|
||||
|
||||
/// `Boolean.prototype.toSource`:门禁后按 ToBoolean 判位,返 `"true"` / `"false"`。
|
||||
pub fn to_source_boolean<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let b = oxide_runtime_api::to_boolean(this_val);
|
||||
NativeResult::Ok(vm.new_string_owned(if b { "true".to_string() } else { "false".to_string() }))
|
||||
}
|
||||
|
||||
/// `Number.prototype.toSource`:门禁后按 ToNumber 取 double,十进制字面量返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - this 为 double 时直读;非 double 走完整 ToNumber(用户转换异常原样传播)。
|
||||
pub fn to_source_number<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let d = if this_val.is_double() {
|
||||
this_val.as_double()
|
||||
} else {
|
||||
match oxide_runtime_api::to_number_full(this_val, vm) {
|
||||
Ok(n) => n,
|
||||
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
|
||||
}
|
||||
};
|
||||
NativeResult::Ok(vm.number_to_string_cached(d))
|
||||
}
|
||||
|
||||
/// `Date.prototype.toSource`:门禁后读时间戳(prop 0),返 `new Date(<ms>)` 形;
|
||||
/// 非 Date 对象或时间戳缺失时回退 `new Date(NaN)`。
|
||||
pub fn to_source_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let ms = if this_val.is_object() {
|
||||
let ptr = this_val.as_js_object_ptr();
|
||||
if !ptr.is_null() && unsafe { &*ptr }.is_date_obj() {
|
||||
let v = unsafe { &*ptr }.get_prop_at(0);
|
||||
if v.is_double() {
|
||||
v.as_double()
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
} else {
|
||||
f64::NAN
|
||||
};
|
||||
let num_text = oxide_runtime_api::js_number_to_string(ms);
|
||||
NativeResult::Ok(vm.new_string_owned(format!("new Date({})", num_text)))
|
||||
}
|
||||
|
||||
/// `RegExp.prototype.toSource`:门禁后读 `[[OriginalSource]]` / `[[OriginalFlags]]` 槽,
|
||||
/// 返 `/source/flags` 形;非 RegExp 对象回退 `/(?:)/`。
|
||||
pub fn to_source_regexp<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
if !this_val.is_object() {
|
||||
return NativeResult::Ok(vm.new_string_owned("/(?:)/".to_string()));
|
||||
}
|
||||
let ptr = this_val.as_js_object_ptr();
|
||||
if ptr.is_null() || !unsafe { &*ptr }.is_regexp_obj() {
|
||||
return NativeResult::Ok(vm.new_string_owned("/(?:)/".to_string()));
|
||||
}
|
||||
let re = unsafe { &*ptr };
|
||||
let source = vm.lookup_str(re.get_regexp_source()).unwrap_or_default();
|
||||
let flags = vm.lookup_str(re.get_regexp_flags()).unwrap_or_default();
|
||||
NativeResult::Ok(vm.new_string_owned(format!("/{}/{}", source, flags)))
|
||||
}
|
||||
|
||||
/// `Error.prototype.toSource`:门禁后读 `message` 属性,返 `new Error("message")` 形;
|
||||
/// 无 message 时 message 段为空串。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - message 经原型链 Get 读取(访问器触发,用户异常吞掉回落空串);
|
||||
/// - message 非 undefined 时走完整 ToString(Symbol 等不可转换场景回落空串)。
|
||||
pub fn to_source_error<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = match to_source_check_this(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let mut msg = String::new();
|
||||
if this_val.is_object() {
|
||||
let ptr = this_val.as_js_object_ptr();
|
||||
if !ptr.is_null() {
|
||||
let obj = unsafe { &*ptr };
|
||||
let si_msg = vm.perm_intern("message");
|
||||
if let Ok(v) = vm.ordinary_get(obj, si_msg, this_val) {
|
||||
if !v.is_undefined() {
|
||||
if let Ok(s) = oxide_runtime_api::to_string_full(v, vm) {
|
||||
msg = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NativeResult::Ok(vm.new_string_owned(format!("new Error({:?})", msg)))
|
||||
}
|
||||
@@ -67,7 +67,10 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
session.builtin_world(),
|
||||
array_proto,
|
||||
core,
|
||||
&[("toString", oxide_builtins::array::array_to_string::<crate::vm::Vm> as *const (), 0)],
|
||||
&[
|
||||
("toString", oxide_builtins::array::array_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("toSource", oxide_builtins::to_source::to_source_array::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
// 内置原型方法不可枚举(否则会泄漏进 for-in)。
|
||||
let si = core.perm_interner().intern("toString").0;
|
||||
|
||||
@@ -29,6 +29,7 @@ pub fn bind_boolean(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
|
||||
oxide_builtins::boolean::boolean_prototype_to_string::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
),
|
||||
("toSource", oxide_builtins::to_source::to_source_boolean::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
|
||||
0,
|
||||
),
|
||||
("valueOf", oxide_builtins::date::date_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
("toSource", oxide_builtins::to_source::to_source_date::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -135,6 +135,20 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
);
|
||||
}
|
||||
|
||||
// toSource 遗留扩展(Error 原型方法不走 kernel 侧 bind_error_methods,
|
||||
// 此处独立经 apply_binding_table 安装,描述符非枚举由绑定器内置)。
|
||||
{
|
||||
let proto_ptr = session.builtin_world().error_proto.as_ptr() as *mut JsObject;
|
||||
// SAFETY: error_proto 由 session 持有,存活整个 session;本块内只改其 shape/属性区,无 reset。
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
super::apply_binding_table(
|
||||
session.builtin_world(),
|
||||
proto,
|
||||
core,
|
||||
&[("toSource", oxide_builtins::to_source::to_source_error::<crate::vm::Vm> as *const (), 0)],
|
||||
);
|
||||
}
|
||||
|
||||
// 全局 Error 槽位既有槽原位更新(旧家族构造器指针不得滞留在属性 vec),
|
||||
// 无槽时开新槽;描述符非枚举(规范 { writable:true, enumerable:false, configurable:true })。
|
||||
let si_err = core.perm_interner().intern("Error").0;
|
||||
|
||||
@@ -6,6 +6,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::apply_binding_table;
|
||||
use super::bind_global_value;
|
||||
use super::configure_native_constructor;
|
||||
|
||||
@@ -66,6 +67,15 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
// 函数对象,任何访问抛 TypeError(AddRestrictedFunctionProperties 语义)。
|
||||
bind_function_proto_restricted(core, session, proto);
|
||||
|
||||
// toSource 遗留扩展(Function 原型方法不走 kernel 侧 bind_function_methods,
|
||||
// 此处独立经 apply_binding_table 安装,描述符非枚举由绑定器内置)。
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
proto,
|
||||
core,
|
||||
&[("toSource", oxide_builtins::to_source::to_source_function::<crate::vm::Vm> as *const (), 0)],
|
||||
);
|
||||
|
||||
bind_global_value(core, global, "Function", JsValue::from_js_object(function_ctor));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
0,
|
||||
),
|
||||
("valueOf", oxide_builtins::number::number_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
("toSource", oxide_builtins::to_source::to_source_number::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
oxide_builtins::object::object_proto_lookup_setter::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
("toSource", oxide_builtins::to_source::to_source_object::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
// 内置原型方法不可枚举(否则会泄漏进 for-in)。
|
||||
@@ -98,6 +99,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
"__defineSetter__",
|
||||
"__lookupGetter__",
|
||||
"__lookupSetter__",
|
||||
"toSource",
|
||||
] {
|
||||
let si = core.perm_interner().intern(name).0;
|
||||
if let Some(pos) = core.shape_forge().lookup_position(object_proto.shape_id(), si) {
|
||||
|
||||
@@ -38,6 +38,7 @@ pub fn bind_regexp(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
("test", oxide_builtins::regexp::regexp_test::<crate::vm::Vm> as *const (), 1),
|
||||
("toString", oxide_builtins::regexp::regexp_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("compile", oxide_builtins::regexp::regexp_compile::<crate::vm::Vm> as *const (), 2),
|
||||
("toSource", oxide_builtins::to_source::to_source_regexp::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -86,7 +86,10 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
session.builtin_world(),
|
||||
proto,
|
||||
core,
|
||||
&[("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0)],
|
||||
&[
|
||||
("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("toSource", oxide_builtins::to_source::to_source_string::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
// String.prototype[@@iterator]:逐 code point 迭代字符;name 属性按规范
|
||||
// 落 "[Symbol.iterator]"。
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
//! toSource 遗留扩展(九原型 own 方法与 null/undefined this 门禁)单测。
|
||||
//!
|
||||
//! 验收面:九原型 own toSource 存在性、描述符非枚举、null/undefined/de-Reference
|
||||
//! 三形态门禁抛 TypeError、返回值形态抽查。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&Arc::new(module))
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.lookup_str(val).unwrap_or_default()
|
||||
}
|
||||
|
||||
const CLASSES: [&str; 9] = ["Object", "Function", "Array", "String", "Boolean", "Number", "Date", "RegExp", "Error"];
|
||||
|
||||
// 九原型 own toSource 存在且为函数。
|
||||
#[test]
|
||||
fn tosource_own_exists_on_nine_prototypes() {
|
||||
let mut vm = Vm::new();
|
||||
for cls in CLASSES {
|
||||
let source = format!("typeof {}.prototype.toSource", cls);
|
||||
let result = eval(&mut vm, &source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "function", "toSource missing on {cls}");
|
||||
}
|
||||
}
|
||||
|
||||
// 九原型 toSource 描述符 { writable:true, enumerable:false, configurable:true }。
|
||||
#[test]
|
||||
fn tosource_descriptor_non_enumerable() {
|
||||
let mut vm = Vm::new();
|
||||
for cls in CLASSES {
|
||||
let source = format!(
|
||||
"(() => {{ var d = Object.getOwnPropertyDescriptor({}.prototype, 'toSource'); return d.writable + ',' + d.enumerable + ',' + d.configurable; }})()",
|
||||
cls
|
||||
);
|
||||
let result = eval(&mut vm, &source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "true,false,true", "descriptor wrong on {cls}");
|
||||
}
|
||||
}
|
||||
|
||||
// 九原型 toSource.call(null) 抛 TypeError(门禁核)。
|
||||
#[test]
|
||||
fn tosource_rejects_null_this() {
|
||||
let mut vm = Vm::new();
|
||||
for cls in CLASSES {
|
||||
let source = format!(
|
||||
"(() => {{ try {{ {}.prototype.toSource.call(null); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
|
||||
cls
|
||||
);
|
||||
let result = eval(&mut vm, &source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "TypeError", "null this not rejected on {cls}");
|
||||
}
|
||||
}
|
||||
|
||||
// 九原型 toSource.call(undefined) 抛 TypeError(门禁核)。
|
||||
#[test]
|
||||
fn tosource_rejects_undefined_this() {
|
||||
let mut vm = Vm::new();
|
||||
for cls in CLASSES {
|
||||
let source = format!(
|
||||
"(() => {{ try {{ {}.prototype.toSource.call(undefined); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
|
||||
cls
|
||||
);
|
||||
let result = eval(&mut vm, &source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "TypeError", "undefined this not rejected on {cls}");
|
||||
}
|
||||
}
|
||||
|
||||
// 九原型 (0, X.prototype.toSource)() de-Reference 形态抛 TypeError。
|
||||
#[test]
|
||||
fn tosource_rejects_dereference_call() {
|
||||
let mut vm = Vm::new();
|
||||
for cls in CLASSES {
|
||||
let source = format!(
|
||||
"(() => {{ try {{ (0, {}.prototype.toSource)(); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
|
||||
cls
|
||||
);
|
||||
let result = eval(&mut vm, &source).unwrap();
|
||||
assert_eq!(to_str(&vm, result), "TypeError", "de-Reference not rejected on {cls}");
|
||||
}
|
||||
}
|
||||
|
||||
// 返回值形态抽查:Object/Array/Boolean/Number/String 五个最小合理形。
|
||||
#[test]
|
||||
fn tosource_return_value_forms() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Object.prototype.toSource.call({})").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "{}");
|
||||
|
||||
let result = eval(&mut vm, "Array.prototype.toSource.call([])").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "[]");
|
||||
|
||||
let result = eval(&mut vm, "Boolean.prototype.toSource.call(true)").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "true");
|
||||
|
||||
let result = eval(&mut vm, "Number.prototype.toSource.call(42)").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "42");
|
||||
|
||||
let result = eval(&mut vm, "String.prototype.toSource.call('hi')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "\"hi\"");
|
||||
}
|
||||
|
||||
// Function 臂:引擎无函数源文本,恒返空串。
|
||||
#[test]
|
||||
fn tosource_function_returns_empty() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Function.prototype.toSource.call(function(){})").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "");
|
||||
}
|
||||
|
||||
// RegExp 臂:/source/flags 斜杠形。
|
||||
#[test]
|
||||
fn tosource_regexp_slash_form() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "RegExp.prototype.toSource.call(/ab+c/i)").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "/ab+c/i");
|
||||
}
|
||||
|
||||
// Error 臂:new Error("message") 构造形。
|
||||
#[test]
|
||||
fn tosource_error_construction_form() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Error.prototype.toSource.call(new Error('boom'))").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "new Error(\"boom\")");
|
||||
}
|
||||
|
||||
// Date 臂:new Date(<ms>) 构造形。
|
||||
#[test]
|
||||
fn tosource_date_construction_form() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Date.prototype.toSource.call(new Date(1234))").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "new Date(1234)");
|
||||
}
|
||||
Reference in New Issue
Block a user