Repository navigation
Description
Description
When crossgen'ing an assembly for the browser/wasm target, compilation of a method that makes an
unresolvable virtual call to a method whose declaring type lives in another version bubble (e.g. a
System.Private.CoreLib type) fails in the ReadyToRun front-end with:
System.NotImplementedException: [S.P.CoreLib]System.Reflection.MemberInfo
at ILCompiler.DependencyAnalysis.ReadyToRun.ModuleTokenResolver.GetModuleTokenForType(...) ModuleTokenResolver.cs:92
at ILCompiler.DependencyAnalysis.ReadyToRun.SignatureContext.GetTargetModule(...)
at ILCompiler.DependencyAnalysis.ReadyToRun.TypeFixupSignature.GetData(...)
at ILCompiler.DependencyAnalysis.ReadyToRun.ImportSectionNode.MaterializeSignature(...)
This is wasm-target-specific — the identical crossgen of the same assembly for x64 succeeds.
Repro
Found while crossgen'ing xunit.runner.utility.netcoreapp10.dll from a CoreCLR wasm test Core_Root.
Minimal single-method repro:
crossgen2 --targetarch:wasm --obj-format:wasm --targetos:browser \
--jitpath:<clrjit_universal_wasm_x64.dll> \
-r:<Core_Root>\*.dll \
--singlemethodtypename:"Xunit.RunnerReporterUtility, xunit.runner.utility.netcoreapp10" \
--singlemethodname:"GetAvailableRunnerReporters" \
<Core_Root>\xunit.runner.utility.netcoreapp10.dll
GetAvailableRunnerReporters contains callvirt System.Reflection.MemberInfo::get_Name() on a
System.Type value that the JIT cannot devirtualize (the declaring method is not in the version bubble).
Root cause
The wasm JIT lowers the unresolved virtual call through the generic
CORINFO_HELP_VIRTUAL_FUNC_PTR helper, which embeds the declaring type's handle:
// src/coreclr/jit/importer.cpp (~2732)
// Wasm R2R cannot use the CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR fast path because it
// relies on DelayLoad_Helper_Obj dynamic-helper thunks, which are not implemented on wasm.
// Fall through to the runtime CORINFO_HELP_VIRTUAL_FUNC_PTR helper instead.
#if defined(FEATURE_READYTORUN) && !defined(TARGET_WASM)
else if (IsAot()) { ... CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR ... } // method-keyed fast path
#endif
if (call == nullptr) {
GenTree* exactTypeDesc = impParentClassTokenToHandle(pResolvedToken); // <-- embeds MemberInfo
GenTree* exactMethodDesc = impTokenToHandle(pResolvedToken);
call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL,
thisPtr, exactMethodDesc, exactTypeDesc);
}On non-wasm R2R, IsAot() takes the CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR fast path, which is
keyed on the method (an entry-point fixup) and never needs the declaring type handle. Wasm is
explicitly excluded from that path (!defined(TARGET_WASM), because the required DelayLoad_Helper_Obj
thunks aren't implemented on wasm), so it falls through and embeds exactTypeDesc =
impParentClassTokenToHandle(...) = the declaring type, System.Reflection.MemberInfo.
That handle becomes a TypeHandle TypeFixupSignature. When crossgen2 materializes the import section,
ModuleTokenResolver.GetModuleTokenForType(MemberInfo) can't reverse-map MemberInfo to a module token
— the consuming assembly (xunit.runner.utility) has no TypeRef for MemberInfo (it references it
only transitively via the System.Type → get_Name MemberRef), and it isn't in the version bubble —
so it throws NotImplementedException.
JIT dump of the call site:
CALL help CORINFO_HELP_VIRTUAL_FUNC_PTR
this: LCL_VAR ref V15
arg1: IND(CNS_INT(h) 0x4204D8 class) ← MemberInfo type handle (the fixup that fails)
meth hnd: IND(CNS_INT(h) 0x4204E0 token)
Why x64 is unaffected
x64 R2R uses the method-keyed fast path (or virtual stub dispatch); the fixup references the (tokenizable)
get_Name MemberRef, never the declaring type MemberInfo, so there is nothing to fail on.
Impact
Any wasm R2R compilation of a method that makes a non-devirtualizable virtual/interface call to a method
whose declaring type is cross-version-bubble and not otherwise TypeRef'd by the consuming assembly will
fail crossgen. Calls to System.Object/MemberInfo/etc. virtual members on framework types are common,
so this is likely to be hit broadly once more wasm R2R assemblies are compiled.
Related
Same underlying gap (missing wasm DelayLoad_Helper_Obj / VirtualFunctionPointer R2R support) as the
runtime traps previously seen for cross-module virtual dispatch on wasm (e.g. the Runtime_45250 /
address_d cases). Here it surfaces at crossgen time as a token-resolution failure rather than a runtime
trap.
Note
This issue was drafted with the assistance of GitHub Copilot.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
dotnet-policy-service commentedon Jul 15, 2026
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.
lewing commentedon Sep 20, 2026
Thanks for the detailed report and root-cause analysis. The crossgen2 failure described here was fixed by #132528, which changes the ReadyToRun fixup to reference the declaring method when the declaring type cannot be represented by a module token.
The temporary fallback workaround was tracked separately in #132412 and has been superseded by that fix.
Closing this issue as already fixed.
Note
This response was generated with assistance from GitHub Copilot.