fix: sloppy 模式属性赋值静默 no-op——写失败按写方严格模式分派,内置写路径一律按严格

This commit is contained in:
2026-09-09 17:48:33 +08:00
parent 64f582d34b
commit c8bcff83f6
18 changed files with 529 additions and 89 deletions
+2 -2
View File
@@ -160,7 +160,7 @@ impl Vm {
let prop_val = self.ordinary_get(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n + 1.0);
self.ordinary_set(obj, prop_name_si, new_val, receiver)?;
self.ordinary_set(obj, prop_name_si, new_val, receiver, self.current_strict())?;
self.regs[b] = new_val;
Ok(())
}
@@ -176,7 +176,7 @@ impl Vm {
let prop_val = self.ordinary_get(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n - 1.0);
self.ordinary_set(obj, prop_name_si, new_val, receiver)?;
self.ordinary_set(obj, prop_name_si, new_val, receiver, self.current_strict())?;
self.regs[b] = new_val;
Ok(())
}
+6 -6
View File
@@ -435,7 +435,7 @@ impl Vm {
}
if obj.has_prop_meta() {
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver, self.current_strict())?;
return Ok(());
}
@@ -451,10 +451,10 @@ impl Vm {
// 写新属性(shape 转换):数组 length / 整数索引键仍走完整
// ordinary_set_dispatch 语义,其余走 CreateDataProperty 快路径。
if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) {
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver, self.current_strict())?;
return Ok(());
}
self.create_named_prop_fast(obj, prop_name_si, value, receiver, ic_pc, true)?;
self.create_named_prop_fast(obj, prop_name_si, value, receiver, ic_pc, true, self.current_strict())?;
}
Ok(())
@@ -496,7 +496,7 @@ impl Vm {
}
let value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[a]);
let obj = unsafe { &mut *obj_ptr };
self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd])?;
self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd], self.current_strict())?;
Ok(())
}
@@ -593,7 +593,7 @@ impl Vm {
}
let value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[b]);
let obj = unsafe { &mut *obj_ptr };
self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd])?;
self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd], self.current_strict())?;
Ok(())
}
@@ -616,7 +616,7 @@ impl Vm {
// 直写绕过冻结语义。
if obj.has_prop_meta() {
let si = make_int_key(idx);
return self.ordinary_set_dispatch(obj, si, value, self.regs[rd]);
return self.ordinary_set_dispatch(obj, si, value, self.regs[rd], self.current_strict());
}
// 无 meta 快路径:不可扩展对象禁止新增元素(越界下标);已有元素直写不受限
// (无 meta 即默认可写)。
+1
View File
@@ -1012,6 +1012,7 @@ mod tests {
saved_new_target: JsValue::from_js_object(child_session),
callee: JsValue::from_js_object(child_session),
construct_result_reg: None,
strict: false,
constructed_this: Some(JsValue::from_js_object(child_session)),
is_derived_constructor: false,
super_called: false,
+2
View File
@@ -289,6 +289,7 @@ impl SuspendedFrame {
constructed_this: f.constructed_this.map(&mut rewrite),
is_derived_constructor: f.is_derived_constructor,
super_called: f.super_called,
strict: f.strict,
continuation: f.continuation,
}),
spill_stack: self.spill_stack.iter().copied().map(&mut rewrite).collect(),
@@ -365,6 +366,7 @@ mod tests {
constructed_this: Some(tag),
is_derived_constructor: false,
super_called: false,
strict: false,
continuation: FrameContinuation::None,
}
}
+47 -6
View File
@@ -220,6 +220,9 @@ pub struct CallFrame {
/// 旧方案——多层继承时中间 derived 帧由 SUPER_CALL 以构造 this 压入,
/// regs[254] 恒非 undefined,值判定三重失效。
pub super_called: bool,
/// 本帧函数的严格模式标志(来源:模块编译产物 `is_strict`)。
/// 属性写失败时据此分派:严格模式抛 TypeError,sloppy 静默 no-op。
pub strict: bool,
pub continuation: FrameContinuation,
}
@@ -372,6 +375,11 @@ pub(crate) struct InlineSyncState {
pub(crate) spill_stack: Vec<JsValue>,
pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
pub(crate) inline_callee: Option<JsValue>,
/// inline 目标函数的严格模式标志(内联执行期间写路径的 strict/sloppy 判定
/// 来源;嵌套内联时随本快照保存/恢复)。
pub(crate) inline_strict: bool,
/// inline 起始时 `frames.len()` 基线(嵌套内联时随本快照保存/恢复)。
pub(crate) inline_frames_base: usize,
pub(crate) inline_args_base: u32,
pub(crate) inline_args_count: u16,
pub(crate) accessor_frame_target_reg: Option<u8>,
@@ -474,6 +482,16 @@ pub struct Vm {
/// frames 为空(inline 隔离状态)时由此取闭包 upvalues。嵌套 inline 由
/// InlineSyncState 保存/恢复。
pub(crate) inline_callee: Option<JsValue>,
/// inline 目标函数的严格模式标志(与 `inline_callee` 同生命周期,
/// InlineSyncState 保存/恢复):内联执行期间帧表被隔离,写路径的
/// strict/sloppy 判定取此值而非帧标志。
pub(crate) inline_strict: bool,
/// inline 起始时 `frames.len()` 基线(随 InlineSyncState 保存/恢复):
/// 内联执行中新压的帧(CALL/accessor)越过该基线,其严格性归帧栈顶。
pub(crate) inline_frames_base: usize,
/// 本次 run 顶层脚本的严格模式标志(`module.is_strict`,run 时填充):
/// 无帧且无 inline(顶层脚本赋值)时写路径的 strict/sloppy 判定来源。
pub(crate) top_level_strict: bool,
/// inline 同步调用寄存器窗口缓冲池:`save_inline_state` 取出复用、
/// `restore_inline_state` 归还。热回调循环内 save/restore 反复使用同一块
/// 缓冲,只在嵌套(池已被外层取走)时新分配。
@@ -1327,6 +1345,27 @@ impl Vm {
.max(1)
}
/// 当前执行上下文的严格模式标志:属性写失败路径据此分派(严格抛
/// TypeError,sloppy 静默 no-op)。
///
/// # 边界与前提
/// - inline 执行期间帧表可能非空(调用方帧隔离在外),须以
/// `inline_frames_base` 为基线区分"inline 前已有帧"与"inline 内新压帧"
/// (CALL/accessor 帧严格性归目标函数,最内层执行上下文即帧栈顶)。
/// - 无帧且无 inline 时为顶层脚本执行,取 `top_level_strict`。
pub(crate) fn current_strict(&self) -> bool {
if self.inline_callee.is_some() {
if self.frames.len() > self.inline_frames_base {
return self.frames.last().unwrap().strict;
}
return self.inline_strict;
}
if let Some(frame) = self.frames.last() {
return frame.strict;
}
self.top_level_strict
}
#[expect(clippy::too_many_arguments)]
pub(crate) fn push_bytecode_frame(
&mut self, callee: JsValue, this_value: JsValue, args: FrameArgs, construct_result_reg: Option<u8>,
@@ -1436,6 +1475,7 @@ impl Vm {
constructed_this,
is_derived_constructor: obj.is_derived_constructor(),
super_called: false,
strict: sub_is_strict,
continuation,
});
@@ -2207,9 +2247,9 @@ impl oxide_runtime_api::VmHost for Vm {
self.ordinary_get(obj, prop_name_si, receiver)
}
fn ordinary_set(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool,
) -> Result<(), String> {
self.ordinary_set(obj, prop_name_si, val, receiver)
self.ordinary_set(obj, prop_name_si, val, receiver, strict)
}
fn define_data_property(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
@@ -2528,6 +2568,7 @@ mod tests {
saved_new_target: JsValue::undefined(),
callee: JsValue::undefined(),
construct_result_reg: None,
strict: false,
constructed_this: None,
is_derived_constructor: false,
super_called: false,
@@ -2682,7 +2723,7 @@ mod tests {
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
vm.ordinary_set(obj, x_si, JsValue::int(9), obj_val).expect("setter");
vm.ordinary_set(obj, x_si, JsValue::int(9), obj_val, true).expect("setter");
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let obj = unsafe { &*obj_val.as_js_object_ptr() };
@@ -2720,7 +2761,7 @@ mod tests {
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let child = unsafe { &mut *child_val.as_js_object_ptr() };
vm.ordinary_set(child, x_si, JsValue::int(12), child_val).expect("setter");
vm.ordinary_set(child, x_si, JsValue::int(12), child_val, true).expect("setter");
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let child = unsafe { &*child_val.as_js_object_ptr() };
@@ -2744,7 +2785,7 @@ mod tests {
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let child = unsafe { &mut *child_val.as_js_object_ptr() };
vm.ordinary_set(child, x_si, JsValue::int(15), child_val).expect("setter");
vm.ordinary_set(child, x_si, JsValue::int(15), child_val, true).expect("setter");
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let child = unsafe { &*child_val.as_js_object_ptr() };
@@ -2761,7 +2802,7 @@ mod tests {
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
assert!(!obj.has_prop_meta());
assert_eq!(vm.ordinary_get(obj, x_si, obj_val).expect("get"), JsValue::int(1));
vm.ordinary_set(obj, x_si, JsValue::int(2), obj_val).expect("set");
vm.ordinary_set(obj, x_si, JsValue::int(2), obj_val, true).expect("set");
assert_eq!(vm.ordinary_get(obj, x_si, obj_val).expect("get"), JsValue::int(2));
}
+71 -24
View File
@@ -152,22 +152,25 @@ impl Vm {
None
}
/// `strict` 为写方(执行赋值的那个函数/脚本)的严格模式:写失败时严格抛
/// TypeError,sloppy 静默 no-op(ECMA-262 OrdinarySetOwnProperty 失败分支)。
pub(crate) fn ordinary_set(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool,
) -> Result<(), String> {
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
self.ordinary_set_inner(obj, prop_name_si, val, receiver, false)
self.ordinary_set_inner(obj, prop_name_si, val, receiver, false, strict)
}
/// 分发期入口:调用方(dispatch_set_prop 等)已对值做过 promote。
pub(crate) fn ordinary_set_dispatch(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool,
) -> Result<(), String> {
self.ordinary_set_inner(obj, prop_name_si, val, receiver, true)
self.ordinary_set_inner(obj, prop_name_si, val, receiver, true, strict)
}
fn ordinary_set_inner(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, use_frame_push: bool,
strict: bool,
) -> Result<(), String> {
vm_trace!(
"ordinary_set_inner: shape={} prop_si={} frame_push={}",
@@ -180,7 +183,15 @@ impl Vm {
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
if !std::ptr::eq(receiver.as_js_object_ptr(), obj as *mut JsObject) {
return self.set_to_receiver(obj, prop_name_si, val, receiver, index as usize, use_frame_push);
return self.set_to_receiver(
obj,
prop_name_si,
val,
receiver,
index as usize,
use_frame_push,
strict,
);
}
return oxide_builtins::typed_array::typed_array_element_set(self, obj, index, val);
}
@@ -190,8 +201,8 @@ impl Vm {
// prop_count/迭代/内置方法看到的长度不一致。
let length_si = self.length_si;
if obj.is_array() && prop_name_si == length_si {
// 冻结数组的 length 属性不可写(writable=false),赋值直接失败。
// sloppy/strict 差异未实现,与其它只读属性写一致统一抛 TypeError。
// 冻结数组的 length 属性不可写(writable=false),赋值直接失败;
// 两模式统一抛 TypeError(length 冻结检查的 strict/sloppy 差异不在本路径范围)。
if obj.is_frozen() {
return self.raise_type_error("Cannot assign to read only property 'length'");
}
@@ -215,12 +226,12 @@ impl Vm {
let old_logical = obj.logical_len() as usize;
let old_count = obj.array_prop_count as usize;
// ArraySetLength:增长(newLen > oldLen)要求对象可扩展,不可扩展时
// 整个赋值失败且不修改(sloppy 静默失败;VM 未实现 strict 标志)。
// 整个赋值失败且不修改(length 失败两模式均静默 no-op,不抛)。
if raw_new_len > old_logical && !obj.is_extensible() {
return Ok(());
}
// ArraySetLength:收缩时若 [newLen, oldLen) 内存在不可配置元素,整个收缩
// 失败且不做任何修改(sloppy 赋值静默失败;VM 未实现 strict 标志,统一按 no-op)。
// 失败且不做任何修改(length 失败两模式均静默 no-op,不抛)。
if raw_new_len < old_logical {
for idx in raw_new_len..old_count {
if let Some(meta) = obj.prop_meta_at(idx) {
@@ -256,12 +267,20 @@ impl Vm {
if let Some(meta) = obj.prop_meta_at(pos) {
if meta.is_accessor {
if meta.set.is_undefined() {
return self.raise_type_error("property has no setter");
// 无 setter:严格抛错,sloppy 静默 no-op。
if strict {
return self.raise_type_error("property has no setter");
}
return Ok(());
}
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
}
if !meta.attributes.writable() {
return self.raise_type_error("cannot assign to read-only property");
// 只读数据属性:严格抛错,sloppy 静默 no-op。
if strict {
return self.raise_type_error("cannot assign to read-only property");
}
return Ok(());
}
}
// pos 是存储索引(get_own_property_slot 对数组已加元素区偏移)。
@@ -272,19 +291,30 @@ impl Vm {
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
if meta.is_accessor {
if meta.set.is_undefined() {
return self.raise_type_error("property has no setter");
// 继承无 setter:严格抛错,sloppy 静默 no-op。
if strict {
return self.raise_type_error("property has no setter");
}
return Ok(());
}
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
}
if !meta.attributes.writable() {
return self.raise_type_error("cannot assign to read-only property");
// 继承只读数据属性:严格抛错,sloppy 静默 no-op(不遮蔽)。
if strict {
return self.raise_type_error("cannot assign to read-only property");
}
return Ok(());
}
}
// 新属性(自身与原型链均无同名):须对象可扩展(OrdinarySet 的 extensible
// 检查),不可扩展时赋值失败。
// 检查),不可扩展时赋值失败(严格抛错,sloppy 静默 no-op)。
if !obj.is_extensible() {
return self.raise_type_error("object is not extensible");
if strict {
return self.raise_type_error("object is not extensible");
}
return Ok(());
}
self.set_or_create_prop_value(obj, prop_name_si, val);
Ok(())
@@ -293,9 +323,10 @@ impl Vm {
/// TypedArray 整数索引在 `receiver` ≠ TA 时的 [[Set]] 语义:越界或非对象
/// receiver 直接返回 true(不写、不 ToNumber);界内对象 receiver 走普通 set
/// 把属性落到 receiver 自身。
#[allow(clippy::too_many_arguments)]
fn set_to_receiver(
&mut self, ta_obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, index: usize,
use_frame_push: bool,
use_frame_push: bool, strict: bool,
) -> Result<(), String> {
let Some((_, length)) = oxide_builtins::typed_array::typed_array_integer_index(self, ta_obj, prop_name_si)
else {
@@ -310,7 +341,7 @@ impl Vm {
}
let promoted = self.promote_if_needed_for_write_ptr(receiver_ptr, val);
let receiver_obj = unsafe { &mut *receiver_ptr };
self.ordinary_set_inner(receiver_obj, prop_name_si, promoted, receiver, use_frame_push)
self.ordinary_set_inner(receiver_obj, prop_name_si, promoted, receiver, use_frame_push, strict)
}
pub(crate) fn call_or_push_setter(
@@ -430,8 +461,10 @@ impl Vm {
) -> Result<(), String> {
ic_trace!("set_member_prop: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
// 写方即当前执行函数(赋值语义),strict/sloppy 判定取当前上下文。
let strict = self.current_strict();
if obj.has_prop_meta() {
self.ordinary_set(obj, prop_name_si, val, receiver)?;
self.ordinary_set(obj, prop_name_si, val, receiver, strict)?;
return Ok(());
}
if crate::ic_helper::ic_set_hit_own(obj, &self.bytecode, ext_pc, val) {
@@ -445,9 +478,9 @@ impl Vm {
} else if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) {
// 数组 length / 整数索引键写新属性:分流回 ordinary_set(ArraySetLength /
// 元素区写),与 dispatch_ic_set_prop 对称,防快路径建影子槽破坏数组语义。
self.ordinary_set(obj, prop_name_si, val, receiver)?;
self.ordinary_set(obj, prop_name_si, val, receiver, strict)?;
} else {
self.create_named_prop_fast(obj, prop_name_si, val, receiver, ext_pc, false)?;
self.create_named_prop_fast(obj, prop_name_si, val, receiver, ext_pc, false, strict)?;
}
Ok(())
}
@@ -459,26 +492,40 @@ impl Vm {
/// - 调用方已确认属性不存在于 shape 链(lookup_position miss)。
/// - 数组 length / 数组与 TA 整数索引键不进入本路径(调用方先分流到 ordinary_set)。
/// - `use_frame_push` 与 ordinary_set* 入口一致(member 写 false / IC_SET 分发 true)。
/// - `strict` 为写方的严格模式:原型链只读/无 setter/不可扩展时严格抛错、sloppy 静默 no-op。
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_named_prop_fast(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, ext_pc: usize,
use_frame_push: bool,
use_frame_push: bool, strict: bool,
) -> Result<(), String> {
// 原型链同名 accessor 须触发 setter、只读 data 须报错(与 ordinary_set 一致),
// 命中时不得创建 own 属性(规范 [[Set]] shadow 语义)。
if let Some(meta) = self.inherited_property_meta(obj, prop_name_si) {
if meta.is_accessor {
if meta.set.is_undefined() {
return self.raise_type_error("property has no setter");
// 继承无 setter:严格抛错,sloppy 静默 no-op。
if strict {
return self.raise_type_error("property has no setter");
}
return Ok(());
}
return self.call_or_push_setter(meta.set, receiver, val, use_frame_push);
}
if !meta.attributes.writable() {
return self.raise_type_error("cannot assign to read-only property");
// 继承只读数据属性:严格抛错,sloppy 静默 no-op(不遮蔽)。
if strict {
return self.raise_type_error("cannot assign to read-only property");
}
return Ok(());
}
}
// 新属性创建要求对象可扩展(规范 extensible 检查在原型链 setter/只读判定之后)。
if !obj.is_extensible() {
return self.raise_type_error("object is not extensible");
// 不可扩展:严格抛错,sloppy 静默 no-op。
if strict {
return self.raise_type_error("object is not extensible");
}
return Ok(());
}
// 新 shape 槽位 = 追加前命名属性数(与 push_prop 的追加位置一致)。
let slot = obj.prop_vec_len() as u32;
+21
View File
@@ -100,6 +100,12 @@ macro_rules! inline_save_field {
($recv:ident, $window_regs:ident, inline_callee, opt_copy) => {
$recv.inline_callee
};
($recv:ident, $window_regs:ident, inline_strict, copy) => {
$recv.inline_strict
};
($recv:ident, $window_regs:ident, inline_frames_base, copy) => {
$recv.inline_frames_base
};
($recv:ident, $window_regs:ident, inline_args_base, copy) => {
$recv.inline_args_base
};
@@ -183,6 +189,12 @@ macro_rules! inline_restore_field {
($recv:ident, $saved:ident, inline_callee, opt_copy) => {
$recv.inline_callee = $saved.inline_callee
};
($recv:ident, $saved:ident, inline_strict, copy) => {
$recv.inline_strict = $saved.inline_strict
};
($recv:ident, $saved:ident, inline_frames_base, copy) => {
$recv.inline_frames_base = $saved.inline_frames_base
};
($recv:ident, $saved:ident, inline_args_base, copy) => {
$recv.inline_args_base = $saved.inline_args_base
};
@@ -228,6 +240,8 @@ macro_rules! inline_core_fields {
(spill_stack, move_field), // V M
(cell_stack, move_field), // V M
(inline_callee, opt_copy), // V M
(inline_strict, copy), // M
(inline_frames_base, copy), // M
(inline_args_base, copy), // M
(inline_args_count, copy), // M
(accessor_frame_target_reg, copy), // M
@@ -382,6 +396,11 @@ impl Vm {
self.root_reg_limit = self.active_reg_limit;
self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
self.inline_callee = Some(callee);
// inline 无 CallFrame:目标函数严格模式单独记录,内联执行期间的写路径
// strict/sloppy 判定据此分派(嵌套内联时外层值由 InlineSyncState 恢复)。
self.inline_strict = sub.is_strict;
// 帧基线快照:内联期间新压的帧(CALL/accessor)越过基线,其严格性归帧栈顶。
self.inline_frames_base = self.frames.len();
// inline 同步调用无 CallFrame:完整实参写入 spill 栈实参区,供 CREATE_ARGUMENTS 读取。
self.inline_args_base = self.spill_stack.len() as u32;
self.spill_stack.extend_from_slice(args);
@@ -486,6 +505,8 @@ impl Vm {
self.cell_stack.push(Vec::new());
self.sub_modules = Arc::new(collect_flat_modules(module));
self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect();
// 顶层脚本严格模式:无帧且无 inline 时写路径的 strict/sloppy 判定来源。
self.top_level_strict = module.is_strict;
self.bytecode = Arc::clone(&module.bytecode);
self.activate_immutables(0, &module.constants);
self.root_reg_limit = module.n_registers.max(1);
+6
View File
@@ -75,6 +75,9 @@ impl Vm {
inline_args_count: 0,
accessor_frame_target_reg: None,
inline_callee: None,
inline_strict: false,
inline_frames_base: 0,
top_level_strict: false,
inline_reg_pool: None,
generator_suspended: None,
delegated_iterator: None,
@@ -184,6 +187,9 @@ impl Vm {
inline_args_count: 0,
accessor_frame_target_reg: None,
inline_callee: None,
inline_strict: false,
inline_frames_base: 0,
top_level_strict: false,
inline_reg_pool: None,
generator_suspended: None,
delegated_iterator: None,