Skip to content

Commit 7c5a6bf

Browse files
committed
OS#15992595: On debug reparse, update all entry points associated with all parsed functions, not just the ones associated with function objects. Object may evolve after reparse and use types that are not referenced at reparse time.
1 parent 4a6c0ec commit 7c5a6bf

File tree

7 files changed

+70
-66
lines changed

7 files changed

+70
-66
lines changed

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,17 +2406,10 @@ namespace Js
24062406

24072407
if (isDebugOrAsmJsReparse)
24082408
{
2409-
grfscr &= ~fscrWillDeferFncParse; // Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse
2410-
}
2411-
2412-
if (isDebugOrAsmJsReparse)
2413-
{
2414-
grfscr |= fscrNoAsmJs; // Disable asm.js when debugging or if linking failed
2415-
}
2416-
2417-
if (isDebugOrAsmJsReparse)
2418-
{
2419-
grfscr &= ~fscrCreateParserState; // Disable parser state cache if we're debugging or reparsing asm.js
2409+
// Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse
2410+
// Disable asm.js when debugging or if linking failed
2411+
// Disable parser state cache if we're debugging or reparsing asm.js
2412+
grfscr = fscrNoAsmJs | (grfscr & ~(fscrWillDeferFncParse | fscrCreateParserState));
24202413
}
24212414

24222415
BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT
@@ -3520,6 +3513,23 @@ namespace Js
35203513
#endif
35213514
}
35223515

3516+
void FunctionBody::UpdateEntryPointsOnDebugReparse()
3517+
{
3518+
// Update all function types associated with this function body. Note that we can't rely on updating
3519+
// types pointed to by function objects, because the type may evolve to one that is not currently referenced.
3520+
3521+
ProxyEntryPointInfo * entryPointInfo = this->GetDefaultFunctionEntryPointInfo();
3522+
JavascriptMethod newEntryPoint = this->GetDirectEntryPoint(entryPointInfo);
3523+
bool isAsmJS = this->GetIsAsmjsMode();
3524+
3525+
auto updateOneType = [&](ScriptFunctionType* functionType) {
3526+
// Note that the ScriptFunctionType method will handle cross-site thunks correctly.
3527+
functionType->ChangeEntryPoint(entryPointInfo, newEntryPoint, isAsmJS);
3528+
};
3529+
3530+
this->MapFunctionObjectTypes(updateOneType);
3531+
}
3532+
35233533
void FunctionProxy::Finalize(bool isShutdown)
35243534
{
35253535
this->CleanupFunctionProxyCounters();
@@ -5188,10 +5198,6 @@ namespace Js
51885198
}
51895199

51905200
this->SetOriginalEntryPoint(DefaultDeferredParsingThunk);
5191-
5192-
// Abandon the shared type so a new function will get a new one
5193-
this->deferredPrototypeType = nullptr;
5194-
this->undeferredFunctionType = nullptr;
51955201
this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
51965202
}
51975203

lib/Runtime/Base/FunctionBody.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3213,6 +3213,8 @@ namespace Js
32133213
void ClearEntryPoints();
32143214
void ResetEntryPoint();
32153215
void CleanupToReparseHelper();
3216+
void UpdateEntryPointsOnDebugReparse();
3217+
32163218
void AddDeferParseAttribute();
32173219
void RemoveDeferParseAttribute();
32183220
#if DBG

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,7 +4021,6 @@ namespace Js
40214021
// Wrapped function are not allocated with the EnumClass bit
40224022
Assert(pFunction->GetFunctionInfo() != &JavascriptExternalFunction::EntryInfo::WrappedFunctionThunk);
40234023

4024-
JavascriptMethod entryPoint = pFunction->GetEntryPoint();
40254024
FunctionInfo * info = pFunction->GetFunctionInfo();
40264025
FunctionProxy * proxy = info->GetFunctionProxy();
40274026

@@ -4093,26 +4092,13 @@ namespace Js
40934092
}
40944093
#endif
40954094

4096-
ScriptFunction * scriptFunction = ScriptFunction::FromVar(pFunction);
4097-
40984095
#ifdef ASMJS_PLAT
4096+
ScriptFunction * scriptFunction = ScriptFunction::FromVar(pFunction);
40994097
scriptContext->TransitionEnvironmentForDebugger(scriptFunction);
41004098
#endif
4101-
4102-
JavascriptMethod newEntryPoint;
4103-
if (CrossSite::IsThunk(entryPoint))
4104-
{
4105-
// Can't change from cross-site to non-cross-site, but still need to update the e.p.info on ScriptFunctionType.
4106-
newEntryPoint = entryPoint;
4107-
}
4108-
else
4109-
{
4110-
newEntryPoint = pBody->GetDirectEntryPoint(pBody->GetDefaultFunctionEntryPointInfo());
4111-
}
4112-
4113-
scriptFunction->ChangeEntryPoint(pBody->GetDefaultFunctionEntryPointInfo(), newEntryPoint);
41144099
}
4115-
#endif
4100+
4101+
#endif // ENABLE_SCRIPT_DEBUGGING
41164102

41174103
#if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
41184104
void ScriptContext::RecyclerEnumClassEnumeratorCallback(void *address, size_t size)

lib/Runtime/Debug/DebugContext.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,10 @@ namespace Js
238238
{
239239
pFuncBody->ReinitializeExecutionModeAndLimits();
240240
}
241+
pFuncBody->UpdateEntryPointsOnDebugReparse();
241242
});
242243
}
244+
243245
return false;
244246
}
245247

