From b58a334001b857d8dadf3a18972e056eac5b2cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Pasi=C5=84ski?= Date: Wed, 11 Sep 2024 13:48:21 +0200 Subject: [PATCH] feat: implement local eval --- include/hermes/BCGen/HBC/StackFrameLayout.h | 21 +++++++------ include/hermes/VM/RootSections.def | 3 +- include/hermes/VM/StackFrame-inline.h | 12 ++++---- include/hermes/VM/StackFrame.h | 34 +++++++++++---------- lib/VM/Debugger/Debugger.cpp | 4 +-- lib/VM/Interpreter-slowpaths.cpp | 4 ++- lib/VM/Interpreter.cpp | 12 ++------ lib/VM/JSLib/JSLibInternal.h | 9 ++++++ lib/VM/JSLib/eval.cpp | 12 +++++++- lib/VM/StackFrame.cpp | 2 +- lib/VM/StaticH.cpp | 2 +- 11 files changed, 66 insertions(+), 49 deletions(-) diff --git a/include/hermes/BCGen/HBC/StackFrameLayout.h b/include/hermes/BCGen/HBC/StackFrameLayout.h index 530f9abecfc..6fa7e287849 100644 --- a/include/hermes/BCGen/HBC/StackFrameLayout.h +++ b/include/hermes/BCGen/HBC/StackFrameLayout.h @@ -28,7 +28,7 @@ namespace hbc { /// ... /// 2 callee local0 : HermesValue /// 1 scratch : HermesValue -/// 0 debugEnvironment : Environment* +/// 0 environment : Environment* /// ---------------------------------------------- /// -1 previousFrame : NativeValue(HermesValue*) -- calleeFramePtr /// -2 savedIP : NativeValue(void*) @@ -45,7 +45,7 @@ namespace hbc { /// ... /// caller local 0 : HermesValue /// scratch : HermesValue -/// debugEnvironment : Environment* +/// environment : Environment* /// ---------------------------------------------- /// -- callerFramePtr /// \endverbatim @@ -67,7 +67,7 @@ namespace hbc { /// - The caller populates calleeClosureOrCB, newTarget and argCount. /// - The caller saves the current CodeBlock, IP and frame offset in the /// corresponding fields. -/// - "debugEnvironment" is initialized to "undefined". (It will be populated +/// - "environment" is initialized to "undefined". (It will be populated /// later by the callee.) /// - Execution is transferred to callee. /// - The callee updates the global "frame" register to point to the top of the @@ -89,12 +89,13 @@ struct StackFrameLayout { Scratch = 1, /// The environment associated with the callee's stack frame, that is, the /// Environment created by the last CreateEnvironment instruction to execute - /// in the callee's stack frame. It is null if debugging support is not - /// present, or if no CreateEnvironment instruction has executed, which is - /// possible if we are early in the code block, or with optimized code. This - /// is stored in the call frame so that the debugger can gain access to the - /// Environment at arbitrary frames. Note this is managed by the GC. - DebugEnvironment = 0, + /// in the callee's stack frame. It is null if no CreateEnvironment + /// instruction has executed, which is possible if we are early in the code + /// block, or with optimized code. This is stored in the call frame so + /// that the debugger can gain access to the Environment at arbitrary frames, + /// and to enable local eval support + /// Note this is managed by the GC. + Environment = 0, /// Saved value of the caller's "frame" register, which points to the first /// register of the caller's stack frame. PreviousFrame = -1, @@ -131,7 +132,7 @@ struct StackFrameLayout { /// The number of additional registers the callee needs to allocate in the /// beginning of its frame. - CalleeExtraRegistersAtStart = Scratch - DebugEnvironment + 1, + CalleeExtraRegistersAtStart = Scratch - Environment + 1, }; /// Calculate the number of register slots needed for an outgoing call: it diff --git a/include/hermes/VM/RootSections.def b/include/hermes/VM/RootSections.def index cff1002108a..603bd387990 100644 --- a/include/hermes/VM/RootSections.def +++ b/include/hermes/VM/RootSections.def @@ -18,8 +18,7 @@ ROOT_SECTION(CharStrings) ROOT_SECTION(StringCycleCheckVisited) ROOT_SECTION(Builtins) ROOT_SECTION(Jobs) -// We don't need DebugEnvironment anymore, but keep it for MobileLab. -ROOT_SECTION(DebugEnvironment) +ROOT_SECTION(Environment) ROOT_SECTION(IdentifierTable) ROOT_SECTION(GCScopes) ROOT_SECTION(WeakRefs) diff --git a/include/hermes/VM/StackFrame-inline.h b/include/hermes/VM/StackFrame-inline.h index d5681fcb7ec..cb7f14e9291 100644 --- a/include/hermes/VM/StackFrame-inline.h +++ b/include/hermes/VM/StackFrame-inline.h @@ -39,18 +39,18 @@ StackFramePtrT::getCalleeCodeBlock(Runtime &runtime) const { } template -inline Handle StackFramePtrT::getDebugEnvironmentHandle() +inline Handle StackFramePtrT::getEnvironmentHandle() const { - return getDebugEnvironmentRef().isUndefined() + return getEnvironmentRef().isUndefined() ? HandleRootOwner::makeNullHandle() - : Handle::vmcast_or_null(&getDebugEnvironmentRef()); + : Handle::vmcast_or_null(&getEnvironmentRef()); } template -inline Environment *StackFramePtrT::getDebugEnvironment() const { - return getDebugEnvironmentRef().isUndefined() +inline Environment *StackFramePtrT::getEnvironment() const { + return getEnvironmentRef().isUndefined() ? nullptr - : vmcast_or_null(getDebugEnvironmentRef()); + : vmcast_or_null(getEnvironmentRef()); } } // namespace vm diff --git a/include/hermes/VM/StackFrame.h b/include/hermes/VM/StackFrame.h index af1c8293c87..8d7c613444c 100644 --- a/include/hermes/VM/StackFrame.h +++ b/include/hermes/VM/StackFrame.h @@ -103,7 +103,7 @@ class StackFramePtrT { // Declare convenience accessors to the underlying HermesValue slots. _HERMESVM_DEFINE_STACKFRAME_REF(FirstLocal) _HERMESVM_DEFINE_STACKFRAME_REF(Scratch) - _HERMESVM_DEFINE_STACKFRAME_REF(DebugEnvironment) + _HERMESVM_DEFINE_STACKFRAME_REF(Environment) _HERMESVM_DEFINE_STACKFRAME_REF(PreviousFrame) _HERMESVM_DEFINE_STACKFRAME_REF(SavedIP) _HERMESVM_DEFINE_STACKFRAME_REF(SavedCodeBlock) @@ -144,25 +144,27 @@ class StackFramePtrT { return getSHLocalsRef().template getNativePointer(); } - /// \return a handle holding the callee debug environment. + /// \return a handle holding the callee environment. /// The environment associated with the callee's stack frame, that is, the /// Environment created by the last CreateEnvironment instruction to execute - /// in the callee's stack frame. It is null if debugging support is not - /// present, or if no CreateEnvironment instruction has executed, which is - /// possible if we are early in the code block, or with optimized code. This - /// is stored in the call frame so that the debugger can gain access to the - /// Environment at arbitrary frames. Note this is managed by the GC. - inline Handle getDebugEnvironmentHandle() const; - - /// \return the callee debug environment. + /// in the callee's stack frame. It is null if no CreateEnvironment + /// instruction has executed, which is possible if we are early in the code + /// block, or with optimized code. This is stored in the call frame so + /// that the debugger can gain access to the Environment at arbitrary frames, + /// and to enable local eval support + /// Note this is managed by the GC. + inline Handle getEnvironmentHandle() const; + + /// \return the callee environment. /// The environment associated with the callee's stack frame, that is, the /// Environment created by the last CreateEnvironment instruction to execute - /// in the callee's stack frame. It is null if debugging support is not - /// present, or if no CreateEnvironment instruction has executed, which is - /// possible if we are early in the code block, or with optimized code. This - /// is stored in the call frame so that the debugger can gain access to the - /// Environment at arbitrary frames. Note this is managed by the GC. - inline Environment *getDebugEnvironment() const; + /// in the callee's stack frame. It is null if no CreateEnvironment + /// instruction has executed, which is possible if we are early in the code + /// block, or with optimized code. This is stored in the call frame so + /// that the debugger can gain access to the Environment at arbitrary frames, + /// and to enable local eval support + /// Note this is managed by the GC. + inline Environment *getEnvironment() const; /// \return the number of JavaScript arguments passed to the callee excluding /// \c "this". diff --git a/lib/VM/Debugger/Debugger.cpp b/lib/VM/Debugger/Debugger.cpp index a1b5159246e..9dea4c7e894 100644 --- a/lib/VM/Debugger/Debugger.cpp +++ b/lib/VM/Debugger/Debugger.cpp @@ -970,7 +970,7 @@ HermesValue Debugger::getVariableInFrame( // Descend the environment chain to the desired depth, or stop at null. // We may get a null environment if it has not been created. MutableHandle env( - runtime_, frameInfo->frame->getDebugEnvironment()); + runtime_, frameInfo->frame->getEnvironment()); for (uint32_t i = 0; env && i < scopeDepth; i++) env = env->getParentEnvironment(runtime_); @@ -1052,7 +1052,7 @@ HermesValue Debugger::evalInFrame( bool singleFunction = false; // Environment may be undefined if it has not been created yet. - Handle env = frameInfo->frame->getDebugEnvironmentHandle(); + Handle env = frameInfo->frame->getEnvironmentHandle(); if (!env) { // TODO: this comes about when we break in a function before its environment // has been created. What we would like to do here is synthesize an diff --git a/lib/VM/Interpreter-slowpaths.cpp b/lib/VM/Interpreter-slowpaths.cpp index 5f0b8394571..bc53c988e11 100644 --- a/lib/VM/Interpreter-slowpaths.cpp +++ b/lib/VM/Interpreter-slowpaths.cpp @@ -34,12 +34,14 @@ ExecutionStatus Interpreter::caseDirectEval( } GCScopeMarkerRAII gcMarker{runtime}; + auto cb = FRAME.getCalleeCodeBlock(runtime); auto cr = vm::directEval( runtime, Handle::vmcast(evalText), strictCaller, - nullptr, + cb, + FRAME.getEnvironmentHandle(), false); if (cr == ExecutionStatus::EXCEPTION) return ExecutionStatus::EXCEPTION; diff --git a/lib/VM/Interpreter.cpp b/lib/VM/Interpreter.cpp index 8e00660452b..036d83a4e0d 100644 --- a/lib/VM/Interpreter.cpp +++ b/lib/VM/Interpreter.cpp @@ -2036,9 +2036,7 @@ CallResult Interpreter::interpretFunction( ip->iCreateEnvironment.op3)); O1REG(CreateEnvironment) = envHV; -#ifdef HERMES_ENABLE_DEBUGGER - FRAME.getDebugEnvironmentRef() = envHV; -#endif + FRAME.getEnvironmentRef() = envHV; ip = NEXTINST(CreateEnvironment); DISPATCH; } @@ -2052,9 +2050,7 @@ CallResult Interpreter::interpretFunction( ip->iCreateFunctionEnvironment.op2)); O1REG(CreateFunctionEnvironment) = HermesValue::encodeObjectValue(env); -#ifdef HERMES_ENABLE_DEBUGGER - FRAME.getDebugEnvironmentRef() = O1REG(CreateFunctionEnvironment); -#endif + FRAME.getEnvironmentRef() = O1REG(CreateFunctionEnvironment); tmpHandle = HermesValue::encodeUndefinedValue(); ip = NEXTINST(CreateFunctionEnvironment); DISPATCH; @@ -2069,9 +2065,7 @@ CallResult Interpreter::interpretFunction( ip->iCreateTopLevelEnvironment.op2)); O1REG(CreateTopLevelEnvironment) = envHV; -#ifdef HERMES_ENABLE_DEBUGGER - FRAME.getDebugEnvironmentRef() = envHV; -#endif + FRAME.getEnvironmentRef() = envHV; ip = NEXTINST(CreateTopLevelEnvironment); DISPATCH; } diff --git a/lib/VM/JSLib/JSLibInternal.h b/lib/VM/JSLib/JSLibInternal.h index 8a14cf83250..b6b60e3f2b3 100644 --- a/lib/VM/JSLib/JSLibInternal.h +++ b/lib/VM/JSLib/JSLibInternal.h @@ -454,6 +454,15 @@ CallResult directEval( const CodeBlock *codeBlock, bool singleFunction = false); +/// A direct passthrough to call eval() on \p str. +CallResult directEval( + Runtime &runtime, + Handle str, + bool strictCaller, + const CodeBlock *codeBlock, + Handle environment, + bool singleFunction = false); + /// ES10 23.1.1.2 AddEntriesFromIterable /// Calls a callback with each pair of [key, value] from an iterable. /// \param target the object to which to add the entries diff --git a/lib/VM/JSLib/eval.cpp b/lib/VM/JSLib/eval.cpp index 09b37af4f85..36d8e840f32 100644 --- a/lib/VM/JSLib/eval.cpp +++ b/lib/VM/JSLib/eval.cpp @@ -129,6 +129,16 @@ CallResult directEval( bool strictCaller, const CodeBlock *codeBlock, bool singleFunction) { + return directEval(runtime, str, strictCaller, codeBlock, Runtime::makeNullHandle(), singleFunction); +} + +CallResult directEval( + Runtime &runtime, + Handle str, + bool strictCaller, + const CodeBlock *codeBlock, + Handle environment, + bool singleFunction) { // Convert the code into UTF8. std::string code; auto view = StringPrimitive::createStringView(runtime, str); @@ -143,7 +153,7 @@ CallResult directEval( runtime, code, strictCaller, - Runtime::makeNullHandle(), + environment, codeBlock, runtime.getGlobal(), singleFunction); diff --git a/lib/VM/StackFrame.cpp b/lib/VM/StackFrame.cpp index 29ce0c02ff6..7f207356f6f 100644 --- a/lib/VM/StackFrame.cpp +++ b/lib/VM/StackFrame.cpp @@ -29,7 +29,7 @@ void dumpStackFrame( << "\n" << " SavedIP : " << format_ptr(frame.getSavedIP()) << "\n" << " SavedCodeBlock : " << format_ptr(frame.getSavedCodeBlock()) << "\n" - << " DebugEnvironment: " << frame.getDebugEnvironmentRef() << "\n" + << " Environment : " << frame.getEnvironmentRef() << "\n" << " ArgCount : " << frame.getArgCount() << "\n" << " NewTarget : " << frame.getNewTargetRef() << "\n" << " CalleeClosure : " << frame.getCalleeClosureOrCBRef() << "\n" diff --git a/lib/VM/StaticH.cpp b/lib/VM/StaticH.cpp index ea966c2cbe3..9fb819cc550 100644 --- a/lib/VM/StaticH.cpp +++ b/lib/VM/StaticH.cpp @@ -498,7 +498,7 @@ extern "C" SHLegacyValue _sh_ljs_create_environment( GCScopeMarkerRAII marker{runtime}; return Environment::create(runtime, parentHandle, size); // #ifdef HERMES_ENABLE_DEBUGGER - // framePtr.getDebugEnvironmentRef() = *res; + // framePtr.getEnvironmentRef() = *res; // #endif }