Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/mono/mono/mini/method-to-ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -10167,9 +10167,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (!field || CLASS_HAS_FAILURE (klass)) {
HANDLE_TYPELOAD_ERROR (cfg, klass);

// Reached only in AOT. Cannot turn a token into a class. We silence the compilation error
// and generate a runtime exception.
if (cfg->error->error_code == MONO_ERROR_BAD_IMAGE)
/*
* Reached only in AOT. After lowering the field resolution failure into a runtime
* throw, consume the expected recoverable metadata errors as well. Memberref field
* resolution can report MissingField/BadImage directly through cfg->error without
* setting cfg->exception_type, and leaving one of those live lets an accepted inline
* trip the inline_method () cfg->error assert later on.
*/
if (cfg->error->error_code == MONO_ERROR_BAD_IMAGE || cfg->error->error_code == MONO_ERROR_MISSING_FIELD)
clear_cfg_error (cfg);

// We need to push a dummy value onto the stack, respecting the intended type.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using ReproCore;

internal static class AotTypeLoadRecoveryProbe
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Run()
{
AotTypeLoadRecoveryHarness.Run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ReproContracts;

public static class ContractBridge
{
public static T FromPointer<T>(nint pointer)
where T : class
=> default!;
}

public sealed class MissingReference
{
}

public struct MissingInitObjValue
{
public int Value;
}

public sealed class MissingFieldOwner
{
public static int Counter;

public int InstanceCounter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<AssemblyName>ReproContracts</AssemblyName>
<RootNamespace>ReproContracts</RootNamespace>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<IsTestProject>false</IsTestProject>
<IsFunctionalTest>false</IsFunctionalTest>
</PropertyGroup>

<ItemGroup>
<Compile Include="Contract.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

public static class Program
{
public static int Main(string[] args)
{
AotTypeLoadRecoveryProbe.Run();
Console.WriteLine("Done!");
return 42;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace ReproCore;

public static class AotTypeLoadRecoveryHarness
{
public static void Run()
{
StorePathHarness.Run();
InitObjTypeLoadHarness.Run();
RunAndExpectFailure(nameof(LoadSideInlineHarness), LoadSideInlineHarness.Run);
}

private static void RunAndExpectFailure(string scenarioName, Action scenario)
{
try
{
scenario();
}
catch (Exception ex) when (IsExpectedRecoveryFailure(ex))
{
return;
}

throw new InvalidOperationException($"{scenarioName} completed without the expected typeload recovery failure.");
}

private static bool IsExpectedRecoveryFailure(Exception ex)
{
return ex is TypeLoadException
or MissingFieldException
or BadImageFormatException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using ReproContracts;

namespace ReproCore;

public static class InitObjTypeLoadHarness
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Run()
{
if (Environment.TickCount == int.MinValue)
{
DirectInitObj();
InitObjInsideExceptionClause();
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void DirectInitObj()
{
MissingInitObjValue value = default;
Consume(value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void InitObjInsideExceptionClause()
{
try
{
MissingInitObjValue value = default;
Consume(value);
}
catch (Exception ex) when (ex.GetHashCode() == int.MinValue)
{
GC.KeepAlive(ex);
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Consume<T>(T value)
{
if (Environment.TickCount == int.MinValue)
GC.KeepAlive(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using ReproContracts;

namespace ReproCore;

public static class LoadSideInlineHarness
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Run()
{
Consume(LoadThroughInlineCandidate());
Consume(LoadThroughInlineInstanceCandidate());
Consume(LoadInsideExceptionClause());
Consume(new LoadSideTargetNode().ReadTarget());
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int LoadThroughInlineCandidate()
{
return InlineableMissingFieldLoad();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int InlineableMissingFieldLoad()
{
return MissingFieldOwner.Counter;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int LoadThroughInlineInstanceCandidate()
{
return InlineableMissingInstanceFieldLoad(new MissingFieldOwner());
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int InlineableMissingInstanceFieldLoad(MissingFieldOwner owner)
{
return owner.InstanceCounter;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int LoadInsideExceptionClause()
{
try
{
return new MissingFieldOwner().InstanceCounter;
}
finally
{
Consume(0);
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Consume(int value)
{
if (Environment.TickCount == int.MinValue)
GC.KeepAlive(value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Consume(object? value)
{
if (Environment.TickCount == int.MinValue)
GC.KeepAlive(value);
}
}

public static class LoadSideStepExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TTarget ReadTarget<TValue, TCurve, TTarget>(
LoadSideBoundStep<TValue, TCurve, TTarget> step)
where TCurve : struct
where TTarget : class
=> step.Target;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static LoadSideTargetNode ReadTarget<TCurve>(this LoadSideTargetNode target)
where TCurve : struct
=> ReadTarget<LoadSidePayload, TCurve, LoadSideTargetNode>(new LoadSideConcreteStep<TCurve>());
}

public abstract class LoadSideBoundStep<TValue, TCurve, TTarget>
where TCurve : struct
where TTarget : class
{
public TTarget Target
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
} = null!;
}

public sealed class LoadSideTargetNode
{
[MethodImpl(MethodImplOptions.NoInlining)]
public LoadSideTargetNode ReadTarget()
{
return this.ReadTarget<LoadSideCurve>();
}
}

public sealed class LoadSideConcreteStep<TCurve> : LoadSideBoundStep<LoadSidePayload, TCurve, LoadSideTargetNode>
where TCurve : struct
{
}

public readonly struct LoadSidePayload
{
}

public readonly struct LoadSideCurve
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<AssemblyName>ReproCore</AssemblyName>
<RootNamespace>ReproCore</RootNamespace>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<IsTestProject>false</IsTestProject>
<IsFunctionalTest>false</IsFunctionalTest>
<OutputPath>$([MSBuild]::NormalizeDirectory('$(BaseOutputPath)', '$(Configuration)', '$(TargetFramework)'))</OutputPath>
<IntermediateOutputPath>$([MSBuild]::NormalizeDirectory('$(BaseIntermediateOutputPath)', '$(Configuration)', '$(TargetFramework)'))</IntermediateOutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<ItemGroup>
<Compile Include="AotTypeLoadRecoveryHarness.cs" />
<Compile Include="StorePathVariant.cs" />
<Compile Include="InitObjTypeLoadVariant.cs" />
<Compile Include="LoadSideInlineVariant.cs" />
<ProjectReference Include="..\Contracts\ReproContracts\ReproContracts.Contract.csproj" />
</ItemGroup>
</Project>
Loading