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

[generator] Support default interface methods. #459

Merged
merged 10 commits into from
Aug 23, 2019
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
1 change: 1 addition & 0 deletions tools/generator/CodeGenerationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal CodeGenerator CreateCodeGenerator (TextWriter writer)
public bool UseShortFileNames { get; set; }
public int ProductVersion { get; set; }
public bool SupportInterfaceConstants { get; set; }
public bool SupportDefaultInterfaceMethods { get; set; }
public bool UseShallowReferencedTypes { get; set; }

bool? buildingCoreAssembly;
Expand Down
3 changes: 2 additions & 1 deletion tools/generator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
UseShortFileNames = options.UseShortFileNames,
ProductVersion = options.ProductVersion,
SupportInterfaceConstants = options.SupportInterfaceConstants,
SupportDefaultInterfaceMethods = options.SupportDefaultInterfaceMethods
};

// Load reference libraries
Expand Down Expand Up @@ -143,7 +144,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
// disable interface default methods here, especially before validation.
gens = gens.Where (g => !g.IsObfuscated && g.Visibility != "private").ToList ();
foreach (var gen in gens) {
gen.StripNonBindables ();
gen.StripNonBindables (opt);
if (gen.IsGeneratable)
AddTypeToTable (opt, gen);
}
Expand Down
8 changes: 6 additions & 2 deletions tools/generator/CodeGeneratorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public CodeGeneratorOptions ()
public bool OnlyRunApiXmlAdjuster { get; set; }
public string ApiXmlAdjusterOutput { get; set; }
public bool SupportInterfaceConstants { get; set; }
public bool SupportDefaultInterfaceMethods { get; set; }

public static CodeGeneratorOptions Parse (string[] args)
{
Expand Down Expand Up @@ -87,8 +88,11 @@ public static CodeGeneratorOptions Parse (string[] args)
"SDK Platform {VERSION}/API level.",
v => opts.ApiLevel = v },
{ "lang-features=",
"For internal use. (Flags: interface-constants)",
v => opts.SupportInterfaceConstants = v?.Contains ("interface-constants") == true },
"For internal use. (Flags: interface-constants,default-interface-methods)",
v => {
opts.SupportInterfaceConstants = v?.Contains ("interface-constants") == true;
opts.SupportDefaultInterfaceMethods = v?.Contains ("default-interface-methods") == true;
}},
{ "preserve-enums",
"For internal use.",
v => opts.PreserveEnums = v != null },
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ internal override void WriteMethodIdField (Method method, string indent)
// No method id_ field required; it's now an `id` constant in the binding.
}

internal override void WriteMethodBody (Method method, string indent)
internal override void WriteMethodBody (Method method, string indent, GenBase type)
{
writer.WriteLine ("{0}const string __id = \"{1}.{2}\";", indent, method.JavaName, method.JniSignature);
foreach (string prep in method.Parameters.GetCallPrep (opt))
Expand All @@ -151,21 +151,26 @@ internal override void WriteMethodBody (Method method, string indent)
writer.Write ("var __rm = ");
}

var castToPeerable = type is InterfaceGen ? "(IJavaPeerable) " : string.Empty;

