Open
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
Type layout below causes System.TypeInitializationException
to be thrown in WASM (same code can run in a server-type project):
public sealed class TwistedType {
public static IEnumerable<TwistedType> TheTroubleMaker = NestedStatic.ArrayOfTwistedType.Prepend(new());
public static class NestedStatic {
public static TwistedType[] ArrayOfTwistedType = { new() };
}
public string World { get; } = "World";
}
The relevant member of NestedStatic
type is referenced like:
<h1>Hello, @TwistedType.NestedStatic.ArrayOfTwistedType[0].World!</h1>
Expected Behavior
Type initialization should be successful in WASM as in server.
Steps To Reproduce
Clone this repo and run.
Versions:
- Chrome: 116.0.5829.0
- .net: 7.0.304
Exceptions (if any)
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: The type initializer for 'NestedStatic' threw an exception.
System.TypeInitializationException: The type initializer for 'NestedStatic' threw an exception.
---> System.TypeInitializationException: The type initializer for 'BlzWasmTypeInitRepro.TwistedType' threw an exception.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'source')
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Linq.Enumerable.Prepend[TwistedType](IEnumerable`1 source, TwistedType element)
at BlzWasmTypeInitRepro.TwistedType..cctor() in BlzWasmTypeInitRepro\Types.cs:line 5
--- End of inner exception stack trace ---
at BlzWasmTypeInitRepro.TwistedType.NestedStatic..cctor() in BlzWasmTypeInitRepro\Types.cs:line 9
--- End of inner exception stack trace ---
at BlzWasmTypeInitRepro.Pages.Index.BuildRenderTree(RenderTreeBuilder __builder) in BlzWasmTypeInitRepro\Pages\Index.razor:line 3
at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__6_0(RenderTreeBuilder builder)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
.NET Version
7.0.304
Anything else?
No response
Activity
ghost commentedon Jul 24, 2023
Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.
Issue Details
Is there an existing issue for this?
Describe the bug
Type layout below causes
System.TypeInitializationException
to be thrown in WASM (same code can run in a server-type project):The relevant member of
NestedStatic
type is referenced like:Expected Behavior
Type initialization should be successful in WASM as in server.
Steps To Reproduce
Clone this repo and run.
Versions:
Exceptions (if any)
.NET Version
7.0.304
Anything else?
No response
arch-wasm
,untriaged
,area-VM-meta-mono
marek-safar commentedon Jul 24, 2023
/cc @lambdageek
lambdageek commentedon Jul 24, 2023
Repros in console app using mono as the runtime:
$ dotnet run --self-contained -p:UseMonoRuntime=true -r osx-arm64
At first glance, I think this might be allowed by ECMA you cannot depend on the order of execution for static constructors...
lambdageek commentedon Jul 24, 2023
CoreCLR is also quite fragile here. change
#if true
to#if false
below to see a difference - the first one runs, the second one throwslambdageek commentedon Jul 24, 2023
Hmm... comparing the IL for the working and broken versions of
FirstOne
, it looks like the working version hasbeforefieldinit
and the broken one doesn't.lambdageek commentedon Jul 24, 2023
Ok, this isn't one of the cases where ECMA allows us some leeway because of circular dependencies. Rather, this is a case where the cctor must run "at or before static field access" and mono is leaning on the "or before" (specifically I think we run it when the instance constructor for
FirstOne
is called)It looks like mono is calling a
beforefieldinit
static constructor even if we never touch the static fields of that classIt looks like we're running the cctor for
FirstOne
whenSecondOne
tries to instantiate it. But constructor invocation for abeforefieldinit
class is not sufficient. Only static field access (or explicit cctor invocation) should trigger it, I think.There is a variant where instead of calling the instance constructor, we call a static method from
FirstOne
. In this case, too, CoreCLR doesn't call the cctor forFirstOne
19 remaining items