Skip to content

Commit 310dd0a

Browse files
committed
OS#16050168: fix jit crash for splitscope using 'with' statements
When 'with' statements are used in inside param scopes when param and body scopes are split, there will not always be a local closure generated. See attached test for an example case. The IRBuilder would attempt to build a RegOpnd for the closure, and would assert on a nullptr sym. The backward pass would later AV on that nullptr sym. This commit instead assigns nullptr to the param closure when there is no local closure, which is the behavior of InterpreterStackFrame::OP_BeginBodyScope()
1 parent 6d2cd72 commit 310dd0a

File tree

3 files changed

+42
-2
lines changed

3 files changed

+42
-2
lines changed

lib/Backend/IRBuilder.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6937,23 +6937,34 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
69376937
break;
69386938

69396939
case Js::OpCode::BeginBodyScope:
6940+
{
69406941
// This marks the end of a param socpe which is not merged with body scope.
69416942
// So we have to first cache the closure so that we can use it to copy the initial values for
69426943
// body syms from corresponding param syms (LdParamSlot). Body should get its own scope slot.
69436944
Assert(!this->IsParamScopeDone());
69446945
this->SetParamScopeDone();
69456946

6947+
IR::Opnd * localClosureOpnd;
6948+
if (this->m_func->GetLocalClosureSym() != nullptr)
6949+
{
6950+
localClosureOpnd = IR::RegOpnd::New(this->m_func->GetLocalClosureSym(), TyVar, this->m_func);
6951+
}
6952+
else
6953+
{
6954+
localClosureOpnd = IR::IntConstOpnd::New(0, TyVar, this->m_func);
6955+
}
6956+
69466957
this->AddInstr(
69476958
IR::Instr::New(
69486959
Js::OpCode::Ld_A,
69496960
this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetParamClosureReg()),
6950-
IR::RegOpnd::New(this->m_func->GetLocalClosureSym(), TyVar, this->m_func),
6961+
localClosureOpnd,
69516962
this->m_func),
69526963
offset);
69536964

69546965
// Create a new local closure for the body when either body scope has scope slots allocated or
69556966
// eval is present which can leak declarations.
6956-
if (this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() > 0 || this->m_func->GetJITFunctionBody()->HasScopeObject())
6967+
if (this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() > 0 || this->m_func->GetJITFunctionBody()->HasScopeObject())
69576968
{
69586969
if (this->m_func->GetJITFunctionBody()->HasScopeObject())
69596970
{
@@ -6994,6 +7005,7 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
69947005
lfd->isNonFastPathFrameDisplay = true;
69957006
}
69967007
break;
7008+
}
69977009

69987010
default:
69997011
this->AddInstr(instr, offset);

test/Bugs/rlexe.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,4 +496,9 @@
496496
<compile-flags>-force:deferparse -parserstatecache -useparserstatecache</compile-flags>
497497
</default>
498498
</test>
499+
<test>
500+
<default>
501+
<files>withSplitScope.js</files>
502+
</default>
503+
</test>
499504
</regress-exe>

test/Bugs/withSplitScope.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
function foo()
7+
{
8+
(function bar(a =
9+
(function()
10+
{
11+
with (1)
12+
{
13+
bar;
14+
}
15+
})()
16+
){})();
17+
}
18+
19+
foo();
20+
foo();
21+
foo();
22+
23+
WScript.Echo("Pass");

0 commit comments

Comments
 (0)