diff --git a/deps/chakrashim/core/lib/Backend/BailOut.cpp b/deps/chakrashim/core/lib/Backend/BailOut.cpp index 50c28e157a1..35ee85856f4 100644 --- a/deps/chakrashim/core/lib/Backend/BailOut.cpp +++ b/deps/chakrashim/core/lib/Backend/BailOut.cpp @@ -1217,6 +1217,12 @@ BailOutRecord::BailOutInlinedCommon(Js::JavascriptCallStackLayout * layout, Bail BailOutReturnValue bailOutReturnValue; Js::ScriptFunction * innerMostInlinee = nullptr; BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, false, branchValue); + + bool * hasBailOutBit = layout->functionObject->GetScriptContext()->GetThreadContext()->GetHasBailedOutBitPtr(); + if (hasBailOutBit != nullptr && bailOutRecord->ehBailoutData) + { + *hasBailOutBit = true; + } Js::Var result = BailOutCommonNoCodeGen(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset, returnAddress, bailOutKind, branchValue, registerSaves, &bailOutReturnValue); ScheduleFunctionCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind, bailOutOffset, savedImplicitCallFlags, returnAddress); @@ -1255,6 +1261,11 @@ BailOutRecord::BailOutFromLoopBodyInlinedCommon(Js::JavascriptCallStackLayout * BailOutReturnValue bailOutReturnValue; Js::ScriptFunction * innerMostInlinee = nullptr; BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, true, branchValue); + bool * hasBailOutBit = layout->functionObject->GetScriptContext()->GetThreadContext()->GetHasBailedOutBitPtr(); + if (hasBailOutBit != nullptr && bailOutRecord->ehBailoutData) + { + *hasBailOutBit = true; + } uint32 result = BailOutFromLoopBodyHelper(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset, bailOutKind, nullptr, registerSaves, &bailOutReturnValue); ScheduleLoopBodyCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind); diff --git a/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.cpp b/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.cpp index 9cec9aa0b35..40413025d4a 100644 --- a/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.cpp +++ b/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.cpp @@ -93,6 +93,7 @@ ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager, stackProber(nullptr), isThreadBound(false), hasThrownPendingException(false), + hasBailedOutBitPtr(nullptr), pendingFinallyException(nullptr), noScriptScope(false), heapEnum(nullptr), diff --git a/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.h b/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.h index 668b141efa8..79ead8092d5 100644 --- a/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.h +++ b/deps/chakrashim/core/lib/Runtime/Base/ThreadContext.h @@ -638,6 +638,7 @@ class ThreadContext sealed : StackProber * stackProber; bool isThreadBound; bool hasThrownPendingException; + bool * hasBailedOutBitPtr; bool callDispose; #if ENABLE_JS_REENTRANCY_CHECK bool noJsReentrancy; @@ -1531,6 +1532,16 @@ class ThreadContext sealed : this->hasThrownPendingException = true; } + bool * GetHasBailedOutBitPtr() + { + return this->hasBailedOutBitPtr; + } + + void SetHasBailedOutBitPtr(bool *setValue) + { + this->hasBailedOutBitPtr = setValue; + } + void SetRecordedException(Js::JavascriptExceptionObject* exceptionObject, bool propagateToDebugger = false) { this->recyclableData->exceptionObject = exceptionObject; diff --git a/deps/chakrashim/core/lib/Runtime/Language/JavascriptExceptionOperators.cpp b/deps/chakrashim/core/lib/Runtime/Language/JavascriptExceptionOperators.cpp index a850feea9c1..452bd169686 100644 --- a/deps/chakrashim/core/lib/Runtime/Language/JavascriptExceptionOperators.cpp +++ b/deps/chakrashim/core/lib/Runtime/Language/JavascriptExceptionOperators.cpp @@ -94,10 +94,11 @@ namespace Js void *continuation = nullptr; JavascriptExceptionObject *exception = nullptr; void *tryCatchFrameAddr = nullptr; + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)frame + hasBailedOutOffset)); + PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize); { Js::JavascriptExceptionOperators::TryCatchFrameAddrStack tryCatchFrameAddrStack(scriptContext, frame); - try { Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext); @@ -129,18 +130,22 @@ namespace Js exception = exception->CloneIfStaticExceptionObject(scriptContext); bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative + // If an inlinee bailed out due to some reason, the execution of the current function enclosing the try catch will also continue in the interpreter + // During execution in the interpreter, if we throw outside the region enclosed in try/catch, this catch ends up catching that exception because its present on the call stack if (hasBailedOut) { // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(exception, scriptContext); - } + } + Var exceptionObject = exception->GetThrownObject(scriptContext); AssertMsg(exceptionObject, "Caught object is null."); continuation = amd64_CallWithFakeFrame(catchAddr, frame, spillSize, argsSize, exceptionObject); } - + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return continuation; } @@ -154,6 +159,7 @@ namespace Js { void *tryContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)frame + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize); @@ -189,6 +195,7 @@ namespace Js // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(exception, scriptContext); } @@ -197,6 +204,7 @@ namespace Js return continuation; } + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return tryContinuation; } @@ -250,6 +258,7 @@ namespace Js void *continuation = nullptr; JavascriptExceptionObject *exception = nullptr; void * tryCatchFrameAddr = nullptr; + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)localsPtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize); { @@ -295,8 +304,10 @@ namespace Js // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(exception, scriptContext); } + Var exceptionObject = exception->GetThrownObject(scriptContext); AssertMsg(exceptionObject, "Caught object is null."); #if defined(_M_ARM) @@ -306,6 +317,7 @@ namespace Js #endif } + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return continuation; } @@ -320,9 +332,9 @@ namespace Js { void *tryContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)localsPtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize); - try { #if defined(_M_ARM) @@ -355,8 +367,10 @@ namespace Js // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(exception, scriptContext); } + scriptContext->GetThreadContext()->SetPendingFinallyException(exception); #if defined(_M_ARM) void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); @@ -366,6 +380,7 @@ namespace Js return finallyContinuation; } + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return tryContinuation; } @@ -429,6 +444,7 @@ namespace Js void* continuationAddr = NULL; Js::JavascriptExceptionObject* pExceptionObject = NULL; void *tryCatchFrameAddr = nullptr; + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)framePtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackDefault); { @@ -526,8 +542,10 @@ namespace Js // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); } + Var catchObject = pExceptionObject->GetThrownObject(scriptContext); AssertMsg(catchObject, "Caught object is NULL"); #ifdef _M_IX86 @@ -581,6 +599,7 @@ namespace Js #endif } + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return continuationAddr; } @@ -588,7 +607,7 @@ namespace Js { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; - + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)framePtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackDefault); try @@ -676,8 +695,10 @@ namespace Js // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; // it so happens that this catch was on the stack and caught the exception. // Re-throw! + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); } + scriptContext->GetThreadContext()->SetPendingFinallyException(pExceptionObject); void* newContinuationAddr = NULL; @@ -733,6 +754,8 @@ namespace Js #endif return newContinuationAddr; } + + scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); return continuationAddr; } diff --git a/deps/chakrashim/core/test/EH/hasBailedOutBug.baseline b/deps/chakrashim/core/test/EH/hasBailedOutBug.baseline new file mode 100644 index 00000000000..3a45d1d2cc9 --- /dev/null +++ b/deps/chakrashim/core/test/EH/hasBailedOutBug.baseline @@ -0,0 +1 @@ +TypeError: Assignment to read-only properties is not allowed in strict mode diff --git a/deps/chakrashim/core/test/EH/hasBailedOutBug.js b/deps/chakrashim/core/test/EH/hasBailedOutBug.js new file mode 100644 index 00000000000..611118be580 --- /dev/null +++ b/deps/chakrashim/core/test/EH/hasBailedOutBug.js @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +var shouldBailout = false; +function test0() { + var obj0 = {}; + var func0 = function () { + }; + var func1 = function () { + (function () { + 'use strict'; + try { + function func8() { + obj0.prop2; + } + var uniqobj4 = func8(); + } catch (ex) { + return 'somestring'; + } finally { + } + func0(ary.push(ary.unshift(Object.prototype.length = protoObj0))); + }(shouldBailout ? (Object.defineProperty(Object.prototype, 'length', { + get: function () { + } + })) : arguments)); + }; + var ary = Array(); + var protoObj0 = Object(); + ({ + prop7: shouldBailout ? (Object.defineProperty(obj0, 'prop2', { + set: function () { + } + })) : Object + }); + for (; func1(); ) { + } +} +test0(); +test0(); +shouldBailout = true; +try { + test0(); +} +catch(ex) { + print(ex); +} diff --git a/deps/chakrashim/core/test/EH/rlexe.xml b/deps/chakrashim/core/test/EH/rlexe.xml index af75ca7873e..41009e625de 100644 --- a/deps/chakrashim/core/test/EH/rlexe.xml +++ b/deps/chakrashim/core/test/EH/rlexe.xml @@ -176,4 +176,10 @@ -force:inline + + + hasBailedOutBug.js + hasBailedOutBug.baseline + +