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

support default interface methods #341

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
466f204
Initial default interface methods work.
atsushieno Jun 28, 2018
ab831c5
Forgot to limit property implementation generation only to DIM-based …
atsushieno Jun 28, 2018
cd537ff
generator unit tests now supports DIM-supported csc via nuget package.
atsushieno Jun 29, 2018
60a950e
"_members" field is needed inside InterfaceGen, whenever DIM bindings…
atsushieno Jun 29, 2018
e027582
Now generator generates missing (IPeerable) casts so that code compiles.
atsushieno Jul 2, 2018
8de5313
Add a bit more complicated DIM tests: property setter, String, String…
atsushieno Jul 2, 2018
0e75928
Add more test lines to ensure that the generated code actually loads …
atsushieno Jul 3, 2018
2321d79
Explicitly set JniRuntime(TestJVM) so that nunit can initialize JVM t…
atsushieno Jul 3, 2018
b7d00cf
Add binding-integration-Tests (ongoing work).
atsushieno Jul 5, 2018
4db1295
implement binding integration tests up to api-xml-adjuster.
atsushieno Jul 5, 2018
ef578da
Add generator verification in binding-integration-Tests.
atsushieno Jul 6, 2018
f157e4c
default interface method run-time tests will not be done in generator…
atsushieno Jul 6, 2018
8931d84
Make generator support files common and add csc test in binding-integ…
atsushieno Jul 9, 2018
6757070
Disable integration tests that has no possibility to work.
atsushieno Jul 9, 2018
5899233
Revert "Disable integration tests that has no possibility to work."
atsushieno Jul 9, 2018
18e62b3
Revert "Make generator support files common and add csc test in bindi…
atsushieno Jul 9, 2018
1d1c085
Revert most of the changes that are done only to get integration csc …
atsushieno Jul 9, 2018
770d400
remove unnecessary project references from generator-Tests made in di…
atsushieno Jul 9, 2018
9775acb
Futher removal of extraneous project references that causes build bre…
atsushieno Jul 9, 2018
8128a1b
Do not generate method implementations for DIM in ClassGen if DIM is …
atsushieno Jul 10, 2018
6b46e43
Fix t4 template for JniInstanceMethods.
atsushieno Jul 11, 2018
f5927af
For default interface methods, use CallNonvirtualXxxMethods().
atsushieno Jul 11, 2018
a6e7d82
Fix DIM test expected output C# code. Should emit "Nonvirtual"
atsushieno Jul 12, 2018
92547ee
Partially revert Method generation switch for DIMs and non-DIMs.
atsushieno Jul 12, 2018
fb6e911
Revert previous changes to generator-Tests, it was not really doable.
atsushieno Jul 17, 2018
5f8562b
[generator] generate appropriate JniPeerMembers for XAJavaInterop1.
atsushieno Jul 17, 2018
1ec6568
[generator] For DIM-supported mode, make interfaces implement IJavaPe…
atsushieno Jul 17, 2018
ea55079
[generator] fix bogus method code generation in interface Extensions …
atsushieno Jul 18, 2018
36b57da
[generator] Fix and extend marking of "this is overriding DIM" step.
atsushieno Jul 18, 2018
8eb0b0a
[generator] change DIM override marking.
atsushieno Jul 18, 2018
7400369
[generator] explicit DIM needs to specify exactly-declaring type.
atsushieno Jul 18, 2018
24e0f45
[generator] mark overriding method of non-DIMs in interfaces too.
atsushieno Jul 18, 2018
b85ecad
[generator] fix excess override marking from the previous change.
atsushieno Jul 18, 2018
5ffada2
[generator] we need to check all the interfaces "of all the base types".
atsushieno Jul 18, 2018
0e9b043
[generator] fix DIM override lookup loop.
atsushieno Jul 18, 2018
bc7400f
[generator] reduce extraneous abstract methods and properties for DIM…
atsushieno Jul 19, 2018
a914538
[generator] add some code comment for future understanding.
atsushieno Jul 19, 2018
a6253f7
[generator] makr as DIM overrides as `virtual`.
atsushieno Jul 19, 2018
e6be186
[generator] bring back static methods in the public API.
atsushieno Jul 19, 2018
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
13 changes: 12 additions & 1 deletion Java.Interop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{4C173212-3
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop", "src\Java.Interop\Java.Interop.csproj", "{94BD81F7-B06F-4295-9636-F8A3B6BDC762}"
EndProject
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.Dynamic", "src\Java.Interop.Dynamic\Java.Interop.Dynamic.csproj", "{AD4468F8-8883-434B-9D4C-E1801BB3B52A}"
EndProject
Expand Down Expand Up @@ -99,6 +99,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.GenericMarshal
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.BootstrapTasks", "src\Java.Interop.BootstrapTasks\Java.Interop.BootstrapTasks.csproj", "{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "binding-integration-Tests", "tests\binding-integrated-Tests\binding-integration-Tests.csproj", "{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -349,6 +351,14 @@ Global
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Release|Any CPU.Build.0 = Release|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0C001D50-4176-45AE-BDC8-BA626508B0CC} = {C8F58966-94BF-407F-914A-8654F8B8AE3B}
Expand Down Expand Up @@ -392,5 +402,6 @@ Global
{C0487169-8F81-497F-919E-EB42B1D0243F} = {C8F58966-94BF-407F-914A-8654F8B8AE3B}
{D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF} = {4C173212-371D-45D8-BA83-9226194F48DC}
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A} = {172B608B-E6F3-41CC-9949-203A76BA247C}
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E} = {271C9F30-F679-4793-942B-0D9527CB3E2F}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace Java.Interop {
JniPeerMembers.AssertSelf (self);

var declaringType = DeclaringType;
if (Members.ShouldUseVirtualDispatch (self, declaringType)) {
if (Members.UsesVirtualDispatch (self, declaringType)) {
var m = GetMethodInfo (encodedMember);
<#= returnType.ReturnType != "void" ? "return " : "" #>JniEnvironment.InstanceMethods.Call<#= returnType.JniCallType #>Method (self.PeerReference, m, parameters);
<#= returnType.ReturnType == "void" ? "return;" : "" #>
Expand Down
292 changes: 292 additions & 0 deletions tests/binding-integrated-Tests/BindingBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Xamarin.Android.Tools.Bytecode;
using Xamarin.Android.Tools.ApiXmlAdjuster;

namespace BindingIntegrationTests
{
public class BindingBuilder
{
// This is required to get generator.exe deployed next to this assembly
// so that the default GeneratorPath can probe that it is right there.
static readonly Type dummy = typeof (MonoDroid.Generation.GenBase);

[Flags]
public enum Steps
{
Javac = 1,
Jar = 2,
ClassParse = 4,
ApiXmlAdjuster = 8,
Generator = 16,
Csc = 32,
All = Javac | Jar | ClassParse | ApiXmlAdjuster | Generator | Csc
}

public const string JavaSourcesSubDir = "java-sources";
public const string ClassesSubDir = "classes";
public const string ClassParseSubDir = "class-parse-xml";
public const string ApiXmlSubDir = "api-xml";
public const string MetadataXmlSubDir = "metadata";
public const string CSharpSourcesSubDir = "csharp";

public Steps ProcessSteps { get; set; } = Steps.All;

// entire work (intermediate output) directory
public string IntermediateOutputPathRelative { get; set; } = "intermediate-output";

// Used to resolve javac and rt.jar
public string JdkPath { get; set; }

public string GeneratorPath { get; set; } = Path.Combine (Path.GetDirectoryName (new Uri (typeof (BindingBuilder).Assembly.CodeBase).LocalPath), "generator.exe");

static string ProbeJavaHome ()
{
var env = Environment.GetEnvironmentVariable ("JAVA_HOME");
if (!string.IsNullOrEmpty (env))
return env;
return "/usr/lib/jvm/java-8-openjdk-amd64/";
}

public static BindingBuilder CreateBestBetDefault (BindingProject project)
{
return new BindingBuilder (project) { JdkPath = ProbeJavaHome () };
}

public BindingBuilder (BindingProject project)
{
this.project = project;
}

readonly BindingProject project;

public string IntermediateOutputPathAbsolute => Path.Combine (Path.GetDirectoryName (new Uri (GetType ().Assembly.CodeBase).LocalPath), IntermediateOutputPathRelative, project.Id);

public void Clean ()
{
if (Directory.Exists (IntermediateOutputPathAbsolute))
Directory.Delete (IntermediateOutputPathAbsolute, true);
}

public void Build ()
{
Javac ();
Jar ();
ClassParse ();
AdjustApiXml ();
GenerateBindingSources ();
CompileBindings ();
}

void EnsureDirectory (string dir)
{
var parent = Path.GetDirectoryName (dir);
if (parent != Path.GetPathRoot (parent))
EnsureDirectory (parent);
if (!Directory.Exists (dir))
Directory.CreateDirectory (dir);
}

void Javac ()
{
if ((ProcessSteps & Steps.Javac) == 0)
return;

if (JdkPath == null)
throw new InvalidOperationException ("JdkPath is not set.");

var objDir = IntermediateOutputPathAbsolute;
EnsureDirectory (objDir);

string sourcesSaved = Path.Combine (objDir, JavaSourcesSubDir);
EnsureDirectory (sourcesSaved);
foreach (var item in project.JavaSourceStrings)
File.WriteAllText (Path.Combine (sourcesSaved, item.FileName), item.Content);
var sourceFiles = project.JavaSourceFiles.Concat (project.JavaSourceStrings.Select (i => Path.Combine (sourcesSaved, i.FileName)));

if (project.CompiledClassesDirectory == null)
project.CompiledClassesDirectory = Path.Combine (objDir, ClassesSubDir);
EnsureDirectory (project.CompiledClassesDirectory);

var psi = new ProcessStartInfo () {
UseShellExecute = false,
FileName = JdkPath != null ? Path.Combine (JdkPath, "bin", "javac") : "javac",
Arguments = $"{project.JavacOptions} -d \"{project.CompiledClassesDirectory}\" {string.Join (" ", sourceFiles.Select (s => '"' + s + '"'))}",
RedirectStandardOutput = true,
RedirectStandardError = true,
};
if (project.CustomRuntimeJar != null)
psi.Arguments += $" -bootclasspath {project.CustomRuntimeJar} -classpath {project.CustomRuntimeJar}";

project.JavacExecutionOutput = $"Execute javac as: {psi.FileName} {psi.Arguments}\n";

var proc = new Process () { StartInfo = psi };
proc.OutputDataReceived += (sender, e) => project.JavacExecutionOutput += e.Data;
proc.ErrorDataReceived += (sender, e) => project.JavacExecutionOutput += e.Data;
proc.Start ();
proc.BeginOutputReadLine ();
proc.BeginErrorReadLine ();
proc.WaitForExit ();
if (proc.ExitCode != 0)
throw new Exception ("Javac failed: " + project.JavacExecutionOutput);
}

void Jar ()
{
if ((ProcessSteps & Steps.Jar) == 0)
return;

if (JdkPath == null)
throw new InvalidOperationException ("JdkPath is not set.");

var objDir = IntermediateOutputPathAbsolute;
if (project.CompiledClassesDirectory == null)
project.CompiledClassesDirectory = Path.Combine (objDir, ClassesSubDir);
if (project.CompiledJarFile == null)
project.CompiledJarFile = Path.Combine (project.CompiledClassesDirectory, project.Id + ".jar");

var psi = new ProcessStartInfo () {
UseShellExecute = false,
FileName = JdkPath != null ? Path.Combine (JdkPath, "bin", "jar") : "jar",
Arguments = $"cvf \"{project.CompiledJarFile}\" -C \"{project.CompiledClassesDirectory}\" .",
RedirectStandardOutput = true,
RedirectStandardError = true,
};

project.JarExecutionOutput = $"Execute jar as: {psi.FileName} {psi.Arguments}\n";

var proc = new Process () { StartInfo = psi };
proc.OutputDataReceived += (sender, e) => project.JarExecutionOutput += e.Data;
proc.ErrorDataReceived += (sender, e) => project.JarExecutionOutput += e.Data;
proc.Start ();
proc.BeginOutputReadLine ();
proc.BeginErrorReadLine ();
proc.WaitForExit ();
if (proc.ExitCode != 0)
throw new Exception ("Jar failed: " + project.JarExecutionOutput);
}

void ClassParse ()
{
if ((ProcessSteps & Steps.ClassParse) == 0)
return;

if (project.CompiledJarFile == null && !project.InputJarFiles.Any ())
throw new InvalidOperationException ("Input Jar files are not set either at CompiledJarFile or InputJarFiles.");

var objDir = IntermediateOutputPathAbsolute;
EnsureDirectory (objDir);
var cpDir = Path.Combine (objDir, ClassParseSubDir);
EnsureDirectory (cpDir);
if (project.GeneratedClassParseXmlFile == null)
project.GeneratedClassParseXmlFile = Path.Combine (cpDir, project.Id + ".class-parse");

// FIXME: logging
var cp = new ClassPath ();
cp.Load (project.CompiledJarFile);
foreach (var jar in project.InputJarFiles)
cp.Load (jar);
cp.SaveXmlDescription (project.GeneratedClassParseXmlFile);
}

void AdjustApiXml ()
{
if ((ProcessSteps & Steps.ApiXmlAdjuster) == 0)
return;

var objDir = IntermediateOutputPathAbsolute;
var cpDir = Path.Combine (objDir, ClassParseSubDir);
EnsureDirectory (cpDir);
if (project.GeneratedApiXmlFile == null)
project.GeneratedApiXmlFile = Path.Combine (objDir, "api.xml");
if (!File.Exists (project.GeneratedClassParseXmlFile) && !project.ClassParseXmlFiles.Any () && !project.ClassParseXmlStrings.Any ())
throw new InvalidOperationException ("Input class-parse file does not exist.");

foreach (var cpSource in project.ClassParseXmlStrings)
File.WriteAllText (Path.Combine (cpDir, cpSource.FileName), cpSource.Content);
var cpFiles = project.ClassParseXmlFiles.Concat (project.ClassParseXmlStrings.Select (i => Path.Combine (cpDir, i.FileName)));

// FIXME: this does not scale for parallel tasking.
var writer = new StringWriter ();
Xamarin.Android.Tools.ApiXmlAdjuster.Log.DefaultWriter = writer;

var api = new JavaApi ();
if (File.Exists (project.GeneratedClassParseXmlFile))
api.Load (project.GeneratedClassParseXmlFile);
foreach (var apixml in cpFiles)
api.Load (apixml);
api.Resolve ();
api.CreateGenericInheritanceMapping ();
api.MarkOverrides ();
api.FindDefects ();
api.Save (project.GeneratedApiXmlFile);
project.ApiXmlAdjusterExecutionOutput = writer.ToString ();
if (project.ApiXmlAdjusterExecutionOutput != string.Empty)
throw new Exception ("api-xml-adjuster failed: " + project.ApiXmlAdjusterExecutionOutput);
}

void GenerateBindingSources ()
{
if ((ProcessSteps & Steps.Generator) == 0)
return;

if (GeneratorPath == null)
throw new InvalidOperationException ("GeneratorPath is not set.");

var objDir = IntermediateOutputPathAbsolute;
EnsureDirectory (objDir);

if (project.GeneratedApiXmlFile == null)
project.GeneratedApiXmlFile = Path.Combine (objDir, "api.xml");
if (!File.Exists (project.GeneratedApiXmlFile) && !project.ApiXmlFiles.Any () && !project.ApiXmlStrings.Any ())
throw new InvalidOperationException ("Input api xml file does not exist.");
if (project.GeneratedCSharpSourceDirectory == null)
project.GeneratedCSharpSourceDirectory = Path.Combine (objDir, CSharpSourcesSubDir);

string apiXmlSaved = Path.Combine (objDir, ApiXmlSubDir);
EnsureDirectory (apiXmlSaved);
foreach (var item in project.ApiXmlStrings)
File.WriteAllText (Path.Combine (apiXmlSaved, item.FileName), item.Content);
var apiXmlFiles = project.ApiXmlFiles.Concat (project.ApiXmlStrings.Select (i => Path.Combine (apiXmlSaved, i.FileName)));

string metadataSaved = Path.Combine (objDir, MetadataXmlSubDir);
EnsureDirectory (metadataSaved);
foreach (var item in project.MetadataXmlStrings)
File.WriteAllText (Path.Combine (metadataSaved, item.FileName), item.Content);
var metadataFiles = project.MetadataXmlFiles.Concat (project.MetadataXmlStrings.Select (i => Path.Combine (metadataSaved, i.FileName)));

var psi = new ProcessStartInfo () {
UseShellExecute = false,
FileName = GeneratorPath,
Arguments = $"{project.GeneratorOptions}" +
$" {(File.Exists (project.GeneratedApiXmlFile) ? project.GeneratedApiXmlFile : string.Empty)}" +
$" {string.Join (" ", apiXmlFiles.Select (s => '"' + s + '"'))}" +
$" {string.Join (" ", metadataFiles.Select (s => " --fixup=\"" + s + '"'))}" +
$" {string.Join (" ", project.ReferenceDlls.Select (s => " -r \"" + s + '"'))}" +
$" --csdir=\"{project.GeneratedCSharpSourceDirectory}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
};

project.GeneratorExecutionOutput = $"Execute generator as: {psi.FileName} {psi.Arguments}\n";

var proc = new Process () { StartInfo = psi };
proc.OutputDataReceived += (sender, e) => project.GeneratorExecutionOutput += e.Data;
proc.ErrorDataReceived += (sender, e) => project.GeneratorExecutionOutput += e.Data;
proc.Start ();
proc.BeginOutputReadLine ();
proc.BeginErrorReadLine ();
proc.WaitForExit ();
if (proc.ExitCode != 0)
throw new Exception ("generator failed: " + project.GeneratorExecutionOutput);
}

void CompileBindings ()
{
}
}
}

Loading