diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index bd0d8a69df1..a8ad0ffc1b7 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -6727,6 +6727,10 @@ namespace Js // Finally exited with LeaveNull, We don't throw for early returns if (finallyEndOffset == 0 && exceptionObj) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + JavascriptExceptionOperators::WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(const_cast(exceptionObj), scriptContext); } if (finallyEndOffset != 0) diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.cpp b/lib/Runtime/Language/JavascriptExceptionOperators.cpp index e8d09238320..4207cc1f430 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.cpp +++ b/lib/Runtime/Language/JavascriptExceptionOperators.cpp @@ -190,19 +190,23 @@ namespace Js { void *tryContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)frame + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize); - - try - { - tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); - } - catch (const Js::JavascriptException& err) { - exception = err.GetAndClear(); + void * addrOfReturnAddr = (void*)((char*)frame + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { + tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - if (exception) { // Clone static exception object early in case finally block overwrites it @@ -212,19 +216,9 @@ namespace Js if (exception) { #if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } - } - else + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, frame); - } + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); } #endif bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative @@ -251,21 +245,31 @@ namespace Js void *tryContinuation = nullptr; void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize); - try - { - tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); - } - catch (const Js::JavascriptException& err) { - exception = err.GetAndClear(); + void * addrOfReturnAddr = (void*)((char*)frame + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { + tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - if (exception) { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } } finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize); @@ -276,6 +280,10 @@ namespace Js if (exception) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(exception, scriptContext); } @@ -365,44 +373,40 @@ namespace Js int hasBailedOutOffset, ScriptContext *scriptContext) { - void *tryContinuation = nullptr; - JavascriptExceptionObject *exception = nullptr; + void *tryContinuation = nullptr; + JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; + Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)localsPtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize); - try { + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { #if defined(_M_ARM) tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #elif defined(_M_ARM64) tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #endif - } - catch (const Js::JavascriptException& err) - { - exception = err.GetAndClear(); - } - - if (exception) - { -#if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } } - else + catch (const Js::JavascriptException& err) { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr); - } + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); } -#endif + } + if (exception) + { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } + bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative if (hasBailedOut) { @@ -437,26 +441,36 @@ namespace Js void *tryContinuation = nullptr; void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize); - - try { + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + + try + { #if defined(_M_ARM) - tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); + tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #elif defined(_M_ARM64) - tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); + tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #endif + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch (const Js::JavascriptException& err) - { - exception = err.GetAndClear(); - } - if (exception) { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } } #if defined(_M_ARM) @@ -472,6 +486,10 @@ namespace Js if (exception) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(exception, scriptContext); } @@ -647,21 +665,24 @@ namespace Js { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; + void* tryHandlerAddrOfReturnAddr = nullptr; Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)framePtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout); - - try { - // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function - // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. - // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). - // In order to circumvent that we are throwing OutOfMemory. - if (!tryAddr) + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try { - Assert(false); - ThrowOutOfMemory(scriptContext); - } + // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function + // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. + // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). + // In order to circumvent that we are throwing OutOfMemory. + if (!tryAddr) + { + Assert(false); + ThrowOutOfMemory(scriptContext); + } #ifdef _M_IX86 void *savedEsp; @@ -711,28 +732,19 @@ namespace Js #else AssertMsg(FALSE, "Unsupported native try-finally handler"); #endif + } + catch (const Js::JavascriptException& err) + { + pExceptionObject = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch(const Js::JavascriptException& err) - { - pExceptionObject = err.GetAndClear(); - } - if (pExceptionObject) { #if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } - } - else + if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) { - if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr); - } + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); } #endif // Clone static exception object early in case finally block overwrites it @@ -817,79 +829,91 @@ namespace Js { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; + void * tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout); - - try { - // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function - // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. - // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). - // In order to circumvent that we are throwing OutOfMemory. - if (!tryAddr) + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + + try { - Assert(false); - ThrowOutOfMemory(scriptContext); - } + // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function + // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. + // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). + // In order to circumvent that we are throwing OutOfMemory. + if (!tryAddr) + { + Assert(false); + ThrowOutOfMemory(scriptContext); + } #ifdef _M_IX86 - void *savedEsp; - __asm - { - // Save and restore the callee-saved registers around the call. - // TODO: track register kills by region and generate per-region prologs and epilogs - push esi - push edi - push ebx + void *savedEsp; + __asm + { + // Save and restore the callee-saved registers around the call. + // TODO: track register kills by region and generate per-region prologs and epilogs + push esi + push edi + push ebx - // 8-byte align frame to improve floating point perf of our JIT'd code. - // Save ESP - mov ecx, esp - mov savedEsp, ecx - and esp, -8 + // 8-byte align frame to improve floating point perf of our JIT'd code. + // Save ESP + mov ecx, esp + mov savedEsp, ecx + and esp, -8 - // Set up the call target, save the current frame ptr, and adjust the frame to access - // locals in native code. - mov eax, tryAddr + // Set up the call target, save the current frame ptr, and adjust the frame to access + // locals in native code. + mov eax, tryAddr #if 0 && defined(_CONTROL_FLOW_GUARD) - // verify that the call target is valid - mov ebx, eax; save call target - mov ecx, eax - call[__guard_check_icall_fptr] - mov eax, ebx; restore call target + // verify that the call target is valid + mov ebx, eax; save call target + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ebx; restore call target #endif - push ebp - mov ebp, framePtr - call eax - pop ebp + push ebp + mov ebp, framePtr + call eax + pop ebp - // The native code gives us the address where execution should continue on exit - // from the region. - mov continuationAddr, eax + // The native code gives us the address where execution should continue on exit + // from the region. + mov continuationAddr, eax - // Restore ESP - mov ecx, savedEsp - mov esp, ecx + // Restore ESP + mov ecx, savedEsp + mov esp, ecx - pop ebx - pop edi - pop esi - } + pop ebx + pop edi + pop esi + } #else - AssertMsg(FALSE, "Unsupported native try-finally handler"); + AssertMsg(FALSE, "Unsupported native try-finally handler"); #endif + } + catch (const Js::JavascriptException& err) + { + pExceptionObject = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch (const Js::JavascriptException& err) - { - pExceptionObject = err.GetAndClear(); - } - if (pExceptionObject) { // Clone static exception object early in case finally block overwrites it pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext); + +#if ENABLE_NATIVE_CODEGEN + if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } +#endif } void* newContinuationAddr = NULL; @@ -952,6 +976,10 @@ namespace Js if (pExceptionObject) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); }