Skip to content

Commit b04b132

Browse files
committed
Solve an AST anomaly in rare redeferral cases. We may redefer a nested function but choose not to defer it when we recompile the enclosing function. In such a case, the existing nested FunctionProxy is a compact ParseableFunctionInfo, not the full FunctionBody the front end expects to generate. We were keeping the compact structure and discarding the AST subtree belonging to the nested function, but this seems to be producing anomalous AST's that cause problems downstream. Generate the full FunctionBody on the fly instead.
1 parent 341a8c9 commit b04b132

File tree

3 files changed

+42
-26
lines changed

3 files changed

+42
-26
lines changed

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,28 +2132,7 @@ namespace Js
21322132
bool isDebugOrAsmJsReparse = false;
21332133
FunctionBody* funcBody = nullptr;
21342134

2135-
// If we throw or fail with the function body in an unfinished state, make sure the function info is still
2136-
// pointing to the old ParseableFunctionInfo and has the right attributes.
2137-
class AutoRestoreFunctionInfo {
2138-
public:
2139-
AutoRestoreFunctionInfo(ParseableFunctionInfo *pfi) : pfi(pfi), funcBody(nullptr) {}
2140-
~AutoRestoreFunctionInfo() {
2141-
if (this->pfi != nullptr && this->pfi->GetFunctionInfo()->GetFunctionProxy() != this->pfi)
2142-
{
2143-
FunctionInfo *functionInfo = this->pfi->functionInfo;
2144-
functionInfo->SetAttributes(
2145-
(FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
2146-
functionInfo->SetFunctionProxy(this->pfi);
2147-
functionInfo->SetOriginalEntryPoint(DefaultEntryThunk);
2148-
}
2149-
2150-
Assert(this->pfi == nullptr || (this->pfi->GetFunctionInfo()->GetFunctionProxy() == this->pfi && !this->pfi->IsFunctionBody()));
2151-
}
2152-
void Clear() { pfi = nullptr; funcBody = nullptr; }
2153-
2154-
ParseableFunctionInfo * pfi;
2155-
FunctionBody * funcBody;
2156-
} autoRestoreFunctionInfo(this);
2135+
AutoRestoreFunctionInfo autoRestoreFunctionInfo(this, DefaultEntryThunk);
21572136

21582137
// If m_hasBeenParsed = true, one of the following things happened:
21592138
// - We had multiple function objects which were all defer-parsed, but with the same function body and one of them

lib/Runtime/Base/FunctionBody.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3669,6 +3669,30 @@ namespace Js
36693669
StatementAdjustmentRecordList* GetStatementAdjustmentRecords();
36703670
};
36713671

3672+
class AutoRestoreFunctionInfo {
3673+
public:
3674+
AutoRestoreFunctionInfo(ParseableFunctionInfo *pfi, const JavascriptMethod originalEntryPoint) : pfi(pfi), funcBody(nullptr), originalEntryPoint(originalEntryPoint) {}
3675+
~AutoRestoreFunctionInfo() {
3676+
if (this->pfi != nullptr && this->pfi->GetFunctionInfo()->GetFunctionProxy() != this->pfi)
3677+
{
3678+
FunctionInfo *functionInfo = this->pfi->GetFunctionInfo();
3679+
functionInfo->SetAttributes(
3680+
(FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
3681+
functionInfo->SetFunctionProxy(this->pfi);
3682+
functionInfo->SetOriginalEntryPoint(originalEntryPoint);
3683+
}
3684+
3685+
Assert(this->pfi == nullptr || (this->pfi->GetFunctionInfo()->GetFunctionProxy() == this->pfi && !this->pfi->IsFunctionBody()));
3686+
}
3687+
void Clear() { pfi = nullptr; funcBody = nullptr; }
3688+
3689+
ParseableFunctionInfo * pfi;
3690+
FunctionBody * funcBody;
3691+
const JavascriptMethod originalEntryPoint;
3692+
};
3693+
3694+
// If we throw or fail with the function body in an unfinished state, make sure the function info is still
3695+
// pointing to the old ParseableFunctionInfo and has the right attributes.
36723696
typedef SynchronizableList<FunctionBody*, JsUtil::List<FunctionBody*, ArenaAllocator, false, Js::FreeListedRemovePolicy> > FunctionBodyList;
36733697

36743698
struct ScopeSlots

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,8 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
11681168
bool funcExprWithName;
11691169
Js::ParseableFunctionInfo* parseableFunctionInfo = nullptr;
11701170

1171+
Js::AutoRestoreFunctionInfo autoRestoreFunctionInfo(reuseNestedFunc, reuseNestedFunc ? reuseNestedFunc->GetOriginalEntryPoint() : nullptr);
1172+
11711173
if (this->pCurrentFunction &&
11721174
this->pCurrentFunction->IsFunctionParsed())
11731175
{
@@ -1228,7 +1230,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
12281230
// Create a function body if:
12291231
// 1. The parse node is not defer parsed
12301232
// 2. Or creating function proxies is disallowed
1231-
bool createFunctionBody = (pnode->sxFnc.pnodeBody != nullptr) && (!reuseNestedFunc || reuseNestedFunc->IsFunctionBody());
1233+
bool createFunctionBody = (pnode->sxFnc.pnodeBody != nullptr);
12321234
if (!CONFIG_FLAG(CreateFunctionProxy)) createFunctionBody = true;
12331235

12341236
Js::FunctionInfo::Attributes attributes = Js::FunctionInfo::Attributes::None;
@@ -1278,8 +1280,17 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
12781280
propertyRecordList = EnsurePropertyRecordList();
12791281
if (reuseNestedFunc)
12801282
{
1281-
Assert(reuseNestedFunc->IsFunctionBody());
1282-
parseableFunctionInfo = reuseNestedFunc->GetFunctionBody();
1283+
if (!reuseNestedFunc->IsFunctionBody())
1284+
{
1285+
Js::FunctionBody * parsedFunctionBody =
1286+
Js::FunctionBody::NewFromParseableFunctionInfo(reuseNestedFunc->GetParseableFunctionInfo());
1287+
autoRestoreFunctionInfo.funcBody = parsedFunctionBody;
1288+
parseableFunctionInfo = parsedFunctionBody;
1289+
}
1290+
else
1291+
{
1292+
parseableFunctionInfo = reuseNestedFunc->GetFunctionBody();
1293+
}
12831294
}
12841295
else
12851296
{
@@ -1309,7 +1320,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
13091320
if (reuseNestedFunc)
13101321
{
13111322
Assert(!reuseNestedFunc->IsFunctionBody() || reuseNestedFunc->GetFunctionBody()->GetByteCode() != nullptr);
1312-
pnode->sxFnc.pnodeBody = nullptr;
1323+
Assert(pnode->sxFnc.pnodeBody == nullptr);
13131324
parseableFunctionInfo = reuseNestedFunc;
13141325
}
13151326
else
@@ -1437,6 +1448,8 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
14371448
this->maxAstSize = currentAstSize;
14381449
}
14391450

1451+
autoRestoreFunctionInfo.Clear();
1452+
14401453
return funcInfo;
14411454
}
14421455

0 commit comments

Comments
 (0)