Skip to content

Commit 16fc8ac

Browse files
committed
[MERGE #6117 @atulkatti] Cleanup parser arena in more module error cases. Also, make sure we clean up parent and child modules where applicable.
Merge pull request #6117 from atulkatti:ModulesCleanup.1
2 parents 49f594b + 287b269 commit 16fc8ac

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

lib/Runtime/Language/SourceTextModuleRecord.cpp

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ namespace Js
172172
}
173173

174174
// Cleanup in case of error.
175-
this->ReleaseParserResources();
175+
this->ReleaseParserResourcesForHierarchy();
176176
return E_FAIL;
177177
}
178178
}
@@ -189,6 +189,8 @@ namespace Js
189189
}
190190
*exceptionVar = JavascriptError::CreateFromCompileScriptException(scriptContext, &se, sourceUrl);
191191
}
192+
// Cleanup in case of error.
193+
this->ReleaseParserResourcesForHierarchy();
192194
if (this->parser)
193195
{
194196
this->parseTree = nullptr;
@@ -247,7 +249,7 @@ namespace Js
247249
{
248250
Assert(scriptContext->GetConfig()->IsES6ModuleEnabled());
249251
ParseNodeModule* moduleParseNode = this->parseTree->AsParseNodeModule();
250-
SetrequestedModuleList(moduleParseNode->requestedModules);
252+
SetRequestedModuleList(moduleParseNode->requestedModules);
251253
SetImportRecordList(moduleParseNode->importEntries);
252254
SetStarExportRecordList(moduleParseNode->starExportEntries);
253255
SetIndirectExportRecordList(moduleParseNode->indirectExportEntries);
@@ -262,6 +264,19 @@ namespace Js
262264
}
263265
}
264266

267+
void SourceTextModuleRecord::ReleaseParserResourcesForHierarchy()
268+
{
269+
this->ReleaseParserResources();
270+
271+
if (this->childrenModuleSet != nullptr)
272+
{
273+
this->childrenModuleSet->EachValue([=](SourceTextModuleRecord* childModuleRecord)
274+
{
275+
childModuleRecord->ReleaseParserResources();
276+
});
277+
}
278+
}
279+
265280
HRESULT SourceTextModuleRecord::PostParseProcess()
266281
{
267282
HRESULT hr = NOERROR;
@@ -276,7 +291,7 @@ namespace Js
276291
else
277292
{
278293
// Cleanup in case of error.
279-
this->ReleaseParserResources();
294+
this->ReleaseParserResourcesForHierarchy();
280295
}
281296

282297
return hr;
@@ -306,7 +321,7 @@ namespace Js
306321
if (this->errorObject != nullptr)
307322
{
308323
// Cleanup in case of error.
309-
this->ReleaseParserResources();
324+
this->ReleaseParserResourcesForHierarchy();
310325
SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, this->errorObject, scriptContext, this, false);
311326
}
312327
else
@@ -327,7 +342,7 @@ namespace Js
327342
if (FAILED(hr))
328343
{
329344
// Cleanup in case of error.
330-
this->ReleaseParserResources();
345+
this->ReleaseParserResourcesForHierarchy();
331346

332347
// We cannot just use the buffer in the specifier string - need to make a copy here.
333348
const char16* moduleName = this->GetSpecifierSz();
@@ -366,6 +381,13 @@ namespace Js
366381
{
367382
GenerateRootFunction();
368383
}
384+
385+
if (this->errorObject != nullptr)
386+
{
387+
// Cleanup in case of error.
388+
this->ReleaseParserResourcesForHierarchy();
389+
}
390+
369391
if (!hadNotifyHostReady && !WasEvaluated())
370392
{
371393
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyHostAboutModuleReady %s (PrepareForModuleDeclarationInitialization)\n"), this->GetSpecifierSz());
@@ -394,7 +416,7 @@ namespace Js
394416
}
395417

396418
// Cleanup in case of error.
397-
this->ReleaseParserResources();
419+
this->ReleaseParserResourcesForHierarchy();
398420

399421
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyParentAsNeeded (childException)\n"), this->GetSpecifierSz());
400422
NotifyParentsAsNeeded();
@@ -632,6 +654,7 @@ namespace Js
632654
SourceTextModuleRecord* childModuleRecord = GetChildModuleRecord(exportEntry.moduleRequest->Psz());
633655
if (childModuleRecord == nullptr)
634656
{
657+
this->ReleaseParserResourcesForHierarchy();
635658
JavascriptError::ThrowReferenceError(scriptContext, JSERR_CannotResolveModule, exportEntry.moduleRequest->Psz());
636659
}
637660

@@ -683,10 +706,12 @@ namespace Js
683706
SourceTextModuleRecord* childModule = GetChildModuleRecord(starExportEntry.moduleRequest->Psz());
684707
if (childModule == nullptr)
685708
{
709+
this->ReleaseParserResourcesForHierarchy();
686710
JavascriptError::ThrowReferenceError(GetScriptContext(), JSERR_CannotResolveModule, starExportEntry.moduleRequest->Psz());
687711
}
688712
if (childModule->errorObject != nullptr)
689713
{
714+
this->ReleaseParserResourcesForHierarchy();
690715
JavascriptExceptionOperators::Throw(childModule->errorObject, GetScriptContext());
691716
}
692717

@@ -851,10 +876,6 @@ namespace Js
851876
Assert(wasDeclarationInitialized);
852877
// Debugger can reparse the source and generate the byte code again. Don't cleanup the
853878
// helper information for now.
854-
855-
// Parser uses a temporary guest arena to keep regex patterns alive. We need to release this arena only after we have no further use
856-
// for the regex pattern objects.
857-
this->ReleaseParserResources();
858879
}
859880