if (method.IsStatic) {
writer.WriteLine ("_members.StaticMethods.Invoke{0}Method (__id{1});",
invokeType,
method.Parameters.GetCallArgs (opt, invoker: false));
} else if (method.IsFinal) {
writer.WriteLine ("_members.InstanceMethods.InvokeNonvirtual{0}Method (__id, this{1});",
} else if (method.IsFinal || method.IsInterfaceDefaultMethod) {
writer.WriteLine ("_members.InstanceMethods.InvokeNonvirtual{0}Method (__id, {1}this{2});",
invokeType,
castToPeerable,
method.Parameters.GetCallArgs (opt, invoker: false));
} else if (method.IsVirtual && !method.IsAbstract) {
writer.WriteLine ("_members.InstanceMethods.InvokeVirtual{0}Method (__id, this{1});",
writer.WriteLine ("_members.InstanceMethods.InvokeVirtual{0}Method (__id, {1}this{2});",
invokeType,
castToPeerable,
method.Parameters.GetCallArgs (opt, invoker: false));
} else {
writer.WriteLine ("_members.InstanceMethods.InvokeAbstract{0}Method (__id, this{1});",
writer.WriteLine ("_members.InstanceMethods.InvokeAbstract{0}Method (__id, {1}this{2});",
invokeType,
castToPeerable,
method.Parameters.GetCallArgs (opt, invoker: false));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void GenerateJNICall (Method method, string indent, string call, bool declare_re
writer.WriteLine ("{0}return {1};", indent, method.RetVal.FromNative (opt, call, true));
}

internal override void WriteMethodBody (Method method, string indent)
internal override void WriteMethodBody (Method method, string indent, GenBase type)
{
writer.WriteLine ("{0}if ({1} == IntPtr.Zero)", indent, method.EscapedIdName);
writer.WriteLine ("{0}\t{1} = JNIEnv.Get{2}MethodID (class_ref, \"{3}\", \"{4}\");", indent, method.EscapedIdName, method.IsStatic ? "Static" : String.Empty, method.JavaName, method.JniSignature);
Expand Down
36 changes: 24 additions & 12 deletions tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public virtual void FixupExplicitImplementation ()

public void FixupMethodOverrides (CodeGenerationOptions opt)
{
foreach (var m in Methods.Where (m => !m.IsInterfaceDefaultMethod)) {
foreach (var m in Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod)) {
for (var bt = GetBaseGen (opt); bt != null; bt = bt.GetBaseGen (opt)) {
var bm = bt.Methods.FirstOrDefault (mm => mm.Name == m.Name && mm.Visibility == m.Visibility && ParameterList.Equals (mm.Parameters, m.Parameters));
if (bm != null && bm.RetVal.FullName == m.RetVal.FullName) { // if return type is different, it could be still "new", not "override".
Expand All @@ -301,11 +301,22 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
}

// Interface default methods can be overriden. We want to process them differently.
foreach (var m in Methods.Where (m => m.IsInterfaceDefaultMethod)) {
foreach (var bt in GetAllDerivedInterfaces ()) {
var bm = bt.Methods.FirstOrDefault (mm => mm.Name == m.Name && ParameterList.Equals (mm.Parameters, m.Parameters));
var checkDimOverrideTargets = opt.SupportDefaultInterfaceMethods ? Methods : Methods.Where (m => m.IsInterfaceDefaultMethod);

// We need to check all the implemented interfaces of all the base types.
var allIfaces = new List<InterfaceGen> ();

for (var gen = this; gen != null; gen = gen.BaseGen)
gen.GetAllDerivedInterfaces (allIfaces);

foreach (var m in checkDimOverrideTargets.Where (m => !m.IsStatic)) {
foreach (var bt in allIfaces.Distinct ()) {
// We mark a method as an override if (1) it is a DIM, or (2) if the base method is DIM
// (i.e. we don't mark as override if a class method "implements" normal iface method.)
var bm = bt.Methods.FirstOrDefault (mm => (m.IsInterfaceDefaultMethod || !mm.IsAbstract) && mm.Name == m.Name && ParameterList.Equals (mm.Parameters, m.Parameters));

if (bm != null) {
m.IsInterfaceDefaultMethodOverride = true;
m.OverriddenInterfaceMethod = bm;
break;
}
}
Expand Down Expand Up @@ -411,7 +422,7 @@ void visit (ISymbol isym)
}

public IEnumerable<Method> GetAllMethods () =>
Methods.Concat (Properties.Select (p => p.Getter)).Concat (Properties.Select (p => p.Setter).Where (m => m != null));
Methods.Concat (Properties.Select (p => p.Getter)).Concat (Properties.Select (p => p.Setter)).Where (m => m != null);

GenBase GetBaseGen (CodeGenerationOptions opt)
{
Expand Down Expand Up @@ -734,15 +745,16 @@ bool ReturnTypeMatches (Method m, Method mm)

public bool ShouldGenerateAnnotationAttribute => IsAnnotation;

public void StripNonBindables ()
public void StripNonBindables (CodeGenerationOptions opt)
{
// As of now, if we generate bindings for interface default methods, that means users will
// have to "implement" those methods because they are declared and you have to implement
// any declared methods in C#. That is going to be problematic a lot.
Methods = Methods.Where (m => !m.IsInterfaceDefaultMethod).ToList ();
// Strip out default interface methods if not desired
if (!opt.SupportDefaultInterfaceMethods)
Methods = Methods.Where (m => !m.IsInterfaceDefaultMethod).ToList ();

NestedTypes = NestedTypes.Where (n => !n.IsObfuscated && n.Visibility != "private").ToList ();

foreach (var n in NestedTypes)
n.StripNonBindables ();
n.StripNonBindables (opt);
}

static readonly HashSet<string> ThrowableRequiresNew = new HashSet<string> (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ public IEnumerable<Field> GetGeneratableFields (CodeGenerationOptions options)
return Fields.Where (f => !f.NeedsProperty && !(f.DeprecatedComment?.Contains ("constant will be removed") == true));
}

public bool HasDefaultMethods => GetAllMethods ().Any (m => m.IsInterfaceDefaultMethod);

public bool IsConstSugar {
get {
if (Methods.Count > 0 || Properties.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public Method (GenBase declaringType) : base (declaringType)
public bool IsAbstract { get; set; }
public bool IsFinal { get; set; }
public bool IsInterfaceDefaultMethod { get; set; }
public bool IsInterfaceDefaultMethodOverride { get; set; }
public Method OverriddenInterfaceMethod { get; set; }
public bool IsReturnEnumified { get; set; }
public bool IsStatic { get; set; }
public bool IsVirtual { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")]
public partial interface IMyInterface : IJavaObject {
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface2']"
[Register ("java/code/IMyInterface2", "", "java.code.IMyInterface2Invoker")]
public partial interface IMyInterface2 : java.code.IMyInterface {

// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='DoSomething' and count(parameter)=0]"
[global::Java.Interop.JavaInterfaceDefaultMethod]
[Register ("DoSomething", "()V", "GetDoSomethingHandler:java.code.IMyInterfaceInvoker, ")]
[Register ("DoSomething", "()V", "GetDoSomethingHandler:java.code.IMyInterface2Invoker, ")]
void DoSomething ();

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")]
public partial interface IMyInterface : IJavaObject, IJavaPeerable {
static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface));

static Delegate cb_DoSomething;
#pragma warning disable 0169
static Delegate GetDoSomethingHandler ()
{
if (cb_DoSomething == null)
cb_DoSomething = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_DoSomething);
return cb_DoSomething;
}

static void n_DoSomething (IntPtr jnienv, IntPtr native__this)
{
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
__this.DoSomething ();
}
#pragma warning restore 0169

// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='DoSomething' and count(parameter)=0]"
[Register ("DoSomething", "()V", "GetDoSomethingHandler")]
virtual unsafe void DoSomething ()
{
const string __id = "DoSomething.()V";
try {
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, (IJavaPeerable) this, null);
} finally {
}
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")]
public partial interface IMyInterface : IJavaObject, IJavaPeerable {
static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface));

static Delegate cb_get_Value;
#pragma warning disable 0169
static Delegate Getget_ValueHandler ()
{
if (cb_get_Value == null)
cb_get_Value = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_get_Value);
return cb_get_Value;
}

static int n_get_Value (IntPtr jnienv, IntPtr native__this)
{
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.Value;
}
#pragma warning restore 0169

static Delegate cb_set_Value_I;
#pragma warning disable 0169
static Delegate Getset_Value_IHandler ()
{
if (cb_set_Value_I == null)
cb_set_Value_I = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, int>) n_set_Value_I);
return cb_set_Value_I;
}

static void n_set_Value_I (IntPtr jnienv, IntPtr native__this, int value)
{
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
__this.Value = value;
}
#pragma warning restore 0169

virtual unsafe int Value {
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='get_Value' and count(parameter)=0]"
[Register ("get_Value", "()I", "Getget_ValueHandler")]
get {
const string __id = "get_Value.()I";
try {
var __rm = _members.InstanceMethods.InvokeNonvirtualInt32Method (__id, (IJavaPeerable) this, null);
return __rm;
} finally {
}
}
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='set_Value' and count(parameter)=1 and parameter[1][@type='int']]"
[Register ("set_Value", "(I)V", "Getset_Value_IHandler")]
set {
const string __id = "set_Value.(I)V";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (value);
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, (IJavaPeerable) this, __args);
} finally {
}
}
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")]
public partial interface IMyInterface : IJavaObject, IJavaPeerable {
static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface));

static Delegate cb_get_Value;
#pragma warning disable 0169
static Delegate Getget_ValueHandler ()
{
if (cb_get_Value == null)
cb_get_Value = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_get_Value);
return cb_get_Value;
}

static int n_get_Value (IntPtr jnienv, IntPtr native__this)
{
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.Value;
}
#pragma warning restore 0169

virtual unsafe int Value {
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='get_Value' and count(parameter)=0]"
[Register ("get_Value", "()I", "Getget_ValueHandler")]
get {
const string __id = "get_Value.()I";
try {
var __rm = _members.InstanceMethods.InvokeNonvirtualInt32Method (__id, (IJavaPeerable) this, null);
return __rm;
} finally {
}
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")]
public partial interface IMyInterface : IJavaObject, IJavaPeerable {
static new readonly JniPeerMembers _members = new XAPeerMembers ("java/code/IMyInterface", typeof (IMyInterface));

static Delegate cb_DoSomething;
#pragma warning disable 0169
static Delegate GetDoSomethingHandler ()
{
if (cb_DoSomething == null)
cb_DoSomething = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_DoSomething);
return cb_DoSomething;
}

static void n_DoSomething (IntPtr jnienv, IntPtr native__this)
{
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
__this.DoSomething ();
}
#pragma warning restore 0169

// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='DoSomething' and count(parameter)=0]"
[Register ("DoSomething", "()V", "GetDoSomethingHandler")]
virtual unsafe void DoSomething ()
{
const string __id = "DoSomething.()V";
try {
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, (IJavaPeerable) this, null);
} finally {
}
}

}

Loading