Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hello-NativeAOTFromJNI] Add NativeAOT sample #1153

Merged
merged 52 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
007317b
[Hello-NativeAOTFromJNI] Add NativeAOT sample
jonpryor Oct 24, 2023
db84cc3
Try to create a JreRuntime
jonpryor Oct 25, 2023
a32e244
icanhaz blittable method registration?
jonpryor Oct 25, 2023
4c30ab5
Linux builds require -Wl,-soname to be set properly.
jonpryor Oct 25, 2023
a08dce8
Fix build break.
jonpryor Oct 25, 2023
da9f188
Use [UnmanagedFunctionPointer]
jonpryor Oct 25, 2023
69c90ba
"Full(er)" sample
jonpryor Nov 1, 2023
1fc0af5
Use JniTransition.
jonpryor Nov 2, 2023
a499a1d
Allow init from JNIEnv*, not just JavaVM*
jonpryor Nov 2, 2023
e1822f0
Make it (mostly) work!
jonpryor Nov 2, 2023
9027591
Fix Java.Interop.Export-Tests
jonpryor Nov 7, 2023
1d422da
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Nov 7, 2023
902fe28
Remove more `n_`s?!
jonpryor Nov 7, 2023
0392169
Enable activation
jonpryor Nov 7, 2023
93a901a
Allow marking constructors as callable from Java.
jonpryor Nov 7, 2023
a50457b
Partially revert 9027591c41232c82a582f8ab16ddaa783795582c
jonpryor Nov 8, 2023
ce850ca
Rethink `Type.GetType()` alternatives
jonpryor Nov 9, 2023
6920e6c
Remove need for NativeAotValueManager.
jonpryor Nov 9, 2023
4ceaa34
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Nov 13, 2023
cf73d77
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Nov 14, 2023
986f0c4
Reduce patch size.
jonpryor Nov 14, 2023
17a4403
Work with .NET 8 GA (…?)
jonpryor Nov 15, 2023
1b86f9a
Use `net.dot.jni` package prefix
jonpryor Nov 16, 2023
f71cadb
Forgot to fix up a header path…
jonpryor Nov 16, 2023
b5fc4cf
Don't add new public API
jonpryor Nov 16, 2023
4741dc9
Reduce patch size.
jonpryor Nov 16, 2023
8eb2850
Remove more unneeded bits.
jonpryor Nov 16, 2023
c3e4870
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Nov 23, 2023
aeed449
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Nov 24, 2023
1e71988
Remove GetTypeFromAssemblyQualifiedName()
jonpryor Nov 23, 2023
1b7be6c
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Dec 2, 2023
14f6713
Cleanup.
jonpryor Dec 3, 2023
508dcde
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Feb 9, 2024
74248fb
Merge remote-tracking branch 'origin/main' into dev/jonp/jonp-hello-f…
jonpryor Feb 16, 2024
12e187e
Fix spelling of JNI_OnUnload
jonpryor Feb 16, 2024
cfdfeaf
Reduce unnecessary changes.
jonpryor Feb 16, 2024
0803048
Integrate Hello-NativeAOTFromJNI.csproj into Java.Interop.sln
jonpryor Feb 16, 2024
f8bb97f
Cleanup.
jonpryor Feb 16, 2024
12cda04
Run the sample on CI!
jonpryor Feb 16, 2024
3861773
Reduce manual string duplication.
jonpryor Feb 16, 2024
adf5773
Remove unnecessary line.
jonpryor Feb 16, 2024
65f7d90
Set publishWebProjects: false.
jonpryor Feb 16, 2024
429bb4d
Try setting workingDirectory.
jonpryor Feb 16, 2024
b4cddb7
Setting workingDirectory didn't fix things.
jonpryor Feb 16, 2024
42c7251
$CWD isn't what I expect, so use a rooted WorkingDirectory.
jonpryor Feb 16, 2024
f656870
MOAR PRINTFS
jonpryor Feb 16, 2024
1b8b1af
Avoid DotNetCoreCLI@2
jonpryor Feb 16, 2024
59314fc
Cleanup.
jonpryor Feb 16, 2024
2a012c7
Fix some `dotnet publish` linker warnings.
jonpryor Feb 16, 2024
f79d3e1
Quote `$(DotnetToolPath)`, as it will contain spaces on Windows.
jonpryor Feb 16, 2024
213a57b
$(Standalone) by default, and prefer `native-library` loader in JreRu…
jonpryor Feb 16, 2024
cf240f9
Run the Hello-NativeAOT tests on Windows, too
jonpryor Feb 16, 2024
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
7 changes: 7 additions & 0 deletions Java.Interop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.Tools.Expressi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.Tools.Expressions-Tests", "tests\Java.Interop.Tools.Expressions-Tests\Java.Interop.Tools.Expressions-Tests.csproj", "{211BAA88-66B1-41B2-88B2-530DBD8DF702}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hello-NativeAOTFromJNI", "samples\Hello-NativeAOTFromJNI\Hello-NativeAOTFromJNI.csproj", "{8DB3842B-73D7-491C-96F9-EBC863E2C917}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Java.Interop.NamingCustomAttributes\Java.Interop.NamingCustomAttributes.projitems*{58b564a1-570d-4da2-b02d-25bddb1a9f4f}*SharedItemsImports = 5
Expand Down Expand Up @@ -320,6 +322,10 @@ Global
{211BAA88-66B1-41B2-88B2-530DBD8DF702}.Debug|Any CPU.Build.0 = Debug|Any CPU
{211BAA88-66B1-41B2-88B2-530DBD8DF702}.Release|Any CPU.ActiveCfg = Release|Any CPU
{211BAA88-66B1-41B2-88B2-530DBD8DF702}.Release|Any CPU.Build.0 = Release|Any CPU
{8DB3842B-73D7-491C-96F9-EBC863E2C917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DB3842B-73D7-491C-96F9-EBC863E2C917}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DB3842B-73D7-491C-96F9-EBC863E2C917}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DB3842B-73D7-491C-96F9-EBC863E2C917}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -374,6 +380,7 @@ Global
{CB05E11B-B96F-4179-A4E9-5D6BDE29A8FC} = {271C9F30-F679-4793-942B-0D9527CB3E2F}
{1A0262FE-3CDB-4AF2-AAD8-65C59524FE8A} = {0998E45F-8BCE-4791-A944-962CD54E2D80}
{211BAA88-66B1-41B2-88B2-530DBD8DF702} = {271C9F30-F679-4793-942B-0D9527CB3E2F}
{8DB3842B-73D7-491C-96F9-EBC863E2C917} = {D5A93398-AEB1-49F3-89DC-3904A47DB0C7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {29204E0C-382A-49A0-A814-AD7FBF9774A5}
Expand Down
2 changes: 2 additions & 0 deletions build-tools/automation/azure-pipelines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
- template: templates\core-tests.yaml
parameters:
runNativeDotnetTests: true
nativeAotRid: win-x64
platformName: .NET - Windows

- template: templates\fail-on-issue.yaml
Expand Down Expand Up @@ -87,6 +88,7 @@ jobs:
- template: templates\core-tests.yaml
parameters:
runNativeTests: true
nativeAotRid: osx-x64
platformName: .NET - MacOS

- template: templates\fail-on-issue.yaml
Expand Down
14 changes: 14 additions & 0 deletions build-tools/automation/templates/core-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ parameters:
condition: succeeded()
runNativeTests: false
platformName:
nativeAotRid:

steps:
- task: DotNetCoreCLI@2
Expand Down Expand Up @@ -173,6 +174,19 @@ steps:
arguments: -c $(Build.Configuration) tools/java-source-utils/java-source-utils.csproj -t:RunTests
continueOnError: true

- powershell: >
Copy link
Member

Choose a reason for hiding this comment

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

Do these actually need to be powershell or can they be script? I think script would be the same as cmd on Windows and bash on macOS.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have no idea, I'm just going with @akoeplinger 's suggestion on Discord.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are only testing osx-x64 here. Should this PR work on Windows?

Copy link
Contributor

Choose a reason for hiding this comment

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

Conditioning this off a display name seems brittle, we probably want:

condition: eq(variables['Agent.OS'], 'Darwin')

Copy link
Member

Choose a reason for hiding this comment

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

script would work since the command is the same i.e. it doesn't matter whether cmd or bash is used but I agree that this should probably only execute on Mac.

dotnet publish -c $(Build.Configuration) -r ${{ parameters.nativeAotRid }}
samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj
displayName: 'Tests: publish Hello-NativeAOTFromJNI'
continueOnError: true

- powershell: >
dotnet build -c $(Build.Configuration) -r ${{ parameters.nativeAotRid }}
-t:RunJavaSample
samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj
displayName: 'Tests: run Hello-NativeAOTFromJNI'
continueOnError: true

- task: PublishTestResults@2
displayName: Publish JUnit Test Results
inputs:
Expand Down
31 changes: 31 additions & 0 deletions samples/Hello-NativeAOTFromJNI/App.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Runtime.InteropServices;

using Java.Interop;

namespace Hello_NativeAOTFromJNI;

static class App {

// symbol name from `$(IntermediateOutputPath)obj/Release/osx-x64/h-classes/net_dot_jni_hello_App.h`
[UnmanagedCallersOnly (EntryPoint="Java_net_dot_jni_hello_App_sayHello")]
static IntPtr sayHello (IntPtr jnienv, IntPtr klass)
{
var envp = new JniTransition (jnienv);
try {
var s = $"Hello from .NET NativeAOT!";
Console.WriteLine (s);
var h = JniEnvironment.Strings.NewString (s);
var r = JniEnvironment.References.NewReturnToJniRef (h);
JniObjectReference.Dispose (ref h);
return r;
}
catch (Exception e) {
Console.Error.WriteLine ($"Error in App.sayHello(): {e.ToString ()}");
envp.SetPendingException (e);
}
finally {
envp.Dispose ();
}
return nint.Zero;
}
}
52 changes: 52 additions & 0 deletions samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(DotNetTargetFramework)</TargetFramework>
</PropertyGroup>

<Import Project="..\..\TargetFrameworkDependentValues.props" />

<PropertyGroup>
<RootNamespace>Hello_NativeAOTFromJNI</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NativeLib>Shared</NativeLib>
<!-- Needed for cross-compilation, e.g. build osx-x64 from osx-arm64 -->
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
<ProjectReference Include="..\..\src\Java.Base\Java.Base.csproj" />
<ProjectReference Include="..\..\src\Java.Interop.Export\Java.Interop.Export.csproj" />
<ProjectReference
Include="..\..\tools\jcw-gen\jcw-gen.csproj"
ReferenceOutputAssembly="false"
/>
<ProjectReference
Include="..\..\tools\jnimarshalmethod-gen\Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj"
ReferenceOutputAssembly="false"
/>
</ItemGroup>

<ItemGroup>
<TrimmerRootAssembly Include="Hello-NativeAOTFromJNI" />
</ItemGroup>

<ItemGroup>
<Content Include="$(OutputPath)hello-from-java.jar" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<HelloNativeAOTFromJNIJar Include="$(MSBuildThisFileDirectory)java\**\*.java" />
</ItemGroup>

<ItemGroup Condition=" $([MSBuild]::IsOSPlatform('linux')) Or $([MSBuild]::IsOSPlatform('FreeBSD')) Or $([MSBuild]::IsOSPlatform('Android')) ">
<CustomLinkerArg Include="-Wl,-soname,lib$(NativeBinary)$(NativeBinaryExt)" />
</ItemGroup>

<Import Project="Hello-NativeAOTFromJNI.targets" />
</Project>
106 changes: 106 additions & 0 deletions samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<Project>

<PropertyGroup>
<DotnetToolPath>"$(DOTNET_HOST_PATH)"</DotnetToolPath>
</PropertyGroup>

<Target Name="_CreateJavaCallableWrappers"
Condition=" '$(TargetPath)' != '' "
BeforeTargets="BuildNativeAOTFromJNIJar"
Inputs="$(TargetPath)"
Outputs="$(IntermediateOutputPath)java\.stamp">
<RemoveDir Directories="$(IntermediateOutputPath)java" />
<MakeDir Directories="$(IntermediateOutputPath)java" />
<ItemGroup>
<!-- I can't find a good way to trim the trailing `\`, so append with `.` so we can sanely quote for $(_Libpath) -->
<_JcwGenRefAsmDirs Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" />
</ItemGroup>
<PropertyGroup>
<_JcwGen>"$(UtilityOutputFullPath)/jcw-gen.dll"</_JcwGen>
<_Target>--codegen-target JavaInterop1</_Target>
<_Output>-o "$(IntermediateOutputPath)/java"</_Output>
<_Libpath>@(_JcwGenRefAsmDirs->'-L "%(Identity)"', ' ')</_Libpath>
</PropertyGroup>
<Exec Command="$(DotnetToolPath) $(_JcwGen) &quot;$(TargetPath)&quot; $(_Target) $(_Output) $(_Libpath)" />
<Touch Files="$(IntermediateOutputPath)java\.stamp" AlwaysCreate="True" />
</Target>

<Target Name="_AddMarshalMethods"
Condition=" '$(TargetPath)' != '' "
Inputs="$(TargetPath)"
Outputs="$(IntermediateOutputPath).added-marshal-methods"
AfterTargets="_CreateJavaCallableWrappers">
<ItemGroup>
<!-- I can't find a good way to trim the trailing `\`, so append with `.` so we can sanely quote for $(_Libpath) -->
<_JnimmRefAsmDirs Include="@(RuntimePackAsset->'%(RootDir)%(Directory).'->Distinct())" />
</ItemGroup>
<PropertyGroup>
<_JnimarshalmethodGen>"$(UtilityOutputFullPath)/jnimarshalmethod-gen.dll"</_JnimarshalmethodGen>
<_Verbosity>-v -v --keeptemp</_Verbosity>
<_Libpath>-L "$(TargetDir)" @(_JnimmRefAsmDirs->'-L "%(Identity)"', ' ')</_Libpath>
<!-- <_Output>-o "$(IntermediateOutputPath)/jonp"</_Output> -->
</PropertyGroup>

<Exec Command="$(DotnetToolPath) $(_JnimarshalmethodGen) &quot;$(TargetPath)&quot; $(_Verbosity) $(_Libpath)" />

<!-- the IlcCompile target uses files from `$(IntermediateOutputPath)`, not `$(TargetPath)`, so… update both? -->
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(IntermediateOutputPath)" />

<Touch Files="$(IntermediateOutputPath).added-marshal-methods" AlwaysCreate="True" />
</Target>

<Target Name="BuildNativeAOTFromJNIJar"
AfterTargets="Build"
Inputs="@(HelloNativeAOTFromJNIJar)"
Outputs="$(OutputPath)hello-from-java.jar">
<MakeDir Directories="$(IntermediateOutputPath)h-classes" />
<ItemGroup>
<_JcwSource Include="$(IntermediateOutputPath)java\**\*.java" />
</ItemGroup>
<ItemGroup>
<_Source Include="@(_JcwSource->Replace('%5c', '/'))" />
<_Source Include="@(HelloNativeAOTFromJNIJar->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(IntermediateOutputPath)_java_sources.txt"
Lines="@(_Source)"
Overwrite="True"
/>
<ItemGroup>
<_JavacOpt Include="$(_JavacSourceOptions)" />
<_JavacOpt Include="-d &quot;$(IntermediateOutputPath)h-classes&quot; " />
<_JavacOpt Include="-classpath &quot;$(OutputPath)java-interop.jar&quot; " />
<_JavacOpt Include="&quot;@$(IntermediateOutputPath)_java_sources.txt&quot;" />
<_JavacOpt Include="-h &quot;$(IntermediateOutputPath)h-classes&quot; " />
</ItemGroup>
<Exec Command="&quot;$(JavaCPath)&quot; @(_JavacOpt, ' ')" />
<Delete Files="$(IntermediateOutputPath)_java_sources.txt" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)hello-from-java.jar&quot; -C &quot;$(IntermediateOutputPath)h-classes&quot; ." />
</Target>

<Target Name="_NativeLibRequiresLibPrefix"
AfterTargets="Publish">
<Copy
Condition=" '$(OS)' != 'Windows_NT' "
SourceFiles="$(PublishDir)$(AssemblyName).dylib"
DestinationFiles="$(PublishDir)lib$(AssemblyName).dylib"
/>
<Copy SourceFiles="$(OutputPath)hello-from-java.jar" DestinationFolder="$(PublishDir)" />
</Target>

<Target Name="RunJavaSample">
<ItemGroup>
<_Classpath Include="hello-from-java.jar" />
<_Classpath Include="java-interop.jar" />
</ItemGroup>
<PropertyGroup>
<_CPSep Condition=" '$(OS)' == 'Windows_NT' ">;</_CPSep>
<_CPSep Condition=" '$(_CPSep)' == '' ">:</_CPSep>
<_CP>@(_Classpath, '$(_CPSep)')</_CP>
</PropertyGroup>
<Exec
Command="&quot;$(JavaPath)&quot; -classpath &quot;$(_CP)&quot; net/dot/jni/hello/App"
WorkingDirectory="$(MSBuildThisFileDirectory)$(PublishDir)"
/>
</Target>
</Project>
40 changes: 40 additions & 0 deletions samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Runtime.InteropServices;

using Java.Interop;

namespace Hello_NativeAOTFromJNI;

static class JavaInteropRuntime
{
static JniRuntime? runtime;

[UnmanagedCallersOnly (EntryPoint="JNI_OnLoad")]
static int JNI_OnLoad (IntPtr vm, IntPtr reserved)
{
return (int) JniVersion.v1_6;
}

[UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")]
static void JNI_OnUnload (IntPtr vm, IntPtr reserved)
{
runtime?.Dispose ();
}

// symbol name from `$(IntermediateOutputPath)obj/Release/osx-arm64/h-classes/net_dot_jni_hello_JavaInteropRuntime.h`
[UnmanagedCallersOnly (EntryPoint="Java_net_dot_jni_hello_JavaInteropRuntime_init")]
static void init (IntPtr jnienv, IntPtr klass)
{
Console.WriteLine ($"C# init()");
try {
var options = new JreRuntimeOptions {
EnvironmentPointer = jnienv,
TypeManager = new NativeAotTypeManager (),
UseMarshalMemberBuilder = false,
};
runtime = options.CreateJreVM ();
}
catch (Exception e) {
Console.Error.WriteLine ($"JavaInteropRuntime.init: error: {e}");
}
}
}
22 changes: 22 additions & 0 deletions samples/Hello-NativeAOTFromJNI/ManagedType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Example;

using Java.Interop;

[JniTypeSignature (JniTypeName)]
class ManagedType : Java.Lang.Object {
internal const string JniTypeName = "example/ManagedType";

[JavaCallableConstructor(SuperConstructorExpression="")]
public ManagedType (int value)
{
this.value = value;
}

int value;

[JavaCallable ("getString")]
public Java.Lang.String GetString ()
{
return new Java.Lang.String ($"Hello from C#, via Java.Interop! Value={value}");
}
}
37 changes: 37 additions & 0 deletions samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Java.Interop;

namespace Hello_NativeAOTFromJNI;

class NativeAotTypeManager : JniRuntime.JniTypeManager {

#pragma warning disable IL2026
Dictionary<string, Type> typeMappings = new () {
[Example.ManagedType.JniTypeName] = typeof (Example.ManagedType),
};
#pragma warning restore IL2026


protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
{
if (typeMappings.TryGetValue (jniSimpleReference, out var target))
yield return target;
foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference))
yield return t;
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
return base.GetSimpleReferences (type)
.Concat (CreateSimpleReferencesEnumerator (type));
}

IEnumerable<string> CreateSimpleReferencesEnumerator (Type type)
{
if (typeMappings == null)
yield break;
foreach (var e in typeMappings) {
if (e.Value == type)
yield return e.Key;
}
}
}
Loading