Skip to content

Commit d7d99e1

Browse files
author
meg-gupta
committed
[1.8>1.9] [MERGE #4535 @meg-gupta] Fix setting hasBailout when there are inlined functions in try/catch
Merge pull request #4535 from meg-gupta:fixCatchEatingUpEx Fixes OS#15078638 When we bailout executing trycode from within OP_TryCatch, we complete the execution of the current function enclosing the try/catch in the interpreter. If there was an exception within the try region, it is caught and handled accordingly in ProcessTryHandlerBailOut which reconstructs try/catch/finally frames when we bailout midway executing code within OP_TryCatch. If there was an exception outside the try region, the catch of the OP_TryCatch ends up catching it, because it happens to be on the callstack. For this we use the hasBailOut bit which is per function, so we know that this exception has to be passed above. When we have inlined functions inside the try, and for bailouts inside the inlined code, we do not set the hasBailedOut bit, so that the enclosing functions catch in OP_TryCatch catches it. But when we bailout from inlined code inside try, we execute inlined code, as well as the enclosing function's code in the interpreter. We will be execution code past the try/catch of the current function in the interpreter. Now if any code outside the eh region throws, we will catch that in OP_TryCatch which happens to be on the callstack. And we will end up handling it instead of passing above because we have not set the hasBailedOutBit from the bailout point. This change fixes this issue. We pass a pointer to the hasBailedOutBit and set it once we have finished executing the inlined frames and are ready to process the interpreter frame of the current function.
2 parents 7c4f6a5 + 6c055d9 commit d7d99e1

File tree

7 files changed

+106
-5
lines changed

7 files changed

+106
-5
lines changed

Diff for: lib/Backend/BailOut.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,12 @@ BailOutRecord::BailOutInlinedCommon(Js::JavascriptCallStackLayout * layout, Bail
12171217
BailOutReturnValue bailOutReturnValue;
12181218
Js::ScriptFunction * innerMostInlinee = nullptr;
12191219
BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, false, branchValue);
1220+
1221+
bool * hasBailOutBit = layout->functionObject->GetScriptContext()->GetThreadContext()->GetHasBailedOutBitPtr();
1222+
if (hasBailOutBit != nullptr && bailOutRecord->ehBailoutData)
1223+
{
1224+
*hasBailOutBit = true;
1225+
}
12201226
Js::Var result = BailOutCommonNoCodeGen(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset, returnAddress, bailOutKind, branchValue,
12211227
registerSaves, &bailOutReturnValue);
12221228
ScheduleFunctionCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind, bailOutOffset, savedImplicitCallFlags, returnAddress);
@@ -1255,6 +1261,11 @@ BailOutRecord::BailOutFromLoopBodyInlinedCommon(Js::JavascriptCallStackLayout *
12551261
BailOutReturnValue bailOutReturnValue;
12561262
Js::ScriptFunction * innerMostInlinee = nullptr;
12571263
BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, true, branchValue);
1264+
bool * hasBailOutBit = layout->functionObject->GetScriptContext()->GetThreadContext()->GetHasBailedOutBitPtr();
1265+
if (hasBailOutBit != nullptr && bailOutRecord->ehBailoutData)
1266+
{
1267+
*hasBailOutBit = true;
1268+
}
12581269
uint32 result = BailOutFromLoopBodyHelper(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset,
12591270
bailOutKind, nullptr, registerSaves, &bailOutReturnValue);
12601271
ScheduleLoopBodyCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind);

Diff for: lib/Runtime/Base/ThreadContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager,
9393
stackProber(nullptr),
9494
isThreadBound(false),
9595
hasThrownPendingException(false),
96+
hasBailedOutBitPtr(nullptr),
9697
pendingFinallyException(nullptr),
9798
noScriptScope(false),
9899
heapEnum(nullptr),

Diff for: lib/Runtime/Base/ThreadContext.h