@@ -330,6 +332,7 @@ namespace Js
330332
{
331333
pFuncBody->ReinitializeExecutionModeAndLimits();
332334
}
335+
pFuncBody->UpdateEntryPointsOnDebugReparse();
333336
});
334337
}
335338

lib/Runtime/Library/ScriptFunction.cpp

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -210,48 +210,15 @@ using namespace Js;
210210

211211
void ScriptFunction::ChangeEntryPoint(ProxyEntryPointInfo* entryPointInfo, JavascriptMethod entryPoint)
212212
{
213-
Assert(entryPoint != nullptr);
214213
Assert(this->GetTypeId() == TypeIds_Function);
215214
#if ENABLE_NATIVE_CODEGEN
216215
Assert(!IsCrossSiteObject() || entryPoint != (Js::JavascriptMethod)checkCodeGenThunk);
217216
#endif
218217

219218
Assert((entryPointInfo != nullptr && this->GetFunctionProxy() != nullptr));
220-
if (this->GetEntryPoint() == entryPoint && this->GetScriptFunctionType()->GetEntryPointInfo() == entryPointInfo)
221-
{
222-
return;
223-
}
224219

225220
bool isAsmJS = HasFunctionBody() && this->GetFunctionBody()->GetIsAsmjsMode();
226-
227-
// ASMJS:- for asmjs we don't need to update the entry point here as it updates the types entry point
228-
if (!isAsmJS)
229-
{
230-
// We can't go from cross-site to non-cross-site. Update only in the non-cross site case
231-
if (!CrossSite::IsThunk(this->GetEntryPoint()))
232-
{
233-
this->SetEntryPoint(entryPoint);
234-
}
235-
}
236-
// instead update the address in the function entrypoint info
237-
else
238-
{
239-
entryPointInfo->jsMethod = entryPoint;
240-
}
241-
242-
ProxyEntryPointInfo* oldEntryPointInfo = this->GetScriptFunctionType()->GetEntryPointInfo();
243-
if (oldEntryPointInfo
244-
&& oldEntryPointInfo != entryPointInfo
245-
&& oldEntryPointInfo->SupportsExpiration())
246-
{
247-
// The old entry point could be executing so we need root it to make sure
248-
// it isn't prematurely collected. The rooting is done by queuing it up on the threadContext
249-
ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
250-
251-
threadContext->QueueFreeOldEntryPointInfoIfInScript((FunctionEntryPointInfo*)oldEntryPointInfo);
252-
}
253-
254-
this->GetScriptFunctionType()->SetEntryPointInfo(entryPointInfo);
221+
this->GetScriptFunctionType()->ChangeEntryPoint(entryPointInfo, entryPoint, isAsmJS);
255222
}
256223

257224
FunctionProxy * ScriptFunction::GetFunctionProxy() const

lib/Runtime/Types/ScriptFunctionType.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,43 @@ namespace Js
3434
library->ScriptFunctionTypeHandler(!proxy->IsConstructor(), proxy->GetIsAnonymousFunction()),
3535
isShared, isShared);
3636
}
37+
38+
void ScriptFunctionType::ChangeEntryPoint(ProxyEntryPointInfo * entryPointInfo, JavascriptMethod entryPoint, bool isAsmJS)
39+
{
40+
Assert(entryPoint != nullptr);
41+
Assert(entryPointInfo != nullptr);
42+
if (this->GetEntryPoint() == entryPoint && this->GetEntryPointInfo() == entryPointInfo)
43+
{
44+
return;
45+
}
46+
47+
// ASMJS:- for asmjs we don't need to update the entry point here as it updates the types entry point
48+
if (!isAsmJS)
49+
{
50+
// We can't go from cross-site to non-cross-site. Update only in the non-cross site case
51+
if (!CrossSite::IsThunk(this->GetEntryPoint()))
52+
{
53+
this->SetEntryPoint(entryPoint);
54+
}
55+
}
56+
// instead update the address in the function entrypoint info
57+
else
58+
{
59+
entryPointInfo->jsMethod = entryPoint;
60+
}
61+
62+
ProxyEntryPointInfo* oldEntryPointInfo = this->GetEntryPointInfo();
63+
if (oldEntryPointInfo
64+
&& oldEntryPointInfo != entryPointInfo
65+
&& oldEntryPointInfo->SupportsExpiration())
66+
{
67+
// The old entry point could be executing so we need root it to make sure
68+
// it isn't prematurely collected. The rooting is done by queuing it up on the threadContext
69+
ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
70+
71+
threadContext->QueueFreeOldEntryPointInfoIfInScript((FunctionEntryPointInfo*)oldEntryPointInfo);
72+
}
73+
74+
this->SetEntryPointInfo(entryPointInfo);
75+
}
3776
};

lib/Runtime/Types/ScriptFunctionType.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Js
1313
static DWORD GetEntryPointInfoOffset() { return offsetof(ScriptFunctionType, entryPointInfo); }
1414
ProxyEntryPointInfo * GetEntryPointInfo() const { return entryPointInfo; }
1515
void SetEntryPointInfo(ProxyEntryPointInfo * entryPointInfo) { this->entryPointInfo = entryPointInfo; }
16+
void ChangeEntryPoint(ProxyEntryPointInfo * entryPointInfo, JavascriptMethod entryPoint, bool isAsmJS);
1617
private:
1718
ScriptFunctionType(ScriptFunctionType * type);
1819
ScriptFunctionType(ScriptContext* scriptContext, RecyclableObject* prototype,

0 commit comments

Comments
 (0)