Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<IlcArg Condition="$(IlcMaxVectorTBitWidth) != ''" Include="--max-vectort-bitwidth:$(IlcMaxVectorTBitWidth)" />
<IlcArg Condition="$(IlcSingleThreaded) == 'true'" Include="--parallelism:1" />
<IlcArg Condition="$(IlcSystemModule) != ''" Include="--systemmodule:$(IlcSystemModule)" />
<IlcArg Condition="$(TypeMapEntryAssembly) != ''" Include="--typemap-entry-assembly:$(TypeMapEntryAssembly)" />
<IlcArg Condition="'$(_targetOS)' == 'win' and $(IlcMultiModule) != 'true' and '$(IlcGenerateWin32Resources)' != 'false' and '$(NativeLib)' != 'Static'" Include="--win32resourcemodule:%(ManagedBinary.Filename)" />
<IlcArg Condition="$(IlcDumpIL) == 'true'" Include="--ildump:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).il" />
<IlcArg Condition="$(NoWarn) != ''" Include='--nowarn:"$([MSBuild]::Escape($(NoWarn)).Replace(`%0A`, ``).Replace(`%0D`, ``))"' />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ public class ILCompilerOptions
public Dictionary<int, bool> WarningsAsErrors = new Dictionary<int, bool>();
public List<string> SuppressedWarningCategories = new List<string>();
public bool DisableGeneratedCodeHeuristics;
public string? TypeMapEntryAssembly;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ public virtual void AddAdditionalArgument(string flag, string[] values)
{
Options.FeatureSwitches.Add(values[0], bool.Parse(values[1]));
}
else if (flag == "--typemap-entry-assembly")
{
Options.TypeMapEntryAssembly = values[0];
}
else if (flag == "--singlewarn")
{
Options.SingleWarn = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,14 @@ public ILScanResults Trim(ILCompilerOptions options, TrimmingCustomizations? cus
InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger);

TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.Empty);
if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
if (options.TypeMapEntryAssembly is not null
&& typeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(options.TypeMapEntryAssembly), throwIfNotFound: true) is EcmaAssembly typeMapEntryAssembly)
{
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(typeMapEntryAssembly, typeSystemContext));
}
else if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
{
// Pass null for typeMappingEntryAssembly to use default entry assembly behavior in tests
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext));
}

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
new("--generateunmanagedentrypoints") { DefaultValueFactory = _ => Array.Empty<string>(), Description = "Generate unmanaged entrypoints for a given assembly" };
public Option<bool> DisableGeneratedCodeHeuristics { get; } =
new("--disable-generated-code-heuristics") { Description = "Disable heuristics for detecting compiler-generated code" };
public Option<string> TypeMapEntryAssembly { get; } =
new("--typemap-entry-assembly") { Description = "Assembly name to use as entry point for TypeMap generation" };

public OptimizationMode OptimizationMode { get; private set; }
public ParseResult Result;
Expand Down Expand Up @@ -273,6 +275,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler")
Options.Add(MakeReproPath);
Options.Add(UnmanagedEntryPointsAssemblies);
Options.Add(DisableGeneratedCodeHeuristics);
Options.Add(TypeMapEntryAssembly);