+11
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ class ThreadContext sealed :
638638
StackProber * stackProber;
639639
bool isThreadBound;
640640
bool hasThrownPendingException;
641+
bool * hasBailedOutBitPtr;
641642
bool callDispose;
642643
#if ENABLE_JS_REENTRANCY_CHECK
643644
bool noJsReentrancy;
@@ -1531,6 +1532,16 @@ class ThreadContext sealed :
15311532
this->hasThrownPendingException = true;
15321533
}
15331534

1535+
bool * GetHasBailedOutBitPtr()
1536+
{
1537+
return this->hasBailedOutBitPtr;
1538+
}
1539+
1540+
void SetHasBailedOutBitPtr(bool *setValue)
1541+
{
1542+
this->hasBailedOutBitPtr = setValue;
1543+
}
1544+
15341545
void SetRecordedException(Js::JavascriptExceptionObject* exceptionObject, bool propagateToDebugger = false)
15351546
{
15361547
this->recyclableData->exceptionObject = exceptionObject;

Diff for: lib/Runtime/Language/JavascriptExceptionOperators.cpp

+28-5
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,11 @@ namespace Js
9494
void *continuation = nullptr;
9595
JavascriptExceptionObject *exception = nullptr;
9696
void *tryCatchFrameAddr = nullptr;
97+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)frame + hasBailedOutOffset));
98+
9799
PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
98100
{
99101
Js::JavascriptExceptionOperators::TryCatchFrameAddrStack tryCatchFrameAddrStack(scriptContext, frame);
100-
101102
try
102103
{
103104
Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
@@ -129,18 +130,22 @@ namespace Js
129130

130131
exception = exception->CloneIfStaticExceptionObject(scriptContext);
131132
bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative
133+
// 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
134+
// 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
132135
if (hasBailedOut)
133136
{
134137
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
135138
// it so happens that this catch was on the stack and caught the exception.
136139
// Re-throw!
140+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
137141
JavascriptExceptionOperators::DoThrow(exception, scriptContext);
138-
}
142+
}
143+
139144
Var exceptionObject = exception->GetThrownObject(scriptContext);
140145
AssertMsg(exceptionObject, "Caught object is null.");
141146
continuation = amd64_CallWithFakeFrame(catchAddr, frame, spillSize, argsSize, exceptionObject);
142147
}
143-
148+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
144149
return continuation;
145150
}
146151

@@ -154,6 +159,7 @@ namespace Js
154159
{
155160
void *tryContinuation = nullptr;
156161
JavascriptExceptionObject *exception = nullptr;
162+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)frame + hasBailedOutOffset));
157163

158164
PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
159165

@@ -189,6 +195,7 @@ namespace Js
189195
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
190196
// it so happens that this catch was on the stack and caught the exception.
191197
// Re-throw!
198+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
192199
JavascriptExceptionOperators::DoThrow(exception, scriptContext);
193200
}
194201

@@ -197,6 +204,7 @@ namespace Js
197204
return continuation;
198205
}
199206

207+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
200208
return tryContinuation;
201209
}
202210

@@ -250,6 +258,7 @@ namespace Js
250258
void *continuation = nullptr;
251259
JavascriptExceptionObject *exception = nullptr;
252260
void * tryCatchFrameAddr = nullptr;
261+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)localsPtr + hasBailedOutOffset));
253262

254263
PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
255264
{
@@ -295,8 +304,10 @@ namespace Js
295304
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
296305
// it so happens that this catch was on the stack and caught the exception.
297306
// Re-throw!
307+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
298308
JavascriptExceptionOperators::DoThrow(exception, scriptContext);
299309
}
310+
300311
Var exceptionObject = exception->GetThrownObject(scriptContext);
301312
AssertMsg(exceptionObject, "Caught object is null.");
302313
#if defined(_M_ARM)
@@ -306,6 +317,7 @@ namespace Js
306317
#endif
307318
}
308319

320+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
309321
return continuation;
310322
}
311323

@@ -320,9 +332,9 @@ namespace Js
320332
{
321333
void *tryContinuation = nullptr;
322334
JavascriptExceptionObject *exception = nullptr;
335+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)localsPtr + hasBailedOutOffset));
323336