860881
bool SourceTextModuleRecord::ModuleDeclarationInstantiation()
@@ -900,7 +921,7 @@ namespace Js
900921
{
901922
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyParentsAsNeeded (errorObject)\n"));
902923
// Cleanup in case of error.
903-
this->ReleaseParserResources();
924+
this->ReleaseParserResourcesForHierarchy();
904925
NotifyParentsAsNeeded();
905926
return false;
906927
}
@@ -925,6 +946,11 @@ namespace Js
925946
Assert(this == scriptContext->GetLibrary()->GetModuleRecord(this->pSourceInfo->GetSrcInfo()->moduleID));
926947

927948
this->rootFunction = scriptContext->GenerateRootFunction(parseTree, sourceIndex, this->parser, this->pSourceInfo->GetParseFlags(), &se, _u("module"));
949+
950+
// Parser uses a temporary guest arena to keep regex patterns alive. We need to release this arena only after we have no further use
951+
// for the regex pattern objects.
952+
this->ReleaseParserResources();
953+
928954
if (rootFunction == nullptr)
929955
{
930956
const WCHAR * sourceUrl = nullptr;
@@ -963,7 +989,7 @@ namespace Js
963989
if (this->errorObject != nullptr)
964990
{
965991
// Cleanup in case of error.
966-
this->ReleaseParserResources();
992+
this->ReleaseParserResourcesForHierarchy();
967993

968994
if (this->promise != nullptr)
969995
{
@@ -1007,6 +1033,8 @@ namespace Js
10071033
// if child module has been dynamically imported and has exception need to throw
10081034
if (childModuleRecord->GetErrorObject() != nullptr)
10091035
{
1036+
this->ReleaseParserResourcesForHierarchy();
1037+
10101038
JavascriptExceptionOperators::Throw(childModuleRecord->GetErrorObject(), this->scriptContext);
10111039
}
10121040
});
@@ -1137,6 +1165,7 @@ namespace Js
11371165
// 2G is too big already.
11381166
if (localExportCount >= INT_MAX)
11391167
{
1168+
this->ReleaseParserResourcesForHierarchy();
11401169
JavascriptError::ThrowRangeError(scriptContext, JSERR_TooManyImportExports);
11411170
}
11421171
localExportCount++;
@@ -1160,6 +1189,7 @@ namespace Js
11601189
currentSlotCount++;
11611190
if (currentSlotCount >= INT_MAX)
11621191
{
1192+
this->ReleaseParserResourcesForHierarchy();
11631193
JavascriptError::ThrowRangeError(scriptContext, JSERR_TooManyImportExports);
11641194
}
11651195
}

lib/Runtime/Language/SourceTextModuleRecord.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace Js
1717
{
1818
public:
1919
friend class ModuleNamespace;
20+
friend class JavascriptLibrary;
2021

2122
SourceTextModuleRecord(ScriptContext* scriptContext);
2223
IdentPtrList* GetRequestedModuleList() const { return requestedModuleList; }
@@ -70,7 +71,7 @@ namespace Js
7071
void SetLocalExportRecordList(ModuleImportOrExportEntryList* localExports) { localExportRecordList = localExports; }
7172
void SetIndirectExportRecordList(ModuleImportOrExportEntryList* indirectExports) { indirectExportRecordList = indirectExports; }
7273
void SetStarExportRecordList(ModuleImportOrExportEntryList* starExports) { starExportRecordList = starExports; }
73-
void SetrequestedModuleList(IdentPtrList* requestModules) { requestedModuleList = requestModules; }
74+
void SetRequestedModuleList(IdentPtrList* requestModules) { requestedModuleList = requestModules; }
7475

7576
ScriptContext* GetScriptContext() const { return scriptContext; }
7677
HRESULT ParseSource(__in_bcount(sourceLength) byte* sourceText, uint32 sourceLength, SRCINFO * srcInfo, Var* exceptionVar, bool isUtf8);
@@ -160,6 +161,7 @@ namespace Js
160161
HRESULT PostParseProcess();
161162
HRESULT PrepareForModuleDeclarationInitialization();
162163
void ReleaseParserResources();
164+
void ReleaseParserResourcesForHierarchy();
163165
void ImportModuleListsFromParser();
164166
HRESULT OnChildModuleReady(SourceTextModuleRecord* childModule, Var errorObj);
165167
void NotifyParentsAsNeeded();

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ namespace Js
128128

129129
void JavascriptLibrary::Uninitialize()
130130
{
131+
#if DBG
132+
if (moduleRecordList != nullptr)
133+
{
134+
// This should mostly be a no-op except for error cases where we may not have had a chance to cleaup a module record yet.
135+
// Do this only in debug builds to avoid reporting of a memory leak. In release builds this doesn't matter as we will cleanup anyways.
136+
for (int index = 0; index < moduleRecordList->Count(); index++)
137+
{
138+
moduleRecordList->Item(index)->ReleaseParserResources();
139+
}
140+
}
141+
#endif
131142
this->globalObject = nullptr;
132143
}
133144

0 commit comments

Comments
 (0)