this.SetAction(result =>
{
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/tools/aot/ILCompiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,16 @@ public int Run()
compilationRoots.Add(new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode(linkTrimFilePath));
}

if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
// Get TypeMappingEntryAssembly from command-line option if specified
string typeMappingEntryAssembly = Get(_command.TypeMapEntryAssembly);
if (typeMappingEntryAssembly is not null)
{
var typeMapEntryAssembly = (EcmaAssembly)typeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(typeMappingEntryAssembly), throwIfNotFound: true);
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(typeMapEntryAssembly, typeSystemContext));
}
else if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
{
// Fall back to entryassembly if not specified
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,16 @@ private static unsafe CallbackContext CreateMaps(
delegate* unmanaged<CallbackContext*, ProcessAttributesCallbackArg*, Interop.BOOL> newExternalTypeEntry,
delegate* unmanaged<CallbackContext*, ProcessAttributesCallbackArg*, Interop.BOOL> newProxyTypeEntry)
{
RuntimeAssembly? startingAssembly = (RuntimeAssembly?)Assembly.GetEntryAssembly();
RuntimeAssembly? startingAssembly;
if (AppContext.GetData("System.Runtime.InteropServices.TypeMappingEntryAssembly") is string entryAssemblyName)
{
startingAssembly = Assembly.Load(entryAssemblyName) as RuntimeAssembly;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is odd. The as operator implies the returned type could be a non RuntimeAssembly. I don't believe that is true. This is a confusing cast. It should just be (RuntimeAssembly).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot (RuntimeAssembly)null! returns null instead of throwing. Will update to make that clear.

}
else
{
startingAssembly = Assembly.GetEntryAssembly() as RuntimeAssembly;
}

if (startingAssembly is null)
{
throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly);
Expand Down
20 changes: 20 additions & 0 deletions src/tests/Interop/TypeMap/Lib5.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.InteropServices;

// This library defines TypeMap entries that should be used when
// TypeMapLib5 is specified as the TypeMappingEntryAssembly
[assembly: TypeMap<AlternateEntryPoint>("lib5_type1", typeof(Lib5Type1))]
[assembly: TypeMap<AlternateEntryPoint>("lib5_type2", typeof(Lib5Type2))]

[assembly: TypeMapAssociation<AlternateEntryPoint>(typeof(Lib5Type1), typeof(Lib5Proxy1))]
[assembly: TypeMapAssociation<AlternateEntryPoint>(typeof(Lib5Type2), typeof(Lib5Proxy2))]

public class Lib5Type1 { }
public class Lib5Type2 { }
public class Lib5Proxy1 { }
public class Lib5Proxy2 { }

public class AlternateEntryPoint { }
48 changes: 48 additions & 0 deletions src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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.Collections.Generic;
using System.Runtime.InteropServices;
using Xunit;

// Note: This test does NOT define any TypeMap attributes.
// The TypeMappingEntryAssembly is set to TypeMapLib5 via RuntimeHostConfigurationOption,
// so the type maps should be resolved from TypeMapLib5 instead of this assembly.

public class TypeMapEntryAssemblyTest
{
[Fact]
public static void Validate_TypeMappingEntryAssembly_ExternalMap()
{
Console.WriteLine(nameof(Validate_TypeMappingEntryAssembly_ExternalMap));

// These types should be resolved from TypeMapLib5's type maps
IReadOnlyDictionary<string, Type> externalMap = TypeMapping.GetOrCreateExternalTypeMapping<AlternateEntryPoint>();

Assert.Equal(typeof(Lib5Type1), externalMap["lib5_type1"]);
Assert.Equal(typeof(Lib5Type2), externalMap["lib5_type2"]);

Assert.True(externalMap.TryGetValue("lib5_type1", out Type? type1));
Assert.Equal(typeof(Lib5Type1), type1);

Assert.True(externalMap.TryGetValue("lib5_type2", out Type? type2));
Assert.Equal(typeof(Lib5Type2), type2);

Assert.False(externalMap.TryGetValue("nonexistent", out Type? _));
}

[Fact]
public static void Validate_TypeMappingEntryAssembly_ProxyMap()
{
Console.WriteLine(nameof(Validate_TypeMappingEntryAssembly_ProxyMap));

// These proxy mappings should be resolved from TypeMapLib5's type maps
IReadOnlyDictionary<Type, Type> proxyMap = TypeMapping.GetOrCreateProxyTypeMapping<AlternateEntryPoint>();

Assert.Equal(typeof(Lib5Proxy1), proxyMap[new Lib5Type1().GetType()]);
Assert.Equal(typeof(Lib5Proxy2), proxyMap[new Lib5Type2().GetType()]);

Assert.False(proxyMap.TryGetValue(typeof(string), out Type? _));
}
}
27 changes: 27 additions & 0 deletions src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<TypeMapEntryAssembly>TypeMapLib5</TypeMapEntryAssembly>
<MonoAotIncompatible>true</MonoAotIncompatible>
<DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'mono'">true</DisableProjectBuild>
<IlcGenerateMstatFile>true</IlcGenerateMstatFile>
<IlcGenerateDgmlFile>true</IlcGenerateDgmlFile>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>

<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Runtime.InteropServices.TypeMappingEntryAssembly"
Value="$(TypeMapEntryAssembly)" />
</ItemGroup>

<ItemGroup>
<Compile Include="TypeMapEntryAssemblyApp.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="TypeMapLib5.csproj" />
<ProjectReference Include="TypeMapLib1.csproj" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions src/tests/Interop/TypeMap/TypeMapLib5.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<Compile Include="Lib5.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="TypeMapLib1.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ Copyright (c) .NET Foundation. All rights reserved.
<EnableTrimAnalyzer Condition="'$(EnableTrimAnalyzer)' == ''">false</EnableTrimAnalyzer>
</PropertyGroup>

<!-- Configure TypeMap entry assembly -->
<PropertyGroup Condition="'$(TypeMapEntryAssembly)' != ''">
<_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --typemap-entry-assembly "$(TypeMapEntryAssembly)"</_ExtraTrimmerArgs>
</PropertyGroup>

<!-- Disable Redundant Warning Suppressions by default-->
<PropertyGroup Condition="'$(_TrimmerShowRedundantSuppressions)' != 'true' And '$(PublishTrimmed)' == 'true'">
<NoWarn>$(NoWarn);IL2121</NoWarn>
Expand Down
8 changes: 8 additions & 0 deletions src/tools/illink/src/linker/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,14 @@
<Left>ref/net10.0/illink.dll</Left>
<Right>lib/net10.0/illink.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.get_TypeMapEntryAssembly</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.set_TypeMapEntryAssembly(System.String)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Mono.Linker.LinkContext</Target>
Expand Down
13 changes: 12 additions & 1 deletion src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,18 @@ protected virtual void Initialize()
{
InitializeCorelibAttributeXml();
Context.Pipeline.InitializeMarkHandlers(Context, MarkContext);
_typeMapHandler.Initialize(Context, this, Annotations.GetEntryPointAssembly());

// Check for TypeMappingEntryAssembly override
AssemblyDefinition? startingAssembly = null;
if (Context.TypeMapEntryAssembly is not null)
{
var assemblyName = AssemblyNameReference.Parse(Context.TypeMapEntryAssembly);
startingAssembly = Context.TryResolve(assemblyName);
}
// If resolution fails, fall back to entry point assembly
startingAssembly ??= Annotations.GetEntryPointAssembly();

_typeMapHandler.Initialize(Context, this, startingAssembly);
ProcessMarkedPending();
}

Expand Down
Loading
Loading