From fb8e16b2d9211145ba65df748230e76cefecb5b8 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Thu, 28 Mar 2024 00:56:45 +0100 Subject: [PATCH] Cached `this` value (#3771) --- core/engine/src/builtins/function/mod.rs | 8 ++++++- core/engine/src/vm/call_frame/mod.rs | 9 ++++++++ .../src/vm/opcode/control_flow/return.rs | 21 ++++++++++++------- core/engine/src/vm/opcode/environment/mod.rs | 12 ++++++++++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 2be258be07..da223b3bc0 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -1054,11 +1054,17 @@ fn function_construct( Some(this) }; - let frame = CallFrame::new(code.clone(), script_or_module, environments, realm) + let mut frame = CallFrame::new(code.clone(), script_or_module, environments, realm) .with_argument_count(argument_count as u32) .with_env_fp(env_fp) .with_flags(CallFrameFlags::CONSTRUCT); + // We push the `this` below so we can mark this function as having the this value + // cached if it's initialized. + frame + .flags + .set(CallFrameFlags::THIS_VALUE_CACHED, this.is_some()); + let len = context.vm.stack.len(); context.vm.push_frame(frame); diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index e994f4dd00..1913a77f37 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -31,6 +31,9 @@ bitflags::bitflags! { /// Does this [`CallFrame`] need to push registers on [`Vm::push_frame()`]. const REGISTERS_ALREADY_PUSHED = 0b0000_0100; + + /// If the `this` value has been cached. + const THIS_VALUE_CACHED = 0b0000_1000; } } @@ -323,6 +326,12 @@ impl CallFrame { self.flags .contains(CallFrameFlags::REGISTERS_ALREADY_PUSHED) } + /// Does this [`CallFrame`] have a cached `this` value. + /// + /// The cached value is placed in the [`CallFrame::THIS_POSITION`] position. + pub(crate) fn has_this_value_cached(&self) -> bool { + self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED) + } } /// ---- `CallFrame` stack methods ---- diff --git a/core/engine/src/vm/opcode/control_flow/return.rs b/core/engine/src/vm/opcode/control_flow/return.rs index 2296c41021..31e4294e0f 100644 --- a/core/engine/src/vm/opcode/control_flow/return.rs +++ b/core/engine/src/vm/opcode/control_flow/return.rs @@ -54,16 +54,21 @@ impl Operation for CheckReturn { ); return Ok(CompletionType::Throw); } else { - let realm = context.vm.frame().realm.clone(); - let this = context.vm.environments.get_this_binding(); + let frame = context.vm.frame(); + if frame.has_this_value_cached() { + this + } else { + let realm = frame.realm.clone(); + let this = context.vm.environments.get_this_binding(); - match this { - Err(err) => { - let err = err.inject_realm(realm); - context.vm.pending_exception = Some(err); - return Ok(CompletionType::Throw); + match this { + Err(err) => { + let err = err.inject_realm(realm); + context.vm.pending_exception = Some(err); + return Ok(CompletionType::Throw); + } + Ok(this) => this, } - Ok(this) => this, } }; diff --git a/core/engine/src/vm/opcode/environment/mod.rs b/core/engine/src/vm/opcode/environment/mod.rs index 0f3dcb4b9c..2d04092ec7 100644 --- a/core/engine/src/vm/opcode/environment/mod.rs +++ b/core/engine/src/vm/opcode/environment/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::OrdinaryFunction, error::JsNativeError, object::internal_methods::InternalMethodContext, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CallFrameFlags, CompletionType}, Context, JsResult, JsValue, }; @@ -19,7 +19,17 @@ impl Operation for This { const COST: u8 = 1; fn execute(context: &mut Context) -> JsResult { + let frame = context.vm.frame_mut(); + let this_index = frame.fp(); + if frame.has_this_value_cached() { + let this = context.vm.stack[this_index as usize].clone(); + context.vm.push(this); + return Ok(CompletionType::Normal); + } + let this = context.vm.environments.get_this_binding()?; + context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED; + context.vm.stack[this_index as usize] = this.clone(); context.vm.push(this); Ok(CompletionType::Normal) }