324337
PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
325-
326338
try
327339
{
328340
#if defined(_M_ARM)
@@ -355,8 +367,10 @@ namespace Js
355367
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
356368
// it so happens that this catch was on the stack and caught the exception.
357369
// Re-throw!
370+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
358371
JavascriptExceptionOperators::DoThrow(exception, scriptContext);
359372
}
373+
360374
scriptContext->GetThreadContext()->SetPendingFinallyException(exception);
361375
#if defined(_M_ARM)
362376
void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
@@ -366,6 +380,7 @@ namespace Js
366380
return finallyContinuation;
367381
}
368382

383+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
369384
return tryContinuation;
370385
}
371386

@@ -429,6 +444,7 @@ namespace Js
429444
void* continuationAddr = NULL;
430445
Js::JavascriptExceptionObject* pExceptionObject = NULL;
431446
void *tryCatchFrameAddr = nullptr;
447+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)framePtr + hasBailedOutOffset));
432448

433449
PROBE_STACK(scriptContext, Constants::MinStackDefault);
434450
{
@@ -526,8 +542,10 @@ namespace Js
526542
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
527543
// it so happens that this catch was on the stack and caught the exception.
528544
// Re-throw!
545+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
529546
JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
530547
}
548+
531549
Var catchObject = pExceptionObject->GetThrownObject(scriptContext);
532550
AssertMsg(catchObject, "Caught object is NULL");
533551
#ifdef _M_IX86
@@ -581,14 +599,15 @@ namespace Js
581599
#endif
582600
}
583601

602+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
584603
return continuationAddr;
585604
}
586605

587606
void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext)
588607
{
589608
Js::JavascriptExceptionObject* pExceptionObject = NULL;
590609
void* continuationAddr = NULL;
591-
610+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr((bool*)((char*)framePtr + hasBailedOutOffset));
592611
PROBE_STACK(scriptContext, Constants::MinStackDefault);
593612

594613
try
@@ -676,8 +695,10 @@ namespace Js
676695
// If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
677696
// it so happens that this catch was on the stack and caught the exception.
678697
// Re-throw!
698+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
679699
JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
680700
}
701+
681702
scriptContext->GetThreadContext()->SetPendingFinallyException(pExceptionObject);
682703

683704
void* newContinuationAddr = NULL;
@@ -733,6 +754,8 @@ namespace Js
733754
#endif
734755
return newContinuationAddr;
735756
}
757+
758+
scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr);
736759
return continuationAddr;
737760
}
738761

Diff for: test/EH/hasBailedOutBug.baseline

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TypeError: Assignment to read-only properties is not allowed in strict mode

Diff for: test/EH/hasBailedOutBug.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
var shouldBailout = false;
7+
function test0() {
8+
var obj0 = {};
9+
var func0 = function () {
10+
};
11+
var func1 = function () {
12+
(function () {
13+
'use strict';
14+
try {
15+
function func8() {
16+
obj0.prop2;
17+
}
18+
var uniqobj4 = func8();
19+
} catch (ex) {
20+
return 'somestring';
21+
} finally {
22+
}
23+
func0(ary.push(ary.unshift(Object.prototype.length = protoObj0)));
24+
}(shouldBailout ? (Object.defineProperty(Object.prototype, 'length', {
25+
get: function () {
26+
}
27+
})) : arguments));
28+
};
29+
var ary = Array();
30+
var protoObj0 = Object();
31+
({
32+
prop7: shouldBailout ? (Object.defineProperty(obj0, 'prop2', {
33+
set: function () {
34+
}
35+
})) : Object
36+
});
37+
for (; func1(); ) {
38+
}
39+
}
40+
test0();
41+
test0();
42+
shouldBailout = true;
43+
try {
44+
test0();
45+
}
46+
catch(ex) {
47+
print(ex);
48+
}

Diff for: test/EH/rlexe.xml

+6
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,10 @@
176176
<compile-flags> -force:inline </compile-flags>
177177
</default>
178178
</test>
179+
<test>
180+
<default>
181+
<files>hasBailedOutBug.js</files>
182+
<baseline>hasBailedOutBug.baseline</baseline>
183+
</default>
184+
</test>
179185
</regress-exe>

0 commit comments

Comments
 (0)