feat: Function.prototype 调用返回 undefined 且动态函数 prototype 不可配置

This commit is contained in:
2026-10-16 12:22:01 +00:00
parent 9af3041ff5
commit 97779b901a
8 changed files with 133 additions and 37 deletions
Generated
+21 -21
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1258,7 +1258,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1272,7 +1272,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1288,7 +1288,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1300,7 +1300,7 @@ dependencies = [
[[package]]
name = "oxide_fold"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"num-bigint",
"oxide_bytecode",
@@ -1312,7 +1312,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"bumpalo",
"dashmap",
@@ -1335,7 +1335,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1347,7 +1347,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"tracing",
"tracing-appender",
@@ -1356,11 +1356,11 @@ dependencies = [
[[package]]
name = "oxide_net"
version = "0.19.28"
version = "0.19.29"
[[package]]
name = "oxide_parser"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1375,7 +1375,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1391,7 +1391,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"num-bigint",
"num-traits",
@@ -1404,7 +1404,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1424,7 +1424,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"bumpalo",
"dashmap",
@@ -1437,7 +1437,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.19.28"
version = "0.19.29"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "0.19.28"
version = "0.19.29"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+18 -7
View File
@@ -100,12 +100,18 @@ impl Vm {
NativeResult::TailCall { callee, this, args } => {
if callee.is_object() {
let obj = unsafe { &*callee.as_js_object_ptr() };
// 不可调用目标(apply/call/bound 转发的 thisArg 不可调用等)抛
// 非函数目标(apply/call/bound 转发的 thisArg 非函数等)抛
// 可捕获 TypeError;落入 push_bytecode_frame 会产生引擎级错误,
// 绕过 JS try/catch。
if !obj.is_function() || (obj.native_fn().is_none() && obj.sub_module_index() == 0) {
if !obj.is_function() {
return self.raise_type_error("CALL target is not callable");
}
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参
// 返回 undefined,不执行函数体。
if obj.native_fn().is_none() && obj.sub_module_index() == 0 {
self.regs[0] = JsValue::undefined();
return Ok(());
}
if obj.native_fn().is_some() {
// 尾调用转发为普通调用形态。
self.constructing_native = false;
@@ -967,6 +973,12 @@ impl Vm {
.raise_type_error("class constructor cannot be invoked without 'new'")
.map(|_| true);
}
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参
// 返回 undefined,不执行函数体。
if obj.native_fn().is_none() && obj.sub_module_index() == 0 {
self.regs[0] = JsValue::undefined();
return Ok(false);
}
if obj.native_fn().is_some() {
// spread 普通调用形态:清零构造形态标记。
self.constructing_native = false;
@@ -1174,11 +1186,10 @@ impl Vm {
)?;
Ok(true)
} else {
let error =
oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported");
self.exception_value = Some(error);
self.pending_error_kind = Some(self.thrown_error_kind(error));
self.unwind().map(|_| true)
// 无函数体的函数对象(如 Function.prototype):IsConstructor 为假,
// 按规范抛 TypeError(与非 spread 的 NEW_EXPRESSION 臂同形)。
self.raise_type_error("NEW_EXPRESSION: constructor is not a constructor")
.map(|_| true)
}
}
+6
View File
@@ -191,6 +191,12 @@ impl Vm {
};
}
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参返回
// undefined,不执行函数体。此处 callee 已确认为非 native 的函数对象,
// sub_module_index == 0 即无本体(accessor 非函数,已在上方拦截)。
if callee_obj.sub_module_index() == 0 {
return Ok(JsValue::undefined());
}
// 字节码函数在自身(同一 epoch)内联执行,防止 use-after-free。
// 独立子 VM 拥有不同 epoch:返回含子 VM epoch 指针的 JsValue 后再销毁子 VM,
// 会在 release 构建中产生悬垂指针 / 访问违规。
+3 -1
View File
@@ -98,7 +98,9 @@ impl Vm {
table.immutables.resize(table.modules.len(), OnceLock::new());
table.si_tables.resize(table.modules.len(), OnceLock::new());
let func_val = self.create_function_object(base, self.current_gen, FunctionFlags::none());
// 动态函数对象置 DYNAMIC 旗:`prototype` 属性按规范不可配置
// (区别于普通函数的可配置面)。
let func_val = self.create_function_object(base, self.current_gen, FunctionFlags(FunctionFlags::DYNAMIC));
let func_obj = unsafe { &mut *func_val.as_js_object_ptr() };
let length_si = self.kernel_core.perm_interner().intern("length").0;
let name_si = self.kernel_core.perm_interner().intern("name").0;
+7
View File
@@ -41,6 +41,13 @@ impl Vm {
let call_window = (ext >> 8) as u8;
crate::vm_debug!("CALL rd={} this={} args={} depth={}", rd, this_reg, arg_count, self.frames.len());
// 无本体函数对象(Function.prototype):[[Call]] 接受任意实参
// 返回 undefined,不执行函数体。
if obj.native_fn().is_none() && obj.sub_module_index() == 0 {
self.regs[0] = JsValue::undefined();
return Ok(false);
}
if obj.native_fn().is_some() {
self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)?;
return Ok(true);
+17 -7
View File
@@ -26,9 +26,10 @@ fn gc_pressure_mode_from_env() -> bool {
std::env::var_os("OXIDE_GC_PRESSURE").is_some()
}
/// 函数种类标志位集合:箭头、类构造器、派生构造器,压缩在单个 `u8` 中。
/// 函数种类标志位集合:箭头、类构造器、派生构造器、动态函数,压缩在单个 `u8` 中。
///
/// 位定义:bit0 = arrow,bit1 = class_constructor,bit2 = derived_constructor。
/// 位定义:bit0 = arrow,bit1 = class_constructor,bit2 = derived_constructor,
/// bit3 = dynamic。
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct FunctionFlags(pub u8);
@@ -39,6 +40,9 @@ impl FunctionFlags {
pub(crate) const CLASS_CONSTRUCTOR: u8 = 0b010;
/// 派生构造器标志位(bit2)。
pub(crate) const DERIVED_CONSTRUCTOR: u8 = 0b100;
/// 动态函数标志位(bit3):经 Function 构造器创建的函数对象,其
/// `prototype` 属性不可配置。
pub(crate) const DYNAMIC: u8 = 0b1000;
/// 无标志(普通函数)。
pub(crate) const fn none() -> Self {
@@ -74,6 +78,11 @@ impl FunctionFlags {
pub(crate) const fn is_derived_constructor(self) -> bool {
self.0 & Self::DERIVED_CONSTRUCTOR != 0
}
/// 是否动态函数(经 Function 构造器创建)。
pub(crate) const fn is_dynamic(self) -> bool {
self.0 & Self::DYNAMIC != 0
}
}
/// 模块级装载缓存条目三件套:已转换常量池、BigInt 自持存储、si 侧表。
@@ -891,16 +900,17 @@ impl Vm {
prototype.bump_generation();
}
// 函数自身 `prototype` 属性:生成器 writable:true / enumerable:false /
// configurable:false;普通函数与构造器 writable:true / enumerable:false /
// configurable:true。显式落元数据——缺元数据按默认描述符(可枚举)
// 判定,`prototype` 会泄漏进 keys / for-in / 聚合枚举。
// 函数自身 `prototype` 属性:生成器与动态函数(Function 构造器产物)
// writable:true / enumerable:false / configurable:false;普通函数与
// 构造器 writable:true / enumerable:false / configurable:true。显式
// 落元数据——缺元数据按默认描述符(可枚举)判定,`prototype` 会泄漏
// 进 keys / for-in / 聚合枚举。
let prototype_si = self.kernel_core.perm_interner().intern("prototype").0;
let func = unsafe { &mut *obj_ptr };
let prototype_shape = self.kernel_core.shape_forge().make_shape(func.shape_id(), prototype_si);
func.set_shape_id(prototype_shape);
let prototype_pos = func.push_prop(prototype_val);
if is_generator || is_async_generator {
if is_generator || is_async_generator || flags.is_dynamic() {
func.set_data_meta(prototype_pos, PropAttributes::new(true, false, false));
} else {
func.set_data_meta(prototype_pos, PropAttributes::new(true, false, true));
@@ -448,6 +448,66 @@ fn function_bind_async_generator_target_returns_iterator() {
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "object");
}
#[test]
fn function_prototype_call_returns_undefined() {
// Function.prototype 是函数对象:[[Call]] 接受任意实参返回 undefined,
// 不执行函数体。三个调用入口(CALL、CALL_SPREAD、TailCall 转发)同口径。
let mut vm = Vm::new();
// 直调(CALL 指令)。
let result = eval(&mut vm, "Function.prototype()").unwrap();
assert!(result.is_undefined());
// 带任意实参。
let result = eval(&mut vm, "Function.prototype(null, void 0, 7)").unwrap();
assert!(result.is_undefined());
// 展开调用(CALL_SPREAD 指令)。
let result = eval(&mut vm, "Function.prototype(...[1, 2, 3])").unwrap();
assert!(result.is_undefined());
// call/apply 尾调用转发(TailCall 分支)。
let result = eval(&mut vm, "Function.prototype.call.call(Function.prototype)").unwrap();
assert!(result.is_undefined());
let result = eval(&mut vm, "Function.prototype.apply.call(Function.prototype, [7])").unwrap();
assert!(result.is_undefined());
}
#[test]
fn function_prototype_new_throws_catchable_type_error() {
// Function.prototype 无 [[Construct]]:new 表达式抛可捕获 TypeError,
// 不得绕过 try/catch 成为引擎级错误。
let mut vm = Vm::new();
let name = eval(&mut vm, "try { new Function.prototype() } catch (e) { e.name }").unwrap();
assert_eq!(vm.lookup_str(name).unwrap_or_default(), "TypeError");
// 展开构造形态(NEW_EXPRESSION_SPREAD)同口径:抛 TypeError。
let name = eval(&mut vm, "try { new Function.prototype(...[]) } catch (e) { e.name }").unwrap();
assert_eq!(vm.lookup_str(name).unwrap_or_default(), "TypeError");
}
#[test]
fn dynamic_function_prototype_descriptor_not_configurable() {
// 动态函数(Function 构造器产物)的 prototype 属性为
// {writable:true, enumerable:false, configurable:false},delete 失败且值不变;
// 普通函数的 prototype 属性保持可配置,不回归。
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(
&mut vm,
"var d = Object.getOwnPropertyDescriptor(new Function('a', 'return a;'), 'prototype'); d.writable + ',' + d.enumerable + ',' + d.configurable",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "true,false,false");
let result = eval(
&mut vm,
"var f = new Function('a', 'return a;'); var p = f.prototype; delete f.prototype; f.prototype === p",
)
.unwrap();
assert!(result.as_bool());
let result = eval(
&mut vm,
"function g() {} var d = Object.getOwnPropertyDescriptor(g, 'prototype'); d.writable + ',' + d.enumerable + ',' + d.configurable",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "true,false,true");
}
#[test]
fn function_call_generator_param_default_throw_keeps_catch_value() {
// 生成器目标带抛错的参数默认值,经 call/apply/bind 调用且调用点在 try/catch 内: