Skip to content

Commit

Permalink
[generator] Fix StackOverflow when copying DIM via private interfaces (
Browse files Browse the repository at this point in the history
…#1261

Context: dotnet/android-libraries#988
Context: 9e0a469
Context: 1adb796
Context: #1183
Context: #1269

Imagine you bind the following Java interfaces:

	// Java
	package io.grpc;

	interface Configurator {
	  default void configureChannelBuilder(ManagedChannelBuilder<?> channelBuilder) {}
	}

	public interface InternalConfigurator extends Configurator {
	}

Because the `Configurator` interface is package-protected, its method
is copied into the `public` `InternalConfigurator` interface via
`generator`'s `FixupAccessModifiers()` code.

Later, we look for the "base" method that the copied
`InternalConfigurator.configureChannelBuilder()` overrides, which is
`Configurator.configureChannelBuilder`.  However, because we simply
added the same method instance to two types, this method reference is
actually pointing to itself.  

Eventually we hit a StackOverflowException when trying to retrieve the
[overridden method's declaring type][0].

Fix this by _cloning_ the method when we "copy" it, rather than having
two types reference the same `Method` instance.

…***then*** we get to *testing* this behavior: during code review,
@jonpryor noticed this change:

	diff --git a/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs b/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs
	index 3ea50e6bd..83d01f24a 100644
	--- a/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs
	+++ b/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs
	@@ -46,7 +46,7 @@ public unsafe void BaseMethod ()
	 		{
	 			const string __id = "baseMethod.()V";
	 			try {
	-				_members_xamarin_test_BaseInterface.InstanceMethods.InvokeAbstractVoidMethod (__id, this, null);
	+				_members_xamarin_test_ExtendedInterface.InstanceMethods.InvokeAbstractVoidMethod (__id, this, null);
	 			} finally {
	 			}
	 		}

and went "hmm".

Recall and consider 1adb796: if we invoke the interface method on
the "wrong" declaring type, things break.  This is why 1adb796
updated interface invokers to only invoke methods upon their declared
interfaces.

The concern comes around *non-`public`* interface method invocation:

	/* package */ interface PackagePrivateInterface {
	    void m();
	}
	public interface PublicInterface extends PackagePrivateInterface {
	}

With this commit, attempts to invoke `m()` are made upon
`PublicInterface`, not `PackagePrivateInterface`.

Is this a problem?

Begin partially implementing #1183, adding a new
`build-tools/Java.Interop.Sdk` (not quite a) "project", which has an
`Sdk.props` and `Sdk.targets` file, which combined add support for:

  * A `@(JavaCompile)` build action, for Java source.
  * A `@(JavaReference)` build action, for Java libraries.
  * As a pre-Compile step, files with `%(JavaCompile.Bind)`=True or
    `%(JavaReference.Bind)`=True will be automatically bound, via
    `class-parse` + `generator`.
  * As a post-Compile step, the assembly will be processed with
    `jcw-gen` to generate Java Callable Wrappers, which will be
    compiled into `$(AssemblyName).jar`.

Then, update `tests/Java.Base-Tests` to use this new functionality,
adding a test case to hit the "invoke method declared in a private
interface upon the public interface" scenario.

Result: it works!

Of partial interest is `IInterfaceMethodInheritanceInvoker`:

	internal partial class IInterfaceMethodInheritanceInvoker : global::Java.Lang.Object, IInterfaceMethodInheritance {
	    static readonly JniPeerMembers _members_net_dot_jni_test_BaseInterface = new JniPeerMembers ("net/dot/jni/test/BaseInterface", typeof (IInterfaceMethodInheritanceInvoker));
	    static readonly JniPeerMembers _members_net_dot_jni_test_InterfaceMethodInheritance = new JniPeerMembers ("net/dot/jni/test/InterfaceMethodInheritance", typeof (IInterfaceMethodInheritanceInvoker));
	    static readonly JniPeerMembers _members_net_dot_jni_test_InternalInterface = new JniPeerMembers ("net/dot/jni/test/InternalInterface", typeof (IInterfaceMethodInheritanceInvoker));
	    static readonly JniPeerMembers _members_net_dot_jni_test_PublicInterface = new JniPeerMembers ("net/dot/jni/test/PublicInterface", typeof (IInterfaceMethodInheritanceInvoker));
	}

Of these four fields, two are for internal types:
`_members_net_dot_jni_test_BaseInterface` and
`_members_net_dot_jni_test_InternalInterface`.

Fortunately those types aren't otherwise used.

Of concern, though, is that the constructor invocation *does* result
in a `JNIEnv::FindClass()` invocation, meaning these bindings would
look up (ostensibly) "private" types, which could change!

This presents a compatibility concern: if (when?) those type names
change, then the generated bindings will break.

TODO:

  * #1269: Update `generator` output to *not* emit the
    `static readonly JniPeerMembers` fields for internal types.

Finally, update `jnimarshalmethod-gen` to support resolving types
from the active assembly

For reasons I'm not going to investigate, if an interface type is
defined in the assembly that `jnimarshalmethod-gen` is processing,
`Assembly.Location` will be the empty string, which causes an error:

	% dotnet bin/Release-net8.0/jnimarshalmethod-gen.dll bin/TestRelease-net8.0/Java.Base-Tests.dll -v -v --keeptemp
	…
	error JM4006: jnimarshalmethod-gen: Unable to process assembly 'bin/TestRelease-net8.0/Java.Base-Tests.dll'
	Name can not be empty
	System.ArgumentException: Name can not be empty
	   at Mono.Cecil.AssemblyNameReference.Parse(String fullName)
	   at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve(String fullName, ReaderParameters parameters) in /Users/runner/work/1/s/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:line 261
	   at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve(String fullName) in /Users/runner/work/1/s/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:line 256
	   at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.GetAssembly(String fileName) in /Users/runner/work/1/s/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:line 251
	   at Xamarin.Android.Tools.JniMarshalMethodGenerator.Extensions.NeedsMarshalMethod(MethodDefinition md, DirectoryAssemblyResolver resolver, TypeDefinitionCache cache, MethodInfo method, String& name, String& methodName, String& signature) in /Users/runner/work/1/s/tools/jnimarshalmethod-gen/App.cs:line 790
	   at Xamarin.Android.Tools.JniMarshalMethodGenerator.App.CreateMarshalMethodAssembly(String path) in /Users/runner/work/1/s/tools/jnimarshalmethod-gen/App.cs:line 538
	   at Xamarin.Android.Tools.JniMarshalMethodGenerator.App.ProcessAssemblies(List`1 assemblies) in /Users/runner/work/1/s/tools/jnimarshalmethod-gen/App.cs:line 285

Update `App.NeedsMarshalMethod()` so that when the assembly Location
is not present, `DirectoryAssemblyResolver.Resolve(assemblyName)`
is instead used.  This prevents the `ArgumentException`.

[0]: https://github.com/dotnet/java-interop/blob/9d997232f0ce3ca6a5f788b1cdb0fb9b2f978c84/tools/generator/SourceWriters/BoundMethod.cs#L95-L100
  • Loading branch information
jpobst authored Oct 21, 2024
1 parent f863351 commit 2a1e180
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 54 deletions.
6 changes: 6 additions & 0 deletions build-tools/Java.Interop.Sdk/Sdk/Sdk.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<JavaPathSeparator Condition=" $([MSBuild]::IsOSPlatform('windows')) ">;</JavaPathSeparator>
<JavaPathSeparator Condition=" '$(JavacClasspathSeparator)' == '' ">:</JavaPathSeparator>
</PropertyGroup>
</Project>
218 changes: 218 additions & 0 deletions build-tools/Java.Interop.Sdk/Sdk/Sdk.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
<Project>

<ItemDefinitionGroup>
<JavaCompile>
<Bind>True</Bind>
</JavaCompile>
<JavaReference>
<Bind>True</Bind>
</JavaReference>
</ItemDefinitionGroup>

<PropertyGroup>
<JavaOutputJar Condition=" '$(JavaOutputJar)' == '' ">$(OutputPath)$(AssemblyName).jar</JavaOutputJar>
</PropertyGroup>

<PropertyGroup>
<JavaCreateManagedBindingsDependsOn>
_BuildJavaCompileForManagedBinding;
_GenerateApiDescription;
_GenerateManagedBinding;
_CleanupManagedBinding;
</JavaCreateManagedBindingsDependsOn>
<JavaCreateOutputJarDependsOn>
_JavaCreateJcws;
_JavaCreateOutputJar;
</JavaCreateOutputJarDependsOn>
</PropertyGroup>

<Target Name="JavaCreateManagedBindings"
BeforeTargets="CoreCompile"
DependsOnTargets="$(JavaCreateManagedBindingsDependsOn)">
</Target>

<Target Name="JavaCreateOutputJar"
AfterTargets="Build"
DependsOnTargets="$(JavaCreateOutputJarDependsOn)">
</Target>

<PropertyGroup>
<_GeneratorPath>$(UtilityOutputFullPath)generator.dll</_GeneratorPath>
<_JavaIntermediateDir>$(IntermediateOutputPath)_ji\</_JavaIntermediateDir>
<_JavaManagedBindingInput>$(_JavaIntermediateDir)o.jar</_JavaManagedBindingInput>
<_JavaManagedBindingDir>$(_JavaIntermediateDir)mcw\</_JavaManagedBindingDir>
<_JavaJcwClassesDir>$(_JavaIntermediateDir)classes\</_JavaJcwClassesDir>
<_JavaJcwSourcesDir>$(_JavaIntermediateDir)java\</_JavaJcwSourcesDir>
</PropertyGroup>

<Target Name="_CollectJavaCompileForManagedBindingInputs">
<ItemGroup>
<_JavaCompileForBindingInputs
Condition=" '%(JavaCompile.Bind)' == 'True' "
Include="@(JavaCompile)"
/>
</ItemGroup>
</Target>

<Target Name="_JavaCollectJavacRefs">
<ItemGroup>
<_JavacRefs Include="$(ToolOutputFullPath)java-interop.jar" />
<_JavacRefs Include="@(JavaReference)" />
<_JavacRefsWithForwardSlash Include="@(_JavacRefs->Replace('%5c', '/'))" />
</ItemGroup>
</Target>

<Target Name="_BuildJavaCompileForManagedBinding"
DependsOnTargets="_CollectJavaCompileForManagedBindingInputs;_JavaCollectJavacRefs"
Inputs="@(_JavaCompileForBindingInputs)"
Outputs="$(_JavaManagedBindingInput)">
<PropertyGroup>
<_ClassesDir>$(_JavaIntermediateDir)\bound-classes</_ClassesDir>
<_ResponseFile>$(_JavaIntermediateDir)r.txt</_ResponseFile>
<_Classpath>@(_JavacRefsWithForwardSlash, '$(JavaPathSeparator)')</_Classpath>
</PropertyGroup>
<MakeDir Directories="$(_ClassesDir)" />
<ItemGroup>
<_Response Include="-classpath" />
<_Response Include="&quot;$(_Classpath)&quot;" />
<_Response Include="@(_JavaCompileForBindingInputs->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(_ResponseFile)"
Lines="@(_Response)"
Overwrite="True"
/>
<Message Text="`javac` response file contents:" Importance="Low" />
<Message Text="@(_Response, '
')" Importance="Low" />
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(_ClassesDir)&quot; &quot;@$(_ResponseFile)&quot;" />
<Delete Files="$(_ResponseFile)" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(_JavaManagedBindingInput)&quot; -C &quot;$(_ClassesDir)&quot; ." />
</Target>

<Target Name="_CollectClassParseInputs">
<ItemGroup>
<_ClassParseInputs
Condition=" Exists($(_JavaManagedBindingInput))"
Include="$(_JavaManagedBindingInput)"
/>
<_ClassParseInputs
Condition=" '%(JavaReference.Bind)' == 'True' "
Include="@(JavaReference)"
/>
</ItemGroup>
</Target>

<Target Name="_GenerateApiDescription"
DependsOnTargets="_CollectClassParseInputs"
Inputs="@(_ClassParseInputs)"
Outputs="$(_JavaManagedBindingDir)api.xml">
<MakeDir Directories="$(_JavaManagedBindingDir)" />
<PropertyGroup>
<_ClassParse>"$(UtilityOutputFullPath)class-parse.dll"</_ClassParse>
<_Inputs>@(_ClassParseInputs, ' ')</_Inputs>
<_Output>"-o=$(_JavaManagedBindingDir)api.xml"</_Output>
</PropertyGroup>
<Exec
Command="$(DotnetToolPath) $(_ClassParse) $(_Inputs) $(_Output)"
/>
</Target>

<Target Name="_GenerateManagedBinding"
DependsOnTargets="_GenerateApiDescription"
Inputs="$(_JavaManagedBindingDir)api.xml"
Outputs="$(_JavaManagedBindingDir)$(AssemblyName).projitems">
<MakeDir Directories="$(_JavaManagedBindingDir)" />
<PropertyGroup>
<Generator>"$(_GeneratorPath)"</Generator>
<_GenFlags>--public --global</_GenFlags>
<_Out>-o "$(_JavaManagedBindingDir)."</_Out>
<_Codegen>--codegen-target=JavaInterop1</_Codegen>
<_Assembly>"--assembly=$(AssemblyName)"</_Assembly>
<_TypeMap>--type-map-report=$(_JavaManagedBindingDir)type-mapping.txt</_TypeMap>
<_Api>$(_JavaManagedBindingDir)api.xml</_Api>
<_Dirs>"--enumdir=$(_JavaManagedBindingDir)."</_Dirs>
<_FullIntermediateOutputPath>$([System.IO.Path]::GetFullPath('$(_JavaManagedBindingDir)'))</_FullIntermediateOutputPath>
<_LangFeatures>--lang-features=nullable-reference-types,default-interface-methods,nested-interface-types,interface-constants</_LangFeatures>
</PropertyGroup>
<ItemGroup>
<!-- I can't find a good way to trim the trailing `\`, so append with `.` so we can sanely quote for $(_Libpath) -->
<_RefAsmDir Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" />
<_Lib Include="@(_RefAsmDir->'-L &quot;%(Identity)&quot;')" />
<_JavaBaseRef Include="@(ReferencePathWithRefAssemblies)"
Condition=" '%(FileName)' == 'Java.Base' "
/>
<_Ref Include="@(_JavaBaseRef->'-r &quot;%(FullPath)&quot;')" />
</ItemGroup>
<Exec
Command="$(DotnetToolPath) $(Generator) $(_GenFlags) $(_ApiLevel) $(_Out) @(_Lib, ' ') @(_Ref, ' ') $(_Codegen) $(_Assembly) $(_TypeMap) $(_LangFeatures) $(_Dirs) $(_Api) $(_WithJavadocXml)"
IgnoreStandardErrorWarningFormat="True"
/>
<ItemGroup>
<Compile Include="$(_FullIntermediateOutputPath)\**\*.cs" KeepDuplicates="False" />
<FileWrites Include="$(_FullIntermediateOutputPath)\**\*.cs" />
</ItemGroup>
<XmlPeek
Namespaces="&lt;Namespace Prefix='msbuild' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
XmlInputPath="$(_JavaManagedBindingDir)$(AssemblyName).projitems"
Query="/msbuild:Project/msbuild:PropertyGroup/msbuild:DefineConstants/text()" >
<Output TaskParameter="Result" PropertyName="_GeneratedDefineConstants" />
</XmlPeek>
<PropertyGroup>
<DefineConstants>$(DefineConstants);$([System.String]::Copy('$(_GeneratedDefineConstants)').Replace ('%24(DefineConstants);', ''))</DefineConstants>
</PropertyGroup>
</Target>

<Target Name="_CleanupManagedBinding" />

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

<Target Name="_JavaCollectGeneratdJcwSource">
<ItemGroup>
<_JavaGeneratedJcwSource Include="$(_JavaJcwSourcesDir)**\*.java" />
</ItemGroup>
</Target>

<Target Name="_JavaCreateOutputJar"
DependsOnTargets="_JavaCollectGeneratdJcwSource;_JavaCollectJavacRefs"
Inputs="@(_JavaGeneratedJcwSource)"
Outputs="$(JavaOutputJar)">
<RemoveDir Directories="$(_JavaJcwClassesDir)" />
<MakeDir Directories="$(_JavaJcwClassesDir)" />
<PropertyGroup>
<_ResponseFile>$(_JavaIntermediateDir)r.txt</_ResponseFile>
<_Classpath>@(_JavacRefsWithForwardSlash, '$(JavaPathSeparator)')</_Classpath>
</PropertyGroup>
<ItemGroup>
<_Source Include="@(JavaCompile->Replace('%5c', '/'))" />
<_Source Include="@(_JavaGeneratedJcwSource->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(_JavaIntermediateDir)_java_sources.txt"
Lines="@(_Source)"
Overwrite="True"
/>
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(_JavaJcwClassesDir).&quot; -classpath &quot;$(_Classpath)&quot; &quot;@$(_JavaIntermediateDir)_java_sources.txt&quot;" />
<Delete Files="$(_JavaIntermediateDir)_java_sources.txt" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(JavaOutputJar)&quot; -C &quot;$(_JavaJcwClassesDir).&quot; ." />
</Target>

</Project>
11 changes: 11 additions & 0 deletions tests/Java.Base-Tests/Java.Base-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
<IsPackable>false</IsPackable>
</PropertyGroup>

<Import Project="..\..\build-tools\Java.Interop.Sdk\Sdk\Sdk.props" />
<Import Project="..\..\TargetFrameworkDependentValues.props" />

<PropertyGroup>
<OutputPath>$(TestOutputFullPath)</OutputPath>
<JavaOutputJar>$(OutputPath)java.base-tests.jar</JavaOutputJar>
</PropertyGroup>

<ItemGroup>
Expand All @@ -26,6 +28,15 @@
<ProjectReference Include="..\TestJVM\TestJVM.csproj" />
</ItemGroup>

<ItemGroup>
<JavaCompile Include="$(MSBuildThisFileDirectory)java\net\dot\jni\test\HasInterfaceMethodInheritance.java" />
<JavaCompile Include="$(MSBuildThisFileDirectory)java\net\dot\jni\test\InterfaceMethodInheritance.java" />
<JavaCompile Include="$(MSBuildThisFileDirectory)java\net\dot\jni\test\PublicInterface.java" />
<JavaCompile Include="java\**\*.java" Bind="False" />

</ItemGroup>

<Import Project="Java.Base-Tests.targets" />
<Import Project="..\..\build-tools\Java.Interop.Sdk\Sdk\Sdk.targets" />

</Project>
47 changes: 0 additions & 47 deletions tests/Java.Base-Tests/Java.Base-Tests.targets
Original file line number Diff line number Diff line change
@@ -1,49 +1,2 @@
<Project>

<ItemGroup>
<_BuildJavaBaseTestsJarInputs Include="$(TargetPath)" />
<_BuildJavaBaseTestsJarInputs Include="$(MSBuildThisFileFullPath)" />
<_BuildJavaBaseTestsJarInputs Include="$(MSBuildThisFileFullPath)" />
<_BuildJavaBaseTestsJarInputs Include="java\**\*.java" />
</ItemGroup>

<Target Name="_CreateJavaCallableWrappers"
Condition=" '$(TargetPath)' != '' "
AfterTargets="Build"
Inputs="@(_BuildJavaBaseTestsJarInputs)"
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) -->
<_RefAsmDirs Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" />
</ItemGroup>
<PropertyGroup>
<_JcwGen>"$(UtilityOutputFullPath)/jcw-gen.dll"</_JcwGen>
<_Target>--codegen-target JavaInterop1</_Target>
<_Output>-o "$(IntermediateOutputPath)/java"</_Output>
<_Libpath>@(_RefAsmDirs->'-L "%(Identity)"', ' ')</_Libpath>
</PropertyGroup>
<Exec Command="$(DotnetToolPath) $(_JcwGen) -v &quot;$(TargetPath)&quot; $(_Target) $(_Output) $(_Libpath)" />
<Touch Files="$(IntermediateOutputPath)java\.stamp" AlwaysCreate="True" />
</Target>

<ItemGroup>
<_JcwSource Include="$(IntermediateOutputPath)java\**\*.java;java\**\*.java" />
</ItemGroup>

<Target Name="_BuildJavaBaseTestsJar"
Condition=" '$(TargetPath)' != '' "
AfterTargets="_CreateJavaCallableWrappers"
Inputs="$(IntermediateOutputPath)java\.stamp;@(_JcwSource)"
Outputs="$(OutputPath)java.base-tests.jar">
<RemoveDir Directories="$(IntermediateOutputPath)classes" />
<MakeDir Directories="$(IntermediateOutputPath)classes" />
<ItemGroup>
<_JcwSourceReal Include="$(IntermediateOutputPath)java\**\*.java;java\**\*.java" />
</ItemGroup>
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(IntermediateOutputPath)classes&quot; -classpath &quot;$(OutputPath)/java-interop.jar&quot; @(_JcwSourceReal->'%(Identity)', ' ')" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)java.base-tests.jar&quot; -C &quot;$(IntermediateOutputPath)classes&quot; ." />
</Target>

</Project>
26 changes: 26 additions & 0 deletions tests/Java.Base-Tests/Java.Base/InterfaceMethodInheritanceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

using Java.Interop;

using NUnit.Framework;

namespace Java.BaseTests {

[TestFixture]
public class InterfaceMethodInheritanceTests : JavaVMFixture {

[Test]
public void InterfaceMethod ()
{
using var iface = global::Net.Dot.Jni.Test.HasInterfaceMethodInheritance.Create ();
var m = iface!.M ();
Assert.AreEqual ("HasInterfaceMethodInheritance.m", m);
var n = iface!.N ();
Assert.AreEqual ("HasInterfaceMethodInheritance.n", n);
var o = iface!.O ();
Assert.AreEqual ("HasInterfaceMethodInheritance.o", o);
var p = iface!.P ();
Assert.AreEqual ("HasInterfaceMethodInheritance.p", p);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.dot.jni.test;

public class HasInterfaceMethodInheritance implements InterfaceMethodInheritance {
private HasInterfaceMethodInheritance() {
}

public static InterfaceMethodInheritance create() {
return new HasInterfaceMethodInheritance();
}

public String m() {
return "HasInterfaceMethodInheritance.m";
}

public String n() {
return "HasInterfaceMethodInheritance.n";
}

public String o() {
return "HasInterfaceMethodInheritance.o";
}

public String p() {
return "HasInterfaceMethodInheritance.p";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.dot.jni.test;

/* package */ interface BaseInterface {
String m();
}

public interface InterfaceMethodInheritance extends BaseInterface, PublicInterface {
String n();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.dot.jni.test;

/* package */ interface InternalInterface {
String o();
}

public interface PublicInterface extends InternalInterface {
String p();
}
Loading

0 comments on commit 2a1e180

Please sign in to comment.